1 use crate::util
::patch
::MirPatch
;
3 use rustc_hir
::lang_items
::{BoxFreeFnLangItem, DropTraitLangItem}
;
4 use rustc_index
::vec
::Idx
;
5 use rustc_middle
::mir
::*;
6 use rustc_middle
::traits
::Reveal
;
7 use rustc_middle
::ty
::subst
::SubstsRef
;
8 use rustc_middle
::ty
::util
::IntTypeExt
;
9 use rustc_middle
::ty
::{self, Ty, TyCtxt}
;
10 use rustc_target
::abi
::VariantIdx
;
13 use std
::convert
::TryInto
;
15 /// The value of an inserted drop flag.
16 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
17 pub enum DropFlagState
{
18 /// The tracked value is initialized and needs to be dropped when leaving its scope.
21 /// The tracked value is uninitialized or was moved out of and does not need to be dropped when
22 /// leaving its scope.
27 pub fn value(self) -> bool
{
29 DropFlagState
::Present
=> true,
30 DropFlagState
::Absent
=> false,
35 /// Describes how/if a value should be dropped.
38 /// The value is already dead at the drop location, no drop will be executed.
41 /// The value is known to always be initialized at the drop location, drop will always be
45 /// Whether the value needs to be dropped depends on its drop flag.
48 /// An "open" drop is one where only the fields of a value are dropped.
50 /// For example, this happens when moving out of a struct field: The rest of the struct will be
51 /// dropped in such an "open" drop. It is also used to generate drop glue for the individual
52 /// components of a value, for example for dropping array elements.
56 /// Which drop flags to affect/check with an operation.
58 pub enum DropFlagMode
{
59 /// Only affect the top-level drop flag, not that of any contained fields.
61 /// Affect all nested drop flags in addition to the top-level one.
65 /// Describes if unwinding is necessary and where to unwind to if a panic occurs.
66 #[derive(Copy, Clone, Debug)]
68 /// Unwind to this block.
70 /// Already in an unwind path, any panic will cause an abort.
75 fn is_cleanup(self) -> bool
{
77 Unwind
::To(..) => false,
78 Unwind
::InCleanup
=> true,
82 fn into_option(self) -> Option
<BasicBlock
> {
84 Unwind
::To(bb
) => Some(bb
),
85 Unwind
::InCleanup
=> None
,
89 fn map
<F
>(self, f
: F
) -> Self
91 F
: FnOnce(BasicBlock
) -> BasicBlock
,
94 Unwind
::To(bb
) => Unwind
::To(f(bb
)),
95 Unwind
::InCleanup
=> Unwind
::InCleanup
,
100 pub trait DropElaborator
<'a
, 'tcx
>: fmt
::Debug
{
101 /// The type representing paths that can be moved out of.
103 /// Users can move out of individual fields of a struct, such as `a.b.c`. This type is used to
104 /// represent such move paths. Sometimes tracking individual move paths is not necessary, in
105 /// which case this may be set to (for example) `()`.
106 type Path
: Copy
+ fmt
::Debug
;
110 fn patch(&mut self) -> &mut MirPatch
<'tcx
>;
111 fn body(&self) -> &'a Body
<'tcx
>;
112 fn tcx(&self) -> TyCtxt
<'tcx
>;
113 fn param_env(&self) -> ty
::ParamEnv
<'tcx
>;
117 /// Returns how `path` should be dropped, given `mode`.
118 fn drop_style(&self, path
: Self::Path
, mode
: DropFlagMode
) -> DropStyle
;
120 /// Returns the drop flag of `path` as a MIR `Operand` (or `None` if `path` has no drop flag).
121 fn get_drop_flag(&mut self, path
: Self::Path
) -> Option
<Operand
<'tcx
>>;
123 /// Modifies the MIR patch so that the drop flag of `path` (if any) is cleared at `location`.
125 /// If `mode` is deep, drop flags of all child paths should also be cleared by inserting
126 /// additional statements.
127 fn clear_drop_flag(&mut self, location
: Location
, path
: Self::Path
, mode
: DropFlagMode
);
131 /// Returns the subpath of a field of `path` (or `None` if there is no dedicated subpath).
133 /// If this returns `None`, `field` will not get a dedicated drop flag.
134 fn field_subpath(&self, path
: Self::Path
, field
: Field
) -> Option
<Self::Path
>;
136 /// Returns the subpath of a dereference of `path` (or `None` if there is no dedicated subpath).
138 /// If this returns `None`, `*path` will not get a dedicated drop flag.
140 /// This is only relevant for `Box<T>`, where the contained `T` can be moved out of the box.
141 fn deref_subpath(&self, path
: Self::Path
) -> Option
<Self::Path
>;
143 /// Returns the subpath of downcasting `path` to one of its variants.
145 /// If this returns `None`, the downcast of `path` will not get a dedicated drop flag.
146 fn downcast_subpath(&self, path
: Self::Path
, variant
: VariantIdx
) -> Option
<Self::Path
>;
148 /// Returns the subpath of indexing a fixed-size array `path`.
150 /// If this returns `None`, elements of `path` will not get a dedicated drop flag.
152 /// This is only relevant for array patterns, which can move out of individual array elements.
153 fn array_subpath(&self, path
: Self::Path
, index
: u32, size
: u32) -> Option
<Self::Path
>;
157 struct DropCtxt
<'l
, 'b
, 'tcx
, D
>
159 D
: DropElaborator
<'b
, 'tcx
>,
161 elaborator
: &'l
mut D
,
163 source_info
: SourceInfo
,
171 /// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it.
173 /// The passed `elaborator` is used to determine what should happen at the drop terminator. It
174 /// decides whether the drop can be statically determined or whether it needs a dynamic drop flag,
175 /// and whether the drop is "open", ie. should be expanded to drop all subfields of the dropped
178 /// When this returns, the MIR patch in the `elaborator` contains the necessary changes.
179 pub fn elaborate_drop
<'b
, 'tcx
, D
>(
181 source_info
: SourceInfo
,
188 D
: DropElaborator
<'b
, 'tcx
>,
191 DropCtxt { elaborator, source_info, place, path, succ, unwind }
.elaborate_drop(bb
)
194 impl<'l
, 'b
, 'tcx
, D
> DropCtxt
<'l
, 'b
, 'tcx
, D
>
196 D
: DropElaborator
<'b
, 'tcx
>,
199 fn place_ty(&self, place
: Place
<'tcx
>) -> Ty
<'tcx
> {
200 place
.ty(self.elaborator
.body(), self.tcx()).ty
203 fn tcx(&self) -> TyCtxt
<'tcx
> {
204 self.elaborator
.tcx()
207 /// This elaborates a single drop instruction, located at `bb`, and
210 /// The elaborated drop checks the drop flags to only drop what
213 /// In addition, the relevant drop flags also need to be cleared
214 /// to avoid double-drops. However, in the middle of a complex
215 /// drop, one must avoid clearing some of the flags before they
216 /// are read, as that would cause a memory leak.
218 /// In particular, when dropping an ADT, multiple fields may be
219 /// joined together under the `rest` subpath. They are all controlled
220 /// by the primary drop flag, but only the last rest-field dropped
221 /// should clear it (and it must also not clear anything else).
223 // FIXME: I think we should just control the flags externally,
224 // and then we do not need this machinery.
225 pub fn elaborate_drop(&mut self, bb
: BasicBlock
) {
226 debug
!("elaborate_drop({:?}, {:?})", bb
, self);
227 let style
= self.elaborator
.drop_style(self.path
, DropFlagMode
::Deep
);
228 debug
!("elaborate_drop({:?}, {:?}): live - {:?}", bb
, self, style
);
233 .patch_terminator(bb
, TerminatorKind
::Goto { target: self.succ }
);
235 DropStyle
::Static
=> {
236 let loc
= self.terminator_loc(bb
);
237 self.elaborator
.clear_drop_flag(loc
, self.path
, DropFlagMode
::Deep
);
238 self.elaborator
.patch().patch_terminator(
240 TerminatorKind
::Drop
{
243 unwind
: self.unwind
.into_option(),
247 DropStyle
::Conditional
=> {
248 let unwind
= self.unwind
; // FIXME(#43234)
249 let succ
= self.succ
;
250 let drop_bb
= self.complete_drop(Some(DropFlagMode
::Deep
), succ
, unwind
);
253 .patch_terminator(bb
, TerminatorKind
::Goto { target: drop_bb }
);
256 let drop_bb
= self.open_drop();
259 .patch_terminator(bb
, TerminatorKind
::Goto { target: drop_bb }
);
264 /// Returns the place and move path for each field of `variant`,
265 /// (the move path is `None` if the field is a rest field).
266 fn move_paths_for_fields(
268 base_place
: Place
<'tcx
>,
269 variant_path
: D
::Path
,
270 variant
: &'tcx ty
::VariantDef
,
271 substs
: SubstsRef
<'tcx
>,
272 ) -> Vec
<(Place
<'tcx
>, Option
<D
::Path
>)> {
278 let field
= Field
::new(i
);
279 let subpath
= self.elaborator
.field_subpath(variant_path
, field
);
280 let tcx
= self.tcx();
282 assert_eq
!(self.elaborator
.param_env().reveal(), Reveal
::All
);
284 tcx
.normalize_erasing_regions(self.elaborator
.param_env(), f
.ty(tcx
, substs
));
285 (tcx
.mk_place_field(base_place
, field
, field_ty
), subpath
)
293 path
: Option
<D
::Path
>,
297 if let Some(path
) = path
{
298 debug
!("drop_subpath: for std field {:?}", place
);
301 elaborator
: self.elaborator
,
302 source_info
: self.source_info
,
308 .elaborated_drop_block()
310 debug
!("drop_subpath: for rest field {:?}", place
);
313 elaborator
: self.elaborator
,
314 source_info
: self.source_info
,
318 // Using `self.path` here to condition the drop on
319 // our own drop flag.
322 .complete_drop(None
, succ
, unwind
)
326 /// Creates one-half of the drop ladder for a list of fields, and return
327 /// the list of steps in it in reverse order, with the first step
328 /// dropping 0 fields and so on.
330 /// `unwind_ladder` is such a list of steps in reverse order,
331 /// which is called if the matching step of the drop glue panics.
334 unwind_ladder
: &[Unwind
],
335 mut succ
: BasicBlock
,
336 fields
: &[(Place
<'tcx
>, Option
<D
::Path
>)],
337 ) -> Vec
<BasicBlock
> {
340 .chain(fields
.iter().rev().zip(unwind_ladder
).map(|(&(place
, path
), &unwind_succ
)| {
341 succ
= self.drop_subpath(place
, path
, succ
, unwind_succ
);
347 fn drop_ladder_bottom(&mut self) -> (BasicBlock
, Unwind
) {
348 // Clear the "master" drop flag at the end. This is needed
349 // because the "master" drop protects the ADT's discriminant,
350 // which is invalidated after the ADT is dropped.
351 let (succ
, unwind
) = (self.succ
, self.unwind
); // FIXME(#43234)
353 self.drop_flag_reset_block(DropFlagMode
::Shallow
, succ
, unwind
),
354 unwind
.map(|unwind
| {
355 self.drop_flag_reset_block(DropFlagMode
::Shallow
, unwind
, Unwind
::InCleanup
)
360 /// Creates a full drop ladder, consisting of 2 connected half-drop-ladders
362 /// For example, with 3 fields, the drop ladder is
365 /// ELAB(drop location.0 [target=.d1, unwind=.c1])
367 /// ELAB(drop location.1 [target=.d2, unwind=.c2])
369 /// ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`])
371 /// ELAB(drop location.1 [target=.c2])
373 /// ELAB(drop location.2 [target=`self.unwind`])
375 /// NOTE: this does not clear the master drop flag, so you need
376 /// to point succ/unwind on a `drop_ladder_bottom`.
379 fields
: Vec
<(Place
<'tcx
>, Option
<D
::Path
>)>,
382 ) -> (BasicBlock
, Unwind
) {
383 debug
!("drop_ladder({:?}, {:?})", self, fields
);
385 let mut fields
= fields
;
386 fields
.retain(|&(place
, _
)| {
387 self.place_ty(place
).needs_drop(self.tcx(), self.elaborator
.param_env())
390 debug
!("drop_ladder - fields needing drop: {:?}", fields
);
392 let unwind_ladder
= vec
![Unwind
::InCleanup
; fields
.len() + 1];
393 let unwind_ladder
: Vec
<_
> = if let Unwind
::To(target
) = unwind
{
394 let halfladder
= self.drop_halfladder(&unwind_ladder
, target
, &fields
);
395 halfladder
.into_iter().map(Unwind
::To
).collect()
400 let normal_ladder
= self.drop_halfladder(&unwind_ladder
, succ
, &fields
);
402 (*normal_ladder
.last().unwrap(), *unwind_ladder
.last().unwrap())
405 fn open_drop_for_tuple(&mut self, tys
: &[Ty
<'tcx
>]) -> BasicBlock
{
406 debug
!("open_drop_for_tuple({:?}, {:?})", self, tys
);
413 self.tcx().mk_place_field(self.place
, Field
::new(i
), ty
),
414 self.elaborator
.field_subpath(self.path
, Field
::new(i
)),
419 let (succ
, unwind
) = self.drop_ladder_bottom();
420 self.drop_ladder(fields
, succ
, unwind
).0
423 fn open_drop_for_box(&mut self, adt
: &'tcx ty
::AdtDef
, substs
: SubstsRef
<'tcx
>) -> BasicBlock
{
424 debug
!("open_drop_for_box({:?}, {:?}, {:?})", self, adt
, substs
);
426 let interior
= self.tcx().mk_place_deref(self.place
);
427 let interior_path
= self.elaborator
.deref_subpath(self.path
);
429 let succ
= self.box_free_block(adt
, substs
, self.succ
, self.unwind
);
431 self.unwind
.map(|unwind
| self.box_free_block(adt
, substs
, unwind
, Unwind
::InCleanup
));
433 self.drop_subpath(interior
, interior_path
, succ
, unwind_succ
)
436 fn open_drop_for_adt(&mut self, adt
: &'tcx ty
::AdtDef
, substs
: SubstsRef
<'tcx
>) -> BasicBlock
{
437 debug
!("open_drop_for_adt({:?}, {:?}, {:?})", self, adt
, substs
);
438 if adt
.variants
.is_empty() {
439 return self.elaborator
.patch().new_block(BasicBlockData
{
441 terminator
: Some(Terminator
{
442 source_info
: self.source_info
,
443 kind
: TerminatorKind
::Unreachable
,
445 is_cleanup
: self.unwind
.is_cleanup(),
450 adt
.is_union() || Some(adt
.did
) == self.tcx().lang_items().manually_drop();
451 let contents_drop
= if skip_contents
{
452 (self.succ
, self.unwind
)
454 self.open_drop_for_adt_contents(adt
, substs
)
457 if adt
.has_dtor(self.tcx()) {
458 self.destructor_call_block(contents_drop
)
464 fn open_drop_for_adt_contents(
466 adt
: &'tcx ty
::AdtDef
,
467 substs
: SubstsRef
<'tcx
>,
468 ) -> (BasicBlock
, Unwind
) {
469 let (succ
, unwind
) = self.drop_ladder_bottom();
471 let fields
= self.move_paths_for_fields(
474 &adt
.variants
[VariantIdx
::new(0)],
477 self.drop_ladder(fields
, succ
, unwind
)
479 self.open_drop_for_multivariant(adt
, substs
, succ
, unwind
)
483 fn open_drop_for_multivariant(
485 adt
: &'tcx ty
::AdtDef
,
486 substs
: SubstsRef
<'tcx
>,
489 ) -> (BasicBlock
, Unwind
) {
490 let mut values
= Vec
::with_capacity(adt
.variants
.len());
491 let mut normal_blocks
= Vec
::with_capacity(adt
.variants
.len());
492 let mut unwind_blocks
=
493 if unwind
.is_cleanup() { None }
else { Some(Vec::with_capacity(adt.variants.len())) }
;
495 let mut have_otherwise_with_drop_glue
= false;
496 let mut have_otherwise
= false;
497 let tcx
= self.tcx();
499 for (variant_index
, discr
) in adt
.discriminants(tcx
) {
500 let variant
= &adt
.variants
[variant_index
];
501 let subpath
= self.elaborator
.downcast_subpath(self.path
, variant_index
);
503 if let Some(variant_path
) = subpath
{
504 let base_place
= tcx
.mk_place_elem(
506 ProjectionElem
::Downcast(Some(variant
.ident
.name
), variant_index
),
508 let fields
= self.move_paths_for_fields(base_place
, variant_path
, &variant
, substs
);
509 values
.push(discr
.val
);
510 if let Unwind
::To(unwind
) = unwind
{
511 // We can't use the half-ladder from the original
512 // drop ladder, because this breaks the
513 // "funclet can't have 2 successor funclets"
514 // requirement from MSVC:
516 // switch unwind-switch
518 // v1.0 v2.0 v2.0-unwind v1.0-unwind
520 // v1.1-unwind v2.1-unwind |
522 // \-------------------------------/
524 // Create a duplicate half-ladder to avoid that. We
525 // could technically only do this on MSVC, but I
526 // I want to minimize the divergence between MSVC
529 let unwind_blocks
= unwind_blocks
.as_mut().unwrap();
530 let unwind_ladder
= vec
![Unwind
::InCleanup
; fields
.len() + 1];
531 let halfladder
= self.drop_halfladder(&unwind_ladder
, unwind
, &fields
);
532 unwind_blocks
.push(halfladder
.last().cloned().unwrap());
534 let (normal
, _
) = self.drop_ladder(fields
, succ
, unwind
);
535 normal_blocks
.push(normal
);
537 have_otherwise
= true;
539 let param_env
= self.elaborator
.param_env();
540 let have_field_with_drop_glue
= variant
543 .any(|field
| field
.ty(tcx
, substs
).needs_drop(tcx
, param_env
));
544 if have_field_with_drop_glue
{
545 have_otherwise_with_drop_glue
= true;
552 } else if !have_otherwise_with_drop_glue
{
553 normal_blocks
.push(self.goto_block(succ
, unwind
));
554 if let Unwind
::To(unwind
) = unwind
{
555 unwind_blocks
.as_mut().unwrap().push(self.goto_block(unwind
, Unwind
::InCleanup
));
558 normal_blocks
.push(self.drop_block(succ
, unwind
));
559 if let Unwind
::To(unwind
) = unwind
{
560 unwind_blocks
.as_mut().unwrap().push(self.drop_block(unwind
, Unwind
::InCleanup
));
565 self.adt_switch_block(adt
, normal_blocks
, &values
, succ
, unwind
),
566 unwind
.map(|unwind
| {
567 self.adt_switch_block(
569 unwind_blocks
.unwrap(),
580 adt
: &'tcx ty
::AdtDef
,
581 blocks
: Vec
<BasicBlock
>,
586 // If there are multiple variants, then if something
587 // is present within the enum the discriminant, tracked
588 // by the rest path, must be initialized.
590 // Additionally, we do not want to switch on the
591 // discriminant after it is free-ed, because that
592 // way lies only trouble.
593 let discr_ty
= adt
.repr
.discr_type().to_ty(self.tcx());
594 let discr
= Place
::from(self.new_temp(discr_ty
));
595 let discr_rv
= Rvalue
::Discriminant(self.place
);
596 let switch_block
= BasicBlockData
{
597 statements
: vec
![self.assign(discr
, discr_rv
)],
598 terminator
: Some(Terminator
{
599 source_info
: self.source_info
,
600 kind
: TerminatorKind
::SwitchInt
{
601 discr
: Operand
::Move(discr
),
603 values
: From
::from(values
.to_owned()),
607 is_cleanup
: unwind
.is_cleanup(),
609 let switch_block
= self.elaborator
.patch().new_block(switch_block
);
610 self.drop_flag_test_block(switch_block
, succ
, unwind
)
613 fn destructor_call_block(&mut self, (succ
, unwind
): (BasicBlock
, Unwind
)) -> BasicBlock
{
614 debug
!("destructor_call_block({:?}, {:?})", self, succ
);
615 let tcx
= self.tcx();
616 let drop_trait
= tcx
.require_lang_item(DropTraitLangItem
, None
);
617 let drop_fn
= tcx
.associated_items(drop_trait
).in_definition_order().next().unwrap();
618 let ty
= self.place_ty(self.place
);
619 let substs
= tcx
.mk_substs_trait(ty
, &[]);
622 tcx
.mk_ref(tcx
.lifetimes
.re_erased
, ty
::TypeAndMut { ty, mutbl: hir::Mutability::Mut }
);
623 let ref_place
= self.new_temp(ref_ty
);
624 let unit_temp
= Place
::from(self.new_temp(tcx
.mk_unit()));
626 let result
= BasicBlockData
{
627 statements
: vec
![self.assign(
628 Place
::from(ref_place
),
630 tcx
.lifetimes
.re_erased
,
631 BorrowKind
::Mut { allow_two_phase_borrow: false }
,
635 terminator
: Some(Terminator
{
636 kind
: TerminatorKind
::Call
{
637 func
: Operand
::function_handle(
641 self.source_info
.span
,
643 args
: vec
![Operand
::Move(Place
::from(ref_place
))],
644 destination
: Some((unit_temp
, succ
)),
645 cleanup
: unwind
.into_option(),
647 fn_span
: self.source_info
.span
,
649 source_info
: self.source_info
,
651 is_cleanup
: unwind
.is_cleanup(),
653 self.elaborator
.patch().new_block(result
)
656 /// Create a loop that drops an array:
660 /// can_go = cur == length_or_end
661 /// if can_go then succ else drop-block
665 /// cur = cur.offset(1)
667 /// ptr = &raw mut P[cur]
676 length_or_end
: Place
<'tcx
>,
681 let copy
= |place
: Place
<'tcx
>| Operand
::Copy(place
);
682 let move_
= |place
: Place
<'tcx
>| Operand
::Move(place
);
683 let tcx
= self.tcx();
685 let ptr_ty
= tcx
.mk_ptr(ty
::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut }
);
686 let ptr
= Place
::from(self.new_temp(ptr_ty
));
687 let can_go
= Place
::from(self.new_temp(tcx
.types
.bool
));
689 let one
= self.constant_usize(1);
690 let (ptr_next
, cur_next
) = if ptr_based
{
691 (Rvalue
::Use(copy(cur
.into())), Rvalue
::BinaryOp(BinOp
::Offset
, move_(cur
.into()), one
))
694 Rvalue
::AddressOf(Mutability
::Mut
, tcx
.mk_place_index(self.place
, cur
)),
695 Rvalue
::BinaryOp(BinOp
::Add
, move_(cur
.into()), one
),
699 let drop_block
= BasicBlockData
{
700 statements
: vec
![self.assign(ptr
, ptr_next
), self.assign(Place
::from(cur
), cur_next
)],
701 is_cleanup
: unwind
.is_cleanup(),
702 terminator
: Some(Terminator
{
703 source_info
: self.source_info
,
704 // this gets overwritten by drop elaboration.
705 kind
: TerminatorKind
::Unreachable
,
708 let drop_block
= self.elaborator
.patch().new_block(drop_block
);
710 let loop_block
= BasicBlockData
{
711 statements
: vec
![self.assign(
713 Rvalue
::BinaryOp(BinOp
::Eq
, copy(Place
::from(cur
)), copy(length_or_end
)),
715 is_cleanup
: unwind
.is_cleanup(),
716 terminator
: Some(Terminator
{
717 source_info
: self.source_info
,
718 kind
: TerminatorKind
::if_(tcx
, move_(can_go
), succ
, drop_block
),
721 let loop_block
= self.elaborator
.patch().new_block(loop_block
);
723 self.elaborator
.patch().patch_terminator(
725 TerminatorKind
::Drop
{
726 place
: tcx
.mk_place_deref(ptr
),
728 unwind
: unwind
.into_option(),
735 fn open_drop_for_array(&mut self, ety
: Ty
<'tcx
>, opt_size
: Option
<u64>) -> BasicBlock
{
736 debug
!("open_drop_for_array({:?}, {:?})", ety
, opt_size
);
738 // if size_of::<ety>() == 0 {
744 let tcx
= self.tcx();
746 if let Some(size
) = opt_size
{
747 let size
: u32 = size
.try_into().unwrap_or_else(|_
| {
748 bug
!("move out check isn't implemented for array sizes bigger than u32::MAX");
750 let fields
: Vec
<(Place
<'tcx
>, Option
<D
::Path
>)> = (0..size
)
755 ProjectionElem
::ConstantIndex
{
761 self.elaborator
.array_subpath(self.path
, i
, size
),
766 if fields
.iter().any(|(_
, path
)| path
.is_some()) {
767 let (succ
, unwind
) = self.drop_ladder_bottom();
768 return self.drop_ladder(fields
, succ
, unwind
).0;
772 let move_
= |place
: Place
<'tcx
>| Operand
::Move(place
);
773 let elem_size
= Place
::from(self.new_temp(tcx
.types
.usize));
774 let len
= Place
::from(self.new_temp(tcx
.types
.usize));
776 static USIZE_SWITCH_ZERO
: &[u128
] = &[0];
778 let base_block
= BasicBlockData
{
780 self.assign(elem_size
, Rvalue
::NullaryOp(NullOp
::SizeOf
, ety
)),
781 self.assign(len
, Rvalue
::Len(self.place
)),
783 is_cleanup
: self.unwind
.is_cleanup(),
784 terminator
: Some(Terminator
{
785 source_info
: self.source_info
,
786 kind
: TerminatorKind
::SwitchInt
{
787 discr
: move_(elem_size
),
788 switch_ty
: tcx
.types
.usize,
789 values
: From
::from(USIZE_SWITCH_ZERO
),
791 self.drop_loop_pair(ety
, false, len
),
792 self.drop_loop_pair(ety
, true, len
),
797 self.elaborator
.patch().new_block(base_block
)
800 /// Creates a pair of drop-loops of `place`, which drops its contents, even
801 /// in the case of 1 panic. If `ptr_based`, creates a pointer loop,
802 /// otherwise create an index loop.
809 debug
!("drop_loop_pair({:?}, {:?})", ety
, ptr_based
);
810 let tcx
= self.tcx();
811 let iter_ty
= if ptr_based { tcx.mk_mut_ptr(ety) }
else { tcx.types.usize }
;
813 let cur
= self.new_temp(iter_ty
);
814 let length_or_end
= if ptr_based { Place::from(self.new_temp(iter_ty)) }
else { length }
;
816 let unwind
= self.unwind
.map(|unwind
| {
817 self.drop_loop(unwind
, cur
, length_or_end
, ety
, Unwind
::InCleanup
, ptr_based
)
820 let loop_block
= self.drop_loop(self.succ
, cur
, length_or_end
, ety
, unwind
, ptr_based
);
822 let cur
= Place
::from(cur
);
823 let drop_block_stmts
= if ptr_based
{
824 let tmp_ty
= tcx
.mk_mut_ptr(self.place_ty(self.place
));
825 let tmp
= Place
::from(self.new_temp(tmp_ty
));
827 // cur = tmp as *mut T;
828 // end = Offset(cur, len);
830 self.assign(tmp
, Rvalue
::AddressOf(Mutability
::Mut
, self.place
)),
831 self.assign(cur
, Rvalue
::Cast(CastKind
::Misc
, Operand
::Move(tmp
), iter_ty
)),
834 Rvalue
::BinaryOp(BinOp
::Offset
, Operand
::Copy(cur
), Operand
::Move(length
)),
838 // cur = 0 (length already pushed)
839 let zero
= self.constant_usize(0);
840 vec
![self.assign(cur
, Rvalue
::Use(zero
))]
842 let drop_block
= self.elaborator
.patch().new_block(BasicBlockData
{
843 statements
: drop_block_stmts
,
844 is_cleanup
: unwind
.is_cleanup(),
845 terminator
: Some(Terminator
{
846 source_info
: self.source_info
,
847 kind
: TerminatorKind
::Goto { target: loop_block }
,
851 // FIXME(#34708): handle partially-dropped array/slice elements.
852 let reset_block
= self.drop_flag_reset_block(DropFlagMode
::Deep
, drop_block
, unwind
);
853 self.drop_flag_test_block(reset_block
, self.succ
, unwind
)
856 /// The slow-path - create an "open", elaborated drop for a type
857 /// which is moved-out-of only partially, and patch `bb` to a jump
858 /// to it. This must not be called on ADTs with a destructor,
859 /// as these can't be moved-out-of, except for `Box<T>`, which is
862 /// This creates a "drop ladder" that drops the needed fields of the
863 /// ADT, both in the success case or if one of the destructors fail.
864 fn open_drop(&mut self) -> BasicBlock
{
865 let ty
= self.place_ty(self.place
);
867 ty
::Closure(_
, substs
) => {
868 let tys
: Vec
<_
> = substs
.as_closure().upvar_tys().collect();
869 self.open_drop_for_tuple(&tys
)
871 // Note that `elaborate_drops` only drops the upvars of a generator,
872 // and this is ok because `open_drop` here can only be reached
873 // within that own generator's resume function.
874 // This should only happen for the self argument on the resume function.
875 // It effetively only contains upvars until the generator transformation runs.
876 // See librustc_body/transform/generator.rs for more details.
877 ty
::Generator(_
, substs
, _
) => {
878 let tys
: Vec
<_
> = substs
.as_generator().upvar_tys().collect();
879 self.open_drop_for_tuple(&tys
)
882 let tys
: Vec
<_
> = ty
.tuple_fields().collect();
883 self.open_drop_for_tuple(&tys
)
885 ty
::Adt(def
, substs
) => {
887 self.open_drop_for_box(def
, substs
)
889 self.open_drop_for_adt(def
, substs
)
893 let unwind
= self.unwind
; // FIXME(#43234)
894 let succ
= self.succ
;
895 self.complete_drop(Some(DropFlagMode
::Deep
), succ
, unwind
)
897 ty
::Array(ety
, size
) => {
898 let size
= size
.try_eval_usize(self.tcx(), self.elaborator
.param_env());
899 self.open_drop_for_array(ety
, size
)
901 ty
::Slice(ety
) => self.open_drop_for_array(ety
, None
),
903 _
=> bug
!("open drop from non-ADT `{:?}`", ty
),
909 drop_mode
: Option
<DropFlagMode
>,
913 debug
!("complete_drop({:?},{:?})", self, drop_mode
);
915 let drop_block
= self.drop_block(succ
, unwind
);
916 let drop_block
= if let Some(mode
) = drop_mode
{
917 self.drop_flag_reset_block(mode
, drop_block
, unwind
)
922 self.drop_flag_test_block(drop_block
, succ
, unwind
)
925 /// Creates a block that resets the drop flag. If `mode` is deep, all children drop flags will
927 fn drop_flag_reset_block(
933 debug
!("drop_flag_reset_block({:?},{:?})", self, mode
);
935 let block
= self.new_block(unwind
, TerminatorKind
::Goto { target: succ }
);
936 let block_start
= Location { block, statement_index: 0 }
;
937 self.elaborator
.clear_drop_flag(block_start
, self.path
, mode
);
941 fn elaborated_drop_block(&mut self) -> BasicBlock
{
942 debug
!("elaborated_drop_block({:?})", self);
943 let blk
= self.drop_block(self.succ
, self.unwind
);
944 self.elaborate_drop(blk
);
948 /// Creates a block that frees the backing memory of a `Box` if its drop is required (either
949 /// statically or by checking its drop flag).
951 /// The contained value will not be dropped.
954 adt
: &'tcx ty
::AdtDef
,
955 substs
: SubstsRef
<'tcx
>,
959 let block
= self.unelaborated_free_block(adt
, substs
, target
, unwind
);
960 self.drop_flag_test_block(block
, target
, unwind
)
963 /// Creates a block that frees the backing memory of a `Box` (without dropping the contained
965 fn unelaborated_free_block(
967 adt
: &'tcx ty
::AdtDef
,
968 substs
: SubstsRef
<'tcx
>,
972 let tcx
= self.tcx();
973 let unit_temp
= Place
::from(self.new_temp(tcx
.mk_unit()));
974 let free_func
= tcx
.require_lang_item(BoxFreeFnLangItem
, Some(self.source_info
.span
));
975 let args
= adt
.variants
[VariantIdx
::new(0)]
980 let field
= Field
::new(i
);
981 let field_ty
= f
.ty(tcx
, substs
);
982 Operand
::Move(tcx
.mk_place_field(self.place
, field
, field_ty
))
986 let call
= TerminatorKind
::Call
{
987 func
: Operand
::function_handle(tcx
, free_func
, substs
, self.source_info
.span
),
989 destination
: Some((unit_temp
, target
)),
991 from_hir_call
: false,
992 fn_span
: self.source_info
.span
,
994 let free_block
= self.new_block(unwind
, call
);
996 let block_start
= Location { block: free_block, statement_index: 0 }
;
997 self.elaborator
.clear_drop_flag(block_start
, self.path
, DropFlagMode
::Shallow
);
1001 fn drop_block(&mut self, target
: BasicBlock
, unwind
: Unwind
) -> BasicBlock
{
1003 TerminatorKind
::Drop { place: self.place, target, unwind: unwind.into_option() }
;
1004 self.new_block(unwind
, block
)
1007 fn goto_block(&mut self, target
: BasicBlock
, unwind
: Unwind
) -> BasicBlock
{
1008 let block
= TerminatorKind
::Goto { target }
;
1009 self.new_block(unwind
, block
)
1012 /// Returns the block to jump to in order to test the drop flag and execute the drop.
1014 /// Depending on the required `DropStyle`, this might be a generated block with an `if`
1015 /// terminator (for dynamic/open drops), or it might be `on_set` or `on_unset` itself, in case
1016 /// the drop can be statically determined.
1017 fn drop_flag_test_block(
1020 on_unset
: BasicBlock
,
1023 let style
= self.elaborator
.drop_style(self.path
, DropFlagMode
::Shallow
);
1025 "drop_flag_test_block({:?},{:?},{:?},{:?}) - {:?}",
1026 self, on_set
, on_unset
, unwind
, style
1030 DropStyle
::Dead
=> on_unset
,
1031 DropStyle
::Static
=> on_set
,
1032 DropStyle
::Conditional
| DropStyle
::Open
=> {
1033 let flag
= self.elaborator
.get_drop_flag(self.path
).unwrap();
1034 let term
= TerminatorKind
::if_(self.tcx(), flag
, on_set
, on_unset
);
1035 self.new_block(unwind
, term
)
1040 fn new_block(&mut self, unwind
: Unwind
, k
: TerminatorKind
<'tcx
>) -> BasicBlock
{
1041 self.elaborator
.patch().new_block(BasicBlockData
{
1043 terminator
: Some(Terminator { source_info: self.source_info, kind: k }
),
1044 is_cleanup
: unwind
.is_cleanup(),
1048 fn new_temp(&mut self, ty
: Ty
<'tcx
>) -> Local
{
1049 self.elaborator
.patch().new_temp(ty
, self.source_info
.span
)
1052 fn terminator_loc(&mut self, bb
: BasicBlock
) -> Location
{
1053 let body
= self.elaborator
.body();
1054 self.elaborator
.patch().terminator_loc(body
, bb
)
1057 fn constant_usize(&self, val
: u16) -> Operand
<'tcx
> {
1058 Operand
::Constant(box Constant
{
1059 span
: self.source_info
.span
,
1061 literal
: ty
::Const
::from_usize(self.tcx(), val
.into()),
1065 fn assign(&self, lhs
: Place
<'tcx
>, rhs
: Rvalue
<'tcx
>) -> Statement
<'tcx
> {
1066 Statement { source_info: self.source_info, kind: StatementKind::Assign(box (lhs, rhs)) }