]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/util/elaborate_drops.rs
Switch wasm math symbols to their original names
[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_nil()));
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                 },
549                 source_info: self.source_info
550             }),
551             is_cleanup: unwind.is_cleanup(),
552         };
553         self.elaborator.patch().new_block(result)
554     }
555
556     /// create a loop that drops an array:
557     ///
558
559     ///
560     /// loop-block:
561     ///    can_go = cur == length_or_end
562     ///    if can_go then succ else drop-block
563     /// drop-block:
564     ///    if ptr_based {
565     ///        ptr = &mut *cur
566     ///        cur = cur.offset(1)
567     ///    } else {
568     ///        ptr = &mut P[cur]
569     ///        cur = cur + 1
570     ///    }
571     ///    drop(ptr)
572     fn drop_loop(&mut self,
573                  succ: BasicBlock,
574                  cur: Local,
575                  length_or_end: &Place<'tcx>,
576                  ety: Ty<'tcx>,
577                  unwind: Unwind,
578                  ptr_based: bool)
579                  -> BasicBlock
580     {
581         let copy = |place: &Place<'tcx>| Operand::Copy(place.clone());
582         let move_ = |place: &Place<'tcx>| Operand::Move(place.clone());
583         let tcx = self.tcx();
584
585         let ref_ty = tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut {
586             ty: ety,
587             mutbl: hir::Mutability::MutMutable
588         });
589         let ptr = &Place::Local(self.new_temp(ref_ty));
590         let can_go = &Place::Local(self.new_temp(tcx.types.bool));
591
592         let one = self.constant_usize(1);
593         let (ptr_next, cur_next) = if ptr_based {
594             (Rvalue::Ref(
595                 tcx.types.re_erased,
596                 BorrowKind::Mut { allow_two_phase_borrow: false },
597                 Place::Projection(Box::new(Projection {
598                     base: Place::Local(cur),
599                     elem: ProjectionElem::Deref,
600                 }))
601              ),
602              Rvalue::BinaryOp(BinOp::Offset, copy(&Place::Local(cur)), one))
603         } else {
604             (Rvalue::Ref(
605                  tcx.types.re_erased,
606                  BorrowKind::Mut { allow_two_phase_borrow: false },
607                  self.place.clone().index(cur)),
608              Rvalue::BinaryOp(BinOp::Add, copy(&Place::Local(cur)), one))
609         };
610
611         let drop_block = BasicBlockData {
612             statements: vec![
613                 self.assign(ptr, ptr_next),
614                 self.assign(&Place::Local(cur), cur_next)
615             ],
616             is_cleanup: unwind.is_cleanup(),
617             terminator: Some(Terminator {
618                 source_info: self.source_info,
619                 // this gets overwritten by drop elaboration.
620                 kind: TerminatorKind::Unreachable,
621             })
622         };
623         let drop_block = self.elaborator.patch().new_block(drop_block);
624
625         let loop_block = BasicBlockData {
626             statements: vec![
627                 self.assign(can_go, Rvalue::BinaryOp(BinOp::Eq,
628                                                      copy(&Place::Local(cur)),
629                                                      copy(length_or_end)))
630             ],
631             is_cleanup: unwind.is_cleanup(),
632             terminator: Some(Terminator {
633                 source_info: self.source_info,
634                 kind: TerminatorKind::if_(tcx, move_(can_go), succ, drop_block)
635             })
636         };
637         let loop_block = self.elaborator.patch().new_block(loop_block);
638
639         self.elaborator.patch().patch_terminator(drop_block, TerminatorKind::Drop {
640             location: ptr.clone().deref(),
641             target: loop_block,
642             unwind: unwind.into_option()
643         });
644
645         loop_block
646     }
647
648     fn open_drop_for_array(&mut self, ety: Ty<'tcx>, opt_size: Option<u64>) -> BasicBlock {
649         debug!("open_drop_for_array({:?}, {:?})", ety, opt_size);
650
651         // if size_of::<ety>() == 0 {
652         //     index_based_loop
653         // } else {
654         //     ptr_based_loop
655         // }
656
657         if let Some(size) = opt_size {
658             assert!(size <= (u32::MAX as u64),
659                     "move out check doesn't implemented for array bigger then u32");
660             let size = size as u32;
661             let fields: Vec<(Place<'tcx>, Option<D::Path>)> = (0..size).map(|i| {
662                 (self.place.clone().elem(ProjectionElem::ConstantIndex{
663                     offset: i,
664                     min_length: size,
665                     from_end: false
666                 }),
667                  self.elaborator.array_subpath(self.path, i, size))
668             }).collect();
669
670             if fields.iter().any(|(_,path)| path.is_some()) {
671                 let (succ, unwind) = self.drop_ladder_bottom();
672                 return self.drop_ladder(fields, succ, unwind).0
673             }
674         }
675
676         let move_ = |place: &Place<'tcx>| Operand::Move(place.clone());
677         let tcx = self.tcx();
678         let size = &Place::Local(self.new_temp(tcx.types.usize));
679         let size_is_zero = &Place::Local(self.new_temp(tcx.types.bool));
680         let base_block = BasicBlockData {
681             statements: vec![
682                 self.assign(size, Rvalue::NullaryOp(NullOp::SizeOf, ety)),
683                 self.assign(size_is_zero, Rvalue::BinaryOp(BinOp::Eq,
684                                                            move_(size),
685                                                            self.constant_usize(0)))
686             ],
687             is_cleanup: self.unwind.is_cleanup(),
688             terminator: Some(Terminator {
689                 source_info: self.source_info,
690                 kind: TerminatorKind::if_(
691                     tcx,
692                     move_(size_is_zero),
693                     self.drop_loop_pair(ety, false),
694                     self.drop_loop_pair(ety, true)
695                 )
696             })
697         };
698         self.elaborator.patch().new_block(base_block)
699     }
700
701     // create a pair of drop-loops of `place`, which drops its contents
702     // even in the case of 1 panic. If `ptr_based`, create a pointer loop,
703     // otherwise create an index loop.
704     fn drop_loop_pair(&mut self, ety: Ty<'tcx>, ptr_based: bool) -> BasicBlock {
705         debug!("drop_loop_pair({:?}, {:?})", ety, ptr_based);
706         let tcx = self.tcx();
707         let iter_ty = if ptr_based {
708             tcx.mk_mut_ptr(ety)
709         } else {
710             tcx.types.usize
711         };
712
713         let cur = self.new_temp(iter_ty);
714         let length = Place::Local(self.new_temp(tcx.types.usize));
715         let length_or_end = if ptr_based {
716             Place::Local(self.new_temp(iter_ty))
717         } else {
718             length.clone()
719         };
720
721         let unwind = self.unwind.map(|unwind| {
722             self.drop_loop(unwind,
723                            cur,
724                            &length_or_end,
725                            ety,
726                            Unwind::InCleanup,
727                            ptr_based)
728         });
729
730         let succ = self.succ; // FIXME(#43234)
731         let loop_block = self.drop_loop(
732             succ,
733             cur,
734             &length_or_end,
735             ety,
736             unwind,
737             ptr_based);
738
739         let cur = Place::Local(cur);
740         let zero = self.constant_usize(0);
741         let mut drop_block_stmts = vec![];
742         drop_block_stmts.push(self.assign(&length, Rvalue::Len(self.place.clone())));
743         if ptr_based {
744             let tmp_ty = tcx.mk_mut_ptr(self.place_ty(self.place));
745             let tmp = Place::Local(self.new_temp(tmp_ty));
746             // tmp = &mut P;
747             // cur = tmp as *mut T;
748             // end = Offset(cur, len);
749             drop_block_stmts.push(self.assign(&tmp, Rvalue::Ref(
750                 tcx.types.re_erased,
751                 BorrowKind::Mut { allow_two_phase_borrow: false },
752                 self.place.clone()
753             )));
754             drop_block_stmts.push(self.assign(&cur, Rvalue::Cast(
755                 CastKind::Misc, Operand::Move(tmp.clone()), iter_ty
756             )));
757             drop_block_stmts.push(self.assign(&length_or_end,
758                 Rvalue::BinaryOp(BinOp::Offset,
759                      Operand::Copy(cur.clone()), Operand::Move(length.clone())
760             )));
761         } else {
762             // index = 0 (length already pushed)
763             drop_block_stmts.push(self.assign(&cur, Rvalue::Use(zero)));
764         }
765         let drop_block = self.elaborator.patch().new_block(BasicBlockData {
766             statements: drop_block_stmts,
767             is_cleanup: unwind.is_cleanup(),
768             terminator: Some(Terminator {
769                 source_info: self.source_info,
770                 kind: TerminatorKind::Goto { target: loop_block }
771             })
772         });
773
774         // FIXME(#34708): handle partially-dropped array/slice elements.
775         let reset_block = self.drop_flag_reset_block(DropFlagMode::Deep, drop_block, unwind);
776         self.drop_flag_test_block(reset_block, succ, unwind)
777     }
778
779     /// The slow-path - create an "open", elaborated drop for a type
780     /// which is moved-out-of only partially, and patch `bb` to a jump
781     /// to it. This must not be called on ADTs with a destructor,
782     /// as these can't be moved-out-of, except for `Box<T>`, which is
783     /// special-cased.
784     ///
785     /// This creates a "drop ladder" that drops the needed fields of the
786     /// ADT, both in the success case or if one of the destructors fail.
787     fn open_drop<'a>(&mut self) -> BasicBlock {
788         let ty = self.place_ty(self.place);
789         match ty.sty {
790             ty::Closure(def_id, substs) => {
791                 let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
792                 self.open_drop_for_tuple(&tys)
793             }
794             // Note that `elaborate_drops` only drops the upvars of a generator,
795             // and this is ok because `open_drop` here can only be reached
796             // within that own generator's resume function.
797             // This should only happen for the self argument on the resume function.
798             // It effetively only contains upvars until the generator transformation runs.
799             // See librustc_mir/transform/generator.rs for more details.
800             ty::Generator(def_id, substs, _) => {
801                 let tys : Vec<_> = substs.upvar_tys(def_id, self.tcx()).collect();
802                 self.open_drop_for_tuple(&tys)
803             }
804             ty::Tuple(tys) => {
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     /// Return 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<'a>(&mut self,
837                          drop_mode: Option<DropFlagMode>,
838                          succ: BasicBlock,
839                          unwind: Unwind) -> BasicBlock
840     {
841         debug!("complete_drop({:?},{:?})", self, drop_mode);
842
843         let drop_block = self.drop_block(succ, unwind);
844         let drop_block = if let Some(mode) = drop_mode {
845             self.drop_flag_reset_block(mode, drop_block, unwind)
846         } else {
847             drop_block
848         };
849
850         self.drop_flag_test_block(drop_block, succ, unwind)
851     }
852
853     fn drop_flag_reset_block(&mut self,
854                              mode: DropFlagMode,
855                              succ: BasicBlock,
856                              unwind: Unwind) -> BasicBlock
857     {
858         debug!("drop_flag_reset_block({:?},{:?})", self, mode);
859
860         let block = self.new_block(unwind, TerminatorKind::Goto { target: succ });
861         let block_start = Location { block: block, statement_index: 0 };
862         self.elaborator.clear_drop_flag(block_start, self.path, mode);
863         block
864     }
865
866     fn elaborated_drop_block<'a>(&mut self) -> BasicBlock {
867         debug!("elaborated_drop_block({:?})", self);
868         let unwind = self.unwind; // FIXME(#43234)
869         let succ = self.succ;
870         let blk = self.drop_block(succ, unwind);
871         self.elaborate_drop(blk);
872         blk
873     }
874
875     fn box_free_block<'a>(
876         &mut self,
877         adt: &'tcx ty::AdtDef,
878         substs: &'tcx Substs<'tcx>,
879         target: BasicBlock,
880         unwind: Unwind,
881     ) -> BasicBlock {
882         let block = self.unelaborated_free_block(adt, substs, target, unwind);
883         self.drop_flag_test_block(block, target, unwind)
884     }
885
886     fn unelaborated_free_block<'a>(
887         &mut self,
888         adt: &'tcx ty::AdtDef,
889         substs: &'tcx Substs<'tcx>,
890         target: BasicBlock,
891         unwind: Unwind
892     ) -> BasicBlock {
893         let tcx = self.tcx();
894         let unit_temp = Place::Local(self.new_temp(tcx.mk_nil()));
895         let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem);
896         let args = adt.variants[0].fields.iter().enumerate().map(|(i, f)| {
897             let field = Field::new(i);
898             let field_ty = f.ty(self.tcx(), substs);
899             Operand::Move(self.place.clone().field(field, field_ty))
900         }).collect();
901
902         let call = TerminatorKind::Call {
903             func: Operand::function_handle(tcx, free_func, substs, self.source_info.span),
904             args: args,
905             destination: Some((unit_temp, target)),
906             cleanup: None
907         }; // FIXME(#43234)
908         let free_block = self.new_block(unwind, call);
909
910         let block_start = Location { block: free_block, statement_index: 0 };
911         self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow);
912         free_block
913     }
914
915     fn drop_block<'a>(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock {
916         let block = TerminatorKind::Drop {
917             location: self.place.clone(),
918             target,
919             unwind: unwind.into_option()
920         };
921         self.new_block(unwind, block)
922     }
923
924     fn drop_flag_test_block(&mut self,
925                             on_set: BasicBlock,
926                             on_unset: BasicBlock,
927                             unwind: Unwind)
928                             -> BasicBlock
929     {
930         let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow);
931         debug!("drop_flag_test_block({:?},{:?},{:?},{:?}) - {:?}",
932                self, on_set, on_unset, unwind, style);
933
934         match style {
935             DropStyle::Dead => on_unset,
936             DropStyle::Static => on_set,
937             DropStyle::Conditional | DropStyle::Open => {
938                 let flag = self.elaborator.get_drop_flag(self.path).unwrap();
939                 let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset);
940                 self.new_block(unwind, term)
941             }
942         }
943     }
944
945     fn new_block<'a>(&mut self,
946                      unwind: Unwind,
947                      k: TerminatorKind<'tcx>)
948                      -> BasicBlock
949     {
950         self.elaborator.patch().new_block(BasicBlockData {
951             statements: vec![],
952             terminator: Some(Terminator {
953                 source_info: self.source_info, kind: k
954             }),
955             is_cleanup: unwind.is_cleanup()
956         })
957     }
958
959     fn new_temp(&mut self, ty: Ty<'tcx>) -> Local {
960         self.elaborator.patch().new_temp(ty, self.source_info.span)
961     }
962
963     fn terminator_loc(&mut self, bb: BasicBlock) -> Location {
964         let mir = self.elaborator.mir();
965         self.elaborator.patch().terminator_loc(mir, bb)
966     }
967
968     fn constant_usize(&self, val: u16) -> Operand<'tcx> {
969         Operand::Constant(box Constant {
970             span: self.source_info.span,
971             ty: self.tcx().types.usize,
972             user_ty: None,
973             literal: ty::Const::from_usize(self.tcx(), val.into()),
974         })
975     }
976
977     fn assign(&self, lhs: &Place<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> {
978         Statement {
979             source_info: self.source_info,
980             kind: StatementKind::Assign(lhs.clone(), rhs)
981         }
982     }
983 }