Skip to content

Commit 68b7475

Browse files
committed
move "ADT master drop flag" logic to open_drop_for_adt_contents
Fixes #41888.
1 parent c6d0b5b commit 68b7475

File tree

3 files changed

+351
-133
lines changed

3 files changed

+351
-133
lines changed

src/librustc_mir/util/elaborate_drops.rs

+140-133
Original file line numberDiff line numberDiff line change
@@ -243,30 +243,37 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
243243
}
244244

245245
/// Create one-half of the drop ladder for a list of fields, and return
246-
/// the list of steps in it in reverse order.
246+
/// the list of steps in it in reverse order, with the first step
247+
/// dropping 0 fields and so on.
247248
///
248249
/// `unwind_ladder` is such a list of steps in reverse order,
249250
/// which is called if the matching step of the drop glue panics.
250251
fn drop_halfladder(&mut self,
251252
unwind_ladder: &[Unwind],
252-
succ: BasicBlock,
253+
mut succ: BasicBlock,
253254
fields: &[(Lvalue<'tcx>, Option<D::Path>)])
254255
-> Vec<BasicBlock>
255256
{
256-
let goto = TerminatorKind::Goto { target: succ };
257-
let mut succ = self.new_block(unwind_ladder[0], goto);
258-
259-
// Always clear the "master" drop flag at the bottom of the
260-
// ladder. This is needed because the "master" drop flag
261-
// protects the ADT's discriminant, which is invalidated
262-
// after the ADT is dropped.
263-
let succ_loc = Location { block: succ, statement_index: 0 };
264-
self.elaborator.clear_drop_flag(succ_loc, self.path, DropFlagMode::Shallow);
265-
266-
fields.iter().rev().zip(unwind_ladder).map(|(&(ref lv, path), &unwind_succ)| {
267-
succ = self.drop_subpath(lv, path, succ, unwind_succ);
268-
succ
269-
}).collect()
257+
Some(succ).into_iter().chain(
258+
fields.iter().rev().zip(unwind_ladder)
259+
.map(|(&(ref lv, path), &unwind_succ)| {
260+
succ = self.drop_subpath(lv, path, succ, unwind_succ);
261+
succ
262+
})
263+
).collect()
264+
}
265+
266+
fn drop_ladder_bottom(&mut self) -> (BasicBlock, Unwind) {
267+
// Clear the "master" drop flag at the end. This is needed
268+
// because the "master" drop protects the ADT's discriminant,
269+
// which is invalidated after the ADT is dropped.
270+
let (succ, unwind) = (self.succ, self.unwind); // FIXME(#6393)
271+
(
272+
self.drop_flag_reset_block(DropFlagMode::Shallow, succ, unwind),
273+
unwind.map(|unwind| {
274+
self.drop_flag_reset_block(DropFlagMode::Shallow, unwind, Unwind::InCleanup)
275+
})
276+
)
270277
}
271278

272279
/// Create a full drop ladder, consisting of 2 connected half-drop-ladders
@@ -283,8 +290,13 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
283290
/// ELAB(drop location.1 [target=.c2])
284291
/// .c2:
285292
/// ELAB(drop location.2 [target=`self.unwind`])
293+
///
294+
/// NOTE: this does not clear the master drop flag, so you need
295+
/// to point succ/unwind on a `drop_ladder_bottom`.
286296
fn drop_ladder<'a>(&mut self,
287-
fields: Vec<(Lvalue<'tcx>, Option<D::Path>)>)
297+
fields: Vec<(Lvalue<'tcx>, Option<D::Path>)>,
298+
succ: BasicBlock,
299+
unwind: Unwind)
288300
-> (BasicBlock, Unwind)
289301
{
290302
debug!("drop_ladder({:?}, {:?})", self, fields);
@@ -297,20 +309,17 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
297309
debug!("drop_ladder - fields needing drop: {:?}", fields);
298310

299311
let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1];
300-
let unwind_ladder: Vec<_> = if let Unwind::To(target) = self.unwind {
312+
let unwind_ladder: Vec<_> = if let Unwind::To(target) = unwind {
301313
let halfladder = self.drop_halfladder(&unwind_ladder, target, &fields);
302-
Some(self.unwind).into_iter().chain(halfladder.into_iter().map(Unwind::To))
303-
.collect()
314+
halfladder.into_iter().map(Unwind::To).collect()
304315
} else {
305316
unwind_ladder
306317
};
307318

308-
let succ = self.succ; // FIXME(#6393)
309319
let normal_ladder =
310320
self.drop_halfladder(&unwind_ladder, succ, &fields);
311321

312-
(normal_ladder.last().cloned().unwrap_or(succ),
313-
unwind_ladder.last().cloned().unwrap_or(self.unwind))
322+
(*normal_ladder.last().unwrap(), *unwind_ladder.last().unwrap())
314323
}
315324

316325
fn open_drop_for_tuple<'a>(&mut self, tys: &[Ty<'tcx>])
@@ -323,7 +332,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
323332
self.elaborator.field_subpath(self.path, Field::new(i)))
324333
}).collect();
325334

326-
self.drop_ladder(fields).0
335+
let (succ, unwind) = self.drop_ladder_bottom();
336+
self.drop_ladder(fields, succ, unwind).0
327337
}
328338

329339
fn open_drop_for_box<'a>(&mut self, ty: Ty<'tcx>) -> BasicBlock
@@ -370,106 +380,100 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
370380
}
371381
}
372382

373-
fn open_drop_for_adt_contents<'a>(&mut self, adt: &'tcx ty::AdtDef,
374-
substs: &'tcx Substs<'tcx>)
375-
-> (BasicBlock, Unwind) {
376-
match adt.variants.len() {
377-
1 => {
378-
let fields = self.move_paths_for_fields(
379-
self.lvalue,
380-
self.path,
381-
&adt.variants[0],
382-
substs
383-
);
384-
self.drop_ladder(fields)
385-
}
386-
_ => {
387-
let succ = self.succ;
388-
let unwind = self.unwind; // FIXME(#6393)
383+
fn open_drop_for_adt_contents(&mut self, adt: &'tcx ty::AdtDef,
384+
substs: &'tcx Substs<'tcx>)
385+
-> (BasicBlock, Unwind) {
386+
let (succ, unwind) = self.drop_ladder_bottom();
387+
if adt.variants.len() == 1 {
388+
let fields = self.move_paths_for_fields(
389+
self.lvalue,
390+
self.path,
391+
&adt.variants[0],
392+
substs
393+
);
394+
self.drop_ladder(fields, succ, unwind)
395+
} else {
396+
self.open_drop_for_multivariant(adt, substs, succ, unwind)
397+
}
398+
}
399+
400+
fn open_drop_for_multivariant(&mut self, adt: &'tcx ty::AdtDef,
401+
substs: &'tcx Substs<'tcx>,
402+
succ: BasicBlock,
403+
unwind: Unwind)
404+
-> (BasicBlock, Unwind) {
405+
let mut values = Vec::with_capacity(adt.variants.len());
406+
let mut normal_blocks = Vec::with_capacity(adt.variants.len());
407+
let mut unwind_blocks = if unwind.is_cleanup() {
408+
None
409+
} else {
410+
Some(Vec::with_capacity(adt.variants.len()))
411+
};
412+
413+
let mut have_otherwise = false;
389414

390-
let mut values = Vec::with_capacity(adt.variants.len());
391-
let mut normal_blocks = Vec::with_capacity(adt.variants.len());
392-
let mut unwind_blocks = if unwind.is_cleanup() {
393-
None
394-
} else {
395-
Some(Vec::with_capacity(adt.variants.len()))
396-
};
397-
let mut otherwise = None;
398-
let mut unwind_otherwise = None;
399-
for (variant_index, discr) in adt.discriminants(self.tcx()).enumerate() {
400-
let subpath = self.elaborator.downcast_subpath(
401-
self.path, variant_index);
402-
if let Some(variant_path) = subpath {
403-
let base_lv = self.lvalue.clone().elem(
404-
ProjectionElem::Downcast(adt, variant_index)
415+
for (variant_index, discr) in adt.discriminants(self.tcx()).enumerate() {
416+
let subpath = self.elaborator.downcast_subpath(
417+
self.path, variant_index);
418+
if let Some(variant_path) = subpath {
419+
let base_lv = self.lvalue.clone().elem(
420+
ProjectionElem::Downcast(adt, variant_index)
405421
);
406-
let fields = self.move_paths_for_fields(
407-
&base_lv,
408-
variant_path,
409-
&adt.variants[variant_index],
410-
substs);
411-
values.push(discr);
412-
if let Unwind::To(unwind) = unwind {
413-
// We can't use the half-ladder from the original
414-
// drop ladder, because this breaks the
415-
// "funclet can't have 2 successor funclets"
416-
// requirement from MSVC:
417-
//
418-
// switch unwind-switch
419-
// / \ / \
420-
// v1.0 v2.0 v2.0-unwind v1.0-unwind
421-
// | | / |
422-
// v1.1-unwind v2.1-unwind |
423-
// ^ |
424-
// \-------------------------------/
425-
//
426-
// Create a duplicate half-ladder to avoid that. We
427-
// could technically only do this on MSVC, but I
428-
// I want to minimize the divergence between MSVC
429-
// and non-MSVC.
430-
431-
let unwind_blocks = unwind_blocks.as_mut().unwrap();
432-
let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1];
433-
let halfladder =
434-
self.drop_halfladder(&unwind_ladder, unwind, &fields);
435-
unwind_blocks.push(halfladder.last().cloned().unwrap_or(unwind));
436-
}
437-
let (normal, _) = self.drop_ladder(fields);
438-
normal_blocks.push(normal);
439-
} else {
440-
// variant not found - drop the entire enum
441-
if let None = otherwise {
442-
otherwise = Some(self.complete_drop(
443-
Some(DropFlagMode::Shallow),
444-
succ,
445-
unwind));
446-
if let Unwind::To(unwind) = unwind {
447-
unwind_otherwise = Some(self.complete_drop(
448-
Some(DropFlagMode::Shallow),
449-
unwind,
450-
Unwind::InCleanup
451-
));
452-
}
453-
}
454-
}
455-
}
456-
if let Some(block) = otherwise {
457-
normal_blocks.push(block);
458-
if let Some(ref mut unwind_blocks) = unwind_blocks {
459-
unwind_blocks.push(unwind_otherwise.unwrap());
460-
}
461-
} else {
462-
values.pop();
422+
let fields = self.move_paths_for_fields(
423+
&base_lv,
424+
variant_path,
425+
&adt.variants[variant_index],
426+
substs);
427+
values.push(discr);
428+
if let Unwind::To(unwind) = unwind {
429+
// We can't use the half-ladder from the original
430+
// drop ladder, because this breaks the
431+
// "funclet can't have 2 successor funclets"
432+
// requirement from MSVC:
433+
//
434+
// switch unwind-switch
435+
// / \ / \
436+
// v1.0 v2.0 v2.0-unwind v1.0-unwind
437+
// | | / |
438+
// v1.1-unwind v2.1-unwind |
439+
// ^ |
440+
// \-------------------------------/
441+
//
442+
// Create a duplicate half-ladder to avoid that. We
443+
// could technically only do this on MSVC, but I
444+
// I want to minimize the divergence between MSVC
445+
// and non-MSVC.
446+
447+
let unwind_blocks = unwind_blocks.as_mut().unwrap();
448+
let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1];
449+
let halfladder =
450+
self.drop_halfladder(&unwind_ladder, unwind, &fields);
451+
unwind_blocks.push(halfladder.last().cloned().unwrap());
463452
}
453+
let (normal, _) = self.drop_ladder(fields, succ, unwind);
454+
normal_blocks.push(normal);
455+
} else {
456+
have_otherwise = true;
457+
}
458+
}
464459

465-
(self.adt_switch_block(adt, normal_blocks, &values, succ, unwind),
466-
unwind.map(|unwind| {
467-
self.adt_switch_block(
468-
adt, unwind_blocks.unwrap(), &values, unwind, Unwind::InCleanup
469-
)
470-
}))
460+
if have_otherwise {
461+
normal_blocks.push(self.drop_block(succ, unwind));
462+
if let Unwind::To(unwind) = unwind {
463+
unwind_blocks.as_mut().unwrap().push(
464+
self.drop_block(unwind, Unwind::InCleanup)
465+
);
471466
}
467+
} else {
468+
values.pop();
472469
}
470+
471+
(self.adt_switch_block(adt, normal_blocks, &values, succ, unwind),
472+
unwind.map(|unwind| {
473+
self.adt_switch_block(
474+
adt, unwind_blocks.unwrap(), &values, unwind, Unwind::InCleanup
475+
)
476+
}))
473477
}
474478

475479
fn adt_switch_block(&mut self,
@@ -652,8 +656,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
652656
});
653657

654658
// FIXME(#34708): handle partially-dropped array/slice elements.
655-
self.drop_flag_test_and_reset_block(
656-
Some(DropFlagMode::Deep), drop_block, succ, unwind)
659+
let reset_block = self.drop_flag_reset_block(DropFlagMode::Deep, drop_block, unwind);
660+
self.drop_flag_test_block(reset_block, succ, unwind)
657661
}
658662

659663
/// The slow-path - create an "open", elaborated drop for a type
@@ -707,23 +711,26 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
707711
debug!("complete_drop({:?},{:?})", self, drop_mode);
708712

709713
let drop_block = self.drop_block(succ, unwind);
710-
self.drop_flag_test_and_reset_block(drop_mode, drop_block, succ, unwind)
714+
let drop_block = if let Some(mode) = drop_mode {
715+
self.drop_flag_reset_block(mode, drop_block, unwind)
716+
} else {
717+
drop_block
718+
};
719+
720+
self.drop_flag_test_block(drop_block, succ, unwind)
711721
}
712722

713-
fn drop_flag_test_and_reset_block(&mut self,
714-
drop_mode: Option<DropFlagMode>,
715-
drop_block: BasicBlock,
716-
succ: BasicBlock,
717-
unwind: Unwind) -> BasicBlock
723+
fn drop_flag_reset_block(&mut self,
724+
mode: DropFlagMode,
725+
succ: BasicBlock,
726+
unwind: Unwind) -> BasicBlock
718727
{
719-
debug!("drop_flag_test_and_reset_block({:?},{:?})", self, drop_mode);
720-
721-
if let Some(mode) = drop_mode {
722-
let block_start = Location { block: drop_block, statement_index: 0 };
723-
self.elaborator.clear_drop_flag(block_start, self.path, mode);
724-
}
728+
debug!("drop_flag_reset_block({:?},{:?})", self, mode);
725729

726-
self.drop_flag_test_block(drop_block, succ, unwind)
730+
let block = self.new_block(unwind, TerminatorKind::Goto { target: succ });
731+
let block_start = Location { block: block, statement_index: 0 };
732+
self.elaborator.clear_drop_flag(block_start, self.path, mode);
733+
block
727734
}
728735

729736
fn elaborated_drop_block<'a>(&mut self) -> BasicBlock {

0 commit comments

Comments
 (0)