don't schedule unnecessary drops when lowering or-patterns
This avoids scheduling drops and immediately unscheduling them. Drops for guard bindings/temporaries are still scheduled and unscheduled as before.
This commit is contained in:
@@ -5,11 +5,11 @@
|
|||||||
//! This also includes code for pattern bindings in `let` statements and
|
//! This also includes code for pattern bindings in `let` statements and
|
||||||
//! function parameters.
|
//! function parameters.
|
||||||
|
|
||||||
use std::assert_matches::assert_matches;
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use itertools::{Itertools, Position};
|
||||||
use rustc_abi::VariantIdx;
|
use rustc_abi::VariantIdx;
|
||||||
use rustc_data_structures::fx::FxIndexMap;
|
use rustc_data_structures::fx::FxIndexMap;
|
||||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||||
@@ -561,16 +561,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
// return: it isn't bound by move until right before enter the arm.
|
// return: it isn't bound by move until right before enter the arm.
|
||||||
// To handle this we instead unschedule it's drop after each time
|
// To handle this we instead unschedule it's drop after each time
|
||||||
// we lower the guard.
|
// we lower the guard.
|
||||||
|
// As a result, we end up with the drop order of the last sub-branch we lower. To use
|
||||||
|
// the drop order for the first sub-branch, we lower sub-branches in reverse (#142163).
|
||||||
|
// TODO: I'm saving the breaking change for the next commit. For now, a stopgap:
|
||||||
|
let sub_branch_to_use_the_drops_from =
|
||||||
|
if arm_match_scope.is_some() { Position::Last } else { Position::First };
|
||||||
let target_block = self.cfg.start_new_block();
|
let target_block = self.cfg.start_new_block();
|
||||||
let mut schedule_drops = ScheduleDrops::Yes;
|
for (pos, sub_branch) in branch.sub_branches.into_iter().with_position() {
|
||||||
let arm = arm_match_scope.unzip().0;
|
debug_assert!(pos != Position::Only);
|
||||||
// We keep a stack of all of the bindings and type ascriptions
|
let schedule_drops = if pos == sub_branch_to_use_the_drops_from {
|
||||||
// from the parent candidates that we visit, that also need to
|
ScheduleDrops::Yes
|
||||||
// be bound for each candidate.
|
} else {
|
||||||
for sub_branch in branch.sub_branches {
|
ScheduleDrops::No
|
||||||
if let Some(arm) = arm {
|
};
|
||||||
self.clear_top_scope(arm.scope);
|
|
||||||
}
|
|
||||||
let binding_end = self.bind_and_guard_matched_candidate(
|
let binding_end = self.bind_and_guard_matched_candidate(
|
||||||
sub_branch,
|
sub_branch,
|
||||||
fake_borrow_temps,
|
fake_borrow_temps,
|
||||||
@@ -579,9 +582,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
schedule_drops,
|
schedule_drops,
|
||||||
emit_storage_live,
|
emit_storage_live,
|
||||||
);
|
);
|
||||||
if arm.is_none() {
|
|
||||||
schedule_drops = ScheduleDrops::No;
|
|
||||||
}
|
|
||||||
self.cfg.goto(binding_end, outer_source_info, target_block);
|
self.cfg.goto(binding_end, outer_source_info, target_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2453,11 +2453,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
|
|
||||||
// Bindings for guards require some extra handling to automatically
|
// Bindings for guards require some extra handling to automatically
|
||||||
// insert implicit references/dereferences.
|
// insert implicit references/dereferences.
|
||||||
self.bind_matched_candidate_for_guard(
|
// This always schedules storage drops, so we may need to unschedule them below.
|
||||||
block,
|
self.bind_matched_candidate_for_guard(block, sub_branch.bindings.iter());
|
||||||
schedule_drops,
|
|
||||||
sub_branch.bindings.iter(),
|
|
||||||
);
|
|
||||||
let guard_frame = GuardFrame {
|
let guard_frame = GuardFrame {
|
||||||
locals: sub_branch
|
locals: sub_branch
|
||||||
.bindings
|
.bindings
|
||||||
@@ -2489,6 +2486,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If this isn't the final sub-branch being lowered, we need to unschedule drops of
|
||||||
|
// bindings and temporaries created for and by the guard. As a result, the drop order
|
||||||
|
// for the arm will correspond to the binding order of the final sub-branch lowered.
|
||||||
|
if matches!(schedule_drops, ScheduleDrops::No) {
|
||||||
|
self.clear_top_scope(arm.scope);
|
||||||
|
}
|
||||||
|
|
||||||
let source_info = self.source_info(guard_span);
|
let source_info = self.source_info(guard_span);
|
||||||
let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span));
|
let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span));
|
||||||
let guard_frame = self.guard_context.pop().unwrap();
|
let guard_frame = self.guard_context.pop().unwrap();
|
||||||
@@ -2538,14 +2542,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
let cause = FakeReadCause::ForGuardBinding;
|
let cause = FakeReadCause::ForGuardBinding;
|
||||||
self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id));
|
self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id));
|
||||||
}
|
}
|
||||||
assert_matches!(
|
// Only schedule drops for the last sub-branch we lower.
|
||||||
schedule_drops,
|
|
||||||
ScheduleDrops::Yes,
|
|
||||||
"patterns with guards must schedule drops"
|
|
||||||
);
|
|
||||||
self.bind_matched_candidate_for_arm_body(
|
self.bind_matched_candidate_for_arm_body(
|
||||||
post_guard_block,
|
post_guard_block,
|
||||||
ScheduleDrops::Yes,
|
schedule_drops,
|
||||||
by_value_bindings,
|
by_value_bindings,
|
||||||
emit_storage_live,
|
emit_storage_live,
|
||||||
);
|
);
|
||||||
@@ -2671,7 +2671,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
fn bind_matched_candidate_for_guard<'b>(
|
fn bind_matched_candidate_for_guard<'b>(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: BasicBlock,
|
block: BasicBlock,
|
||||||
schedule_drops: ScheduleDrops,
|
|
||||||
bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
|
bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
|
||||||
) where
|
) where
|
||||||
'tcx: 'b,
|
'tcx: 'b,
|
||||||
@@ -2690,12 +2689,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
// a reference R: &T pointing to the location matched by
|
// a reference R: &T pointing to the location matched by
|
||||||
// the pattern, and every occurrence of P within a guard
|
// the pattern, and every occurrence of P within a guard
|
||||||
// denotes *R.
|
// denotes *R.
|
||||||
|
// Drops must be scheduled to emit `StorageDead` on the guard's failure/break branches.
|
||||||
let ref_for_guard = self.storage_live_binding(
|
let ref_for_guard = self.storage_live_binding(
|
||||||
block,
|
block,
|
||||||
binding.var_id,
|
binding.var_id,
|
||||||
binding.span,
|
binding.span,
|
||||||
RefWithinGuard,
|
RefWithinGuard,
|
||||||
schedule_drops,
|
ScheduleDrops::Yes,
|
||||||
);
|
);
|
||||||
match binding.binding_mode.0 {
|
match binding.binding_mode.0 {
|
||||||
ByRef::No => {
|
ByRef::No => {
|
||||||
@@ -2705,13 +2705,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
|
self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
|
||||||
}
|
}
|
||||||
ByRef::Yes(mutbl) => {
|
ByRef::Yes(mutbl) => {
|
||||||
// The arm binding will be by reference, so eagerly create it now.
|
// The arm binding will be by reference, so eagerly create it now. Drops must
|
||||||
|
// be scheduled to emit `StorageDead` on the guard's failure/break branches.
|
||||||
let value_for_arm = self.storage_live_binding(
|
let value_for_arm = self.storage_live_binding(
|
||||||
block,
|
block,
|
||||||
binding.var_id,
|
binding.var_id,
|
||||||
binding.span,
|
binding.span,
|
||||||
OutsideGuard,
|
OutsideGuard,
|
||||||
schedule_drops,
|
ScheduleDrops::Yes,
|
||||||
);
|
);
|
||||||
|
|
||||||
let rvalue =
|
let rvalue =
|
||||||
|
|||||||
Reference in New Issue
Block a user