]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/util/elaborate_drops.rs
rustc: reintroduce lifetime bounds where necessary.
[rust.git] / src / librustc_mir / util / elaborate_drops.rs
1 use std::fmt;
2 use rustc::hir;
3 use rustc::mir::*;
4 use rustc::middle::lang_items;
5 use rustc::traits::Reveal;
6 use rustc::ty::{self, Ty, TyCtxt};
7 use rustc::ty::layout::VariantIdx;
8 use rustc::ty::subst::SubstsRef;
9 use rustc::ty::util::IntTypeExt;
10 use rustc_data_structures::indexed_vec::Idx;
11 use crate::util::patch::MirPatch;
12
13 use std::convert::TryInto;
14
15 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
16 pub enum DropFlagState {
17     Present, // i.e., initialized
18     Absent, // i.e., deinitialized or "moved"
19 }
20
21 impl DropFlagState {
22     pub fn value(self) -> bool {
23         match self {
24             DropFlagState::Present => true,
25             DropFlagState::Absent => false
26         }
27     }
28 }
29
30 #[derive(Debug)]
31 pub enum DropStyle {
32     Dead,
33     Static,
34     Conditional,
35     Open,
36 }
37
38 #[derive(Debug)]
39 pub enum DropFlagMode {
40     Shallow,
41     Deep
42 }
43
44 #[derive(Copy, Clone, Debug)]
45 pub enum Unwind {
46     To(BasicBlock),
47     InCleanup
48 }
49
50 impl Unwind {
51     fn is_cleanup(self) -> bool {
52         match self {
53             Unwind::To(..) => false,
54             Unwind::InCleanup => true
55         }
56     }
57
58     fn into_option(self) -> Option<BasicBlock> {
59         match self {
60             Unwind::To(bb) => Some(bb),
61             Unwind::InCleanup => None,
62         }
63     }
64
65     fn map<F>(self, f: F) -> Self where F: FnOnce(BasicBlock) -> BasicBlock {
66         match self {
67             Unwind::To(bb) => Unwind::To(f(bb)),
68             Unwind::InCleanup => Unwind::InCleanup
69         }
70     }
71 }
72
73 pub trait DropElaborator<'a, 'tcx>: fmt::Debug {
74     type Path : Copy + fmt::Debug;
75
76     fn patch(&mut self) -> &mut MirPatch<'tcx>;
77     fn body(&self) -> &'a Body<'tcx>;
78     fn tcx(&self) -> TyCtxt<'tcx>;
79     fn param_env(&self) -> ty::ParamEnv<'tcx>;
80
81     fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle;
82     fn get_drop_flag(&mut self, path: Self::Path) -> Option<Operand<'tcx>>;
83     fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode);
84
85
86     fn field_subpath(&self, path: Self::Path, field: Field) -> Option<Self::Path>;
87     fn deref_subpath(&self, path: Self::Path) -> Option<Self::Path>;
88     fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option<Self::Path>;
89     fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option<Self::Path>;
90 }
91
92 #[derive(Debug)]
93 struct DropCtxt<'l, 'b, 'tcx, D>
94     where D : DropElaborator<'b, 'tcx> + 'l
95 {
96     elaborator: &'l mut D,
97
98     source_info: SourceInfo,
99
100     place: &'l Place<'tcx>,
101     path: D::Path,
102     succ: BasicBlock,
103     unwind: Unwind,
104 }
105
106 pub fn elaborate_drop<'b, 'tcx, D>(
107     elaborator: &mut D,
108     source_info: SourceInfo,
109     place: &Place<'tcx>,
110     path: D::Path,
111     succ: BasicBlock,
112     unwind: Unwind,
113     bb: BasicBlock)
114     where D: DropElaborator<'b, 'tcx>,
115     'tcx: 'b,
116 {
117     DropCtxt {
118         elaborator, source_info, place, path, succ, unwind
119     }.elaborate_drop(bb)
120 }
121
122 impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
123 where
124     D: DropElaborator<'b, 'tcx>,
125     'tcx: 'b,
126 {
127     fn place_ty(&self, place: &Place<'tcx>) -> Ty<'tcx> {
128         place.ty(self.elaborator.body(), self.tcx()).ty
129     }
130
131     fn tcx(&self) -> TyCtxt<'tcx> {
132         self.elaborator.tcx()
133     }
134
135     /// This elaborates a single drop instruction, located at `bb`, and
136     /// patches over it.
137     ///
138     /// The elaborated drop checks the drop flags to only drop what
139     /// is initialized.
140     ///
141     /// In addition, the relevant drop flags also need to be cleared
142     /// to avoid double-drops. However, in the middle of a complex
143     /// drop, one must avoid clearing some of the flags before they
144     /// are read, as that would cause a memory leak.
145     ///
146     /// In particular, when dropping an ADT, multiple fields may be
147     /// joined together under the `rest` subpath. They are all controlled
148     /// by the primary drop flag, but only the last rest-field dropped
149     /// should clear it (and it must also not clear anything else).
150     //
151     // FIXME: I think we should just control the flags externally,
152     // and then we do not need this machinery.
153     pub fn elaborate_drop(&mut self, bb: BasicBlock) {
154         debug!("elaborate_drop({:?})", self);
155         let style = self.elaborator.drop_style(self.path, DropFlagMode::Deep);
156         debug!("elaborate_drop({:?}): live - {:?}", self, style);
157         match style {
158             DropStyle::Dead => {
159                 self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto {
160                     target: self.succ
161                 });
162             }
163             DropStyle::Static => {
164                 let loc = self.terminator_loc(bb);
165                 self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep);
166                 self.elaborator.patch().patch_terminator(bb, TerminatorKind::Drop {
167                     location: self.place.clone(),
168                     target: self.succ,
169                     unwind: self.unwind.into_option(),
170                 });
171             }
172             DropStyle::Conditional => {
173                 let unwind = self.unwind; // FIXME(#43234)
174                 let succ = self.succ;
175                 let drop_bb = self.complete_drop(Some(DropFlagMode::Deep), succ, unwind);
176                 self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto {
177                     target: drop_bb
178                 });
179             }
180             DropStyle::Open => {
181                 let drop_bb = self.open_drop();
182                 self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto {
183                     target: drop_bb
184                 });
185             }
186         }
187     }
188
189     /// Returns the place and move path for each field of `variant`,
190     /// (the move path is `None` if the field is a rest field).
191     fn move_paths_for_fields(&self,
192                              base_place: &Place<'tcx>,
193                              variant_path: D::Path,
194                              variant: &'tcx ty::VariantDef,
195                              substs: SubstsRef<'tcx>)
196                              -> Vec<(Place<'tcx>, Option<D::Path>)>
197     {
198         variant.fields.iter().enumerate().map(|(i, f)| {
199             let field = Field::new(i);
200             let subpath = self.elaborator.field_subpath(variant_path, field);
201
202             assert_eq!(self.elaborator.param_env().reveal, Reveal::All);
203             let field_ty = self.tcx().normalize_erasing_regions(
204                 self.elaborator.param_env(),
205                 f.ty(self.tcx(), substs),
206             );
207             (base_place.clone().field(field, field_ty), subpath)
208         }).collect()
209     }
210
211     fn drop_subpath(&mut self,
212                     place: &Place<'tcx>,
213                     path: Option<D::Path>,
214                     succ: BasicBlock,
215                     unwind: Unwind)
216                     -> BasicBlock
217     {
218         if let Some(path) = path {
219             debug!("drop_subpath: for std field {:?}", place);
220
221             DropCtxt {
222                 elaborator: self.elaborator,
223                 source_info: self.source_info,
224                 path, place, succ, unwind,
225             }.elaborated_drop_block()
226         } else {
227             debug!("drop_subpath: for rest field {:?}", place);
228
229             DropCtxt {
230                 elaborator: self.elaborator,
231                 source_info: self.source_info,
232                 place, succ, unwind,
233                 // Using `self.path` here to condition the drop on
234                 // our own drop flag.
235                 path: self.path
236             }.complete_drop(None, succ, unwind)
237         }
238     }
239
240     /// Creates one-half of the drop ladder for a list of fields, and return
241     /// the list of steps in it in reverse order, with the first step
242     /// dropping 0 fields and so on.
243     ///
244     /// `unwind_ladder` is such a list of steps in reverse order,
245     /// which is called if the matching step of the drop glue panics.
246     fn drop_halfladder(&mut self,
247                        unwind_ladder: &[Unwind],
248                        mut succ: BasicBlock,
249                        fields: &[(Place<'tcx>, Option<D::Path>)])
250                        -> Vec<BasicBlock>
251     {
252         Some(succ).into_iter().chain(
253             fields.iter().rev().zip(unwind_ladder)
254                 .map(|(&(ref place, path), &unwind_succ)| {
255                     succ = self.drop_subpath(place, path, succ, unwind_succ);
256                     succ
257                 })
258         ).collect()
259     }
260
261     fn drop_ladder_bottom(&mut self) -> (BasicBlock, Unwind) {
262         // Clear the "master" drop flag at the end. This is needed
263         // because the "master" drop protects the ADT's discriminant,
264         // which is invalidated after the ADT is dropped.
265         let (succ, unwind) = (self.succ, self.unwind); // FIXME(#43234)
266         (
267             self.drop_flag_reset_block(DropFlagMode::Shallow, succ, unwind),
268             unwind.map(|unwind| {
269                 self.drop_flag_reset_block(DropFlagMode::Shallow, unwind, Unwind::InCleanup)
270             })
271         )
272     }
273
274     /// Creates a full drop ladder, consisting of 2 connected half-drop-ladders
275     ///
276     /// For example, with 3 fields, the drop ladder is
277     ///
278     /// .d0:
279     ///     ELAB(drop location.0 [target=.d1, unwind=.c1])
280     /// .d1:
281     ///     ELAB(drop location.1 [target=.d2, unwind=.c2])
282     /// .d2:
283     ///     ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`])
284     /// .c1:
285     ///     ELAB(drop location.1 [target=.c2])
286     /// .c2:
287     ///     ELAB(drop location.2 [target=`self.unwind`])
288     ///
289     /// NOTE: this does not clear the master drop flag, so you need
290     /// to point succ/unwind on a `drop_ladder_bottom`.
291     fn drop_ladder(
292         &mut self,
293         fields: Vec<(Place<'tcx>, Option<D::Path>)>,
294         succ: BasicBlock,
295         unwind: Unwind,
296     ) -> (BasicBlock, Unwind) {
297         debug!("drop_ladder({:?}, {:?})", self, fields);
298
299         let mut fields = fields;
300         fields.retain(|&(ref place, _)| {
301             self.place_ty(place).needs_drop(self.tcx(), self.elaborator.param_env())
302         });
303
304         debug!("drop_ladder - fields needing drop: {:?}", fields);
305
306         let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1];
307         let unwind_ladder: Vec<_> = if let Unwind::To(target) = unwind {
308             let halfladder = self.drop_halfladder(&unwind_ladder, target, &fields);
309             halfladder.into_iter().map(Unwind::To).collect()
310         } else {
311             unwind_ladder
312         };
313
314         let normal_ladder =
315             self.drop_halfladder(&unwind_ladder, succ, &fields);
316
317         (*normal_ladder.last().unwrap(), *unwind_ladder.last().unwrap())
318     }
319
320     fn open_drop_for_tuple(&mut self, tys: &[Ty<'tcx>]) -> BasicBlock {
321         debug!("open_drop_for_tuple({:?}, {:?})", self, tys);
322
323         let fields = tys.iter().enumerate().map(|(i, &ty)| {
324             (self.place.clone().field(Field::new(i), ty),
325              self.elaborator.field_subpath(self.path, Field::new(i)))
326         }).collect();
327
328         let (succ, unwind) = self.drop_ladder_bottom();
329         self.drop_ladder(fields, succ, unwind).0
330     }
331
332     fn open_drop_for_box(&mut self, adt: &'tcx ty::AdtDef, substs: SubstsRef<'tcx>) -> BasicBlock {
333         debug!("open_drop_for_box({:?}, {:?}, {:?})", self, adt, substs);
334
335         let interior = self.place.clone().deref();
336         let interior_path = self.elaborator.deref_subpath(self.path);
337
338         let succ = self.succ; // FIXME(#43234)
339         let unwind = self.unwind;
340         let succ = self.box_free_block(adt, substs, succ, unwind);
341         let unwind_succ = self.unwind.map(|unwind| {
342             self.box_free_block(adt, substs, unwind, Unwind::InCleanup)
343         });
344
345         self.drop_subpath(&interior, interior_path, succ, unwind_succ)
346     }
347
348     fn open_drop_for_adt(&mut self, adt: &'tcx ty::AdtDef, substs: SubstsRef<'tcx>) -> BasicBlock {
349         debug!("open_drop_for_adt({:?}, {:?}, {:?})", self, adt, substs);
350         if adt.variants.len() == 0 {
351             return self.elaborator.patch().new_block(BasicBlockData {
352                 statements: vec![],
353                 terminator: Some(Terminator {
354                     source_info: self.source_info,
355                     kind: TerminatorKind::Unreachable
356                 }),
357                 is_cleanup: self.unwind.is_cleanup()
358             });
359         }
360
361         let skip_contents =
362             adt.is_union() || Some(adt.did) == self.tcx().lang_items().manually_drop();
363         let contents_drop = if skip_contents {
364             (self.succ, self.unwind)
365         } else {
366             self.open_drop_for_adt_contents(adt, substs)
367         };
368
369         if adt.has_dtor(self.tcx()) {
370             self.destructor_call_block(contents_drop)
371         } else {
372             contents_drop.0
373         }
374     }
375
376     fn open_drop_for_adt_contents(&mut self, adt: &'tcx ty::AdtDef,
377                                   substs: SubstsRef<'tcx>)
378                                   -> (BasicBlock, Unwind) {
379         let (succ, unwind) = self.drop_ladder_bottom();
380         if !adt.is_enum() {
381             let fields = self.move_paths_for_fields(
382                 self.place,
383                 self.path,
384                 &adt.variants[VariantIdx::new(0)],
385                 substs
386             );
387             self.drop_ladder(fields, succ, unwind)
388         } else {
389             self.open_drop_for_multivariant(adt, substs, succ, unwind)
390         }
391     }
392
393     fn open_drop_for_multivariant(&mut self, adt: &'tcx ty::AdtDef,
394                                   substs: SubstsRef<'tcx>,
395                                   succ: BasicBlock,
396                                   unwind: Unwind)
397                                   -> (BasicBlock, Unwind) {
398         let mut values = Vec::with_capacity(adt.variants.len());
399         let mut normal_blocks = Vec::with_capacity(adt.variants.len());
400         let mut unwind_blocks = if unwind.is_cleanup() {
401             None
402         } else {
403             Some(Vec::with_capacity(adt.variants.len()))
404         };
405
406         let mut have_otherwise = false;
407
408         for (variant_index, discr) in adt.discriminants(self.tcx()) {
409             let subpath = self.elaborator.downcast_subpath(
410                 self.path, variant_index);
411             if let Some(variant_path) = subpath {
412                 let base_place = self.place.clone().elem(
413                     ProjectionElem::Downcast(Some(adt.variants[variant_index].ident.name),
414                                              variant_index));
415                 let fields = self.move_paths_for_fields(
416                     &base_place,
417                     variant_path,
418                     &adt.variants[variant_index],
419                     substs);
420                 values.push(discr.val);
421                 if let Unwind::To(unwind) = unwind {
422                     // We can't use the half-ladder from the original
423                     // drop ladder, because this breaks the
424                     // "funclet can't have 2 successor funclets"
425                     // requirement from MSVC:
426                     //
427                     //           switch       unwind-switch
428                     //          /      \         /        \
429                     //         v1.0    v2.0  v2.0-unwind  v1.0-unwind
430                     //         |        |      /             |
431                     //    v1.1-unwind  v2.1-unwind           |
432                     //      ^                                |
433                     //       \-------------------------------/
434                     //
435                     // Create a duplicate half-ladder to avoid that. We
436                     // could technically only do this on MSVC, but I
437                     // I want to minimize the divergence between MSVC
438                     // and non-MSVC.
439
440                     let unwind_blocks = unwind_blocks.as_mut().unwrap();
441                     let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1];
442                     let halfladder =
443                         self.drop_halfladder(&unwind_ladder, unwind, &fields);
444                     unwind_blocks.push(halfladder.last().cloned().unwrap());
445                 }
446                 let (normal, _) = self.drop_ladder(fields, succ, unwind);
447                 normal_blocks.push(normal);
448             } else {
449                 have_otherwise = true;
450             }
451         }
452
453         if have_otherwise {
454             normal_blocks.push(self.drop_block(succ, unwind));
455             if let Unwind::To(unwind) = unwind {
456                 unwind_blocks.as_mut().unwrap().push(
457                     self.drop_block(unwind, Unwind::InCleanup)
458                         );
459             }
460         } else {
461             values.pop();
462         }
463
464         (self.adt_switch_block(adt, normal_blocks, &values, succ, unwind),
465          unwind.map(|unwind| {
466              self.adt_switch_block(
467                  adt, unwind_blocks.unwrap(), &values, unwind, Unwind::InCleanup
468              )
469          }))
470     }
471
472     fn adt_switch_block(&mut self,
473                         adt: &'tcx ty::AdtDef,
474                         blocks: Vec<BasicBlock>,
475                         values: &[u128],
476                         succ: BasicBlock,
477                         unwind: Unwind)
478                         -> BasicBlock {
479         // If there are multiple variants, then if something
480         // is present within the enum the discriminant, tracked
481         // by the rest path, must be initialized.
482         //
483         // Additionally, we do not want to switch on the
484         // discriminant after it is free-ed, because that
485         // way lies only trouble.
486         let discr_ty = adt.repr.discr_type().to_ty(self.tcx());
487         let discr = Place::Base(PlaceBase::Local(self.new_temp(discr_ty)));
488         let discr_rv = Rvalue::Discriminant(self.place.clone());
489         let switch_block = BasicBlockData {
490             statements: vec![self.assign(&discr, discr_rv)],
491             terminator: Some(Terminator {
492                 source_info: self.source_info,
493                 kind: TerminatorKind::SwitchInt {
494                     discr: Operand::Move(discr),
495                     switch_ty: discr_ty,
496                     values: From::from(values.to_owned()),
497                     targets: blocks,
498                 }
499             }),
500             is_cleanup: unwind.is_cleanup(),
501         };
502         let switch_block = self.elaborator.patch().new_block(switch_block);
503         self.drop_flag_test_block(switch_block, succ, unwind)
504     }
505
506     fn destructor_call_block(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> BasicBlock {
507         debug!("destructor_call_block({:?}, {:?})", self, succ);
508         let tcx = self.tcx();
509         let drop_trait = tcx.lang_items().drop_trait().unwrap();
510         let drop_fn = tcx.associated_items(drop_trait).next().unwrap();
511         let ty = self.place_ty(self.place);
512         let substs = tcx.mk_substs_trait(ty, &[]);
513
514         let ref_ty = tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut {
515             ty,
516             mutbl: hir::Mutability::MutMutable
517         });
518         let ref_place = self.new_temp(ref_ty);
519         let unit_temp = Place::Base(PlaceBase::Local(self.new_temp(tcx.mk_unit())));
520
521         let result = BasicBlockData {
522             statements: vec![self.assign(
523                 &Place::Base(PlaceBase::Local(ref_place)),
524                 Rvalue::Ref(tcx.lifetimes.re_erased,
525                             BorrowKind::Mut { allow_two_phase_borrow: false },
526                             self.place.clone())
527             )],
528             terminator: Some(Terminator {
529                 kind: TerminatorKind::Call {
530                     func: Operand::function_handle(tcx, drop_fn.def_id, substs,
531                                                    self.source_info.span),
532                     args: vec![Operand::Move(Place::Base(PlaceBase::Local(ref_place)))],
533                     destination: Some((unit_temp, succ)),
534                     cleanup: unwind.into_option(),
535                     from_hir_call: true,
536                 },
537                 source_info: self.source_info,
538             }),
539             is_cleanup: unwind.is_cleanup(),
540         };
541         self.elaborator.patch().new_block(result)
542     }
543
544     /// Create a loop that drops an array:
545     ///
546     /// ```text
547     /// loop-block:
548     ///    can_go = cur == length_or_end
549     ///    if can_go then succ else drop-block
550     /// drop-block:
551     ///    if ptr_based {
552     ///        ptr = &mut *cur
553     ///        cur = cur.offset(1)
554     ///    } else {
555     ///        ptr = &mut P[cur]
556     ///        cur = cur + 1
557     ///    }
558     ///    drop(ptr)
559     /// ```
560     fn drop_loop(
561         &mut self,
562         succ: BasicBlock,
563         cur: Local,
564         length_or_end: &Place<'tcx>,
565         ety: Ty<'tcx>,
566         unwind: Unwind,
567         ptr_based: bool,
568     ) -> BasicBlock {
569         let copy = |place: &Place<'tcx>| Operand::Copy(place.clone());
570         let move_ = |place: &Place<'tcx>| Operand::Move(place.clone());
571         let tcx = self.tcx();
572
573         let ref_ty = tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut {
574             ty: ety,
575             mutbl: hir::Mutability::MutMutable
576         });
577         let ptr = &Place::Base(PlaceBase::Local(self.new_temp(ref_ty)));
578         let can_go = &Place::Base(PlaceBase::Local(self.new_temp(tcx.types.bool)));
579
580         let one = self.constant_usize(1);
581         let (ptr_next, cur_next) = if ptr_based {
582             (Rvalue::Ref(
583                 tcx.lifetimes.re_erased,
584                 BorrowKind::Mut { allow_two_phase_borrow: false },
585                 Place::Projection(Box::new(Projection {
586                     base: Place::Base(PlaceBase::Local(cur)),
587                     elem: ProjectionElem::Deref,
588                 }))
589              ),
590              Rvalue::BinaryOp(BinOp::Offset, move_(&Place::Base(PlaceBase::Local(cur))), one))
591         } else {
592             (Rvalue::Ref(
593                  tcx.lifetimes.re_erased,
594                  BorrowKind::Mut { allow_two_phase_borrow: false },
595                  self.place.clone().index(cur)),
596              Rvalue::BinaryOp(BinOp::Add, move_(&Place::Base(PlaceBase::Local(cur))), one))
597         };
598
599         let drop_block = BasicBlockData {
600             statements: vec![
601                 self.assign(ptr, ptr_next),
602                 self.assign(&Place::Base(PlaceBase::Local(cur)), cur_next)
603             ],
604             is_cleanup: unwind.is_cleanup(),
605             terminator: Some(Terminator {
606                 source_info: self.source_info,
607                 // this gets overwritten by drop elaboration.
608                 kind: TerminatorKind::Unreachable,
609             })
610         };
611         let drop_block = self.elaborator.patch().new_block(drop_block);
612
613         let loop_block = BasicBlockData {
614             statements: vec![
615                 self.assign(can_go, Rvalue::BinaryOp(BinOp::Eq,
616                                                      copy(&Place::Base(PlaceBase::Local(cur))),
617                                                      copy(length_or_end)))
618             ],
619             is_cleanup: unwind.is_cleanup(),
620             terminator: Some(Terminator {
621                 source_info: self.source_info,
622                 kind: TerminatorKind::if_(tcx, move_(can_go), succ, drop_block)
623             })
624         };
625         let loop_block = self.elaborator.patch().new_block(loop_block);
626
627         self.elaborator.patch().patch_terminator(drop_block, TerminatorKind::Drop {
628             location: ptr.clone().deref(),
629             target: loop_block,
630             unwind: unwind.into_option()
631         });
632
633         loop_block
634     }
635
636     fn open_drop_for_array(&mut self, ety: Ty<'tcx>, opt_size: Option<u64>) -> BasicBlock {
637         debug!("open_drop_for_array({:?}, {:?})", ety, opt_size);
638
639         // if size_of::<ety>() == 0 {
640         //     index_based_loop
641         // } else {
642         //     ptr_based_loop
643         // }
644
645         if let Some(size) = opt_size {
646             let size: u32 = size.try_into().unwrap_or_else(|_| {
647                 bug!("move out check isn't implemented for array sizes bigger than u32::MAX");
648             });
649             let fields: Vec<(Place<'tcx>, Option<D::Path>)> = (0..size).map(|i| {
650                 (self.place.clone().elem(ProjectionElem::ConstantIndex{
651                     offset: i,
652                     min_length: size,
653                     from_end: false
654                 }),
655                  self.elaborator.array_subpath(self.path, i, size))
656             }).collect();
657
658             if fields.iter().any(|(_,path)| path.is_some()) {
659                 let (succ, unwind) = self.drop_ladder_bottom();
660                 return self.drop_ladder(fields, succ, unwind).0
661             }
662         }
663
664         let move_ = |place: &Place<'tcx>| Operand::Move(place.clone());
665         let tcx = self.tcx();
666         let elem_size = &Place::Base(PlaceBase::Local(self.new_temp(tcx.types.usize)));
667         let len = &Place::Base(PlaceBase::Local(self.new_temp(tcx.types.usize)));
668
669         static USIZE_SWITCH_ZERO: &[u128] = &[0];
670
671         let base_block = BasicBlockData {
672             statements: vec![
673                 self.assign(elem_size, Rvalue::NullaryOp(NullOp::SizeOf, ety)),
674                 self.assign(len, Rvalue::Len(self.place.clone())),
675             ],
676             is_cleanup: self.unwind.is_cleanup(),
677             terminator: Some(Terminator {
678                 source_info: self.source_info,
679                 kind: TerminatorKind::SwitchInt {
680                     discr: move_(elem_size),
681                     switch_ty: tcx.types.usize,
682                     values: From::from(USIZE_SWITCH_ZERO),
683                     targets: vec![
684                         self.drop_loop_pair(ety, false, len.clone()),
685                         self.drop_loop_pair(ety, true, len.clone()),
686                     ],
687                 },
688             })
689         };
690         self.elaborator.patch().new_block(base_block)
691     }
692
693     /// Ceates a pair of drop-loops of `place`, which drops its contents, even
694     /// in the case of 1 panic. If `ptr_based`, creates a pointer loop,
695     /// otherwise create an index loop.
696     fn drop_loop_pair(
697         &mut self,
698         ety: Ty<'tcx>,
699         ptr_based: bool,
700         length: Place<'tcx>,
701     ) -> BasicBlock {
702         debug!("drop_loop_pair({:?}, {:?})", ety, ptr_based);
703         let tcx = self.tcx();
704         let iter_ty = if ptr_based {
705             tcx.mk_mut_ptr(ety)
706         } else {
707             tcx.types.usize
708         };
709
710         let cur = self.new_temp(iter_ty);
711         let length_or_end = if ptr_based {
712             // FIXME check if we want to make it return a `Place` directly
713             // if all use sites want a `Place::Base` anyway.
714             let temp = self.new_temp(iter_ty);
715             Place::Base(PlaceBase::Local(temp))
716         } else {
717             length.clone()
718         };
719
720         let unwind = self.unwind.map(|unwind| {
721             self.drop_loop(unwind,
722                            cur,
723                            &length_or_end,
724                            ety,
725                            Unwind::InCleanup,
726                            ptr_based)
727         });
728
729         let loop_block = self.drop_loop(
730             self.succ,
731             cur,
732             &length_or_end,
733             ety,
734             unwind,
735             ptr_based);
736
737         let cur = Place::Base(PlaceBase::Local(cur));
738         let drop_block_stmts = if ptr_based {
739             let tmp_ty = tcx.mk_mut_ptr(self.place_ty(self.place));
740             let tmp = Place::Base(PlaceBase::Local(self.new_temp(tmp_ty)));
741             // tmp = &mut P;
742             // cur = tmp as *mut T;
743             // end = Offset(cur, len);
744             vec![
745                 self.assign(&tmp, Rvalue::Ref(
746                     tcx.lifetimes.re_erased,
747                     BorrowKind::Mut { allow_two_phase_borrow: false },
748                     self.place.clone()
749                 )),
750                 self.assign(
751                     &cur,
752                     Rvalue::Cast(CastKind::Misc, Operand::Move(tmp), iter_ty),
753                 ),
754                 self.assign(
755                     &length_or_end,
756                     Rvalue::BinaryOp(BinOp::Offset, Operand::Copy(cur), Operand::Move(length)
757                 )),
758             ]
759         } else {
760             // cur = 0 (length already pushed)
761             let zero = self.constant_usize(0);
762             vec![self.assign(&cur, Rvalue::Use(zero))]
763         };
764         let drop_block = self.elaborator.patch().new_block(BasicBlockData {
765             statements: drop_block_stmts,
766             is_cleanup: unwind.is_cleanup(),
767             terminator: Some(Terminator {
768                 source_info: self.source_info,
769                 kind: TerminatorKind::Goto { target: loop_block }
770             })
771         });
772
773         // FIXME(#34708): handle partially-dropped array/slice elements.
774         let reset_block = self.drop_flag_reset_block(DropFlagMode::Deep, drop_block, unwind);
775         self.drop_flag_test_block(reset_block, self.succ, unwind)
776     }
777
778     /// The slow-path - create an "open", elaborated drop for a type
779     /// which is moved-out-of only partially, and patch `bb` to a jump
780     /// to it. This must not be called on ADTs with a destructor,
781     /// as these can't be moved-out-of, except for `Box<T>`, which is
782     /// special-cased.
783     ///
784     /// This creates a "drop ladder" that drops the needed fields of the
785     /// ADT, both in the success case or if one of the destructors fail.
786     fn open_drop(&mut self) -> BasicBlock {
787         let ty = self.place_ty(self.place);
788         match ty.sty {
789             ty::Closure(def_id, substs) => {
790                 let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
791                 self.open_drop_for_tuple(&tys)
792             }
793             // Note that `elaborate_drops` only drops the upvars of a generator,
794             // and this is ok because `open_drop` here can only be reached
795             // within that own generator's resume function.
796             // This should only happen for the self argument on the resume function.
797             // It effetively only contains upvars until the generator transformation runs.
798             // See librustc_body/transform/generator.rs for more details.
799             ty::Generator(def_id, substs, _) => {
800                 let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
801                 self.open_drop_for_tuple(&tys)
802             }
803             ty::Tuple(tys) => {
804                 let tys: Vec<_> = tys.iter().map(|k| k.expect_ty()).collect();
805                 self.open_drop_for_tuple(&tys)
806             }
807             ty::Adt(def, substs) => {
808                 if def.is_box() {
809                     self.open_drop_for_box(def, substs)
810                 } else {
811                     self.open_drop_for_adt(def, substs)
812                 }
813             }
814             ty::Dynamic(..) => {
815                 let unwind = self.unwind; // FIXME(#43234)
816                 let succ = self.succ;
817                 self.complete_drop(Some(DropFlagMode::Deep), succ, unwind)
818             }
819             ty::Array(ety, size) => {
820                 let size = size.assert_usize(self.tcx());
821                 self.open_drop_for_array(ety, size)
822             },
823             ty::Slice(ety) => self.open_drop_for_array(ety, None),
824
825             _ => bug!("open drop from non-ADT `{:?}`", ty)
826         }
827     }
828
829     /// Returns a basic block that drop a place using the context
830     /// and path in `c`. If `mode` is something, also clear `c`
831     /// according to it.
832     ///
833     /// if FLAG(self.path)
834     ///     if let Some(mode) = mode: FLAG(self.path)[mode] = false
835     ///     drop(self.place)
836     fn complete_drop(
837         &mut self,
838         drop_mode: Option<DropFlagMode>,
839         succ: BasicBlock,
840         unwind: Unwind,
841     ) -> BasicBlock {
842         debug!("complete_drop({:?},{:?})", self, drop_mode);
843
844         let drop_block = self.drop_block(succ, unwind);
845         let drop_block = if let Some(mode) = drop_mode {
846             self.drop_flag_reset_block(mode, drop_block, unwind)
847         } else {
848             drop_block
849         };
850
851         self.drop_flag_test_block(drop_block, succ, unwind)
852     }
853
854     fn drop_flag_reset_block(&mut self,
855                              mode: DropFlagMode,
856                              succ: BasicBlock,
857                              unwind: Unwind) -> BasicBlock
858     {
859         debug!("drop_flag_reset_block({:?},{:?})", self, mode);
860
861         let block = self.new_block(unwind, TerminatorKind::Goto { target: succ });
862         let block_start = Location { block: block, statement_index: 0 };
863         self.elaborator.clear_drop_flag(block_start, self.path, mode);
864         block
865     }
866
867     fn elaborated_drop_block(&mut self) -> BasicBlock {
868         debug!("elaborated_drop_block({:?})", self);
869         let unwind = self.unwind; // FIXME(#43234)
870         let succ = self.succ;
871         let blk = self.drop_block(succ, unwind);
872         self.elaborate_drop(blk);
873         blk
874     }
875
876     fn box_free_block(
877         &mut self,
878         adt: &'tcx ty::AdtDef,
879         substs: SubstsRef<'tcx>,
880         target: BasicBlock,
881         unwind: Unwind,
882     ) -> BasicBlock {
883         let block = self.unelaborated_free_block(adt, substs, target, unwind);
884         self.drop_flag_test_block(block, target, unwind)
885     }
886
887     fn unelaborated_free_block(
888         &mut self,
889         adt: &'tcx ty::AdtDef,
890         substs: SubstsRef<'tcx>,
891         target: BasicBlock,
892         unwind: Unwind,
893     ) -> BasicBlock {
894         let tcx = self.tcx();
895         let unit_temp = Place::Base(PlaceBase::Local(self.new_temp(tcx.mk_unit())));
896         let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem);
897         let args = adt.variants[VariantIdx::new(0)].fields.iter().enumerate().map(|(i, f)| {
898             let field = Field::new(i);
899             let field_ty = f.ty(self.tcx(), substs);
900             Operand::Move(self.place.clone().field(field, field_ty))
901         }).collect();
902
903         let call = TerminatorKind::Call {
904             func: Operand::function_handle(tcx, free_func, substs, self.source_info.span),
905             args: args,
906             destination: Some((unit_temp, target)),
907             cleanup: None,
908             from_hir_call: false,
909         }; // FIXME(#43234)
910         let free_block = self.new_block(unwind, call);
911
912         let block_start = Location { block: free_block, statement_index: 0 };
913         self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow);
914         free_block
915     }
916
917     fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock {
918         let block = TerminatorKind::Drop {
919             location: self.place.clone(),
920             target,
921             unwind: unwind.into_option()
922         };
923         self.new_block(unwind, block)
924     }
925
926     fn drop_flag_test_block(&mut self,
927                             on_set: BasicBlock,
928                             on_unset: BasicBlock,
929                             unwind: Unwind)
930                             -> BasicBlock
931     {
932         let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow);
933         debug!("drop_flag_test_block({:?},{:?},{:?},{:?}) - {:?}",
934                self, on_set, on_unset, unwind, style);
935
936         match style {
937             DropStyle::Dead => on_unset,
938             DropStyle::Static => on_set,
939             DropStyle::Conditional | DropStyle::Open => {
940                 let flag = self.elaborator.get_drop_flag(self.path).unwrap();
941                 let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset);
942                 self.new_block(unwind, term)
943             }
944         }
945     }
946
947     fn new_block(&mut self, unwind: Unwind, k: TerminatorKind<'tcx>) -> BasicBlock {
948         self.elaborator.patch().new_block(BasicBlockData {
949             statements: vec![],
950             terminator: Some(Terminator {
951                 source_info: self.source_info, kind: k
952             }),
953             is_cleanup: unwind.is_cleanup()
954         })
955     }
956
957     fn new_temp(&mut self, ty: Ty<'tcx>) -> Local {
958         self.elaborator.patch().new_temp(ty, self.source_info.span)
959     }
960
961     fn terminator_loc(&mut self, bb: BasicBlock) -> Location {
962         let body = self.elaborator.body();
963         self.elaborator.patch().terminator_loc(body, bb)
964     }
965
966     fn constant_usize(&self, val: u16) -> Operand<'tcx> {
967         Operand::Constant(box Constant {
968             span: self.source_info.span,
969             ty: self.tcx().types.usize,
970             user_ty: None,
971             literal: ty::Const::from_usize(self.tcx(), val.into()),
972         })
973     }
974
975     fn assign(&self, lhs: &Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> {
976         Statement {
977             source_info: self.source_info,
978             kind: StatementKind::Assign(lhs.clone(), box rhs)
979         }
980     }
981 }