Skip to content

Commit 8dbbe4d

Browse files
committed
Avoid scheduling repeated StorageDeads
Also add some comments
1 parent 9b9dafb commit 8dbbe4d

File tree

3 files changed

+52
-15
lines changed

3 files changed

+52
-15
lines changed

src/librustc_mir_build/build/block.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
149149
&pattern,
150150
UserTypeProjections::none(),
151151
&mut |this, _, _, _, node, span, _, _| {
152-
this.storage_live_binding(block, node, span, OutsideGuard);
152+
this.storage_live_binding(block, node, span, OutsideGuard, true);
153153
this.schedule_drop_for_binding(node, span, OutsideGuard);
154154
},
155155
)

src/librustc_mir_build/build/matches/mod.rs

+48-14
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
274274
end_block.unit()
275275
}
276276

277-
/// Binds the variables and ascribes types for a given `match` arm.
277+
/// Binds the variables and ascribes types for a given `match` arm or
278+
/// `let` binding.
278279
///
279280
/// Also check if the guard matches, if it's provided.
281+
/// `arm_scope` should be `Some` if and only if this is called for a
282+
/// `match` arm.
280283
fn bind_pattern(
281284
&mut self,
282285
outer_source_info: SourceInfo,
@@ -298,6 +301,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
298301
true,
299302
)
300303
} else {
304+
// It's helpful to avoid scheduling drops multiple times to save
305+
// drop elaboration from having to clean up the extra drops.
306+
//
307+
// If we are in a `let` then we only schedule drops for the first
308+
// candidate.
309+
//
310+
// If we're in a `match` arm then we could have a case like so:
311+
//
312+
// Ok(x) | Err(x) if return => { /* ... */ }
313+
//
314+
// In this case we don't want a drop of `x` scheduled when we
315+
// return: it isn't bound by move until right before enter the arm.
316+
// To handle this we instead unschedule it's drop after each time
317+
// we lower the guard.
301318
let target_block = self.cfg.start_new_block();
302319
let mut schedule_drops = true;
303320
// We keep a stack of all of the bindings and type asciptions
@@ -308,7 +325,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
308325
&mut Vec::new(),
309326
&mut |leaf_candidate, parent_bindings| {
310327
if let Some(arm_scope) = arm_scope {
311-
// Avoid scheduling drops multiple times by unscheduling drops.
312328
self.clear_top_scope(arm_scope);
313329
}
314330
let binding_end = self.bind_and_guard_matched_candidate(
@@ -320,9 +336,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
320336
schedule_drops,
321337
);
322338
if arm_scope.is_none() {
323-
// If we aren't in a match, then our bindings may not be
324-
// the only thing in the top scope, so only schedule
325-
// them to drop for the first pattern instead.
326339
schedule_drops = false;
327340
}
328341
self.cfg.goto(binding_end, outer_source_info, target_block);
@@ -350,7 +363,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
350363
// Optimize the case of `let x = ...` to write directly into `x`
351364
PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => {
352365
let place =
353-
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard);
366+
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
354367
unpack!(block = self.into(&place, block, initializer));
355368

356369
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
@@ -385,7 +398,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
385398
hair::pattern::Ascription { user_ty: pat_ascription_ty, variance: _, user_ty_span },
386399
} => {
387400
let place =
388-
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard);
401+
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
389402
unpack!(block = self.into(&place, block, initializer));
390403

391404
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
@@ -532,12 +545,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
532545
var: HirId,
533546
span: Span,
534547
for_guard: ForGuard,
548+
schedule_drop: bool,
535549
) -> Place<'tcx> {
536550
let local_id = self.var_local_id(var, for_guard);
537551
let source_info = self.source_info(span);
538552
self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) });
539553
let region_scope = self.hir.region_scope_tree.var_scope(var.local_id);
540-
self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
554+
if schedule_drop {
555+
self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
556+
}
541557
Place::from(local_id)
542558
}
543559

@@ -1060,25 +1076,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
10601076
/// |
10611077
/// +----------------------------------------+------------------------------------+
10621078
/// | | |
1079+
/// V V V
10631080
/// [ P matches ] [ Q matches ] [ otherwise ]
10641081
/// | | |
1082+
/// V V |
10651083
/// [ match R, S ] [ match R, S ] |
10661084
/// | | |
10671085
/// +--------------+------------+ +--------------+------------+ |
10681086
/// | | | | | | |
1087+
/// V V V V V V |
10691088
/// [ R matches ] [ S matches ] [otherwise ] [ R matches ] [ S matches ] [otherwise ] |
10701089
/// | | | | | | |
10711090
/// +--------------+------------|------------+--------------+ | |
10721091
/// | | | |
10731092
/// | +----------------------------------------+--------+
10741093
/// | |
1094+
/// V V
10751095
/// [ Success ] [ Failure ]
10761096
/// ```
10771097
///
10781098
/// In practice there are some complications:
10791099
///
10801100
/// * If there's a guard, then the otherwise branch of the first match on
1081-
/// `R | S` goes to a test for whether `Q` matches.
1101+
/// `R | S` goes to a test for whether `Q` matches, and the control flow
1102+
/// doesn't merge into a single success block until after the guard is
1103+
/// tested.
10821104
/// * If neither `P` or `Q` has any bindings or type ascriptions and there
10831105
/// isn't a match guard, then we create a smaller CFG like:
10841106
///
@@ -1658,7 +1680,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16581680
.flat_map(|(bindings, _)| bindings)
16591681
.chain(&candidate.bindings);
16601682

1661-
self.bind_matched_candidate_for_guard(block, bindings.clone());
1683+
self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
16621684
let guard_frame = GuardFrame {
16631685
locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(),
16641686
};
@@ -1807,6 +1829,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18071829
fn bind_matched_candidate_for_guard<'b>(
18081830
&mut self,
18091831
block: BasicBlock,
1832+
schedule_drops: bool,
18101833
bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
18111834
) where
18121835
'tcx: 'b,
@@ -1825,8 +1848,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18251848
// a reference R: &T pointing to the location matched by
18261849
// the pattern, and every occurrence of P within a guard
18271850
// denotes *R.
1828-
let ref_for_guard =
1829-
self.storage_live_binding(block, binding.var_id, binding.span, RefWithinGuard);
1851+
let ref_for_guard = self.storage_live_binding(
1852+
block,
1853+
binding.var_id,
1854+
binding.span,
1855+
RefWithinGuard,
1856+
schedule_drops,
1857+
);
18301858
match binding.binding_mode {
18311859
BindingMode::ByValue => {
18321860
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source);
@@ -1838,6 +1866,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18381866
binding.var_id,
18391867
binding.span,
18401868
OutsideGuard,
1869+
schedule_drops,
18411870
);
18421871

18431872
let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source);
@@ -1863,8 +1892,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18631892
// Assign each of the bindings. This may trigger moves out of the candidate.
18641893
for binding in bindings {
18651894
let source_info = self.source_info(binding.span);
1866-
let local =
1867-
self.storage_live_binding(block, binding.var_id, binding.span, OutsideGuard);
1895+
let local = self.storage_live_binding(
1896+
block,
1897+
binding.var_id,
1898+
binding.span,
1899+
OutsideGuard,
1900+
schedule_drops,
1901+
);
18681902
if schedule_drops {
18691903
self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
18701904
}

src/librustc_mir_build/build/matches/simplify.rs

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
7575
}
7676
}
7777

78+
/// Given `candidate` that has a single or-pattern for its match-pairs,
79+
/// creates a fresh candidate for each of its input subpatterns passed via
80+
/// `pats`.
7881
fn create_or_subcandidates<'pat>(
7982
&mut self,
8083
candidate: &Candidate<'pat, 'tcx>,

0 commit comments

Comments
 (0)