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