Skip to main content

rustc_mir_transform/shim/
async_destructor_ctor.rs

1use rustc_hir::def_id::DefId;
2use rustc_hir::lang_items::LangItem;
3use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource};
4use rustc_index::{Idx, IndexVec};
5use rustc_middle::mir::{
6    BasicBlock, BasicBlockData, Body, Local, LocalDecl, MirSource, Operand, Place, Rvalue,
7    SourceInfo, Statement, StatementKind, Terminator, TerminatorKind,
8};
9use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitableExt};
10
11use super::*;
12use crate::deref_separator::deref_finder;
13use crate::patch::MirPatch;
14
15const SELF_ARG: Local = Local::arg(0);
16
17pub(super) fn build_async_destructor_ctor_shim<'tcx>(
18    tcx: TyCtxt<'tcx>,
19    def_id: DefId,
20    ty: Ty<'tcx>,
21) -> Body<'tcx> {
22    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs:22",
                        "rustc_mir_transform::shim::async_destructor_ctor",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs"),
                        ::tracing_core::__macro_support::Option::Some(22u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_mir_transform::shim::async_destructor_ctor"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("build_async_destructor_ctor_shim(def_id={0:?}, ty={1:?})",
                                                    def_id, ty) as &dyn Value))])
            });
    } else { ; }
};debug!("build_async_destructor_ctor_shim(def_id={:?}, ty={:?})", def_id, ty);
23    if true {
    {
        match (&Some(def_id), &tcx.lang_items().async_drop_in_place_fn()) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(Some(def_id), tcx.lang_items().async_drop_in_place_fn());
24    let generic_body = tcx.optimized_mir(def_id);
25    let args = tcx.mk_args(&[ty.into()]);
26    let mut body =
27        EarlyBinder::bind(tcx, generic_body.clone()).instantiate(tcx, args).skip_norm_wip();
28
29    // Minimal shim passes except MentionedItems,
30    // it causes error "mentioned_items for DefId(...async_drop_in_place...) have already been set
31    pm::run_passes(
32        tcx,
33        &mut body,
34        &[
35            &simplify::SimplifyCfg::MakeShim,
36            &abort_unwinding_calls::AbortUnwindingCalls,
37            &add_call_guards::CriticalCallEdges,
38        ],
39        None,
40        pm::Optimizations::Allowed,
41    );
42    body
43}
44
45// build_drop_shim analog for async drop glue (for generated coroutine poll function)
46x;#[tracing::instrument(level = "trace", skip(tcx), ret)]
47pub(super) fn build_async_drop_shim<'tcx>(
48    tcx: TyCtxt<'tcx>,
49    def_id: DefId,
50    ty: Ty<'tcx>,
51) -> Body<'tcx> {
52    let ty::Coroutine(_, parent_args) = ty.kind() else {
53        bug!();
54    };
55    let typing_env = ty::TypingEnv::fully_monomorphized();
56
57    let drop_ty = parent_args.first().unwrap().expect_ty();
58    let drop_ptr_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, drop_ty);
59
60    assert!(tcx.is_coroutine(def_id));
61    let coroutine_kind = tcx.coroutine_kind(def_id).unwrap();
62
63    assert!(matches!(
64        coroutine_kind,
65        CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Fn)
66    ));
67
68    let needs_async_drop = drop_ty.needs_async_drop(tcx, typing_env);
69    let needs_sync_drop = !needs_async_drop && drop_ty.needs_drop(tcx, typing_env);
70
71    let resume_adt = tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, DUMMY_SP));
72    let resume_ty = Ty::new_adt(tcx, resume_adt, ty::List::empty());
73
74    let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig_safe_rust_abi([ty, resume_ty], tcx.types.unit));
75    let sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
76
77    assert!(!drop_ty.is_coroutine());
78    let span = tcx.def_span(def_id);
79    let source_info = SourceInfo::outermost(span);
80
81    // The first argument (index 0) which will be local 1 (after the return value).
82    let coroutine_layout = Place::from(SELF_ARG);
83    let coroutine_layout_dropee =
84        tcx.mk_place_field(coroutine_layout, FieldIdx::new(0), drop_ptr_ty);
85
86    let return_block = BasicBlock::new(1);
87    let mut blocks = IndexVec::with_capacity(2);
88    let block = |blocks: &mut IndexVec<_, _>, kind| {
89        blocks.push(BasicBlockData::new(
90            Some(Terminator { source_info, kind, attributes: ThinVec::new() }),
91            false,
92        ))
93    };
94    block(
95        &mut blocks,
96        if needs_sync_drop {
97            TerminatorKind::Drop {
98                place: tcx.mk_place_deref(coroutine_layout_dropee),
99                target: return_block,
100                unwind: UnwindAction::Continue,
101                replace: false,
102                drop: None,
103            }
104        } else {
105            TerminatorKind::Goto { target: return_block }
106        },
107    );
108    block(&mut blocks, TerminatorKind::Return);
109
110    let source = MirSource::from_shim(ty::ShimKind::AsyncDropGlue(def_id, ty));
111    let mut body =
112        new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span);
113
114    body.coroutine = Some(Box::new(CoroutineInfo::initial(
115        coroutine_kind,
116        parent_args.as_coroutine().yield_ty(),
117        parent_args.as_coroutine().resume_ty(),
118    )));
119    body.phase = MirPhase::Runtime(RuntimePhase::Initial);
120
121    // Returning noop body for types without `need async drop`
122    // (or sync Drop in case of !`need async drop` && `need drop`).
123    // And also for error types.
124    if needs_async_drop && !drop_ty.references_error() {
125        let dropee_ptr = Place::from(body.local_decls.push(LocalDecl::new(drop_ptr_ty, span)));
126        let st_kind = StatementKind::Assign(Box::new((
127            dropee_ptr,
128            Rvalue::Use(Operand::Move(coroutine_layout_dropee), WithRetag::Yes),
129        )));
130        body.basic_blocks_mut()[START_BLOCK].statements.push(Statement::new(source_info, st_kind));
131
132        let dropline = body.basic_blocks.last_index();
133
134        let patch = {
135            let mut elaborator = DropShimElaborator {
136                body: &body,
137                patch: MirPatch::new(&body),
138                tcx,
139                typing_env,
140                produce_async_drops: true,
141            };
142            let dropee = tcx.mk_place_deref(dropee_ptr);
143            let resume_block = elaborator.patch.resume_block();
144            elaborate_drop(
145                &mut elaborator,
146                source_info,
147                dropee,
148                (),
149                return_block,
150                Unwind::To(resume_block),
151                START_BLOCK,
152                dropline,
153            );
154            elaborator.patch
155        };
156        patch.apply(&mut body);
157    }
158
159    // We did not bother respecting deref separation, do it here.
160    deref_finder(tcx, &mut body, false);
161
162    body
163}
164
165// * For async drop a "normal" coroutine:
166// `async_drop_in_place<T>::{closure}.poll()` is converted into `T.future_drop_poll()`.
167// Every coroutine has its `poll` (calculate yourself a little further)
168// and its `future_drop_poll` (drop yourself a little further).
169//
170// * For async drop of "async drop coroutine" (`async_drop_in_place<T>::{closure}`):
171// Correct drop of such coroutine means normal execution of nested async drop.
172// async_drop(async_drop(T))::future_drop_poll() => async_drop(T)::poll().
173pub(super) fn build_future_drop_poll_shim<'tcx>(
174    tcx: TyCtxt<'tcx>,
175    def_id: DefId,
176    proxy_ty: Ty<'tcx>,
177    impl_ty: Ty<'tcx>,
178) -> Body<'tcx> {
179    let shim = ty::ShimKind::FutureDropPoll(def_id, proxy_ty, impl_ty);
180    let ty::Coroutine(coroutine_def_id, _) = impl_ty.kind() else {
181        ::rustc_middle::util::bug::bug_fmt(format_args!("build_future_drop_poll_shim not for coroutine impl type: ({0:?})",
        shim));bug!("build_future_drop_poll_shim not for coroutine impl type: ({:?})", shim);
182    };
183
184    let span = tcx.def_span(def_id);
185
186    if tcx.is_async_drop_in_place_coroutine(*coroutine_def_id) {
187        build_adrop_for_adrop_shim(tcx, proxy_ty, impl_ty, span, shim)
188    } else {
189        build_adrop_for_coroutine_shim(tcx, proxy_ty, impl_ty, span, shim)
190    }
191}
192
193// For async drop a "normal" coroutine:
194// `async_drop_in_place<T>::{closure}.poll()` is converted into `T.future_drop_poll()`.
195// Every coroutine has its `poll` (calculate yourself a little further)
196// and its `future_drop_poll` (drop yourself a little further).
197fn build_adrop_for_coroutine_shim<'tcx>(
198    tcx: TyCtxt<'tcx>,
199    proxy_ty: Ty<'tcx>,
200    impl_ty: Ty<'tcx>,
201    span: Span,
202    shim: ty::ShimKind<'tcx>,
203) -> Body<'tcx> {
204    let ty::Coroutine(coroutine_def_id, impl_args) = impl_ty.kind() else {
205        ::rustc_middle::util::bug::bug_fmt(format_args!("build_adrop_for_coroutine_shim not for coroutine impl type: ({0:?})",
        shim));bug!("build_adrop_for_coroutine_shim not for coroutine impl type: ({:?})", shim);
206    };
207    let source_info = SourceInfo::outermost(span);
208    let body = tcx.optimized_mir(*coroutine_def_id).future_drop_poll().unwrap();
209    let mut body: Body<'tcx> =
210        EarlyBinder::bind(tcx, body.clone()).instantiate(tcx, impl_args).skip_norm_wip();
211    body.source.instance = ty::InstanceKind::Shim(shim);
212    body.phase = MirPhase::Runtime(RuntimePhase::Initial);
213    body.var_debug_info.clear();
214
215    // converting `(_1: Pin<&mut CorLayout>, _2: &mut Context<'_>) -> Poll<()>`
216    // into `(_1: Pin<&mut ProxyLayout>, _2: &mut Context<'_>) -> Poll<()>`
217    // let mut _x: &mut CorLayout = &mut *_1.0.0;
218    // Replace old _1.0 accesses into _x accesses;
219    let proxy_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, proxy_ty);
220
221    let pin_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Pin, span));
222    let args = tcx.mk_args(&[proxy_ref.into()]);
223    let pin_proxy_ref = Ty::new_adt(tcx, pin_adt_ref, args);
224
225    let cor_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, impl_ty);
226    let cor_ref_local = body.local_decls.push(LocalDecl::new(cor_ref, span));
227
228    FixProxyFutureDropVisitor { tcx, replace_to: cor_ref_local }.visit_body(&mut body);
229
230    // Now changing first arg from Pin<&mut ImplCoroutine> to Pin<&mut ProxyCoroutine>
231    body.local_decls[SELF_ARG] = LocalDecl::new(pin_proxy_ref, span);
232
233    // Build the projection to assign `cor_ref_local = _1.<projection>`.
234    let mut pin_proxy_to_cor_projection = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [PlaceElem::Field(FieldIdx::ZERO, proxy_ref)]))vec![
235        // _1.0 : Pin<&mut ProxyLayout> ==> &mut ProxyLayout
236        PlaceElem::Field(FieldIdx::ZERO, proxy_ref),
237    ];
238
239    // _cor_ref_tmp = (*(*_proxy).0).0...
240    proxy_ty.find_async_drop_impl_coroutine(tcx, |ty| {
241        if ty != proxy_ty {
242            let ty_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, ty);
243            pin_proxy_to_cor_projection.push(PlaceElem::Deref);
244            pin_proxy_to_cor_projection.push(PlaceElem::Field(FieldIdx::ZERO, ty_ref));
245        }
246    });
247
248    // _cor_ref = cor_ref_tmp
249    let projected_pin = Place::from(SELF_ARG).project_deeper(&pin_proxy_to_cor_projection, tcx);
250    body.basic_blocks_mut()[START_BLOCK].statements.insert(
251        0,
252        Statement::new(
253            source_info,
254            StatementKind::Assign(Box::new((
255                Place::from(cor_ref_local),
256                Rvalue::Use(Operand::Move(projected_pin), WithRetag::Yes),
257            ))),
258        ),
259    );
260
261    // We did not bother respecting deref separation, do it here.
262    deref_finder(tcx, &mut body, false);
263
264    return body;
265
266    /// Replace Pin<&mut ImplCoroutine> accesses (_1.0) into Pin<&mut ProxyCoroutine> accesses
267    struct FixProxyFutureDropVisitor<'tcx> {
268        tcx: TyCtxt<'tcx>,
269        replace_to: Local,
270    }
271
272    impl<'tcx> MutVisitor<'tcx> for FixProxyFutureDropVisitor<'tcx> {
273        fn tcx(&self) -> TyCtxt<'tcx> {
274            self.tcx
275        }
276
277        fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, _: Location) {
278            if place.local == SELF_ARG
279                && let Some((first, rest)) = place.projection.split_first()
280            {
281                if !#[allow(non_exhaustive_omitted_patterns)] match first {
            ProjectionElem::Field(FieldIdx::ZERO, _) => true,
            _ => false,
        } {
    ::core::panicking::panic("assertion failed: matches!(first, ProjectionElem::Field(FieldIdx::ZERO, _))")
};assert!(matches!(first, ProjectionElem::Field(FieldIdx::ZERO, _)));
282                *place = Place::from(self.replace_to).project_deeper(rest, self.tcx);
283            }
284        }
285    }
286}
287
288// When dropping async drop coroutine, we continue its execution.
289// async_drop(async_drop(T))::future_drop_poll() => async_drop(T)::poll()
290fn build_adrop_for_adrop_shim<'tcx>(
291    tcx: TyCtxt<'tcx>,
292    proxy_ty: Ty<'tcx>,
293    impl_ty: Ty<'tcx>,
294    span: Span,
295    shim: ty::ShimKind<'tcx>,
296) -> Body<'tcx> {
297    let source_info = SourceInfo::outermost(span);
298    let proxy_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, proxy_ty);
299    // taking _1.0 (impl from Pin)
300    let proxy_ref_place =
301        Place::from(SELF_ARG).project_deeper(&[PlaceElem::Field(FieldIdx::ZERO, proxy_ref)], tcx);
302    let cor_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, impl_ty);
303
304    // ret_ty = `Poll<()>`
305    let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, span));
306    let ret_ty = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()]));
307    // env_ty = `Pin<&mut proxy_ty>`
308    let pin_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Pin, span));
309    let env_ty = Ty::new_adt(tcx, pin_adt_ref, tcx.mk_args(&[proxy_ref.into()]));
310    // sig = `fn (Pin<&mut proxy_ty>, &mut Context) -> Poll<()>`
311    let sig = tcx.mk_fn_sig_safe_rust_abi([env_ty, Ty::new_task_context(tcx)], ret_ty);
312    // This function will be called with pinned proxy coroutine layout.
313    // We need to extract `Arg0.0` to get proxy layout, and then get `.0`
314    // further to receive impl coroutine (may be needed)
315    let mut locals = local_decls_for_sig(&sig, span);
316    let mut blocks = IndexVec::with_capacity(3);
317
318    let proxy_ref_local = locals.push(LocalDecl::new(proxy_ref, span));
319
320    let call_bb = BasicBlock::new(1);
321    let return_bb = BasicBlock::new(2);
322
323    let mut statements = Vec::new();
324
325    statements.push(Statement::new(
326        source_info,
327        StatementKind::Assign(Box::new((
328            Place::from(proxy_ref_local),
329            Rvalue::Use(Operand::Copy(proxy_ref_place), WithRetag::Yes),
330        ))),
331    ));
332
333    let mut cor_ptr_local = proxy_ref_local;
334    proxy_ty.find_async_drop_impl_coroutine(tcx, |ty| {
335        if ty != proxy_ty {
336            let ty_ptr = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, ty);
337            let impl_ptr_place = Place::from(cor_ptr_local)
338                .project_deeper(&[PlaceElem::Deref, PlaceElem::Field(FieldIdx::ZERO, ty_ptr)], tcx);
339            cor_ptr_local = locals.push(LocalDecl::new(ty_ptr, span));
340            // _cor_ptr = _proxy.0.0 (... .0)
341            statements.push(Statement::new(
342                source_info,
343                StatementKind::Assign(Box::new((
344                    Place::from(cor_ptr_local),
345                    Rvalue::Use(Operand::Copy(impl_ptr_place), WithRetag::Yes),
346                ))),
347            ));
348        }
349    });
350
351    // convert impl coroutine ptr into ref
352    let reborrow = Rvalue::Ref(
353        tcx.lifetimes.re_erased,
354        BorrowKind::Mut { kind: MutBorrowKind::Default },
355        tcx.mk_place_deref(Place::from(cor_ptr_local)),
356    );
357    let cor_ref_place = Place::from(locals.push(LocalDecl::new(cor_ref, span)));
358    statements.push(Statement::new(
359        source_info,
360        StatementKind::Assign(Box::new((cor_ref_place, reborrow))),
361    ));
362
363    // cor_pin_ty = `Pin<&mut cor_ref>`
364    let cor_pin_ty = Ty::new_adt(tcx, pin_adt_ref, tcx.mk_args(&[cor_ref.into()]));
365    let cor_pin_place = Place::from(locals.push(LocalDecl::new(cor_pin_ty, span)));
366
367    let pin_fn = tcx.require_lang_item(LangItem::PinNewUnchecked, span);
368    // call Pin<FutTy>::new_unchecked(&mut impl_cor)
369    blocks.push(BasicBlockData::new_stmts(
370        statements,
371        Some(Terminator {
372            source_info,
373            kind: TerminatorKind::Call {
374                func: Operand::function_handle(tcx, pin_fn, [cor_ref.into()], span),
375                args: [dummy_spanned(Operand::Move(cor_ref_place))].into(),
376                destination: cor_pin_place,
377                target: Some(call_bb),
378                unwind: UnwindAction::Continue,
379                call_source: CallSource::Misc,
380                fn_span: span,
381            },
382
383            attributes: ThinVec::new(),
384        }),
385        false,
386    ));
387    // When dropping async drop coroutine, we continue its execution:
388    // we call impl::poll (impl_layout, ctx)
389    let poll_fn = tcx.require_lang_item(LangItem::FuturePoll, span);
390    let resume_ctx = Place::from(Local::new(2));
391    blocks.push(BasicBlockData::new(
392        Some(Terminator {
393            source_info,
394            kind: TerminatorKind::Call {
395                func: Operand::function_handle(tcx, poll_fn, [impl_ty.into()], span),
396                args: [
397                    dummy_spanned(Operand::Move(cor_pin_place)),
398                    dummy_spanned(Operand::Move(resume_ctx)),
399                ]
400                .into(),
401                destination: Place::return_place(),
402                target: Some(return_bb),
403                unwind: UnwindAction::Continue,
404                call_source: CallSource::Misc,
405                fn_span: span,
406            },
407
408            attributes: ThinVec::new(),
409        }),
410        false,
411    ));
412    blocks.push(BasicBlockData::new(
413        Some(Terminator { source_info, kind: TerminatorKind::Return, attributes: ThinVec::new() }),
414        false,
415    ));
416
417    let source = MirSource::from_shim(shim);
418    let mut body = new_body(source, blocks, locals, sig.inputs().len(), span);
419    body.phase = MirPhase::Runtime(RuntimePhase::Initial);
420    return body;
421}