]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/shim.rs
Auto merge of #67924 - RalfJung:miri, r=RalfJung
[rust.git] / src / librustc_mir / shim.rs
1 use rustc::mir::*;
2 use rustc::ty::layout::VariantIdx;
3 use rustc::ty::query::Providers;
4 use rustc::ty::subst::{InternalSubsts, Subst};
5 use rustc::ty::{self, Ty, TyCtxt};
6 use rustc_hir as hir;
7 use rustc_hir::def_id::DefId;
8
9 use rustc_index::vec::{Idx, IndexVec};
10
11 use rustc_span::{sym, Span};
12 use rustc_target::spec::abi::Abi;
13
14 use std::fmt;
15 use std::iter;
16
17 use crate::transform::{
18     add_call_guards, add_moves_for_packed_drops, no_landing_pads, remove_noop_landing_pads,
19     run_passes, simplify,
20 };
21 use crate::util::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
22 use crate::util::expand_aggregate;
23 use crate::util::patch::MirPatch;
24
25 pub fn provide(providers: &mut Providers<'_>) {
26     providers.mir_shims = make_shim;
27 }
28
29 fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx BodyAndCache<'tcx> {
30     debug!("make_shim({:?})", instance);
31
32     let mut result = match instance {
33         ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance),
34         ty::InstanceDef::VtableShim(def_id) => {
35             build_call_shim(tcx, instance, Adjustment::DerefMove, CallKind::Direct(def_id), None)
36         }
37         ty::InstanceDef::FnPtrShim(def_id, ty) => {
38             let trait_ = tcx.trait_of_item(def_id).unwrap();
39             let adjustment = match tcx.lang_items().fn_trait_kind(trait_) {
40                 Some(ty::ClosureKind::FnOnce) => Adjustment::Identity,
41                 Some(ty::ClosureKind::FnMut) | Some(ty::ClosureKind::Fn) => Adjustment::Deref,
42                 None => bug!("fn pointer {:?} is not an fn", ty),
43             };
44             // HACK: we need the "real" argument types for the MIR,
45             // but because our substs are (Self, Args), where Args
46             // is a tuple, we must include the *concrete* argument
47             // types in the MIR. They will be substituted again with
48             // the param-substs, but because they are concrete, this
49             // will not do any harm.
50             let sig = tcx.erase_late_bound_regions(&ty.fn_sig(tcx));
51             let arg_tys = sig.inputs();
52
53             build_call_shim(tcx, instance, adjustment, CallKind::Indirect, Some(arg_tys))
54         }
55         // We are generating a call back to our def-id, which the
56         // codegen backend knows to turn to an actual call, be it
57         // a virtual call, or a direct call to a function for which
58         // indirect calls must be codegen'd differently than direct ones
59         // (such as `#[track_caller]`).
60         ty::InstanceDef::ReifyShim(def_id) => {
61             build_call_shim(tcx, instance, Adjustment::Identity, CallKind::Direct(def_id), None)
62         }
63         ty::InstanceDef::ClosureOnceShim { call_once: _ } => {
64             let fn_mut = tcx.lang_items().fn_mut_trait().unwrap();
65             let call_mut = tcx
66                 .associated_items(fn_mut)
67                 .find(|it| it.kind == ty::AssocKind::Method)
68                 .unwrap()
69                 .def_id;
70
71             build_call_shim(tcx, instance, Adjustment::RefMut, CallKind::Direct(call_mut), None)
72         }
73         ty::InstanceDef::DropGlue(def_id, ty) => build_drop_shim(tcx, def_id, ty),
74         ty::InstanceDef::CloneShim(def_id, ty) => {
75             let name = tcx.item_name(def_id);
76             if name == sym::clone {
77                 build_clone_shim(tcx, def_id, ty)
78             } else if name == sym::clone_from {
79                 debug!("make_shim({:?}: using default trait implementation", instance);
80                 return tcx.optimized_mir(def_id);
81             } else {
82                 bug!("builtin clone shim {:?} not supported", instance)
83             }
84         }
85         ty::InstanceDef::Virtual(..) => {
86             bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
87         }
88         ty::InstanceDef::Intrinsic(_) => {
89             bug!("creating shims from intrinsics ({:?}) is unsupported", instance)
90         }
91     };
92     debug!("make_shim({:?}) = untransformed {:?}", instance, result);
93
94     run_passes(
95         tcx,
96         &mut result,
97         instance,
98         None,
99         MirPhase::Const,
100         &[
101             &add_moves_for_packed_drops::AddMovesForPackedDrops,
102             &no_landing_pads::NoLandingPads::new(tcx),
103             &remove_noop_landing_pads::RemoveNoopLandingPads,
104             &simplify::SimplifyCfg::new("make_shim"),
105             &add_call_guards::CriticalCallEdges,
106         ],
107     );
108
109     debug!("make_shim({:?}) = {:?}", instance, result);
110
111     result.ensure_predecessors();
112     tcx.arena.alloc(result)
113 }
114
115 #[derive(Copy, Clone, Debug, PartialEq)]
116 enum Adjustment {
117     Identity,
118     Deref,
119     DerefMove,
120     RefMut,
121 }
122
123 #[derive(Copy, Clone, Debug, PartialEq)]
124 enum CallKind {
125     Indirect,
126     Direct(DefId),
127 }
128
129 fn temp_decl(mutability: Mutability, ty: Ty<'_>, span: Span) -> LocalDecl<'_> {
130     let source_info = SourceInfo { scope: OUTERMOST_SOURCE_SCOPE, span };
131     LocalDecl {
132         mutability,
133         ty,
134         user_ty: UserTypeProjections::none(),
135         source_info,
136         internal: false,
137         local_info: LocalInfo::Other,
138         is_block_tail: None,
139     }
140 }
141
142 fn local_decls_for_sig<'tcx>(
143     sig: &ty::FnSig<'tcx>,
144     span: Span,
145 ) -> IndexVec<Local, LocalDecl<'tcx>> {
146     iter::once(temp_decl(Mutability::Mut, sig.output(), span))
147         .chain(sig.inputs().iter().map(|ity| temp_decl(Mutability::Not, ity, span)))
148         .collect()
149 }
150
151 fn build_drop_shim<'tcx>(
152     tcx: TyCtxt<'tcx>,
153     def_id: DefId,
154     ty: Option<Ty<'tcx>>,
155 ) -> BodyAndCache<'tcx> {
156     debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
157
158     // Check if this is a generator, if so, return the drop glue for it
159     if let Some(&ty::TyS { kind: ty::Generator(gen_def_id, substs, _), .. }) = ty {
160         let body = &**tcx.optimized_mir(gen_def_id).generator_drop.as_ref().unwrap();
161         return body.subst(tcx, substs);
162     }
163
164     let substs = if let Some(ty) = ty {
165         tcx.intern_substs(&[ty.into()])
166     } else {
167         InternalSubsts::identity_for_item(tcx, def_id)
168     };
169     let sig = tcx.fn_sig(def_id).subst(tcx, substs);
170     let sig = tcx.erase_late_bound_regions(&sig);
171     let span = tcx.def_span(def_id);
172
173     let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE };
174
175     let return_block = BasicBlock::new(1);
176     let mut blocks = IndexVec::with_capacity(2);
177     let block = |blocks: &mut IndexVec<_, _>, kind| {
178         blocks.push(BasicBlockData {
179             statements: vec![],
180             terminator: Some(Terminator { source_info, kind }),
181             is_cleanup: false,
182         })
183     };
184     block(&mut blocks, TerminatorKind::Goto { target: return_block });
185     block(&mut blocks, TerminatorKind::Return);
186
187     let body = new_body(blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span);
188
189     let mut body = BodyAndCache::new(body);
190
191     if let Some(..) = ty {
192         // The first argument (index 0), but add 1 for the return value.
193         let dropee_ptr = Place::from(Local::new(1 + 0));
194         if tcx.sess.opts.debugging_opts.mir_emit_retag {
195             // Function arguments should be retagged, and we make this one raw.
196             body.basic_blocks_mut()[START_BLOCK].statements.insert(
197                 0,
198                 Statement {
199                     source_info,
200                     kind: StatementKind::Retag(RetagKind::Raw, box (dropee_ptr.clone())),
201                 },
202             );
203         }
204         let patch = {
205             let param_env = tcx.param_env(def_id).with_reveal_all();
206             let mut elaborator =
207                 DropShimElaborator { body: &body, patch: MirPatch::new(&body), tcx, param_env };
208             let dropee = tcx.mk_place_deref(dropee_ptr);
209             let resume_block = elaborator.patch.resume_block();
210             elaborate_drops::elaborate_drop(
211                 &mut elaborator,
212                 source_info,
213                 &dropee,
214                 (),
215                 return_block,
216                 elaborate_drops::Unwind::To(resume_block),
217                 START_BLOCK,
218             );
219             elaborator.patch
220         };
221         patch.apply(&mut body);
222     }
223
224     body
225 }
226
227 fn new_body<'tcx>(
228     basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
229     local_decls: IndexVec<Local, LocalDecl<'tcx>>,
230     arg_count: usize,
231     span: Span,
232 ) -> Body<'tcx> {
233     Body::new(
234         basic_blocks,
235         IndexVec::from_elem_n(
236             SourceScopeData { span, parent_scope: None, local_data: ClearCrossCrate::Clear },
237             1,
238         ),
239         local_decls,
240         IndexVec::new(),
241         arg_count,
242         vec![],
243         span,
244         vec![],
245         None,
246     )
247 }
248
249 pub struct DropShimElaborator<'a, 'tcx> {
250     pub body: &'a Body<'tcx>,
251     pub patch: MirPatch<'tcx>,
252     pub tcx: TyCtxt<'tcx>,
253     pub param_env: ty::ParamEnv<'tcx>,
254 }
255
256 impl<'a, 'tcx> fmt::Debug for DropShimElaborator<'a, 'tcx> {
257     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
258         Ok(())
259     }
260 }
261
262 impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> {
263     type Path = ();
264
265     fn patch(&mut self) -> &mut MirPatch<'tcx> {
266         &mut self.patch
267     }
268     fn body(&self) -> &'a Body<'tcx> {
269         self.body
270     }
271     fn tcx(&self) -> TyCtxt<'tcx> {
272         self.tcx
273     }
274     fn param_env(&self) -> ty::ParamEnv<'tcx> {
275         self.param_env
276     }
277
278     fn drop_style(&self, _path: Self::Path, mode: DropFlagMode) -> DropStyle {
279         if let DropFlagMode::Shallow = mode { DropStyle::Static } else { DropStyle::Open }
280     }
281
282     fn get_drop_flag(&mut self, _path: Self::Path) -> Option<Operand<'tcx>> {
283         None
284     }
285
286     fn clear_drop_flag(&mut self, _location: Location, _path: Self::Path, _mode: DropFlagMode) {}
287
288     fn field_subpath(&self, _path: Self::Path, _field: Field) -> Option<Self::Path> {
289         None
290     }
291     fn deref_subpath(&self, _path: Self::Path) -> Option<Self::Path> {
292         None
293     }
294     fn downcast_subpath(&self, _path: Self::Path, _variant: VariantIdx) -> Option<Self::Path> {
295         Some(())
296     }
297     fn array_subpath(&self, _path: Self::Path, _index: u32, _size: u32) -> Option<Self::Path> {
298         None
299     }
300 }
301
302 /// Builds a `Clone::clone` shim for `self_ty`. Here, `def_id` is `Clone::clone`.
303 fn build_clone_shim<'tcx>(
304     tcx: TyCtxt<'tcx>,
305     def_id: DefId,
306     self_ty: Ty<'tcx>,
307 ) -> BodyAndCache<'tcx> {
308     debug!("build_clone_shim(def_id={:?})", def_id);
309
310     let param_env = tcx.param_env(def_id);
311
312     let mut builder = CloneShimBuilder::new(tcx, def_id, self_ty);
313     let is_copy = self_ty.is_copy_modulo_regions(tcx, param_env, builder.span);
314
315     let dest = Place::return_place();
316     let src = tcx.mk_place_deref(Place::from(Local::new(1 + 0)));
317
318     match self_ty.kind {
319         _ if is_copy => builder.copy_shim(),
320         ty::Array(ty, len) => {
321             let len = len.eval_usize(tcx, param_env);
322             builder.array_shim(dest, src, ty, len)
323         }
324         ty::Closure(def_id, substs) => {
325             builder.tuple_like_shim(dest, src, substs.as_closure().upvar_tys(def_id, tcx))
326         }
327         ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
328         _ => bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty),
329     };
330
331     BodyAndCache::new(builder.into_mir())
332 }
333
334 struct CloneShimBuilder<'tcx> {
335     tcx: TyCtxt<'tcx>,
336     def_id: DefId,
337     local_decls: IndexVec<Local, LocalDecl<'tcx>>,
338     blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
339     span: Span,
340     sig: ty::FnSig<'tcx>,
341 }
342
343 impl CloneShimBuilder<'tcx> {
344     fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Self {
345         // we must subst the self_ty because it's
346         // otherwise going to be TySelf and we can't index
347         // or access fields of a Place of type TySelf.
348         let substs = tcx.mk_substs_trait(self_ty, &[]);
349         let sig = tcx.fn_sig(def_id).subst(tcx, substs);
350         let sig = tcx.erase_late_bound_regions(&sig);
351         let span = tcx.def_span(def_id);
352
353         CloneShimBuilder {
354             tcx,
355             def_id,
356             local_decls: local_decls_for_sig(&sig, span),
357             blocks: IndexVec::new(),
358             span,
359             sig,
360         }
361     }
362
363     fn into_mir(self) -> Body<'tcx> {
364         new_body(self.blocks, self.local_decls, self.sig.inputs().len(), self.span)
365     }
366
367     fn source_info(&self) -> SourceInfo {
368         SourceInfo { span: self.span, scope: OUTERMOST_SOURCE_SCOPE }
369     }
370
371     fn block(
372         &mut self,
373         statements: Vec<Statement<'tcx>>,
374         kind: TerminatorKind<'tcx>,
375         is_cleanup: bool,
376     ) -> BasicBlock {
377         let source_info = self.source_info();
378         self.blocks.push(BasicBlockData {
379             statements,
380             terminator: Some(Terminator { source_info, kind }),
381             is_cleanup,
382         })
383     }
384
385     /// Gives the index of an upcoming BasicBlock, with an offset.
386     /// offset=0 will give you the index of the next BasicBlock,
387     /// offset=1 will give the index of the next-to-next block,
388     /// offset=-1 will give you the index of the last-created block
389     fn block_index_offset(&mut self, offset: usize) -> BasicBlock {
390         BasicBlock::new(self.blocks.len() + offset)
391     }
392
393     fn make_statement(&self, kind: StatementKind<'tcx>) -> Statement<'tcx> {
394         Statement { source_info: self.source_info(), kind }
395     }
396
397     fn copy_shim(&mut self) {
398         let rcvr = self.tcx.mk_place_deref(Place::from(Local::new(1 + 0)));
399         let ret_statement = self.make_statement(StatementKind::Assign(box (
400             Place::return_place(),
401             Rvalue::Use(Operand::Copy(rcvr)),
402         )));
403         self.block(vec![ret_statement], TerminatorKind::Return, false);
404     }
405
406     fn make_place(&mut self, mutability: Mutability, ty: Ty<'tcx>) -> Place<'tcx> {
407         let span = self.span;
408         Place::from(self.local_decls.push(temp_decl(mutability, ty, span)))
409     }
410
411     fn make_clone_call(
412         &mut self,
413         dest: Place<'tcx>,
414         src: Place<'tcx>,
415         ty: Ty<'tcx>,
416         next: BasicBlock,
417         cleanup: BasicBlock,
418     ) {
419         let tcx = self.tcx;
420
421         let substs = tcx.mk_substs_trait(ty, &[]);
422
423         // `func == Clone::clone(&ty) -> ty`
424         let func_ty = tcx.mk_fn_def(self.def_id, substs);
425         let func = Operand::Constant(box Constant {
426             span: self.span,
427             user_ty: None,
428             literal: ty::Const::zero_sized(tcx, func_ty),
429         });
430
431         let ref_loc = self.make_place(
432             Mutability::Not,
433             tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }),
434         );
435
436         // `let ref_loc: &ty = &src;`
437         let statement = self.make_statement(StatementKind::Assign(box (
438             ref_loc.clone(),
439             Rvalue::Ref(tcx.lifetimes.re_erased, BorrowKind::Shared, src),
440         )));
441
442         // `let loc = Clone::clone(ref_loc);`
443         self.block(
444             vec![statement],
445             TerminatorKind::Call {
446                 func,
447                 args: vec![Operand::Move(ref_loc)],
448                 destination: Some((dest, next)),
449                 cleanup: Some(cleanup),
450                 from_hir_call: true,
451             },
452             false,
453         );
454     }
455
456     fn loop_header(
457         &mut self,
458         beg: Place<'tcx>,
459         end: Place<'tcx>,
460         loop_body: BasicBlock,
461         loop_end: BasicBlock,
462         is_cleanup: bool,
463     ) {
464         let tcx = self.tcx;
465
466         let cond = self.make_place(Mutability::Mut, tcx.types.bool);
467         let compute_cond = self.make_statement(StatementKind::Assign(box (
468             cond.clone(),
469             Rvalue::BinaryOp(BinOp::Ne, Operand::Copy(end), Operand::Copy(beg)),
470         )));
471
472         // `if end != beg { goto loop_body; } else { goto loop_end; }`
473         self.block(
474             vec![compute_cond],
475             TerminatorKind::if_(tcx, Operand::Move(cond), loop_body, loop_end),
476             is_cleanup,
477         );
478     }
479
480     fn make_usize(&self, value: u64) -> Box<Constant<'tcx>> {
481         box Constant {
482             span: self.span,
483             user_ty: None,
484             literal: ty::Const::from_usize(self.tcx, value),
485         }
486     }
487
488     fn array_shim(&mut self, dest: Place<'tcx>, src: Place<'tcx>, ty: Ty<'tcx>, len: u64) {
489         let tcx = self.tcx;
490         let span = self.span;
491
492         let beg = self.local_decls.push(temp_decl(Mutability::Mut, tcx.types.usize, span));
493         let end = self.make_place(Mutability::Not, tcx.types.usize);
494
495         // BB #0
496         // `let mut beg = 0;`
497         // `let end = len;`
498         // `goto #1;`
499         let inits = vec![
500             self.make_statement(StatementKind::Assign(box (
501                 Place::from(beg),
502                 Rvalue::Use(Operand::Constant(self.make_usize(0))),
503             ))),
504             self.make_statement(StatementKind::Assign(box (
505                 end.clone(),
506                 Rvalue::Use(Operand::Constant(self.make_usize(len))),
507             ))),
508         ];
509         self.block(inits, TerminatorKind::Goto { target: BasicBlock::new(1) }, false);
510
511         // BB #1: loop {
512         //     BB #2;
513         //     BB #3;
514         // }
515         // BB #4;
516         self.loop_header(Place::from(beg), end, BasicBlock::new(2), BasicBlock::new(4), false);
517
518         // BB #2
519         // `dest[i] = Clone::clone(src[beg])`;
520         // Goto #3 if ok, #5 if unwinding happens.
521         let dest_field = self.tcx.mk_place_index(dest.clone(), beg);
522         let src_field = self.tcx.mk_place_index(src, beg);
523         self.make_clone_call(dest_field, src_field, ty, BasicBlock::new(3), BasicBlock::new(5));
524
525         // BB #3
526         // `beg = beg + 1;`
527         // `goto #1`;
528         let statements = vec![self.make_statement(StatementKind::Assign(box (
529             Place::from(beg),
530             Rvalue::BinaryOp(
531                 BinOp::Add,
532                 Operand::Copy(Place::from(beg)),
533                 Operand::Constant(self.make_usize(1)),
534             ),
535         )))];
536         self.block(statements, TerminatorKind::Goto { target: BasicBlock::new(1) }, false);
537
538         // BB #4
539         // `return dest;`
540         self.block(vec![], TerminatorKind::Return, false);
541
542         // BB #5 (cleanup)
543         // `let end = beg;`
544         // `let mut beg = 0;`
545         // goto #6;
546         let end = beg;
547         let beg = self.local_decls.push(temp_decl(Mutability::Mut, tcx.types.usize, span));
548         let init = self.make_statement(StatementKind::Assign(box (
549             Place::from(beg),
550             Rvalue::Use(Operand::Constant(self.make_usize(0))),
551         )));
552         self.block(vec![init], TerminatorKind::Goto { target: BasicBlock::new(6) }, true);
553
554         // BB #6 (cleanup): loop {
555         //     BB #7;
556         //     BB #8;
557         // }
558         // BB #9;
559         self.loop_header(
560             Place::from(beg),
561             Place::from(end),
562             BasicBlock::new(7),
563             BasicBlock::new(9),
564             true,
565         );
566
567         // BB #7 (cleanup)
568         // `drop(dest[beg])`;
569         self.block(
570             vec![],
571             TerminatorKind::Drop {
572                 location: self.tcx.mk_place_index(dest, beg),
573                 target: BasicBlock::new(8),
574                 unwind: None,
575             },
576             true,
577         );
578
579         // BB #8 (cleanup)
580         // `beg = beg + 1;`
581         // `goto #6;`
582         let statement = self.make_statement(StatementKind::Assign(box (
583             Place::from(beg),
584             Rvalue::BinaryOp(
585                 BinOp::Add,
586                 Operand::Copy(Place::from(beg)),
587                 Operand::Constant(self.make_usize(1)),
588             ),
589         )));
590         self.block(vec![statement], TerminatorKind::Goto { target: BasicBlock::new(6) }, true);
591
592         // BB #9 (resume)
593         self.block(vec![], TerminatorKind::Resume, true);
594     }
595
596     fn tuple_like_shim<I>(&mut self, dest: Place<'tcx>, src: Place<'tcx>, tys: I)
597     where
598         I: Iterator<Item = Ty<'tcx>>,
599     {
600         let mut previous_field = None;
601         for (i, ity) in tys.enumerate() {
602             let field = Field::new(i);
603             let src_field = self.tcx.mk_place_field(src.clone(), field, ity);
604
605             let dest_field = self.tcx.mk_place_field(dest.clone(), field, ity);
606
607             // #(2i + 1) is the cleanup block for the previous clone operation
608             let cleanup_block = self.block_index_offset(1);
609             // #(2i + 2) is the next cloning block
610             // (or the Return terminator if this is the last block)
611             let next_block = self.block_index_offset(2);
612
613             // BB #(2i)
614             // `dest.i = Clone::clone(&src.i);`
615             // Goto #(2i + 2) if ok, #(2i + 1) if unwinding happens.
616             self.make_clone_call(dest_field.clone(), src_field, ity, next_block, cleanup_block);
617
618             // BB #(2i + 1) (cleanup)
619             if let Some((previous_field, previous_cleanup)) = previous_field.take() {
620                 // Drop previous field and goto previous cleanup block.
621                 self.block(
622                     vec![],
623                     TerminatorKind::Drop {
624                         location: previous_field,
625                         target: previous_cleanup,
626                         unwind: None,
627                     },
628                     true,
629                 );
630             } else {
631                 // Nothing to drop, just resume.
632                 self.block(vec![], TerminatorKind::Resume, true);
633             }
634
635             previous_field = Some((dest_field, cleanup_block));
636         }
637
638         self.block(vec![], TerminatorKind::Return, false);
639     }
640 }
641
642 /// Builds a "call" shim for `instance`. The shim calls the
643 /// function specified by `call_kind`, first adjusting its first
644 /// argument according to `rcvr_adjustment`.
645 ///
646 /// If `untuple_args` is a vec of types, the second argument of the
647 /// function will be untupled as these types.
648 fn build_call_shim<'tcx>(
649     tcx: TyCtxt<'tcx>,
650     instance: ty::InstanceDef<'tcx>,
651     rcvr_adjustment: Adjustment,
652     call_kind: CallKind,
653     untuple_args: Option<&[Ty<'tcx>]>,
654 ) -> BodyAndCache<'tcx> {
655     debug!(
656         "build_call_shim(instance={:?}, rcvr_adjustment={:?}, \
657             call_kind={:?}, untuple_args={:?})",
658         instance, rcvr_adjustment, call_kind, untuple_args
659     );
660
661     let def_id = instance.def_id();
662     let sig = tcx.fn_sig(def_id);
663     let mut sig = tcx.erase_late_bound_regions(&sig);
664
665     // FIXME(eddyb) avoid having this snippet both here and in
666     // `Instance::fn_sig` (introduce `InstanceDef::fn_sig`?).
667     if let ty::InstanceDef::VtableShim(..) = instance {
668         // Modify fn(self, ...) to fn(self: *mut Self, ...)
669         let mut inputs_and_output = sig.inputs_and_output.to_vec();
670         let self_arg = &mut inputs_and_output[0];
671         debug_assert!(tcx.generics_of(def_id).has_self && *self_arg == tcx.types.self_param);
672         *self_arg = tcx.mk_mut_ptr(*self_arg);
673         sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output);
674     }
675
676     let span = tcx.def_span(def_id);
677
678     debug!("build_call_shim: sig={:?}", sig);
679
680     let mut local_decls = local_decls_for_sig(&sig, span);
681     let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE };
682
683     let rcvr_arg = Local::new(1 + 0);
684     let rcvr_l = Place::from(rcvr_arg);
685     let mut statements = vec![];
686
687     let rcvr = match rcvr_adjustment {
688         Adjustment::Identity => Operand::Move(rcvr_l),
689         Adjustment::Deref => Operand::Copy(tcx.mk_place_deref(rcvr_l)),
690         Adjustment::DerefMove => Operand::Move(tcx.mk_place_deref(rcvr_l)),
691         Adjustment::RefMut => {
692             // let rcvr = &mut rcvr;
693             let ref_rcvr = local_decls.push(temp_decl(
694                 Mutability::Not,
695                 tcx.mk_ref(
696                     tcx.lifetimes.re_erased,
697                     ty::TypeAndMut { ty: sig.inputs()[0], mutbl: hir::Mutability::Mut },
698                 ),
699                 span,
700             ));
701             let borrow_kind = BorrowKind::Mut { allow_two_phase_borrow: false };
702             statements.push(Statement {
703                 source_info,
704                 kind: StatementKind::Assign(box (
705                     Place::from(ref_rcvr),
706                     Rvalue::Ref(tcx.lifetimes.re_erased, borrow_kind, rcvr_l),
707                 )),
708             });
709             Operand::Move(Place::from(ref_rcvr))
710         }
711     };
712
713     let (callee, mut args) = match call_kind {
714         CallKind::Indirect => (rcvr, vec![]),
715         CallKind::Direct(def_id) => {
716             let ty = tcx.type_of(def_id);
717             (
718                 Operand::Constant(box Constant {
719                     span,
720                     user_ty: None,
721                     literal: ty::Const::zero_sized(tcx, ty),
722                 }),
723                 vec![rcvr],
724             )
725         }
726     };
727
728     if let Some(untuple_args) = untuple_args {
729         args.extend(untuple_args.iter().enumerate().map(|(i, ity)| {
730             let arg_place = Place::from(Local::new(1 + 1));
731             Operand::Move(tcx.mk_place_field(arg_place, Field::new(i), *ity))
732         }));
733     } else {
734         args.extend((1..sig.inputs().len()).map(|i| Operand::Move(Place::from(Local::new(1 + i)))));
735     }
736
737     let n_blocks = if let Adjustment::RefMut = rcvr_adjustment { 5 } else { 2 };
738     let mut blocks = IndexVec::with_capacity(n_blocks);
739     let block = |blocks: &mut IndexVec<_, _>, statements, kind, is_cleanup| {
740         blocks.push(BasicBlockData {
741             statements,
742             terminator: Some(Terminator { source_info, kind }),
743             is_cleanup,
744         })
745     };
746
747     // BB #0
748     block(
749         &mut blocks,
750         statements,
751         TerminatorKind::Call {
752             func: callee,
753             args,
754             destination: Some((Place::return_place(), BasicBlock::new(1))),
755             cleanup: if let Adjustment::RefMut = rcvr_adjustment {
756                 Some(BasicBlock::new(3))
757             } else {
758                 None
759             },
760             from_hir_call: true,
761         },
762         false,
763     );
764
765     if let Adjustment::RefMut = rcvr_adjustment {
766         // BB #1 - drop for Self
767         block(
768             &mut blocks,
769             vec![],
770             TerminatorKind::Drop {
771                 location: Place::from(rcvr_arg),
772                 target: BasicBlock::new(2),
773                 unwind: None,
774             },
775             false,
776         );
777     }
778     // BB #1/#2 - return
779     block(&mut blocks, vec![], TerminatorKind::Return, false);
780     if let Adjustment::RefMut = rcvr_adjustment {
781         // BB #3 - drop if closure panics
782         block(
783             &mut blocks,
784             vec![],
785             TerminatorKind::Drop {
786                 location: Place::from(rcvr_arg),
787                 target: BasicBlock::new(4),
788                 unwind: None,
789             },
790             true,
791         );
792
793         // BB #4 - resume
794         block(&mut blocks, vec![], TerminatorKind::Resume, true);
795     }
796
797     let mut body = new_body(blocks, local_decls, sig.inputs().len(), span);
798
799     if let Abi::RustCall = sig.abi {
800         body.spread_arg = Some(Local::new(sig.inputs().len()));
801     }
802     BodyAndCache::new(body)
803 }
804
805 pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> &BodyAndCache<'_> {
806     debug_assert!(tcx.is_constructor(ctor_id));
807
808     let span =
809         tcx.hir().span_if_local(ctor_id).unwrap_or_else(|| bug!("no span for ctor {:?}", ctor_id));
810
811     let param_env = tcx.param_env(ctor_id);
812
813     // Normalize the sig.
814     let sig = tcx.fn_sig(ctor_id).no_bound_vars().expect("LBR in ADT constructor signature");
815     let sig = tcx.normalize_erasing_regions(param_env, sig);
816
817     let (adt_def, substs) = match sig.output().kind {
818         ty::Adt(adt_def, substs) => (adt_def, substs),
819         _ => bug!("unexpected type for ADT ctor {:?}", sig.output()),
820     };
821
822     debug!("build_ctor: ctor_id={:?} sig={:?}", ctor_id, sig);
823
824     let local_decls = local_decls_for_sig(&sig, span);
825
826     let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE };
827
828     let variant_index = if adt_def.is_enum() {
829         adt_def.variant_index_with_ctor_id(ctor_id)
830     } else {
831         VariantIdx::new(0)
832     };
833
834     // Generate the following MIR:
835     //
836     // (return as Variant).field0 = arg0;
837     // (return as Variant).field1 = arg1;
838     //
839     // return;
840     debug!("build_ctor: variant_index={:?}", variant_index);
841
842     let statements = expand_aggregate(
843         Place::return_place(),
844         adt_def.variants[variant_index].fields.iter().enumerate().map(|(idx, field_def)| {
845             (Operand::Move(Place::from(Local::new(idx + 1))), field_def.ty(tcx, substs))
846         }),
847         AggregateKind::Adt(adt_def, variant_index, substs, None, None),
848         source_info,
849         tcx,
850     )
851     .collect();
852
853     let start_block = BasicBlockData {
854         statements,
855         terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
856         is_cleanup: false,
857     };
858
859     let body =
860         new_body(IndexVec::from_elem_n(start_block, 1), local_decls, sig.inputs().len(), span);
861
862     crate::util::dump_mir(
863         tcx,
864         None,
865         "mir_map",
866         &0,
867         crate::transform::MirSource::item(ctor_id),
868         &body,
869         |_, _| Ok(()),
870     );
871
872     let mut body = BodyAndCache::new(body);
873     body.ensure_predecessors();
874     tcx.arena.alloc(body)
875 }