@@ -274,9 +274,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
274
274
end_block. unit ( )
275
275
}
276
276
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.
278
279
///
279
280
/// 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.
280
283
fn bind_pattern (
281
284
& mut self ,
282
285
outer_source_info : SourceInfo ,
@@ -298,6 +301,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
298
301
true ,
299
302
)
300
303
} 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.
301
318
let target_block = self . cfg . start_new_block ( ) ;
302
319
let mut schedule_drops = true ;
303
320
// We keep a stack of all of the bindings and type asciptions
@@ -308,7 +325,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
308
325
& mut Vec :: new ( ) ,
309
326
& mut |leaf_candidate, parent_bindings| {
310
327
if let Some ( arm_scope) = arm_scope {
311
- // Avoid scheduling drops multiple times by unscheduling drops.
312
328
self . clear_top_scope ( arm_scope) ;
313
329
}
314
330
let binding_end = self . bind_and_guard_matched_candidate (
@@ -320,9 +336,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
320
336
schedule_drops,
321
337
) ;
322
338
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.
326
339
schedule_drops = false ;
327
340
}
328
341
self . cfg . goto ( binding_end, outer_source_info, target_block) ;
@@ -350,7 +363,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
350
363
// Optimize the case of `let x = ...` to write directly into `x`
351
364
PatKind :: Binding { mode : BindingMode :: ByValue , var, subpattern : None , .. } => {
352
365
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 ) ;
354
367
unpack ! ( block = self . into( & place, block, initializer) ) ;
355
368
356
369
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
@@ -385,7 +398,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
385
398
hair:: pattern:: Ascription { user_ty : pat_ascription_ty, variance : _, user_ty_span } ,
386
399
} => {
387
400
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 ) ;
389
402
unpack ! ( block = self . into( & place, block, initializer) ) ;
390
403
391
404
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
@@ -532,12 +545,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
532
545
var : HirId ,
533
546
span : Span ,
534
547
for_guard : ForGuard ,
548
+ schedule_drop : bool ,
535
549
) -> Place < ' tcx > {
536
550
let local_id = self . var_local_id ( var, for_guard) ;
537
551
let source_info = self . source_info ( span) ;
538
552
self . cfg . push ( block, Statement { source_info, kind : StatementKind :: StorageLive ( local_id) } ) ;
539
553
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
+ }
541
557
Place :: from ( local_id)
542
558
}
543
559
@@ -1060,25 +1076,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1060
1076
/// |
1061
1077
/// +----------------------------------------+------------------------------------+
1062
1078
/// | | |
1079
+ /// V V V
1063
1080
/// [ P matches ] [ Q matches ] [ otherwise ]
1064
1081
/// | | |
1082
+ /// V V |
1065
1083
/// [ match R, S ] [ match R, S ] |
1066
1084
/// | | |
1067
1085
/// +--------------+------------+ +--------------+------------+ |
1068
1086
/// | | | | | | |
1087
+ /// V V V V V V |
1069
1088
/// [ R matches ] [ S matches ] [otherwise ] [ R matches ] [ S matches ] [otherwise ] |
1070
1089
/// | | | | | | |
1071
1090
/// +--------------+------------|------------+--------------+ | |
1072
1091
/// | | | |
1073
1092
/// | +----------------------------------------+--------+
1074
1093
/// | |
1094
+ /// V V
1075
1095
/// [ Success ] [ Failure ]
1076
1096
/// ```
1077
1097
///
1078
1098
/// In practice there are some complications:
1079
1099
///
1080
1100
/// * 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.
1082
1104
/// * If neither `P` or `Q` has any bindings or type ascriptions and there
1083
1105
/// isn't a match guard, then we create a smaller CFG like:
1084
1106
///
@@ -1658,7 +1680,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1658
1680
. flat_map ( |( bindings, _) | bindings)
1659
1681
. chain ( & candidate. bindings ) ;
1660
1682
1661
- self . bind_matched_candidate_for_guard ( block, bindings. clone ( ) ) ;
1683
+ self . bind_matched_candidate_for_guard ( block, schedule_drops , bindings. clone ( ) ) ;
1662
1684
let guard_frame = GuardFrame {
1663
1685
locals : bindings. map ( |b| GuardFrameLocal :: new ( b. var_id , b. binding_mode ) ) . collect ( ) ,
1664
1686
} ;
@@ -1807,6 +1829,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1807
1829
fn bind_matched_candidate_for_guard < ' b > (
1808
1830
& mut self ,
1809
1831
block : BasicBlock ,
1832
+ schedule_drops : bool ,
1810
1833
bindings : impl IntoIterator < Item = & ' b Binding < ' tcx > > ,
1811
1834
) where
1812
1835
' tcx : ' b ,
@@ -1825,8 +1848,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1825
1848
// a reference R: &T pointing to the location matched by
1826
1849
// the pattern, and every occurrence of P within a guard
1827
1850
// 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
+ ) ;
1830
1858
match binding. binding_mode {
1831
1859
BindingMode :: ByValue => {
1832
1860
let rvalue = Rvalue :: Ref ( re_erased, BorrowKind :: Shared , binding. source ) ;
@@ -1838,6 +1866,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1838
1866
binding. var_id ,
1839
1867
binding. span ,
1840
1868
OutsideGuard ,
1869
+ schedule_drops,
1841
1870
) ;
1842
1871
1843
1872
let rvalue = Rvalue :: Ref ( re_erased, borrow_kind, binding. source ) ;
@@ -1863,8 +1892,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1863
1892
// Assign each of the bindings. This may trigger moves out of the candidate.
1864
1893
for binding in bindings {
1865
1894
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
+ ) ;
1868
1902
if schedule_drops {
1869
1903
self . schedule_drop_for_binding ( binding. var_id , binding. span , OutsideGuard ) ;
1870
1904
}
0 commit comments