@@ -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 }
0 commit comments