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