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::visit::{MutVisitor, PlaceContext};
10use rustc_middle::mir::*;
11use rustc_middle::query::Providers;
12use rustc_middle::ty::{
13 self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, GenericArgs, Ty, TyCtxt,
14};
15use rustc_middle::{bug, span_bug};
16use rustc_span::source_map::{Spanned, dummy_spanned};
17use rustc_span::{DUMMY_SP, Span};
18use tracing::{debug, instrument};
19
20use crate::elaborate_drop::{DropElaborator, DropFlagMode, DropStyle, Unwind, elaborate_drop};
21use crate::patch::MirPatch;
22use crate::{
23 abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, inline,
24 instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads,
25 run_optimization_passes, simplify,
26};
27
28mod async_destructor_ctor;
29
30pub(super) fn provide(providers: &mut Providers) {
31 providers.mir_shims = make_shim;
32}
33
34struct FixProxyFutureDropVisitor<'tcx> {
36 tcx: TyCtxt<'tcx>,
37 replace_to: Local,
38}
39
40impl<'tcx> MutVisitor<'tcx> for FixProxyFutureDropVisitor<'tcx> {
41 fn tcx(&self) -> TyCtxt<'tcx> {
42 self.tcx
43 }
44
45 fn visit_place(
46 &mut self,
47 place: &mut Place<'tcx>,
48 _context: PlaceContext,
49 _location: Location,
50 ) {
51 if place.local == Local::from_u32(1) {
52 if place.projection.len() == 1 {
53 assert!(matches!(
54 place.projection.first(),
55 Some(ProjectionElem::Field(FieldIdx::ZERO, _))
56 ));
57 *place = Place::from(self.replace_to);
58 } else if place.projection.len() == 2 {
59 assert!(matches!(place.projection[0], ProjectionElem::Field(FieldIdx::ZERO, _)));
60 assert!(matches!(place.projection[1], ProjectionElem::Deref));
61 *place =
62 Place::from(self.replace_to).project_deeper(&[ProjectionElem::Deref], self.tcx);
63 }
64 }
65 }
66}
67
68fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body<'tcx> {
69 debug!("make_shim({:?})", instance);
70
71 let mut result = match instance {
72 ty::InstanceKind::Item(..) => bug!("item {:?} passed to make_shim", instance),
73 ty::InstanceKind::VTableShim(def_id) => {
74 let adjustment = Adjustment::Deref { source: DerefSource::MutPtr };
75 build_call_shim(tcx, instance, Some(adjustment), CallKind::Direct(def_id))
76 }
77 ty::InstanceKind::FnPtrShim(def_id, ty) => {
78 let trait_ = tcx.trait_of_item(def_id).unwrap();
79 let adjustment = match tcx
81 .fn_trait_kind_from_def_id(trait_)
82 .or_else(|| tcx.async_fn_trait_kind_from_def_id(trait_))
83 {
84 Some(ty::ClosureKind::FnOnce) => Adjustment::Identity,
85 Some(ty::ClosureKind::Fn) => Adjustment::Deref { source: DerefSource::ImmRef },
86 Some(ty::ClosureKind::FnMut) => Adjustment::Deref { source: DerefSource::MutRef },
87 None => bug!("fn pointer {:?} is not an fn", ty),
88 };
89
90 build_call_shim(tcx, instance, Some(adjustment), CallKind::Indirect(ty))
91 }
92 ty::InstanceKind::ReifyShim(def_id, _) => {
98 build_call_shim(tcx, instance, None, CallKind::Direct(def_id))
99 }
100 ty::InstanceKind::ClosureOnceShim { call_once: _, track_caller: _ } => {
101 let fn_mut = tcx.require_lang_item(LangItem::FnMut, DUMMY_SP);
102 let call_mut = tcx
103 .associated_items(fn_mut)
104 .in_definition_order()
105 .find(|it| it.is_fn())
106 .unwrap()
107 .def_id;
108
109 build_call_shim(tcx, instance, Some(Adjustment::RefMut), CallKind::Direct(call_mut))
110 }
111
112 ty::InstanceKind::ConstructCoroutineInClosureShim {
113 coroutine_closure_def_id,
114 receiver_by_ref,
115 } => build_construct_coroutine_by_move_shim(tcx, coroutine_closure_def_id, receiver_by_ref),
116
117 ty::InstanceKind::DropGlue(def_id, ty) => {
118 if let Some(&ty::Coroutine(coroutine_def_id, args)) = ty.map(Ty::kind) {
121 let coroutine_body = tcx.optimized_mir(coroutine_def_id);
122
123 let ty::Coroutine(_, id_args) = *tcx.type_of(coroutine_def_id).skip_binder().kind()
124 else {
125 bug!()
126 };
127
128 let body = if id_args.as_coroutine().kind_ty() == args.as_coroutine().kind_ty() {
134 coroutine_body.coroutine_drop().unwrap()
135 } else {
136 assert_eq!(
137 args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap(),
138 ty::ClosureKind::FnOnce
139 );
140 tcx.optimized_mir(tcx.coroutine_by_move_body_def_id(coroutine_def_id))
141 .coroutine_drop()
142 .unwrap()
143 };
144
145 let mut body = EarlyBinder::bind(body.clone()).instantiate(tcx, args);
146 debug!("make_shim({:?}) = {:?}", instance, body);
147
148 pm::run_passes(
149 tcx,
150 &mut body,
151 &[
152 &mentioned_items::MentionedItems,
153 &abort_unwinding_calls::AbortUnwindingCalls,
154 &add_call_guards::CriticalCallEdges,
155 ],
156 Some(MirPhase::Runtime(RuntimePhase::Optimized)),
157 pm::Optimizations::Allowed,
158 );
159
160 return body;
161 }
162
163 build_drop_shim(tcx, def_id, ty)
164 }
165 ty::InstanceKind::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance),
166 ty::InstanceKind::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
167 ty::InstanceKind::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty),
168 ty::InstanceKind::FutureDropPollShim(def_id, proxy_ty, impl_ty) => {
169 let mut body =
170 async_destructor_ctor::build_future_drop_poll_shim(tcx, def_id, proxy_ty, impl_ty);
171
172 pm::run_passes(
173 tcx,
174 &mut body,
175 &[
176 &mentioned_items::MentionedItems,
177 &abort_unwinding_calls::AbortUnwindingCalls,
178 &add_call_guards::CriticalCallEdges,
179 ],
180 Some(MirPhase::Runtime(RuntimePhase::PostCleanup)),
181 pm::Optimizations::Allowed,
182 );
183 run_optimization_passes(tcx, &mut body);
184 debug!("make_shim({:?}) = {:?}", instance, body);
185 return body;
186 }
187 ty::InstanceKind::AsyncDropGlue(def_id, ty) => {
188 let mut body = async_destructor_ctor::build_async_drop_shim(tcx, def_id, ty);
189
190 pm::run_passes(
194 tcx,
195 &mut body,
196 &[
197 &mentioned_items::MentionedItems,
198 &abort_unwinding_calls::AbortUnwindingCalls,
199 &add_call_guards::CriticalCallEdges,
200 &simplify::SimplifyCfg::MakeShim,
201 &crate::coroutine::StateTransform,
202 ],
203 Some(MirPhase::Runtime(RuntimePhase::PostCleanup)),
204 pm::Optimizations::Allowed,
205 );
206 run_optimization_passes(tcx, &mut body);
207 debug!("make_shim({:?}) = {:?}", instance, body);
208 return body;
209 }
210
211 ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) => {
212 let body = async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty);
213 debug!("make_shim({:?}) = {:?}", instance, body);
214 return body;
215 }
216 ty::InstanceKind::Virtual(..) => {
217 bug!("InstanceKind::Virtual ({:?}) is for direct calls only", instance)
218 }
219 ty::InstanceKind::Intrinsic(_) => {
220 bug!("creating shims from intrinsics ({:?}) is unsupported", instance)
221 }
222 };
223 debug!("make_shim({:?}) = untransformed {:?}", instance, result);
224
225 pm::run_passes_no_validate(
230 tcx,
231 &mut result,
232 &[
233 &mentioned_items::MentionedItems,
234 &add_moves_for_packed_drops::AddMovesForPackedDrops,
235 &deref_separator::Derefer,
236 &remove_noop_landing_pads::RemoveNoopLandingPads,
237 &simplify::SimplifyCfg::MakeShim,
238 &instsimplify::InstSimplify::BeforeInline,
239 &inline::ForceInline,
241 &abort_unwinding_calls::AbortUnwindingCalls,
242 &add_call_guards::CriticalCallEdges,
243 ],
244 Some(MirPhase::Runtime(RuntimePhase::Optimized)),
245 );
246
247 debug!("make_shim({:?}) = {:?}", instance, result);
248
249 result
250}
251
252#[derive(Copy, Clone, Debug, PartialEq)]
253enum DerefSource {
254 ImmRef,
256 MutRef,
258 MutPtr,
260}
261
262#[derive(Copy, Clone, Debug, PartialEq)]
263enum Adjustment {
264 Identity,
266
267 Deref { source: DerefSource },
272
273 RefMut,
278}
279
280#[derive(Copy, Clone, Debug, PartialEq)]
281enum CallKind<'tcx> {
282 Indirect(Ty<'tcx>),
284
285 Direct(DefId),
287}
288
289fn local_decls_for_sig<'tcx>(
290 sig: &ty::FnSig<'tcx>,
291 span: Span,
292) -> IndexVec<Local, LocalDecl<'tcx>> {
293 iter::once(LocalDecl::new(sig.output(), span))
294 .chain(sig.inputs().iter().map(|ity| LocalDecl::new(*ity, span).immutable()))
295 .collect()
296}
297
298fn dropee_emit_retag<'tcx>(
299 tcx: TyCtxt<'tcx>,
300 body: &mut Body<'tcx>,
301 mut dropee_ptr: Place<'tcx>,
302 span: Span,
303) -> Place<'tcx> {
304 if tcx.sess.opts.unstable_opts.mir_emit_retag {
305 let source_info = SourceInfo::outermost(span);
306 let reborrow = Rvalue::Ref(
315 tcx.lifetimes.re_erased,
316 BorrowKind::Mut { kind: MutBorrowKind::Default },
317 tcx.mk_place_deref(dropee_ptr),
318 );
319 let ref_ty = reborrow.ty(body.local_decls(), tcx);
320 dropee_ptr = body.local_decls.push(LocalDecl::new(ref_ty, span)).into();
321 let new_statements = [
322 StatementKind::Assign(Box::new((dropee_ptr, reborrow))),
323 StatementKind::Retag(RetagKind::FnEntry, Box::new(dropee_ptr)),
324 ];
325 for s in new_statements {
326 body.basic_blocks_mut()[START_BLOCK].statements.push(Statement::new(source_info, s));
327 }
328 }
329 dropee_ptr
330}
331
332fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) -> Body<'tcx> {
333 debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
334
335 assert!(!matches!(ty, Some(ty) if ty.is_coroutine()));
336
337 let args = if let Some(ty) = ty {
338 tcx.mk_args(&[ty.into()])
339 } else {
340 GenericArgs::identity_for_item(tcx, def_id)
341 };
342 let sig = tcx.fn_sig(def_id).instantiate(tcx, args);
343 let sig = tcx.instantiate_bound_regions_with_erased(sig);
344 let span = tcx.def_span(def_id);
345
346 let source_info = SourceInfo::outermost(span);
347
348 let return_block = BasicBlock::new(1);
349 let mut blocks = IndexVec::with_capacity(2);
350 let block = |blocks: &mut IndexVec<_, _>, kind| {
351 blocks.push(BasicBlockData::new(Some(Terminator { source_info, kind }), false))
352 };
353 block(&mut blocks, TerminatorKind::Goto { target: return_block });
354 block(&mut blocks, TerminatorKind::Return);
355
356 let source = MirSource::from_instance(ty::InstanceKind::DropGlue(def_id, ty));
357 let mut body =
358 new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span);
359
360 let dropee_ptr = Place::from(Local::new(1 + 0));
362 let dropee_ptr = dropee_emit_retag(tcx, &mut body, dropee_ptr, span);
363
364 if ty.is_some() {
365 let patch = {
366 let typing_env = ty::TypingEnv::post_analysis(tcx, def_id);
367 let mut elaborator = DropShimElaborator {
368 body: &body,
369 patch: MirPatch::new(&body),
370 tcx,
371 typing_env,
372 produce_async_drops: false,
373 };
374 let dropee = tcx.mk_place_deref(dropee_ptr);
375 let resume_block = elaborator.patch.resume_block();
376 elaborate_drop(
377 &mut elaborator,
378 source_info,
379 dropee,
380 (),
381 return_block,
382 Unwind::To(resume_block),
383 START_BLOCK,
384 None,
385 );
386 elaborator.patch
387 };
388 patch.apply(&mut body);
389 }
390
391 body
392}
393
394fn new_body<'tcx>(
395 source: MirSource<'tcx>,
396 basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
397 local_decls: IndexVec<Local, LocalDecl<'tcx>>,
398 arg_count: usize,
399 span: Span,
400) -> Body<'tcx> {
401 let mut body = Body::new(
402 source,
403 basic_blocks,
404 IndexVec::from_elem_n(
405 SourceScopeData {
406 span,
407 parent_scope: None,
408 inlined: None,
409 inlined_parent_scope: None,
410 local_data: ClearCrossCrate::Clear,
411 },
412 1,
413 ),
414 local_decls,
415 IndexVec::new(),
416 arg_count,
417 vec![],
418 span,
419 None,
420 None,
422 );
423 body.set_required_consts(Vec::new());
425 body
426}
427
428pub(super) struct DropShimElaborator<'a, 'tcx> {
429 pub body: &'a Body<'tcx>,
430 pub patch: MirPatch<'tcx>,
431 pub tcx: TyCtxt<'tcx>,
432 pub typing_env: ty::TypingEnv<'tcx>,
433 pub produce_async_drops: bool,
434}
435
436impl fmt::Debug for DropShimElaborator<'_, '_> {
437 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
438 Ok(())
439 }
440}
441
442impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> {
443 type Path = ();
444
445 fn patch_ref(&self) -> &MirPatch<'tcx> {
446 &self.patch
447 }
448 fn patch(&mut self) -> &mut MirPatch<'tcx> {
449 &mut self.patch
450 }
451 fn body(&self) -> &'a Body<'tcx> {
452 self.body
453 }
454 fn tcx(&self) -> TyCtxt<'tcx> {
455 self.tcx
456 }
457 fn typing_env(&self) -> ty::TypingEnv<'tcx> {
458 self.typing_env
459 }
460
461 fn terminator_loc(&self, bb: BasicBlock) -> Location {
462 self.patch.terminator_loc(self.body, bb)
463 }
464 fn allow_async_drops(&self) -> bool {
465 self.produce_async_drops
466 }
467
468 fn drop_style(&self, _path: Self::Path, mode: DropFlagMode) -> DropStyle {
469 match mode {
470 DropFlagMode::Shallow => {
471 DropStyle::Static
474 }
475 DropFlagMode::Deep => {
476 DropStyle::Open
479 }
480 }
481 }
482
483 fn get_drop_flag(&mut self, _path: Self::Path) -> Option<Operand<'tcx>> {
484 None
485 }
486
487 fn clear_drop_flag(&mut self, _location: Location, _path: Self::Path, _mode: DropFlagMode) {}
488
489 fn field_subpath(&self, _path: Self::Path, _field: FieldIdx) -> Option<Self::Path> {
490 None
491 }
492 fn deref_subpath(&self, _path: Self::Path) -> Option<Self::Path> {
493 None
494 }
495 fn downcast_subpath(&self, _path: Self::Path, _variant: VariantIdx) -> Option<Self::Path> {
496 Some(())
497 }
498 fn array_subpath(&self, _path: Self::Path, _index: u64, _size: u64) -> Option<Self::Path> {
499 None
500 }
501}
502
503fn build_thread_local_shim<'tcx>(
504 tcx: TyCtxt<'tcx>,
505 instance: ty::InstanceKind<'tcx>,
506) -> Body<'tcx> {
507 let def_id = instance.def_id();
508
509 let span = tcx.def_span(def_id);
510 let source_info = SourceInfo::outermost(span);
511
512 let blocks = IndexVec::from_raw(vec![BasicBlockData::new_stmts(
513 vec![Statement::new(
514 source_info,
515 StatementKind::Assign(Box::new((
516 Place::return_place(),
517 Rvalue::ThreadLocalRef(def_id),
518 ))),
519 )],
520 Some(Terminator { source_info, kind: TerminatorKind::Return }),
521 false,
522 )]);
523
524 new_body(
525 MirSource::from_instance(instance),
526 blocks,
527 IndexVec::from_raw(vec![LocalDecl::new(tcx.thread_local_ptr_ty(def_id), span)]),
528 0,
529 span,
530 )
531}
532
533fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> {
535 debug!("build_clone_shim(def_id={:?})", def_id);
536
537 let mut builder = CloneShimBuilder::new(tcx, def_id, self_ty);
538
539 let dest = Place::return_place();
540 let src = tcx.mk_place_deref(Place::from(Local::new(1 + 0)));
541
542 match self_ty.kind() {
543 ty::FnDef(..) | ty::FnPtr(..) => builder.copy_shim(),
544 ty::Closure(_, args) => builder.tuple_like_shim(dest, src, args.as_closure().upvar_tys()),
545 ty::CoroutineClosure(_, args) => {
546 builder.tuple_like_shim(dest, src, args.as_coroutine_closure().upvar_tys())
547 }
548 ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
549 ty::Coroutine(coroutine_def_id, args) => {
550 assert_eq!(tcx.coroutine_movability(*coroutine_def_id), hir::Movability::Movable);
551 builder.coroutine_shim(dest, src, *coroutine_def_id, args.as_coroutine())
552 }
553 _ => bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty),
554 };
555
556 builder.into_mir()
557}
558
559struct CloneShimBuilder<'tcx> {
560 tcx: TyCtxt<'tcx>,
561 def_id: DefId,
562 local_decls: IndexVec<Local, LocalDecl<'tcx>>,
563 blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
564 span: Span,
565 sig: ty::FnSig<'tcx>,
566}
567
568impl<'tcx> CloneShimBuilder<'tcx> {
569 fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Self {
570 let sig = tcx.fn_sig(def_id).instantiate(tcx, &[self_ty.into()]);
574 let sig = tcx.instantiate_bound_regions_with_erased(sig);
575 let span = tcx.def_span(def_id);
576
577 CloneShimBuilder {
578 tcx,
579 def_id,
580 local_decls: local_decls_for_sig(&sig, span),
581 blocks: IndexVec::new(),
582 span,
583 sig,
584 }
585 }
586
587 fn into_mir(self) -> Body<'tcx> {
588 let source = MirSource::from_instance(ty::InstanceKind::CloneShim(
589 self.def_id,
590 self.sig.inputs_and_output[0],
591 ));
592 new_body(source, self.blocks, self.local_decls, self.sig.inputs().len(), self.span)
593 }
594
595 fn source_info(&self) -> SourceInfo {
596 SourceInfo::outermost(self.span)
597 }
598
599 fn block(
600 &mut self,
601 statements: Vec<Statement<'tcx>>,
602 kind: TerminatorKind<'tcx>,
603 is_cleanup: bool,
604 ) -> BasicBlock {
605 let source_info = self.source_info();
606 self.blocks.push(BasicBlockData::new_stmts(
607 statements,
608 Some(Terminator { source_info, kind }),
609 is_cleanup,
610 ))
611 }
612
613 fn block_index_offset(&self, offset: usize) -> BasicBlock {
618 BasicBlock::new(self.blocks.len() + offset)
619 }
620
621 fn make_statement(&self, kind: StatementKind<'tcx>) -> Statement<'tcx> {
622 Statement::new(self.source_info(), kind)
623 }
624
625 fn copy_shim(&mut self) {
626 let rcvr = self.tcx.mk_place_deref(Place::from(Local::new(1 + 0)));
627 let ret_statement = self.make_statement(StatementKind::Assign(Box::new((
628 Place::return_place(),
629 Rvalue::Use(Operand::Copy(rcvr)),
630 ))));
631 self.block(vec![ret_statement], TerminatorKind::Return, false);
632 }
633
634 fn make_place(&mut self, mutability: Mutability, ty: Ty<'tcx>) -> Place<'tcx> {
635 let span = self.span;
636 let mut local = LocalDecl::new(ty, span);
637 if mutability.is_not() {
638 local = local.immutable();
639 }
640 Place::from(self.local_decls.push(local))
641 }
642
643 fn make_clone_call(
644 &mut self,
645 dest: Place<'tcx>,
646 src: Place<'tcx>,
647 ty: Ty<'tcx>,
648 next: BasicBlock,
649 cleanup: BasicBlock,
650 ) {
651 let tcx = self.tcx;
652
653 let func_ty = Ty::new_fn_def(tcx, self.def_id, [ty]);
655 let func = Operand::Constant(Box::new(ConstOperand {
656 span: self.span,
657 user_ty: None,
658 const_: Const::zero_sized(func_ty),
659 }));
660
661 let ref_loc =
662 self.make_place(Mutability::Not, Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty));
663
664 let statement = self.make_statement(StatementKind::Assign(Box::new((
666 ref_loc,
667 Rvalue::Ref(tcx.lifetimes.re_erased, BorrowKind::Shared, src),
668 ))));
669
670 self.block(
672 vec![statement],
673 TerminatorKind::Call {
674 func,
675 args: [Spanned { node: Operand::Move(ref_loc), span: DUMMY_SP }].into(),
676 destination: dest,
677 target: Some(next),
678 unwind: UnwindAction::Cleanup(cleanup),
679 call_source: CallSource::Normal,
680 fn_span: self.span,
681 },
682 false,
683 );
684 }
685
686 fn clone_fields<I>(
687 &mut self,
688 dest: Place<'tcx>,
689 src: Place<'tcx>,
690 target: BasicBlock,
691 mut unwind: BasicBlock,
692 tys: I,
693 ) -> BasicBlock
694 where
695 I: IntoIterator<Item = Ty<'tcx>>,
696 {
697 for (i, ity) in tys.into_iter().enumerate() {
699 let field = FieldIdx::new(i);
710 let src_field = self.tcx.mk_place_field(src, field, ity);
711
712 let dest_field = self.tcx.mk_place_field(dest, field, ity);
713
714 let next_unwind = self.block_index_offset(1);
715 let next_block = self.block_index_offset(2);
716 self.make_clone_call(dest_field, src_field, ity, next_block, unwind);
717 self.block(
718 vec![],
719 TerminatorKind::Drop {
720 place: dest_field,
721 target: unwind,
722 unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
723 replace: false,
724 drop: None,
725 async_fut: None,
726 },
727 true,
728 );
729 unwind = next_unwind;
730 }
731 self.block(vec![], TerminatorKind::Goto { target }, false);
733 unwind
734 }
735
736 fn tuple_like_shim<I>(&mut self, dest: Place<'tcx>, src: Place<'tcx>, tys: I)
737 where
738 I: IntoIterator<Item = Ty<'tcx>>,
739 {
740 self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false);
741 let unwind = self.block(vec![], TerminatorKind::UnwindResume, true);
742 let target = self.block(vec![], TerminatorKind::Return, false);
743
744 let _final_cleanup_block = self.clone_fields(dest, src, target, unwind, tys);
745 }
746
747 fn coroutine_shim(
748 &mut self,
749 dest: Place<'tcx>,
750 src: Place<'tcx>,
751 coroutine_def_id: DefId,
752 args: CoroutineArgs<TyCtxt<'tcx>>,
753 ) {
754 self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false);
755 let unwind = self.block(vec![], TerminatorKind::UnwindResume, true);
756 let switch = self.block(vec![], TerminatorKind::Unreachable, false);
758 let unwind = self.clone_fields(dest, src, switch, unwind, args.upvar_tys());
759 let target = self.block(vec![], TerminatorKind::Return, false);
760 let unreachable = self.block(vec![], TerminatorKind::Unreachable, false);
761 let mut cases = Vec::with_capacity(args.state_tys(coroutine_def_id, self.tcx).count());
762 for (index, state_tys) in args.state_tys(coroutine_def_id, self.tcx).enumerate() {
763 let variant_index = VariantIdx::new(index);
764 let dest = self.tcx.mk_place_downcast_unnamed(dest, variant_index);
765 let src = self.tcx.mk_place_downcast_unnamed(src, variant_index);
766 let clone_block = self.block_index_offset(1);
767 let start_block = self.block(
768 vec![self.make_statement(StatementKind::SetDiscriminant {
769 place: Box::new(Place::return_place()),
770 variant_index,
771 })],
772 TerminatorKind::Goto { target: clone_block },
773 false,
774 );
775 cases.push((index as u128, start_block));
776 let _final_cleanup_block = self.clone_fields(dest, src, target, unwind, state_tys);
777 }
778 let discr_ty = args.discr_ty(self.tcx);
779 let temp = self.make_place(Mutability::Mut, discr_ty);
780 let rvalue = Rvalue::Discriminant(src);
781 let statement = self.make_statement(StatementKind::Assign(Box::new((temp, rvalue))));
782 match &mut self.blocks[switch] {
783 BasicBlockData { statements, terminator: Some(Terminator { kind, .. }), .. } => {
784 statements.push(statement);
785 *kind = TerminatorKind::SwitchInt {
786 discr: Operand::Move(temp),
787 targets: SwitchTargets::new(cases.into_iter(), unreachable),
788 };
789 }
790 BasicBlockData { terminator: None, .. } => unreachable!(),
791 }
792 }
793}
794
795#[instrument(level = "debug", skip(tcx), ret)]
798fn build_call_shim<'tcx>(
799 tcx: TyCtxt<'tcx>,
800 instance: ty::InstanceKind<'tcx>,
801 rcvr_adjustment: Option<Adjustment>,
802 call_kind: CallKind<'tcx>,
803) -> Body<'tcx> {
804 let (sig_args, untuple_args) = if let ty::InstanceKind::FnPtrShim(_, ty) = instance {
808 let sig = tcx.instantiate_bound_regions_with_erased(ty.fn_sig(tcx));
809
810 let untuple_args = sig.inputs();
811
812 let arg_tup = Ty::new_tup(tcx, untuple_args);
814
815 (Some([ty.into(), arg_tup.into()]), Some(untuple_args))
816 } else {
817 (None, None)
818 };
819
820 let def_id = instance.def_id();
821
822 let sig = tcx.fn_sig(def_id);
823 let sig = sig.map_bound(|sig| tcx.instantiate_bound_regions_with_erased(sig));
824
825 assert_eq!(sig_args.is_some(), !instance.has_polymorphic_mir_body());
826 let mut sig = if let Some(sig_args) = sig_args {
827 sig.instantiate(tcx, &sig_args)
828 } else {
829 sig.instantiate_identity()
830 };
831
832 if let CallKind::Indirect(fnty) = call_kind {
833 let mut inputs_and_output = sig.inputs_and_output.to_vec();
839
840 assert_eq!(inputs_and_output.len(), 3);
843
844 let self_arg = &mut inputs_and_output[0];
847 *self_arg = match rcvr_adjustment.unwrap() {
848 Adjustment::Identity => fnty,
849 Adjustment::Deref { source } => match source {
850 DerefSource::ImmRef => Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fnty),
851 DerefSource::MutRef => Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, fnty),
852 DerefSource::MutPtr => Ty::new_mut_ptr(tcx, fnty),
853 },
854 Adjustment::RefMut => bug!("`RefMut` is never used with indirect calls: {instance:?}"),
855 };
856 sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
857 }
858
859 if let ty::InstanceKind::VTableShim(..) = instance {
862 let mut inputs_and_output = sig.inputs_and_output.to_vec();
864 let self_arg = &mut inputs_and_output[0];
865 debug_assert!(tcx.generics_of(def_id).has_self && *self_arg == tcx.types.self_param);
866 *self_arg = Ty::new_mut_ptr(tcx, *self_arg);
867 sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
868 }
869
870 let span = tcx.def_span(def_id);
871
872 debug!(?sig);
873
874 let mut local_decls = local_decls_for_sig(&sig, span);
875 let source_info = SourceInfo::outermost(span);
876
877 let destination = Place::return_place();
878
879 let rcvr_place = || {
880 assert!(rcvr_adjustment.is_some());
881 Place::from(Local::new(1))
882 };
883 let mut statements = vec![];
884
885 let rcvr = rcvr_adjustment.map(|rcvr_adjustment| match rcvr_adjustment {
886 Adjustment::Identity => Operand::Move(rcvr_place()),
887 Adjustment::Deref { source: _ } => Operand::Move(tcx.mk_place_deref(rcvr_place())),
888 Adjustment::RefMut => {
889 let ref_rcvr = local_decls.push(
891 LocalDecl::new(
892 Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, sig.inputs()[0]),
893 span,
894 )
895 .immutable(),
896 );
897 let borrow_kind = BorrowKind::Mut { kind: MutBorrowKind::Default };
898 statements.push(Statement::new(
899 source_info,
900 StatementKind::Assign(Box::new((
901 Place::from(ref_rcvr),
902 Rvalue::Ref(tcx.lifetimes.re_erased, borrow_kind, rcvr_place()),
903 ))),
904 ));
905 Operand::Move(Place::from(ref_rcvr))
906 }
907 });
908
909 let (callee, mut args) = match call_kind {
910 CallKind::Indirect(_) => (rcvr.unwrap(), vec![]),
912
913 CallKind::Direct(def_id) => {
915 let ty = tcx.type_of(def_id).instantiate_identity();
916 (
917 Operand::Constant(Box::new(ConstOperand {
918 span,
919 user_ty: None,
920 const_: Const::zero_sized(ty),
921 })),
922 rcvr.into_iter().collect::<Vec<_>>(),
923 )
924 }
925 };
926
927 let mut arg_range = 0..sig.inputs().len();
928
929 if rcvr_adjustment.is_some() {
931 arg_range.start += 1;
932 }
933
934 if untuple_args.is_some() {
936 arg_range.end -= 1;
937 }
938
939 args.extend(arg_range.map(|i| Operand::Move(Place::from(Local::new(1 + i)))));
941
942 if let Some(untuple_args) = untuple_args {
944 let tuple_arg = Local::new(1 + (sig.inputs().len() - 1));
945 args.extend(untuple_args.iter().enumerate().map(|(i, ity)| {
946 Operand::Move(tcx.mk_place_field(Place::from(tuple_arg), FieldIdx::new(i), *ity))
947 }));
948 }
949
950 let n_blocks = if let Some(Adjustment::RefMut) = rcvr_adjustment { 5 } else { 2 };
951 let mut blocks = IndexVec::with_capacity(n_blocks);
952 let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| {
953 blocks.push(BasicBlockData::new_stmts(
954 statements,
955 Some(Terminator { source_info, kind }),
956 is_cleanup,
957 ))
958 };
959
960 let args = args.into_iter().map(|a| Spanned { node: a, span: DUMMY_SP }).collect();
962 block(
963 &mut blocks,
964 statements,
965 TerminatorKind::Call {
966 func: callee,
967 args,
968 destination,
969 target: Some(BasicBlock::new(1)),
970 unwind: if let Some(Adjustment::RefMut) = rcvr_adjustment {
971 UnwindAction::Cleanup(BasicBlock::new(3))
972 } else {
973 UnwindAction::Continue
974 },
975 call_source: CallSource::Misc,
976 fn_span: span,
977 },
978 false,
979 );
980
981 if let Some(Adjustment::RefMut) = rcvr_adjustment {
982 block(
984 &mut blocks,
985 vec![],
986 TerminatorKind::Drop {
987 place: rcvr_place(),
988 target: BasicBlock::new(2),
989 unwind: UnwindAction::Continue,
990 replace: false,
991 drop: None,
992 async_fut: None,
993 },
994 false,
995 );
996 }
997 let stmts = vec![];
999 block(&mut blocks, stmts, TerminatorKind::Return, false);
1000 if let Some(Adjustment::RefMut) = rcvr_adjustment {
1001 block(
1003 &mut blocks,
1004 vec![],
1005 TerminatorKind::Drop {
1006 place: rcvr_place(),
1007 target: BasicBlock::new(4),
1008 unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup),
1009 replace: false,
1010 drop: None,
1011 async_fut: None,
1012 },
1013 true,
1014 );
1015
1016 block(&mut blocks, vec![], TerminatorKind::UnwindResume, true);
1018 }
1019
1020 let mut body =
1021 new_body(MirSource::from_instance(instance), blocks, local_decls, sig.inputs().len(), span);
1022
1023 if let ExternAbi::RustCall = sig.abi {
1024 body.spread_arg = Some(Local::new(sig.inputs().len()));
1025 }
1026
1027 body
1028}
1029
1030pub(super) fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> {
1031 debug_assert!(tcx.is_constructor(ctor_id));
1032
1033 let typing_env = ty::TypingEnv::post_analysis(tcx, ctor_id);
1034
1035 let sig = tcx
1037 .fn_sig(ctor_id)
1038 .instantiate_identity()
1039 .no_bound_vars()
1040 .expect("LBR in ADT constructor signature");
1041 let sig = tcx.normalize_erasing_regions(typing_env, sig);
1042
1043 let ty::Adt(adt_def, args) = sig.output().kind() else {
1044 bug!("unexpected type for ADT ctor {:?}", sig.output());
1045 };
1046
1047 debug!("build_ctor: ctor_id={:?} sig={:?}", ctor_id, sig);
1048
1049 let span = tcx.def_span(ctor_id);
1050
1051 let local_decls = local_decls_for_sig(&sig, span);
1052
1053 let source_info = SourceInfo::outermost(span);
1054
1055 let variant_index =
1056 if adt_def.is_enum() { adt_def.variant_index_with_ctor_id(ctor_id) } else { FIRST_VARIANT };
1057
1058 debug!("build_ctor: variant_index={:?}", variant_index);
1065
1066 let kind = AggregateKind::Adt(adt_def.did(), variant_index, args, None, None);
1067 let variant = adt_def.variant(variant_index);
1068 let statement = Statement::new(
1069 source_info,
1070 StatementKind::Assign(Box::new((
1071 Place::return_place(),
1072 Rvalue::Aggregate(
1073 Box::new(kind),
1074 (0..variant.fields.len())
1075 .map(|idx| Operand::Move(Place::from(Local::new(idx + 1))))
1076 .collect(),
1077 ),
1078 ))),
1079 );
1080
1081 let start_block = BasicBlockData::new_stmts(
1082 vec![statement],
1083 Some(Terminator { source_info, kind: TerminatorKind::Return }),
1084 false,
1085 );
1086
1087 let source = MirSource::item(ctor_id);
1088 let mut body = new_body(
1089 source,
1090 IndexVec::from_elem_n(start_block, 1),
1091 local_decls,
1092 sig.inputs().len(),
1093 span,
1094 );
1095 body.set_mentioned_items(Vec::new());
1098
1099 crate::pass_manager::dump_mir_for_phase_change(tcx, &body);
1100
1101 body
1102}
1103
1104fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> {
1112 assert_matches!(self_ty.kind(), ty::FnPtr(..), "expected fn ptr, found {self_ty}");
1113 let span = tcx.def_span(def_id);
1114 let Some(sig) = tcx.fn_sig(def_id).instantiate(tcx, &[self_ty.into()]).no_bound_vars() else {
1115 span_bug!(span, "FnPtr::addr with bound vars for `{self_ty}`");
1116 };
1117 let locals = local_decls_for_sig(&sig, span);
1118
1119 let source_info = SourceInfo::outermost(span);
1120 let rvalue = Rvalue::Cast(
1123 CastKind::FnPtrToPtr,
1124 Operand::Move(Place::from(Local::new(1))),
1125 Ty::new_imm_ptr(tcx, tcx.types.unit),
1126 );
1127 let stmt = Statement::new(
1128 source_info,
1129 StatementKind::Assign(Box::new((Place::return_place(), rvalue))),
1130 );
1131 let statements = vec![stmt];
1132 let start_block = BasicBlockData::new_stmts(
1133 statements,
1134 Some(Terminator { source_info, kind: TerminatorKind::Return }),
1135 false,
1136 );
1137 let source = MirSource::from_instance(ty::InstanceKind::FnPtrAddrShim(def_id, self_ty));
1138 new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span)
1139}
1140
1141fn build_construct_coroutine_by_move_shim<'tcx>(
1142 tcx: TyCtxt<'tcx>,
1143 coroutine_closure_def_id: DefId,
1144 receiver_by_ref: bool,
1145) -> Body<'tcx> {
1146 let mut self_ty = tcx.type_of(coroutine_closure_def_id).instantiate_identity();
1147 let mut self_local: Place<'tcx> = Local::from_usize(1).into();
1148 let ty::CoroutineClosure(_, args) = *self_ty.kind() else {
1149 bug!();
1150 };
1151
1152 if receiver_by_ref {
1158 self_local = tcx.mk_place_deref(self_local);
1159 self_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, self_ty);
1160 }
1161
1162 let poly_sig = args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
1163 tcx.mk_fn_sig(
1164 [self_ty].into_iter().chain(sig.tupled_inputs_ty.tuple_fields()),
1165 sig.to_coroutine_given_kind_and_upvars(
1166 tcx,
1167 args.as_coroutine_closure().parent_args(),
1168 tcx.coroutine_for_closure(coroutine_closure_def_id),
1169 ty::ClosureKind::FnOnce,
1170 tcx.lifetimes.re_erased,
1171 args.as_coroutine_closure().tupled_upvars_ty(),
1172 args.as_coroutine_closure().coroutine_captures_by_ref_ty(),
1173 ),
1174 sig.c_variadic,
1175 sig.safety,
1176 sig.abi,
1177 )
1178 });
1179 let sig = tcx.liberate_late_bound_regions(coroutine_closure_def_id, poly_sig);
1180 let ty::Coroutine(coroutine_def_id, coroutine_args) = *sig.output().kind() else {
1181 bug!();
1182 };
1183
1184 let span = tcx.def_span(coroutine_closure_def_id);
1185 let locals = local_decls_for_sig(&sig, span);
1186
1187 let mut fields = vec![];
1188
1189 for idx in 1..sig.inputs().len() {
1191 fields.push(Operand::Move(Local::from_usize(idx + 1).into()));
1192 }
1193
1194 for (idx, ty) in args.as_coroutine_closure().upvar_tys().iter().enumerate() {
1195 if receiver_by_ref {
1196 if !matches!(ty.kind(), ty::Ref(_, _, hir::Mutability::Not)) {
1200 tcx.dcx().delayed_bug(format!(
1204 "field should be captured by immutable ref if we have \
1205 an `Fn` instance, but it was: {ty}"
1206 ));
1207 }
1208 fields.push(Operand::Copy(tcx.mk_place_field(
1209 self_local,
1210 FieldIdx::from_usize(idx),
1211 ty,
1212 )));
1213 } else {
1214 fields.push(Operand::Move(tcx.mk_place_field(
1215 self_local,
1216 FieldIdx::from_usize(idx),
1217 ty,
1218 )));
1219 }
1220 }
1221
1222 let source_info = SourceInfo::outermost(span);
1223 let rvalue = Rvalue::Aggregate(
1224 Box::new(AggregateKind::Coroutine(coroutine_def_id, coroutine_args)),
1225 IndexVec::from_raw(fields),
1226 );
1227 let stmt = Statement::new(
1228 source_info,
1229 StatementKind::Assign(Box::new((Place::return_place(), rvalue))),
1230 );
1231 let statements = vec![stmt];
1232 let start_block = BasicBlockData::new_stmts(
1233 statements,
1234 Some(Terminator { source_info, kind: TerminatorKind::Return }),
1235 false,
1236 );
1237
1238 let source = MirSource::from_instance(ty::InstanceKind::ConstructCoroutineInClosureShim {
1239 coroutine_closure_def_id,
1240 receiver_by_ref,
1241 });
1242
1243 let body =
1244 new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span);
1245 dump_mir(
1246 tcx,
1247 false,
1248 if receiver_by_ref { "coroutine_closure_by_ref" } else { "coroutine_closure_by_move" },
1249 &0,
1250 &body,
1251 |_, _| Ok(()),
1252 );
1253
1254 body
1255}