5
5
//! This also includes code for pattern bindings in `let` statements and
6
6
//! function parameters.
7
7
8
- use std:: assert_matches:: assert_matches;
9
8
use std:: borrow:: Borrow ;
10
9
use std:: mem;
11
10
use std:: sync:: Arc ;
12
11
12
+ use itertools:: { Itertools , Position } ;
13
13
use rustc_abi:: VariantIdx ;
14
14
use rustc_data_structures:: fx:: FxIndexMap ;
15
15
use rustc_data_structures:: stack:: ensure_sufficient_stack;
@@ -561,16 +561,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
561
561
// return: it isn't bound by move until right before enter the arm.
562
562
// To handle this we instead unschedule it's drop after each time
563
563
// we lower the guard.
564
+ // As a result, we end up with the drop order of the last sub-branch we lower. To use
565
+ // the drop order for the first sub-branch, we lower sub-branches in reverse (#142163).
564
566
let target_block = self . cfg . start_new_block ( ) ;
565
- let mut schedule_drops = ScheduleDrops :: Yes ;
566
- let arm = arm_match_scope. unzip ( ) . 0 ;
567
- // We keep a stack of all of the bindings and type ascriptions
568
- // from the parent candidates that we visit, that also need to
569
- // be bound for each candidate.
570
- for sub_branch in branch. sub_branches {
571
- if let Some ( arm) = arm {
572
- self . clear_top_scope ( arm. scope ) ;
573
- }
567
+ for ( pos, sub_branch) in branch. sub_branches . into_iter ( ) . rev ( ) . with_position ( ) {
568
+ debug_assert ! ( pos != Position :: Only ) ;
569
+ let schedule_drops =
570
+ if pos == Position :: Last { ScheduleDrops :: Yes } else { ScheduleDrops :: No } ;
574
571
let binding_end = self . bind_and_guard_matched_candidate (
575
572
sub_branch,
576
573
fake_borrow_temps,
@@ -579,9 +576,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
579
576
schedule_drops,
580
577
emit_storage_live,
581
578
) ;
582
- if arm. is_none ( ) {
583
- schedule_drops = ScheduleDrops :: No ;
584
- }
585
579
self . cfg . goto ( binding_end, outer_source_info, target_block) ;
586
580
}
587
581
@@ -996,7 +990,7 @@ struct PatternExtraData<'tcx> {
996
990
span : Span ,
997
991
998
992
/// Bindings that must be established.
999
- bindings : Vec < Binding < ' tcx > > ,
993
+ bindings : Vec < SubpatternBindings < ' tcx > > ,
1000
994
1001
995
/// Types that must be asserted.
1002
996
ascriptions : Vec < Ascription < ' tcx > > ,
@@ -1011,6 +1005,15 @@ impl<'tcx> PatternExtraData<'tcx> {
1011
1005
}
1012
1006
}
1013
1007
1008
+ #[ derive( Debug , Clone ) ]
1009
+ enum SubpatternBindings < ' tcx > {
1010
+ /// A single binding.
1011
+ One ( Binding < ' tcx > ) ,
1012
+ /// Holds the place for an or-pattern's bindings. This ensures their drops are scheduled in the
1013
+ /// order the primary bindings appear. See rust-lang/rust#142163 for more information.
1014
+ FromOrPattern ,
1015
+ }
1016
+
1014
1017
/// A pattern in a form suitable for lowering the match tree, with all irrefutable
1015
1018
/// patterns simplified away.
1016
1019
///
@@ -1226,7 +1229,7 @@ fn traverse_candidate<'tcx, C, T, I>(
1226
1229
}
1227
1230
}
1228
1231
1229
- #[ derive( Clone , Debug ) ]
1232
+ #[ derive( Clone , Copy , Debug ) ]
1230
1233
struct Binding < ' tcx > {
1231
1234
span : Span ,
1232
1235
source : Place < ' tcx > ,
@@ -1452,12 +1455,7 @@ impl<'tcx> MatchTreeSubBranch<'tcx> {
1452
1455
span : candidate. extra_data . span ,
1453
1456
success_block : candidate. pre_binding_block . unwrap ( ) ,
1454
1457
otherwise_block : candidate. otherwise_block . unwrap ( ) ,
1455
- bindings : parent_data
1456
- . iter ( )
1457
- . flat_map ( |d| & d. bindings )
1458
- . chain ( & candidate. extra_data . bindings )
1459
- . cloned ( )
1460
- . collect ( ) ,
1458
+ bindings : sub_branch_bindings ( parent_data, & candidate. extra_data . bindings ) ,
1461
1459
ascriptions : parent_data
1462
1460
. iter ( )
1463
1461
. flat_map ( |d| & d. ascriptions )
@@ -1490,6 +1488,68 @@ impl<'tcx> MatchTreeBranch<'tcx> {
1490
1488
}
1491
1489
}
1492
1490
1491
+ /// Collects the bindings for a [`MatchTreeSubBranch`], preserving the order they appear in the
1492
+ /// pattern, as though the or-alternatives chosen in this sub-branch were inlined.
1493
+ fn sub_branch_bindings < ' tcx > (
1494
+ parents : & [ PatternExtraData < ' tcx > ] ,
1495
+ leaf_bindings : & [ SubpatternBindings < ' tcx > ] ,
1496
+ ) -> Vec < Binding < ' tcx > > {
1497
+ // In the common case, all bindings will be in leaves. Allocate to fit the leaf's bindings.
1498
+ let mut all_bindings = Vec :: with_capacity ( leaf_bindings. len ( ) ) ;
1499
+ let mut remainder = parents
1500
+ . iter ( )
1501
+ . map ( |parent| parent. bindings . as_slice ( ) )
1502
+ . chain ( [ leaf_bindings] )
1503
+ // Skip over unsimplified or-patterns without bindings.
1504
+ . filter ( |bindings| !bindings. is_empty ( ) ) ;
1505
+ if let Some ( candidate_bindings) = remainder. next ( ) {
1506
+ push_sub_branch_bindings ( & mut all_bindings, candidate_bindings, & mut remainder) ;
1507
+ }
1508
+ // Make sure we've included all bindings. For ill-formed patterns like `(x, _ | y)`, we may not
1509
+ // have collected all bindings yet, since we only check the first alternative when determining
1510
+ // whether to inline subcandidates' bindings.
1511
+ // FIXME(@dianne): prevent ill-formed patterns from getting here
1512
+ while let Some ( candidate_bindings) = remainder. next ( ) {
1513
+ ty:: tls:: with ( |tcx| {
1514
+ tcx. dcx ( ) . delayed_bug ( "mismatched or-pattern bindings but no error emitted" )
1515
+ } ) ;
1516
+ // To recover, we collect the rest in an arbitrary order.
1517
+ push_sub_branch_bindings ( & mut all_bindings, candidate_bindings, & mut remainder) ;
1518
+ }
1519
+ all_bindings
1520
+ }
1521
+
1522
+ /// Helper for [`sub_branch_bindings`]. Collects bindings from `candidate_bindings` into
1523
+ /// `flattened`. Bindings in or-patterns are collected recursively from `remainder`.
1524
+ fn push_sub_branch_bindings < ' c , ' tcx : ' c > (
1525
+ flattened : & mut Vec < Binding < ' tcx > > ,
1526
+ candidate_bindings : & ' c [ SubpatternBindings < ' tcx > ] ,
1527
+ remainder : & mut impl Iterator < Item = & ' c [ SubpatternBindings < ' tcx > ] > ,
1528
+ ) {
1529
+ for subpat_bindings in candidate_bindings {
1530
+ match subpat_bindings {
1531
+ SubpatternBindings :: One ( binding) => flattened. push ( * binding) ,
1532
+ SubpatternBindings :: FromOrPattern => {
1533
+ // Inline bindings from an or-pattern. By construction, this always
1534
+ // corresponds to a subcandidate and its closest descendants (i.e. those
1535
+ // from nested or-patterns, but not adjacent or-patterns). To handle
1536
+ // adjacent or-patterns, e.g. `(x | x, y | y)`, we update the `remainder` to
1537
+ // point to the first descendant candidate from outside this or-pattern.
1538
+ if let Some ( subcandidate_bindings) = remainder. next ( ) {
1539
+ push_sub_branch_bindings ( flattened, subcandidate_bindings, remainder) ;
1540
+ } else {
1541
+ // For ill-formed patterns like `x | _`, we may not have any subcandidates left
1542
+ // to inline bindings from.
1543
+ // FIXME(@dianne): prevent ill-formed patterns from getting here
1544
+ ty:: tls:: with ( |tcx| {
1545
+ tcx. dcx ( ) . delayed_bug ( "mismatched or-pattern bindings but no error emitted" )
1546
+ } ) ;
1547
+ } ;
1548
+ }
1549
+ }
1550
+ }
1551
+ }
1552
+
1493
1553
#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
1494
1554
pub ( crate ) enum HasMatchGuard {
1495
1555
Yes ,
@@ -2453,11 +2513,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2453
2513
2454
2514
// Bindings for guards require some extra handling to automatically
2455
2515
// insert implicit references/dereferences.
2456
- self . bind_matched_candidate_for_guard (
2457
- block,
2458
- schedule_drops,
2459
- sub_branch. bindings . iter ( ) ,
2460
- ) ;
2516
+ // This always schedules storage drops, so we may need to unschedule them below.
2517
+ self . bind_matched_candidate_for_guard ( block, sub_branch. bindings . iter ( ) ) ;
2461
2518
let guard_frame = GuardFrame {
2462
2519
locals : sub_branch
2463
2520
. bindings
@@ -2489,6 +2546,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2489
2546
)
2490
2547
} ) ;
2491
2548
2549
+ // If this isn't the final sub-branch being lowered, we need to unschedule drops of
2550
+ // bindings and temporaries created for and by the guard. As a result, the drop order
2551
+ // for the arm will correspond to the binding order of the final sub-branch lowered.
2552
+ if matches ! ( schedule_drops, ScheduleDrops :: No ) {
2553
+ self . clear_top_scope ( arm. scope ) ;
2554
+ }
2555
+
2492
2556
let source_info = self . source_info ( guard_span) ;
2493
2557
let guard_end = self . source_info ( tcx. sess . source_map ( ) . end_point ( guard_span) ) ;
2494
2558
let guard_frame = self . guard_context . pop ( ) . unwrap ( ) ;
@@ -2538,14 +2602,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2538
2602
let cause = FakeReadCause :: ForGuardBinding ;
2539
2603
self . cfg . push_fake_read ( post_guard_block, guard_end, cause, Place :: from ( local_id) ) ;
2540
2604
}
2541
- assert_matches ! (
2542
- schedule_drops,
2543
- ScheduleDrops :: Yes ,
2544
- "patterns with guards must schedule drops"
2545
- ) ;
2605
+ // Only schedule drops for the last sub-branch we lower.
2546
2606
self . bind_matched_candidate_for_arm_body (
2547
2607
post_guard_block,
2548
- ScheduleDrops :: Yes ,
2608
+ schedule_drops ,
2549
2609
by_value_bindings,
2550
2610
emit_storage_live,
2551
2611
) ;
@@ -2671,7 +2731,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2671
2731
fn bind_matched_candidate_for_guard < ' b > (
2672
2732
& mut self ,
2673
2733
block : BasicBlock ,
2674
- schedule_drops : ScheduleDrops ,
2675
2734
bindings : impl IntoIterator < Item = & ' b Binding < ' tcx > > ,
2676
2735
) where
2677
2736
' tcx : ' b ,
@@ -2690,12 +2749,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2690
2749
// a reference R: &T pointing to the location matched by
2691
2750
// the pattern, and every occurrence of P within a guard
2692
2751
// denotes *R.
2752
+ // Drops must be scheduled to emit `StorageDead` on the guard's failure/break branches.
2693
2753
let ref_for_guard = self . storage_live_binding (
2694
2754
block,
2695
2755
binding. var_id ,
2696
2756
binding. span ,
2697
2757
RefWithinGuard ,
2698
- schedule_drops ,
2758
+ ScheduleDrops :: Yes ,
2699
2759
) ;
2700
2760
match binding. binding_mode . 0 {
2701
2761
ByRef :: No => {
@@ -2705,13 +2765,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2705
2765
self . cfg . push_assign ( block, source_info, ref_for_guard, rvalue) ;
2706
2766
}
2707
2767
ByRef :: Yes ( mutbl) => {
2708
- // The arm binding will be by reference, so eagerly create it now.
2768
+ // The arm binding will be by reference, so eagerly create it now. Drops must
2769
+ // be scheduled to emit `StorageDead` on the guard's failure/break branches.
2709
2770
let value_for_arm = self . storage_live_binding (
2710
2771
block,
2711
2772
binding. var_id ,
2712
2773
binding. span ,
2713
2774
OutsideGuard ,
2714
- schedule_drops ,
2775
+ ScheduleDrops :: Yes ,
2715
2776
) ;
2716
2777
2717
2778
let rvalue =
0 commit comments