Skip to content

Commit 9c88f3b

Browse files
committed
Auto merge of #24765 - pnkfelix:fsk-enum-swapindrop, r=nikomatsakis
Inspect enum discriminant *after* calling its destructor Includes some drive-by cleanup (e.g. changed some field and method names to reflect fill-on-drop; added comments about zero-variant enums being classified as `_match::Single`). Probably the most invasive change was the expansion of the maps `available_drop_glues` and `drop_glues` to now hold two different kinds of drop glues; there is the (old) normal drop glue, and there is (new) drop-contents glue that jumps straight to dropping the contents of a struct or enum, skipping its destructor. * For all types that do not have user-defined Drop implementations, the normal glue is generated as usual (i.e. recursively dropping the fields of the data structure). (And this actually is exactly what the newly-added drop-contents glue does as well.) * For types that have user-defined Drop implementations, the "normal" drop glue now schedules a cleanup before invoking the `Drop::drop` method that will call the drop-contents glue after that invocation returns. Fix #23611. ---- Is this a breaking change? The prior behavior was totally unsound, and it seems unreasonable that anyone was actually relying on it. Nonetheless, since there is a user-visible change to the language semantics, I guess I will conservatively mark this as a: [breaking-change] (To see an example of what sort of user-visible change this causes, see the comments in the regression test.)
2 parents 857ef6e + 805349a commit 9c88f3b

File tree

7 files changed

+447
-116
lines changed

7 files changed

+447
-116
lines changed

src/librustc_trans/trans/_match.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -916,8 +916,8 @@ fn insert_lllocals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
916916

917917
let datum = Datum::new(llval, binding_info.ty, Lvalue);
918918
if let Some(cs) = cs {
919-
bcx.fcx.schedule_drop_and_zero_mem(cs, llval, binding_info.ty);
920919
bcx.fcx.schedule_lifetime_end(cs, binding_info.llmatch);
920+
bcx.fcx.schedule_drop_and_fill_mem(cs, llval, binding_info.ty);
921921
}
922922

923923
debug!("binding {} to {}", binding_info.id, bcx.val_to_string(llval));

src/librustc_trans/trans/adt.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,7 @@ pub fn trans_switch<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
771771
(_match::Switch, Some(trans_get_discr(bcx, r, scrutinee, None)))
772772
}
773773
Univariant(..) => {
774+
// N.B.: Univariant means <= 1 enum variants (*not* == 1 variants).
774775
(_match::Single, None)
775776
}
776777
}
@@ -1062,7 +1063,7 @@ pub fn trans_drop_flag_ptr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, r: &Repr<'tcx
10621063
));
10631064
bcx = fold_variants(bcx, r, val, |variant_cx, st, value| {
10641065
let ptr = struct_field_ptr(variant_cx, st, value, (st.fields.len() - 1), false);
1065-
datum::Datum::new(ptr, ptr_ty, datum::Rvalue::new(datum::ByRef))
1066+
datum::Datum::new(ptr, ptr_ty, datum::Lvalue)
10661067
.store_to(variant_cx, scratch.val)
10671068
});
10681069
let expr_datum = scratch.to_expr_datum();

src/librustc_trans/trans/base.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -471,8 +471,11 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>,
471471

472472
match adt::trans_switch(cx, &*repr, av) {
473473
(_match::Single, None) => {
474-
cx = iter_variant(cx, &*repr, av, &*(*variants)[0],
475-
substs, &mut f);
474+
if n_variants != 0 {
475+
assert!(n_variants == 1);
476+
cx = iter_variant(cx, &*repr, av, &*(*variants)[0],
477+
substs, &mut f);
478+
}
476479
}
477480
(_match::Switch, Some(lldiscrim_a)) => {
478481
cx = f(cx, lldiscrim_a, cx.tcx().types.isize);

src/librustc_trans/trans/cleanup.rs

+67-17
Original file line numberDiff line numberDiff line change
@@ -393,19 +393,22 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
393393
must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty),
394394
val: val,
395395
ty: ty,
396-
zero: false
396+
fill_on_drop: false,
397+
skip_dtor: false,
397398
};
398399

399-
debug!("schedule_drop_mem({:?}, val={}, ty={})",
400+
debug!("schedule_drop_mem({:?}, val={}, ty={}) fill_on_drop={} skip_dtor={}",
400401
cleanup_scope,
401402
self.ccx.tn().val_to_string(val),
402-
ty.repr(self.ccx.tcx()));
403+
ty.repr(self.ccx.tcx()),
404+
drop.fill_on_drop,
405+
drop.skip_dtor);
403406

404407
self.schedule_clean(cleanup_scope, drop as CleanupObj);
405408
}
406409

407-
/// Schedules a (deep) drop and zero-ing of `val`, which is a pointer to an instance of `ty`
408-
fn schedule_drop_and_zero_mem(&self,
410+
/// Schedules a (deep) drop and filling of `val`, which is a pointer to an instance of `ty`
411+
fn schedule_drop_and_fill_mem(&self,
409412
cleanup_scope: ScopeId,
410413
val: ValueRef,
411414
ty: Ty<'tcx>) {
@@ -416,14 +419,48 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
416419
must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty),
417420
val: val,
418421
ty: ty,
419-
zero: true
422+
fill_on_drop: true,
423+
skip_dtor: false,
424+
};
425+
426+
debug!("schedule_drop_and_fill_mem({:?}, val={}, ty={}, fill_on_drop={}, skip_dtor={})",
427+
cleanup_scope,
428+
self.ccx.tn().val_to_string(val),
429+
ty.repr(self.ccx.tcx()),
430+
drop.fill_on_drop,
431+
drop.skip_dtor);
432+
433+
self.schedule_clean(cleanup_scope, drop as CleanupObj);
434+
}
435+
436+
/// Issue #23611: Schedules a (deep) drop of the contents of
437+
/// `val`, which is a pointer to an instance of struct/enum type
438+
/// `ty`. The scheduled code handles extracting the discriminant
439+
/// and dropping the contents associated with that variant
440+
/// *without* executing any associated drop implementation.
441+
fn schedule_drop_enum_contents(&self,
442+
cleanup_scope: ScopeId,
443+
val: ValueRef,
444+
ty: Ty<'tcx>) {
445+
// `if` below could be "!contents_needs_drop"; skipping drop
446+
// is just an optimization, so sound to be conservative.
447+
if !self.type_needs_drop(ty) { return; }
448+
449+
let drop = box DropValue {
450+
is_immediate: false,
451+
must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty),
452+
val: val,
453+
ty: ty,
454+
fill_on_drop: false,
455+
skip_dtor: true,
420456
};
421457

422-
debug!("schedule_drop_and_zero_mem({:?}, val={}, ty={}, zero={})",
458+
debug!("schedule_drop_enum_contents({:?}, val={}, ty={}) fill_on_drop={} skip_dtor={}",
423459
cleanup_scope,
424460
self.ccx.tn().val_to_string(val),
425461
ty.repr(self.ccx.tcx()),
426-
true);
462+
drop.fill_on_drop,
463+
drop.skip_dtor);
427464

428465
self.schedule_clean(cleanup_scope, drop as CleanupObj);
429466
}
@@ -440,13 +477,16 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
440477
must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty),
441478
val: val,
442479
ty: ty,
443-
zero: false
480+
fill_on_drop: false,
481+
skip_dtor: false,
444482
};
445483

446-
debug!("schedule_drop_immediate({:?}, val={}, ty={:?})",
484+
debug!("schedule_drop_immediate({:?}, val={}, ty={:?}) fill_on_drop={} skip_dtor={}",
447485
cleanup_scope,
448486
self.ccx.tn().val_to_string(val),
449-
ty.repr(self.ccx.tcx()));
487+
ty.repr(self.ccx.tcx()),
488+
drop.fill_on_drop,
489+
drop.skip_dtor);
450490

451491
self.schedule_clean(cleanup_scope, drop as CleanupObj);
452492
}
@@ -987,7 +1027,8 @@ pub struct DropValue<'tcx> {
9871027
must_unwind: bool,
9881028
val: ValueRef,
9891029
ty: Ty<'tcx>,
990-
zero: bool
1030+
fill_on_drop: bool,
1031+
skip_dtor: bool,
9911032
}
9921033

9931034
impl<'tcx> Cleanup<'tcx> for DropValue<'tcx> {
@@ -1007,13 +1048,18 @@ impl<'tcx> Cleanup<'tcx> for DropValue<'tcx> {
10071048
bcx: Block<'blk, 'tcx>,
10081049
debug_loc: DebugLoc)
10091050
-> Block<'blk, 'tcx> {
1010-
let _icx = base::push_ctxt("<DropValue as Cleanup>::trans");
1051+
let skip_dtor = self.skip_dtor;
1052+
let _icx = if skip_dtor {
1053+
base::push_ctxt("<DropValue as Cleanup>::trans skip_dtor=true")
1054+
} else {
1055+
base::push_ctxt("<DropValue as Cleanup>::trans skip_dtor=false")
1056+
};
10111057
let bcx = if self.is_immediate {
1012-
glue::drop_ty_immediate(bcx, self.val, self.ty, debug_loc)
1058+
glue::drop_ty_immediate(bcx, self.val, self.ty, debug_loc, self.skip_dtor)
10131059
} else {
1014-
glue::drop_ty(bcx, self.val, self.ty, debug_loc)
1060+
glue::drop_ty_core(bcx, self.val, self.ty, debug_loc, self.skip_dtor)
10151061
};
1016-
if self.zero {
1062+
if self.fill_on_drop {
10171063
base::drop_done_fill_mem(bcx, self.val, self.ty);
10181064
}
10191065
bcx
@@ -1190,10 +1236,14 @@ pub trait CleanupMethods<'blk, 'tcx> {
11901236
cleanup_scope: ScopeId,
11911237
val: ValueRef,
11921238
ty: Ty<'tcx>);
1193-
fn schedule_drop_and_zero_mem(&self,
1239+
fn schedule_drop_and_fill_mem(&self,
11941240
cleanup_scope: ScopeId,
11951241
val: ValueRef,
11961242
ty: Ty<'tcx>);
1243+
fn schedule_drop_enum_contents(&self,
1244+
cleanup_scope: ScopeId,
1245+
val: ValueRef,
1246+
ty: Ty<'tcx>);
11971247
fn schedule_drop_immediate(&self,
11981248
cleanup_scope: ScopeId,
11991249
val: ValueRef,

src/librustc_trans/trans/context.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use trans::builder::Builder;
2121
use trans::common::{ExternMap,BuilderRef_res};
2222
use trans::debuginfo;
2323
use trans::declare;
24+
use trans::glue::DropGlueKind;
2425
use trans::monomorphize::MonoId;
2526
use trans::type_::{Type, TypeNames};
2627
use middle::subst::Substs;
@@ -73,7 +74,7 @@ pub struct SharedCrateContext<'tcx> {
7374
check_drop_flag_for_sanity: bool,
7475

7576
available_monomorphizations: RefCell<FnvHashSet<String>>,
76-
available_drop_glues: RefCell<FnvHashMap<Ty<'tcx>, String>>,
77+
available_drop_glues: RefCell<FnvHashMap<DropGlueKind<'tcx>, String>>,
7778
}
7879

7980
/// The local portion of a `CrateContext`. There is one `LocalCrateContext`
@@ -89,7 +90,7 @@ pub struct LocalCrateContext<'tcx> {
8990
item_vals: RefCell<NodeMap<ValueRef>>,
9091
needs_unwind_cleanup_cache: RefCell<FnvHashMap<Ty<'tcx>, bool>>,
9192
fn_pointer_shims: RefCell<FnvHashMap<Ty<'tcx>, ValueRef>>,
92-
drop_glues: RefCell<FnvHashMap<Ty<'tcx>, ValueRef>>,
93+
drop_glues: RefCell<FnvHashMap<DropGlueKind<'tcx>, ValueRef>>,
9394
/// Track mapping of external ids to local items imported for inlining
9495
external: RefCell<DefIdMap<Option<ast::NodeId>>>,
9596
/// Backwards version of the `external` map (inlined items to where they
@@ -574,7 +575,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
574575
&self.local.fn_pointer_shims
575576
}
576577

577-
pub fn drop_glues<'a>(&'a self) -> &'a RefCell<FnvHashMap<Ty<'tcx>, ValueRef>> {
578+
pub fn drop_glues<'a>(&'a self) -> &'a RefCell<FnvHashMap<DropGlueKind<'tcx>, ValueRef>> {
578579
&self.local.drop_glues
579580
}
580581

@@ -660,7 +661,7 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
660661
&self.shared.available_monomorphizations
661662
}
662663

663-
pub fn available_drop_glues<'a>(&'a self) -> &'a RefCell<FnvHashMap<Ty<'tcx>, String>> {
664+
pub fn available_drop_glues(&self) -> &RefCell<FnvHashMap<DropGlueKind<'tcx>, String>> {
664665
&self.shared.available_drop_glues
665666
}
666667

0 commit comments

Comments
 (0)