rustc_mir_transform/
shim.rs

1use std::assert_matches::assert_matches;
2use std::{fmt, iter};
3
4use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx, VariantIdx};
5use rustc_hir as hir;
6use rustc_hir::def_id::DefId;
7use rustc_hir::lang_items::LangItem;
8use rustc_index::{Idx, IndexVec};
9use rustc_middle::mir::*;
10use rustc_middle::query::Providers;
11use rustc_middle::ty::{
12    self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, GenericArgs, Ty, TyCtxt,
13};
14use rustc_middle::{bug, span_bug};
15use rustc_span::source_map::Spanned;
16use rustc_span::{DUMMY_SP, Span};
17use tracing::{debug, instrument};
18
19use crate::elaborate_drop::{DropElaborator, DropFlagMode, DropStyle, Unwind, elaborate_drop};
20use crate::patch::MirPatch;
21use crate::{
22    abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, inline,
23    instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify,
24};
25
26mod async_destructor_ctor;
27
28pub(super) fn provide(providers: &mut Providers) {
29    providers.mir_shims = make_shim;
30}
31
32fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body<'tcx> {
33    debug!("make_shim({:?})", instance);
34
35    let mut result = match instance {
36        ty::InstanceKind::Item(..) => bug!("item {:?} passed to make_shim", instance),
37        ty::InstanceKind::VTableShim(def_id) => {
38            let adjustment = Adjustment::Deref { source: DerefSource::MutPtr };
39            build_call_shim(tcx, instance, Some(adjustment), CallKind::Direct(def_id))
40        }
41        ty::InstanceKind::FnPtrShim(def_id, ty) => {
42            let trait_ = tcx.trait_of_item(def_id).unwrap();
43            // Supports `Fn` or `async Fn` traits.
44            let adjustment = match tcx
45                .fn_trait_kind_from_def_id(trait_)
46                .or_else(|| tcx.async_fn_trait_kind_from_def_id(trait_))
47            {
48                Some(ty::ClosureKind::FnOnce) => Adjustment::Identity,
49                Some(ty::ClosureKind::Fn) => Adjustment::Deref { source: DerefSource::ImmRef },
50                Some(ty::ClosureKind::FnMut) => Adjustment::Deref { source: DerefSource::MutRef },
51                None => bug!("fn pointer {:?} is not an fn", ty),
52            };
53
54            build_call_shim(tcx, instance, Some(adjustment), CallKind::Indirect(ty))
55        }
56        // We are generating a call back to our def-id, which the
57        // codegen backend knows to turn to an actual call, be it
58        // a virtual call, or a direct call to a function for which
59        // indirect calls must be codegen'd differently than direct ones
60        // (such as `#[track_caller]`).
61        ty::InstanceKind::ReifyShim(def_id, _) => {
62            build_call_shim(tcx, instance, None, CallKind::Direct(def_id))
63        }
64        ty::InstanceKind::ClosureOnceShim { call_once: _, track_caller: _ } => {
65            let fn_mut = tcx.require_lang_item(LangItem::FnMut, None);
66            let call_mut = tcx
67                .associated_items(fn_mut)
68                .in_definition_order()
69                .find(|it| it.kind == ty::AssocKind::Fn)
70                .unwrap()
71                .def_id;
72
73            build_call_shim(tcx, instance, Some(Adjustment::RefMut), CallKind::Direct(call_mut))
74        }
75
76        ty::InstanceKind::ConstructCoroutineInClosureShim {
77            coroutine_closure_def_id,
78            receiver_by_ref,
79        } => build_construct_coroutine_by_move_shim(tcx, coroutine_closure_def_id, receiver_by_ref),
80
81        ty::InstanceKind::DropGlue(def_id, ty) => {
82            // FIXME(#91576): Drop shims for coroutines aren't subject to the MIR passes at the end
83            // of this function. Is this intentional?
84            if let Some(&ty::Coroutine(coroutine_def_id, args)) = ty.map(Ty::kind) {
85                let coroutine_body = tcx.optimized_mir(coroutine_def_id);
86
87                let ty::Coroutine(_, id_args) = *tcx.type_of(coroutine_def_id).skip_binder().kind()
88                else {
89                    bug!()
90                };
91
92                // If this is a regular coroutine, grab its drop shim. If this is a coroutine
93                // that comes from a coroutine-closure, and the kind ty differs from the "maximum"
94                // kind that it supports, then grab the appropriate drop shim. This ensures that
95                // the future returned by `<[coroutine-closure] as AsyncFnOnce>::call_once` will
96                // drop the coroutine-closure's upvars.
97                let body = if id_args.as_coroutine().kind_ty() == args.as_coroutine().kind_ty() {
98                    coroutine_body.coroutine_drop().unwrap()
99                } else {
100                    assert_eq!(
101                        args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap(),
102                        ty::ClosureKind::FnOnce
103                    );
104                    tcx.optimized_mir(tcx.coroutine_by_move_body_def_id(coroutine_def_id))
105                        .coroutine_drop()
106                        .unwrap()
107                };
108
109                let mut body = EarlyBinder::bind(body.clone()).instantiate(tcx, args);
110                debug!("make_shim({:?}) = {:?}", instance, body);
111
112                pm::run_passes(
113                    tcx,
114                    &mut body,
115                    &[
116                        &mentioned_items::MentionedItems,
117                        &abort_unwinding_calls::AbortUnwindingCalls,
118                        &add_call_guards::CriticalCallEdges,
119                    ],
120                    Some(MirPhase::Runtime(RuntimePhase::Optimized)),
121                    pm::Optimizations::Allowed,
122                );
123
124                return body;
125            }
126
127            build_drop_shim(tcx, def_id, ty)
128        }
129        ty::InstanceKind::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance),
130        ty::InstanceKind::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
131        ty::InstanceKind::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty),
132        ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) => {
133            async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty)
134        }
135        ty::InstanceKind::Virtual(..) => {
136            bug!("InstanceKind::Virtual ({:?}) is for direct calls only", instance)
137        }
138        ty::InstanceKind::Intrinsic(_) => {
139            bug!("creating shims from intrinsics ({:?}) is unsupported", instance)
140        }
141    };
142    debug!("make_shim({:?}) = untransformed {:?}", instance, result);
143
144    // We don't validate MIR here because the shims may generate code that's
145    // only valid in a `PostAnalysis` param-env. However, since we do initial
146    // validation with the MirBuilt phase, which uses a user-facing param-env.
147    // This causes validation errors when TAITs are involved.
148    pm::run_passes_no_validate(
149        tcx,
150        &mut result,
151        &[
152            &mentioned_items::MentionedItems,
153            &add_moves_for_packed_drops::AddMovesForPackedDrops,
154            &deref_separator::Derefer,
155            &remove_noop_landing_pads::RemoveNoopLandingPads,
156            &simplify::SimplifyCfg::MakeShim,
157            &instsimplify::InstSimplify::BeforeInline,
158            // Perform inlining of `#[rustc_force_inline]`-annotated callees.
159            &inline::ForceInline,
160            &abort_unwinding_calls::AbortUnwindingCalls,
161            &add_call_guards::CriticalCallEdges,
162        ],
163        Some(MirPhase::Runtime(RuntimePhase::Optimized)),
164    );
165
166    debug!("make_shim({:?}) = {:?}", instance, result);
167
168    result
169}
170
171#[derive(Copy, Clone, Debug, PartialEq)]
172enum DerefSource {
173    /// `fn shim(&self) { inner(*self )}`.
174    ImmRef,
175    /// `fn shim(&mut self) { inner(*self )}`.
176    MutRef,
177    /// `fn shim(*mut self) { inner(*self )}`.
178    MutPtr,
179}
180
181#[derive(Copy, Clone, Debug, PartialEq)]
182enum Adjustment {
183    /// Pass the receiver as-is.
184    Identity,
185
186    /// We get passed a reference or a raw pointer to `self` and call the target with `*self`.
187    ///
188    /// This either copies `self` (if `Self: Copy`, eg. for function items), or moves out of it
189    /// (for `VTableShim`, which effectively is passed `&own Self`).
190    Deref { source: DerefSource },
191
192    /// We get passed `self: Self` and call the target with `&mut self`.
193    ///
194    /// In this case we need to ensure that the `Self` is dropped after the call, as the callee
195    /// won't do it for us.
196    RefMut,
197}
198
199#[derive(Copy, Clone, Debug, PartialEq)]
200enum CallKind<'tcx> {
201    /// Call the `FnPtr` that was passed as the receiver.
202    Indirect(Ty<'tcx>),
203
204    /// Call a known `FnDef`.
205    Direct(DefId),
206}
207
208fn local_decls_for_sig<'tcx>(
209    sig: &ty::FnSig<'tcx>,
210    span: Span,
211) -> IndexVec<Local, LocalDecl<'tcx>> {
212    iter::once(LocalDecl::new(sig.output(), span))
213        .chain(sig.inputs().iter().map(|ity| LocalDecl::new(*ity, span).immutable()))
214        .collect()
215}
216
217fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) -> Body<'tcx> {
218    debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
219
220    assert!(!matches!(ty, Some(ty) if ty.is_coroutine()));
221
222    let args = if let Some(ty) = ty {
223        tcx.mk_args(&[ty.into()])
224    } else {
225        GenericArgs::identity_for_item(tcx, def_id)
226    };
227    let sig = tcx.fn_sig(def_id).instantiate(tcx, args);
228    let sig = tcx.instantiate_bound_regions_with_erased(sig);
229    let span = tcx.def_span(def_id);
230
231    let source_info = SourceInfo::outermost(span);
232
233    let return_block = BasicBlock::new(1);
234    let mut blocks = IndexVec::with_capacity(2);
235    let block = |blocks: &mut IndexVec<_, _>, kind| {
236        blocks.push(BasicBlockData {
237            statements: vec![],
238            terminator: Some(Terminator { source_info, kind }),
239            is_cleanup: false,
240        })
241    };
242    block(&mut blocks, TerminatorKind::Goto { target: return_block });
243    block(&mut blocks, TerminatorKind::Return);
244
245    let source = MirSource::from_instance(ty::InstanceKind::DropGlue(def_id, ty));
246    let mut body =
247        new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span);
248
249    // The first argument (index 0), but add 1 for the return value.
250    let mut dropee_ptr = Place::from(Local::new(1 + 0));
251    if tcx.sess.opts.unstable_opts.mir_emit_retag {
252        // We want to treat the function argument as if it was passed by `&mut`. As such, we
253        // generate
254        // ```
255        // temp = &mut *arg;
256        // Retag(temp, FnEntry)
257        // ```
258        // It's important that we do this first, before anything that depends on `dropee_ptr`
259        // has been put into the body.
260        let reborrow = Rvalue::Ref(
261            tcx.lifetimes.re_erased,
262            BorrowKind::Mut { kind: MutBorrowKind::Default },
263            tcx.mk_place_deref(dropee_ptr),
264        );
265        let ref_ty = reborrow.ty(body.local_decls(), tcx);
266        dropee_ptr = body.local_decls.push(LocalDecl::new(ref_ty, span)).into();
267        let new_statements = [
268            StatementKind::Assign(Box::new((dropee_ptr, reborrow))),
269            StatementKind::Retag(RetagKind::FnEntry, Box::new(dropee_ptr)),
270        ];
271        for s in new_statements {
272            body.basic_blocks_mut()[START_BLOCK]
273                .statements
274                .push(Statement { source_info, kind: s });
275        }
276    }
277
278    if ty.is_some() {
279        let patch = {
280            let typing_env = ty::TypingEnv::post_analysis(tcx, def_id);
281            let mut elaborator =
282                DropShimElaborator { body: &body, patch: MirPatch::new(&body), tcx, typing_env };
283            let dropee = tcx.mk_place_deref(dropee_ptr);
284            let resume_block = elaborator.patch.resume_block();
285            elaborate_drop(
286                &mut elaborator,
287                source_info,
288                dropee,
289                (),
290                return_block,
291                Unwind::To(resume_block),
292                START_BLOCK,
293            );
294            elaborator.patch
295        };
296        patch.apply(&mut body);
297    }
298
299    body
300}
301
302fn new_body<'tcx>(
303    source: MirSource<'tcx>,
304    basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
305    local_decls: IndexVec<Local, LocalDecl<'tcx>>,
306    arg_count: usize,
307    span: Span,
308) -> Body<'tcx> {
309    let mut body = Body::new(
310        source,
311        basic_blocks,
312        IndexVec::from_elem_n(
313            SourceScopeData {
314                span,
315                parent_scope: None,
316                inlined: None,
317                inlined_parent_scope: None,
318                local_data: ClearCrossCrate::Clear,
319            },
320            1,
321        ),
322        local_decls,
323        IndexVec::new(),
324        arg_count,
325        vec![],
326        span,
327        None,
328        // FIXME(compiler-errors): is this correct?
329        None,
330    );
331    // Shims do not directly mention any consts.
332    body.set_required_consts(Vec::new());
333    body
334}
335
336pub(super) struct DropShimElaborator<'a, 'tcx> {
337    pub body: &'a Body<'tcx>,
338    pub patch: MirPatch<'tcx>,
339    pub tcx: TyCtxt<'tcx>,
340    pub typing_env: ty::TypingEnv<'tcx>,
341}
342
343impl fmt::Debug for DropShimElaborator<'_, '_> {
344    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
345        Ok(())
346    }
347}
348
349impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> {
350    type Path = ();
351
352    fn patch_ref(&self) -> &MirPatch<'tcx> {
353        &self.patch
354    }
355    fn patch(&mut self) -> &mut MirPatch<'tcx> {
356        &mut self.patch
357    }
358    fn body(&self) -> &'a Body<'tcx> {
359        self.body
360    }
361    fn tcx(&self) -> TyCtxt<'tcx> {
362        self.tcx
363    }
364    fn typing_env(&self) -> ty::TypingEnv<'tcx> {
365        self.typing_env
366    }
367
368    fn drop_style(&self, _path: Self::Path, mode: DropFlagMode) -> DropStyle {
369        match mode {
370            DropFlagMode::Shallow => {
371                // Drops for the contained fields are "shallow" and "static" - they will simply call
372                // the field's own drop glue.
373                DropStyle::Static
374            }
375            DropFlagMode::Deep => {
376                // The top-level drop is "deep" and "open" - it will be elaborated to a drop ladder
377                // dropping each field contained in the value.
378                DropStyle::Open
379            }
380        }
381    }
382
383    fn get_drop_flag(&mut self, _path: Self::Path) -> Option<Operand<'tcx>> {
384        None
385    }
386
387    fn clear_drop_flag(&mut self, _location: Location, _path: Self::Path, _mode: DropFlagMode) {}
388
389    fn field_subpath(&self, _path: Self::Path, _field: FieldIdx) -> Option<Self::Path> {
390        None
391    }
392    fn deref_subpath(&self, _path: Self::Path) -> Option<Self::Path> {
393        None
394    }
395    fn downcast_subpath(&self, _path: Self::Path, _variant: VariantIdx) -> Option<Self::Path> {
396        Some(())
397    }
398    fn array_subpath(&self, _path: Self::Path, _index: u64, _size: u64) -> Option<Self::Path> {
399        None
400    }
401}
402
403fn build_thread_local_shim<'tcx>(
404    tcx: TyCtxt<'tcx>,
405    instance: ty::InstanceKind<'tcx>,
406) -> Body<'tcx> {
407    let def_id = instance.def_id();
408
409    let span = tcx.def_span(def_id);
410    let source_info = SourceInfo::outermost(span);
411
412    let blocks = IndexVec::from_raw(vec![BasicBlockData {
413        statements: vec![Statement {
414            source_info,
415            kind: StatementKind::Assign(Box::new((
416                Place::return_place(),
417                Rvalue::ThreadLocalRef(def_id),
418            ))),
419        }],
420        terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
421        is_cleanup: false,
422    }]);
423
424    new_body(
425        MirSource::from_instance(instance),
426        blocks,
427        IndexVec::from_raw(vec![LocalDecl::new(tcx.thread_local_ptr_ty(def_id), span)]),
428        0,
429        span,
430    )
431}
432
433/// Builds a `Clone::clone` shim for `self_ty`. Here, `def_id` is `Clone::clone`.
434fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> {
435    debug!("build_clone_shim(def_id={:?})", def_id);
436
437    let mut builder = CloneShimBuilder::new(tcx, def_id, self_ty);
438
439    let dest = Place::return_place();
440    let src = tcx.mk_place_deref(Place::from(Local::new(1 + 0)));
441
442    match self_ty.kind() {
443        ty::FnDef(..) | ty::FnPtr(..) => builder.copy_shim(),
444        ty::Closure(_, args) => builder.tuple_like_shim(dest, src, args.as_closure().upvar_tys()),
445        ty::CoroutineClosure(_, args) => {
446            builder.tuple_like_shim(dest, src, args.as_coroutine_closure().upvar_tys())
447        }
448        ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
449        ty::Coroutine(coroutine_def_id, args) => {
450            assert_eq!(tcx.coroutine_movability(*coroutine_def_id), hir::Movability::Movable);
451            builder.coroutine_shim(dest, src, *coroutine_def_id, args.as_coroutine())
452        }
453        _ => bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty),
454    };
455
456    builder.into_mir()
457}
458
459struct CloneShimBuilder<'tcx> {
460    tcx: TyCtxt<'tcx>,
461    def_id: DefId,
462    local_decls: IndexVec<Local, LocalDecl<'tcx>>,
463    blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
464    span: Span,
465    sig: ty::FnSig<'tcx>,
466}
467
468impl<'tcx> CloneShimBuilder<'tcx> {
469    fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Self {
470        // we must instantiate the self_ty because it's
471        // otherwise going to be TySelf and we can't index
472        // or access fields of a Place of type TySelf.
473        let sig = tcx.fn_sig(def_id).instantiate(tcx, &[self_ty.into()]);
474        let sig = tcx.instantiate_bound_regions_with_erased(sig);
475        let span = tcx.def_span(def_id);
476
477        CloneShimBuilder {
478            tcx,
479            def_id,
480            local_decls: local_decls_for_sig(&sig, span),
481            blocks: IndexVec::new(),
482            span,
483            sig,
484        }
485    }
486
487    fn into_mir(self) -> Body<'tcx> {
488        let source = MirSource::from_instance(ty::InstanceKind::CloneShim(
489            self.def_id,
490            self.sig.inputs_and_output[0],
491        ));
492        new_body(source, self.blocks, self.local_decls, self.sig.inputs().len(), self.span)
493    }
494
495    fn source_info(&self) -> SourceInfo {
496        SourceInfo::outermost(self.span)
497    }
498
499    fn block(
500        &mut self,
501        statements: Vec<Statement<'tcx>>,
502        kind: TerminatorKind<'tcx>,
503        is_cleanup: bool,
504    ) -> BasicBlock {
505        let source_info = self.source_info();
506        self.blocks.push(BasicBlockData {
507            statements,
508            terminator: Some(Terminator { source_info, kind }),
509            is_cleanup,
510        })
511    }
512
513    /// Gives the index of an upcoming BasicBlock, with an offset.
514    /// offset=0 will give you the index of the next BasicBlock,
515    /// offset=1 will give the index of the next-to-next block,
516    /// offset=-1 will give you the index of the last-created block
517    fn block_index_offset(&self, offset: usize) -> BasicBlock {
518        BasicBlock::new(self.blocks.len() + offset)
519    }
520
521    fn make_statement(&self, kind: StatementKind<'tcx>) -> Statement<'tcx> {
522        Statement { source_info: self.source_info(), kind }
523    }
524
525    fn copy_shim(&mut self) {
526        let rcvr = self.tcx.mk_place_deref(Place::from(Local::new(1 + 0)));
527        let ret_statement = self.make_statement(StatementKind::Assign(Box::new((
528            Place::return_place(),
529            Rvalue::Use(Operand::Copy(rcvr)),
530        ))));
531        self.block(vec![ret_statement], TerminatorKind::Return, false);
532    }
533
534    fn make_place(&mut self, mutability: Mutability, ty: Ty<'tcx>) -> Place<'tcx> {
535        let span = self.span;
536        let mut local = LocalDecl::new(ty, span);
537        if mutability.is_not() {
538            local = local.immutable();
539        }
540        Place::from(self.local_decls.push(local))
541    }
542
543    fn make_clone_call(
544        &mut self,
545        dest: Place<'tcx>,
546        src: Place<'tcx>,
547        ty: Ty<'tcx>,
548        next: BasicBlock,
549        cleanup: BasicBlock,
550    ) {
551        let tcx = self.tcx;
552
553        // `func == Clone::clone(&ty) -> ty`
554        let func_ty = Ty::new_fn_def(tcx, self.def_id, [ty]);
555        let func = Operand::Constant(Box::new(ConstOperand {
556            span: self.span,
557            user_ty: None,
558            const_: Const::zero_sized(func_ty),
559        }));
560
561        let ref_loc =
562            self.make_place(Mutability::Not, Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty));
563
564        // `let ref_loc: &ty = &src;`
565        let statement = self.make_statement(StatementKind::Assign(Box::new((
566            ref_loc,
567            Rvalue::Ref(tcx.lifetimes.re_erased, BorrowKind::Shared, src),
568        ))));
569
570        // `let loc = Clone::clone(ref_loc);`
571        self.block(
572            vec![statement],
573            TerminatorKind::Call {
574                func,
575                args: [Spanned { node: Operand::Move(ref_loc), span: DUMMY_SP }].into(),
576                destination: dest,
577                target: Some(next),
578                unwind: UnwindAction::Cleanup(cleanup),
579                call_source: CallSource::Normal,
580                fn_span: self.span,
581            },
582            false,
583        );
584    }
585
586    fn clone_fields<I>(
587        &mut self,
588        dest: Place<'tcx>,
589        src: Place<'tcx>,
590        target: BasicBlock,
591        mut unwind: BasicBlock,
592        tys: I,
593    ) -> BasicBlock
594    where
595        I: IntoIterator<Item = Ty<'tcx>>,
596    {
597        // For an iterator of length n, create 2*n + 1 blocks.
598        for (i, ity) in tys.into_iter().enumerate() {
599            // Each iteration creates two blocks, referred to here as block 2*i and block 2*i + 1.
600            //
601            // Block 2*i attempts to clone the field. If successful it branches to 2*i + 2 (the
602            // next clone block). If unsuccessful it branches to the previous unwind block, which
603            // is initially the `unwind` argument passed to this function.
604            //
605            // Block 2*i + 1 is the unwind block for this iteration. It drops the cloned value
606            // created by block 2*i. We store this block in `unwind` so that the next clone block
607            // will unwind to it if cloning fails.
608
609            let field = FieldIdx::new(i);
610            let src_field = self.tcx.mk_place_field(src, field, ity);
611
612            let dest_field = self.tcx.mk_place_field(dest, field, ity);
613
614            let next_unwind = self.block_index_offset(1);
615            let next_block = self.block_index_offset(2);
616            self.make_clone_call(dest_field, src_field, ity, next_block, unwind);
617            self.block(
618                vec![],
619                TerminatorKind::Drop {
620                    place: dest_field,
621                    target: unwind,
622                    unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
623                    replace: false,
624                },
625                /* is_cleanup */ true,
626            );
627            unwind = next_unwind;
628        }
629        // If all clones succeed then we end up here.
630        self.block(vec![], TerminatorKind::Goto { target }, false);
631        unwind
632    }
633
634    fn tuple_like_shim<I>(&mut self, dest: Place<'tcx>, src: Place<'tcx>, tys: I)
635    where
636        I: IntoIterator<Item = Ty<'tcx>>,
637    {
638        self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false);
639        let unwind = self.block(vec![], TerminatorKind::UnwindResume, true);
640        let target = self.block(vec![], TerminatorKind::Return, false);
641
642        let _final_cleanup_block = self.clone_fields(dest, src, target, unwind, tys);
643    }
644
645    fn coroutine_shim(
646        &mut self,
647        dest: Place<'tcx>,
648        src: Place<'tcx>,
649        coroutine_def_id: DefId,
650        args: CoroutineArgs<TyCtxt<'tcx>>,
651    ) {
652        self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false);
653        let unwind = self.block(vec![], TerminatorKind::UnwindResume, true);
654        // This will get overwritten with a switch once we know the target blocks
655        let switch = self.block(vec![], TerminatorKind::Unreachable, false);
656        let unwind = self.clone_fields(dest, src, switch, unwind, args.upvar_tys());
657        let target = self.block(vec![], TerminatorKind::Return, false);
658        let unreachable = self.block(vec![], TerminatorKind::Unreachable, false);
659        let mut cases = Vec::with_capacity(args.state_tys(coroutine_def_id, self.tcx).count());
660        for (index, state_tys) in args.state_tys(coroutine_def_id, self.tcx).enumerate() {
661            let variant_index = VariantIdx::new(index);
662            let dest = self.tcx.mk_place_downcast_unnamed(dest, variant_index);
663            let src = self.tcx.mk_place_downcast_unnamed(src, variant_index);
664            let clone_block = self.block_index_offset(1);
665            let start_block = self.block(
666                vec![self.make_statement(StatementKind::SetDiscriminant {
667                    place: Box::new(Place::return_place()),
668                    variant_index,
669                })],
670                TerminatorKind::Goto { target: clone_block },
671                false,
672            );
673            cases.push((index as u128, start_block));
674            let _final_cleanup_block = self.clone_fields(dest, src, target, unwind, state_tys);
675        }
676        let discr_ty = args.discr_ty(self.tcx);
677        let temp = self.make_place(Mutability::Mut, discr_ty);
678        let rvalue = Rvalue::Discriminant(src);
679        let statement = self.make_statement(StatementKind::Assign(Box::new((temp, rvalue))));
680        match &mut self.blocks[switch] {
681            BasicBlockData { statements, terminator: Some(Terminator { kind, .. }), .. } => {
682                statements.push(statement);
683                *kind = TerminatorKind::SwitchInt {
684                    discr: Operand::Move(temp),
685                    targets: SwitchTargets::new(cases.into_iter(), unreachable),
686                };
687            }
688            BasicBlockData { terminator: None, .. } => unreachable!(),
689        }
690    }
691}
692
693/// Builds a "call" shim for `instance`. The shim calls the function specified by `call_kind`,
694/// first adjusting its first argument according to `rcvr_adjustment`.
695#[instrument(level = "debug", skip(tcx), ret)]
696fn build_call_shim<'tcx>(
697    tcx: TyCtxt<'tcx>,
698    instance: ty::InstanceKind<'tcx>,
699    rcvr_adjustment: Option<Adjustment>,
700    call_kind: CallKind<'tcx>,
701) -> Body<'tcx> {
702    // `FnPtrShim` contains the fn pointer type that a call shim is being built for - this is used
703    // to instantiate into the signature of the shim. It is not necessary for users of this
704    // MIR body to perform further instantiations (see `InstanceKind::has_polymorphic_mir_body`).
705    let (sig_args, untuple_args) = if let ty::InstanceKind::FnPtrShim(_, ty) = instance {
706        let sig = tcx.instantiate_bound_regions_with_erased(ty.fn_sig(tcx));
707
708        let untuple_args = sig.inputs();
709
710        // Create substitutions for the `Self` and `Args` generic parameters of the shim body.
711        let arg_tup = Ty::new_tup(tcx, untuple_args);
712
713        (Some([ty.into(), arg_tup.into()]), Some(untuple_args))
714    } else {
715        (None, None)
716    };
717
718    let def_id = instance.def_id();
719
720    let sig = tcx.fn_sig(def_id);
721    let sig = sig.map_bound(|sig| tcx.instantiate_bound_regions_with_erased(sig));
722
723    assert_eq!(sig_args.is_some(), !instance.has_polymorphic_mir_body());
724    let mut sig = if let Some(sig_args) = sig_args {
725        sig.instantiate(tcx, &sig_args)
726    } else {
727        sig.instantiate_identity()
728    };
729
730    if let CallKind::Indirect(fnty) = call_kind {
731        // `sig` determines our local decls, and thus the callee type in the `Call` terminator. This
732        // can only be an `FnDef` or `FnPtr`, but currently will be `Self` since the types come from
733        // the implemented `FnX` trait.
734
735        // Apply the opposite adjustment to the MIR input.
736        let mut inputs_and_output = sig.inputs_and_output.to_vec();
737
738        // Initial signature is `fn(&? Self, Args) -> Self::Output` where `Args` is a tuple of the
739        // fn arguments. `Self` may be passed via (im)mutable reference or by-value.
740        assert_eq!(inputs_and_output.len(), 3);
741
742        // `Self` is always the original fn type `ty`. The MIR call terminator is only defined for
743        // `FnDef` and `FnPtr` callees, not the `Self` type param.
744        let self_arg = &mut inputs_and_output[0];
745        *self_arg = match rcvr_adjustment.unwrap() {
746            Adjustment::Identity => fnty,
747            Adjustment::Deref { source } => match source {
748                DerefSource::ImmRef => Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fnty),
749                DerefSource::MutRef => Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, fnty),
750                DerefSource::MutPtr => Ty::new_mut_ptr(tcx, fnty),
751            },
752            Adjustment::RefMut => bug!("`RefMut` is never used with indirect calls: {instance:?}"),
753        };
754        sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
755    }
756
757    // FIXME: Avoid having to adjust the signature both here and in
758    // `fn_sig_for_fn_abi`.
759    if let ty::InstanceKind::VTableShim(..) = instance {
760        // Modify fn(self, ...) to fn(self: *mut Self, ...)
761        let mut inputs_and_output = sig.inputs_and_output.to_vec();
762        let self_arg = &mut inputs_and_output[0];
763        debug_assert!(tcx.generics_of(def_id).has_self && *self_arg == tcx.types.self_param);
764        *self_arg = Ty::new_mut_ptr(tcx, *self_arg);
765        sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
766    }
767
768    let span = tcx.def_span(def_id);
769
770    debug!(?sig);
771
772    let mut local_decls = local_decls_for_sig(&sig, span);
773    let source_info = SourceInfo::outermost(span);
774
775    let destination = Place::return_place();
776
777    let rcvr_place = || {
778        assert!(rcvr_adjustment.is_some());
779        Place::from(Local::new(1))
780    };
781    let mut statements = vec![];
782
783    let rcvr = rcvr_adjustment.map(|rcvr_adjustment| match rcvr_adjustment {
784        Adjustment::Identity => Operand::Move(rcvr_place()),
785        Adjustment::Deref { source: _ } => Operand::Move(tcx.mk_place_deref(rcvr_place())),
786        Adjustment::RefMut => {
787            // let rcvr = &mut rcvr;
788            let ref_rcvr = local_decls.push(
789                LocalDecl::new(
790                    Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, sig.inputs()[0]),
791                    span,
792                )
793                .immutable(),
794            );
795            let borrow_kind = BorrowKind::Mut { kind: MutBorrowKind::Default };
796            statements.push(Statement {
797                source_info,
798                kind: StatementKind::Assign(Box::new((
799                    Place::from(ref_rcvr),
800                    Rvalue::Ref(tcx.lifetimes.re_erased, borrow_kind, rcvr_place()),
801                ))),
802            });
803            Operand::Move(Place::from(ref_rcvr))
804        }
805    });
806
807    let (callee, mut args) = match call_kind {
808        // `FnPtr` call has no receiver. Args are untupled below.
809        CallKind::Indirect(_) => (rcvr.unwrap(), vec![]),
810
811        // `FnDef` call with optional receiver.
812        CallKind::Direct(def_id) => {
813            let ty = tcx.type_of(def_id).instantiate_identity();
814            (
815                Operand::Constant(Box::new(ConstOperand {
816                    span,
817                    user_ty: None,
818                    const_: Const::zero_sized(ty),
819                })),
820                rcvr.into_iter().collect::<Vec<_>>(),
821            )
822        }
823    };
824
825    let mut arg_range = 0..sig.inputs().len();
826
827    // Take the `self` ("receiver") argument out of the range (it's adjusted above).
828    if rcvr_adjustment.is_some() {
829        arg_range.start += 1;
830    }
831
832    // Take the last argument, if we need to untuple it (handled below).
833    if untuple_args.is_some() {
834        arg_range.end -= 1;
835    }
836
837    // Pass all of the non-special arguments directly.
838    args.extend(arg_range.map(|i| Operand::Move(Place::from(Local::new(1 + i)))));
839
840    // Untuple the last argument, if we have to.
841    if let Some(untuple_args) = untuple_args {
842        let tuple_arg = Local::new(1 + (sig.inputs().len() - 1));
843        args.extend(untuple_args.iter().enumerate().map(|(i, ity)| {
844            Operand::Move(tcx.mk_place_field(Place::from(tuple_arg), FieldIdx::new(i), *ity))
845        }));
846    }
847
848    let n_blocks = if let Some(Adjustment::RefMut) = rcvr_adjustment { 5 } else { 2 };
849    let mut blocks = IndexVec::with_capacity(n_blocks);
850    let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| {
851        blocks.push(BasicBlockData {
852            statements,
853            terminator: Some(Terminator { source_info, kind }),
854            is_cleanup,
855        })
856    };
857
858    // BB #0
859    let args = args.into_iter().map(|a| Spanned { node: a, span: DUMMY_SP }).collect();
860    block(
861        &mut blocks,
862        statements,
863        TerminatorKind::Call {
864            func: callee,
865            args,
866            destination,
867            target: Some(BasicBlock::new(1)),
868            unwind: if let Some(Adjustment::RefMut) = rcvr_adjustment {
869                UnwindAction::Cleanup(BasicBlock::new(3))
870            } else {
871                UnwindAction::Continue
872            },
873            call_source: CallSource::Misc,
874            fn_span: span,
875        },
876        false,
877    );
878
879    if let Some(Adjustment::RefMut) = rcvr_adjustment {
880        // BB #1 - drop for Self
881        block(
882            &mut blocks,
883            vec![],
884            TerminatorKind::Drop {
885                place: rcvr_place(),
886                target: BasicBlock::new(2),
887                unwind: UnwindAction::Continue,
888                replace: false,
889            },
890            false,
891        );
892    }
893    // BB #1/#2 - return
894    let stmts = vec![];
895    block(&mut blocks, stmts, TerminatorKind::Return, false);
896    if let Some(Adjustment::RefMut) = rcvr_adjustment {
897        // BB #3 - drop if closure panics
898        block(
899            &mut blocks,
900            vec![],
901            TerminatorKind::Drop {
902                place: rcvr_place(),
903                target: BasicBlock::new(4),
904                unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
905                replace: false,
906            },
907            /* is_cleanup */ true,
908        );
909
910        // BB #4 - resume
911        block(&mut blocks, vec![], TerminatorKind::UnwindResume, true);
912    }
913
914    let mut body =
915        new_body(MirSource::from_instance(instance), blocks, local_decls, sig.inputs().len(), span);
916
917    if let ExternAbi::RustCall = sig.abi {
918        body.spread_arg = Some(Local::new(sig.inputs().len()));
919    }
920
921    body
922}
923
924pub(super) fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> {
925    debug_assert!(tcx.is_constructor(ctor_id));
926
927    let typing_env = ty::TypingEnv::post_analysis(tcx, ctor_id);
928
929    // Normalize the sig.
930    let sig = tcx
931        .fn_sig(ctor_id)
932        .instantiate_identity()
933        .no_bound_vars()
934        .expect("LBR in ADT constructor signature");
935    let sig = tcx.normalize_erasing_regions(typing_env, sig);
936
937    let ty::Adt(adt_def, args) = sig.output().kind() else {
938        bug!("unexpected type for ADT ctor {:?}", sig.output());
939    };
940
941    debug!("build_ctor: ctor_id={:?} sig={:?}", ctor_id, sig);
942
943    let span = tcx.def_span(ctor_id);
944
945    let local_decls = local_decls_for_sig(&sig, span);
946
947    let source_info = SourceInfo::outermost(span);
948
949    let variant_index =
950        if adt_def.is_enum() { adt_def.variant_index_with_ctor_id(ctor_id) } else { FIRST_VARIANT };
951
952    // Generate the following MIR:
953    //
954    // (return as Variant).field0 = arg0;
955    // (return as Variant).field1 = arg1;
956    //
957    // return;
958    debug!("build_ctor: variant_index={:?}", variant_index);
959
960    let kind = AggregateKind::Adt(adt_def.did(), variant_index, args, None, None);
961    let variant = adt_def.variant(variant_index);
962    let statement = Statement {
963        kind: StatementKind::Assign(Box::new((
964            Place::return_place(),
965            Rvalue::Aggregate(
966                Box::new(kind),
967                (0..variant.fields.len())
968                    .map(|idx| Operand::Move(Place::from(Local::new(idx + 1))))
969                    .collect(),
970            ),
971        ))),
972        source_info,
973    };
974
975    let start_block = BasicBlockData {
976        statements: vec![statement],
977        terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
978        is_cleanup: false,
979    };
980
981    let source = MirSource::item(ctor_id);
982    let mut body = new_body(
983        source,
984        IndexVec::from_elem_n(start_block, 1),
985        local_decls,
986        sig.inputs().len(),
987        span,
988    );
989    // A constructor doesn't mention any other items (and we don't run the usual optimization passes
990    // so this would otherwise not get filled).
991    body.set_mentioned_items(Vec::new());
992
993    crate::pass_manager::dump_mir_for_phase_change(tcx, &body);
994
995    body
996}
997
998/// ```ignore (pseudo-impl)
999/// impl FnPtr for fn(u32) {
1000///     fn addr(self) -> usize {
1001///         self as usize
1002///     }
1003/// }
1004/// ```
1005fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> {
1006    assert_matches!(self_ty.kind(), ty::FnPtr(..), "expected fn ptr, found {self_ty}");
1007    let span = tcx.def_span(def_id);
1008    let Some(sig) = tcx.fn_sig(def_id).instantiate(tcx, &[self_ty.into()]).no_bound_vars() else {
1009        span_bug!(span, "FnPtr::addr with bound vars for `{self_ty}`");
1010    };
1011    let locals = local_decls_for_sig(&sig, span);
1012
1013    let source_info = SourceInfo::outermost(span);
1014    // FIXME: use `expose_provenance` once we figure out whether function pointers have meaningful
1015    // provenance.
1016    let rvalue = Rvalue::Cast(
1017        CastKind::FnPtrToPtr,
1018        Operand::Move(Place::from(Local::new(1))),
1019        Ty::new_imm_ptr(tcx, tcx.types.unit),
1020    );
1021    let stmt = Statement {
1022        source_info,
1023        kind: StatementKind::Assign(Box::new((Place::return_place(), rvalue))),
1024    };
1025    let statements = vec![stmt];
1026    let start_block = BasicBlockData {
1027        statements,
1028        terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
1029        is_cleanup: false,
1030    };
1031    let source = MirSource::from_instance(ty::InstanceKind::FnPtrAddrShim(def_id, self_ty));
1032    new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span)
1033}
1034
1035fn build_construct_coroutine_by_move_shim<'tcx>(
1036    tcx: TyCtxt<'tcx>,
1037    coroutine_closure_def_id: DefId,
1038    receiver_by_ref: bool,
1039) -> Body<'tcx> {
1040    let mut self_ty = tcx.type_of(coroutine_closure_def_id).instantiate_identity();
1041    let mut self_local: Place<'tcx> = Local::from_usize(1).into();
1042    let ty::CoroutineClosure(_, args) = *self_ty.kind() else {
1043        bug!();
1044    };
1045
1046    // We use `&Self` here because we only need to emit an ABI-compatible shim body,
1047    // rather than match the signature exactly (which might take `&mut self` instead).
1048    //
1049    // We adjust the `self_local` to be a deref since we want to copy fields out of
1050    // a reference to the closure.
1051    if receiver_by_ref {
1052        self_local = tcx.mk_place_deref(self_local);
1053        self_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, self_ty);
1054    }
1055
1056    let poly_sig = args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
1057        tcx.mk_fn_sig(
1058            [self_ty].into_iter().chain(sig.tupled_inputs_ty.tuple_fields()),
1059            sig.to_coroutine_given_kind_and_upvars(
1060                tcx,
1061                args.as_coroutine_closure().parent_args(),
1062                tcx.coroutine_for_closure(coroutine_closure_def_id),
1063                ty::ClosureKind::FnOnce,
1064                tcx.lifetimes.re_erased,
1065                args.as_coroutine_closure().tupled_upvars_ty(),
1066                args.as_coroutine_closure().coroutine_captures_by_ref_ty(),
1067            ),
1068            sig.c_variadic,
1069            sig.safety,
1070            sig.abi,
1071        )
1072    });
1073    let sig = tcx.liberate_late_bound_regions(coroutine_closure_def_id, poly_sig);
1074    let ty::Coroutine(coroutine_def_id, coroutine_args) = *sig.output().kind() else {
1075        bug!();
1076    };
1077
1078    let span = tcx.def_span(coroutine_closure_def_id);
1079    let locals = local_decls_for_sig(&sig, span);
1080
1081    let mut fields = vec![];
1082
1083    // Move all of the closure args.
1084    for idx in 1..sig.inputs().len() {
1085        fields.push(Operand::Move(Local::from_usize(idx + 1).into()));
1086    }
1087
1088    for (idx, ty) in args.as_coroutine_closure().upvar_tys().iter().enumerate() {
1089        if receiver_by_ref {
1090            // The only situation where it's possible is when we capture immuatable references,
1091            // since those don't need to be reborrowed with the closure's env lifetime. Since
1092            // references are always `Copy`, just emit a copy.
1093            if !matches!(ty.kind(), ty::Ref(_, _, hir::Mutability::Not)) {
1094                // This copy is only sound if it's a `&T`. This may be
1095                // reachable e.g. when eagerly computing the `Fn` instance
1096                // of an async closure that doesn't borrowck.
1097                tcx.dcx().delayed_bug(format!(
1098                    "field should be captured by immutable ref if we have \
1099                    an `Fn` instance, but it was: {ty}"
1100                ));
1101            }
1102            fields.push(Operand::Copy(tcx.mk_place_field(
1103                self_local,
1104                FieldIdx::from_usize(idx),
1105                ty,
1106            )));
1107        } else {
1108            fields.push(Operand::Move(tcx.mk_place_field(
1109                self_local,
1110                FieldIdx::from_usize(idx),
1111                ty,
1112            )));
1113        }
1114    }
1115
1116    let source_info = SourceInfo::outermost(span);
1117    let rvalue = Rvalue::Aggregate(
1118        Box::new(AggregateKind::Coroutine(coroutine_def_id, coroutine_args)),
1119        IndexVec::from_raw(fields),
1120    );
1121    let stmt = Statement {
1122        source_info,
1123        kind: StatementKind::Assign(Box::new((Place::return_place(), rvalue))),
1124    };
1125    let statements = vec![stmt];
1126    let start_block = BasicBlockData {
1127        statements,
1128        terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
1129        is_cleanup: false,
1130    };
1131
1132    let source = MirSource::from_instance(ty::InstanceKind::ConstructCoroutineInClosureShim {
1133        coroutine_closure_def_id,
1134        receiver_by_ref,
1135    });
1136
1137    let body =
1138        new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span);
1139    dump_mir(
1140        tcx,
1141        false,
1142        if receiver_by_ref { "coroutine_closure_by_ref" } else { "coroutine_closure_by_move" },
1143        &0,
1144        &body,
1145        |_, _| Ok(()),
1146    );
1147
1148    body
1149}