Skip to content

Commit fd8bc48

Browse files
authored
Rollup merge of rust-lang#120214 - Nadrieril:fix-120210, r=pnkfelix
match lowering: consistently lower bindings deepest-first Currently when lowering match expressions to MIR, we do a funny little dance with the order of bindings. I attempt to explain it in the third commit: we handle refutable (i.e. needing a test) patterns differently than irrefutable ones. This leads to inconsistencies, as reported in rust-lang#120210. The reason we need a dance at all is for situations like: ```rust fn foo1(x: NonCopyStruct) { let y @ NonCopyStruct { copy_field: z } = x; // the above should turn into let z = x.copy_field; let y = x; } ``` Here the `y ``````@``````` binding will move out of `x`, so we need to copy the field first. I believe that the inconsistency came about when we fixed rust-lang#69971, and didn't notice that the fix didn't extend to refutable patterns. My guess then is that ordering bindings by "deepest-first, otherwise source order" is a sound choice. This PR implements that (at least I hope, match lowering is hard to follow 🥲). Fixes rust-lang#120210 r? ``````@oli-obk`````` since you merged the original fix to rust-lang#69971 cc ``````@matthewjasper``````
2 parents e0086b4 + 0825ad3 commit fd8bc48

18 files changed

+316
-262
lines changed

compiler/rustc_mir_build/src/build/matches/simplify.rs

+53-54
Original file line numberDiff line numberDiff line change
@@ -40,43 +40,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
4040
&mut self,
4141
candidate: &mut Candidate<'pat, 'tcx>,
4242
) -> bool {
43-
// repeatedly simplify match pairs until fixed point is reached
4443
debug!("{candidate:#?}");
45-
46-
// existing_bindings and new_bindings exists to keep the semantics in order.
47-
// Reversing the binding order for bindings after `@` changes the binding order in places
48-
// it shouldn't be changed, for example `let (Some(a), Some(b)) = (x, y)`
44+
// In order to please the borrow checker, in a pattern like `x @ pat` we must lower the
45+
// bindings in `pat` before `x`. E.g. (#69971):
46+
//
47+
// struct NonCopyStruct {
48+
// copy_field: u32,
49+
// }
50+
//
51+
// fn foo1(x: NonCopyStruct) {
52+
// let y @ NonCopyStruct { copy_field: z } = x;
53+
// // the above should turn into
54+
// let z = x.copy_field;
55+
// let y = x;
56+
// }
4957
//
50-
// To avoid this, the binding occurs in the following manner:
51-
// * the bindings for one iteration of the following loop occurs in order (i.e. left to
52-
// right)
53-
// * the bindings from the previous iteration of the loop is prepended to the bindings from
54-
// the current iteration (in the implementation this is done by mem::swap and extend)
55-
// * after all iterations, these new bindings are then appended to the bindings that were
56-
// preexisting (i.e. `candidate.binding` when the function was called).
58+
// We can't just reverse the binding order, because we must preserve pattern-order
59+
// otherwise, e.g. in `let (Some(a), Some(b)) = (x, y)`. Our rule then is: deepest-first,
60+
// and bindings at the same depth stay in source order.
61+
//
62+
// To do this, every time around the loop we prepend the newly found bindings to the
63+
// bindings we already had.
5764
//
5865
// example:
5966
// candidate.bindings = [1, 2, 3]
60-
// binding in iter 1: [4, 5]
61-
// binding in iter 2: [6, 7]
67+
// bindings in iter 1: [4, 5]
68+
// bindings in iter 2: [6, 7]
6269
//
63-
// final binding: [1, 2, 3, 6, 7, 4, 5]
64-
let mut existing_bindings = mem::take(&mut candidate.bindings);
65-
let mut new_bindings = Vec::new();
70+
// final bindings: [6, 7, 4, 5, 1, 2, 3]
71+
let mut accumulated_bindings = mem::take(&mut candidate.bindings);
72+
// Repeatedly simplify match pairs until fixed point is reached
6673
loop {
67-
let match_pairs = mem::take(&mut candidate.match_pairs);
68-
69-
if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place }] =
70-
&*match_pairs
71-
{
72-
existing_bindings.extend_from_slice(&new_bindings);
73-
mem::swap(&mut candidate.bindings, &mut existing_bindings);
74-
candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats);
75-
return true;
76-
}
77-
7874
let mut changed = false;
79-
for match_pair in match_pairs {
75+
for match_pair in mem::take(&mut candidate.match_pairs) {
8076
match self.simplify_match_pair(match_pair, candidate) {
8177
Ok(()) => {
8278
changed = true;
@@ -86,36 +82,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
8682
}
8783
}
8884
}
89-
// Avoid issue #69971: the binding order should be right to left if there are more
90-
// bindings after `@` to please the borrow checker
91-
// Ex
92-
// struct NonCopyStruct {
93-
// copy_field: u32,
94-
// }
95-
//
96-
// fn foo1(x: NonCopyStruct) {
97-
// let y @ NonCopyStruct { copy_field: z } = x;
98-
// // the above should turn into
99-
// let z = x.copy_field;
100-
// let y = x;
101-
// }
102-
candidate.bindings.extend_from_slice(&new_bindings);
103-
mem::swap(&mut candidate.bindings, &mut new_bindings);
85+
86+
// This does: accumulated_bindings = candidate.bindings.take() ++ accumulated_bindings
87+
candidate.bindings.extend_from_slice(&accumulated_bindings);
88+
mem::swap(&mut candidate.bindings, &mut accumulated_bindings);
10489
candidate.bindings.clear();
10590

10691
if !changed {
107-
existing_bindings.extend_from_slice(&new_bindings);
108-
mem::swap(&mut candidate.bindings, &mut existing_bindings);
109-
// Move or-patterns to the end, because they can result in us
110-
// creating additional candidates, so we want to test them as
111-
// late as possible.
112-
candidate
113-
.match_pairs
114-
.sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. }));
115-
debug!(simplified = ?candidate, "simplify_candidate");
116-
return false; // if we were not able to simplify any, done.
92+
// If we were not able to simplify anymore, done.
93+
break;
11794
}
11895
}
96+
97+
// Store computed bindings back in `candidate`.
98+
mem::swap(&mut candidate.bindings, &mut accumulated_bindings);
99+
100+
let did_expand_or =
101+
if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place }] =
102+
&*candidate.match_pairs
103+
{
104+
candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats);
105+
candidate.match_pairs.clear();
106+
true
107+
} else {
108+
false
109+
};
110+
111+
// Move or-patterns to the end, because they can result in us
112+
// creating additional candidates, so we want to test them as
113+
// late as possible.
114+
candidate.match_pairs.sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. }));
115+
debug!(simplified = ?candidate, "simplify_candidate");
116+
117+
did_expand_or
119118
}
120119

121120
/// Given `candidate` that has a single or-pattern for its match-pairs,

tests/mir-opt/early_otherwise_branch.opt1.EarlyOtherwiseBranch.diff

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@
5151
- }
5252
-
5353
- bb3: {
54-
StorageLive(_8);
55-
_8 = (((_3.0: std::option::Option<u32>) as Some).0: u32);
5654
StorageLive(_9);
5755
_9 = (((_3.1: std::option::Option<u32>) as Some).0: u32);
56+
StorageLive(_8);
57+
_8 = (((_3.0: std::option::Option<u32>) as Some).0: u32);
5858
_0 = const 0_u32;
59-
StorageDead(_9);
6059
StorageDead(_8);
60+
StorageDead(_9);
6161
- goto -> bb4;
6262
+ goto -> bb3;
6363
}

tests/mir-opt/early_otherwise_branch.opt2.EarlyOtherwiseBranch.diff

+3-3
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@
5858
-
5959
- bb4: {
6060
+ bb2: {
61-
StorageLive(_9);
62-
_9 = (((_3.0: std::option::Option<u32>) as Some).0: u32);
6361
StorageLive(_10);
6462
_10 = (((_3.1: std::option::Option<u32>) as Some).0: u32);
63+
StorageLive(_9);
64+
_9 = (((_3.0: std::option::Option<u32>) as Some).0: u32);
6565
_0 = const 0_u32;
66-
StorageDead(_10);
6766
StorageDead(_9);
67+
StorageDead(_10);
6868
- goto -> bb6;
6969
+ goto -> bb4;
7070
}

tests/mir-opt/early_otherwise_branch.opt3.EarlyOtherwiseBranch.diff

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@
5151
- }
5252
-
5353
- bb3: {
54-
StorageLive(_8);
55-
_8 = (((_3.0: std::option::Option<u32>) as Some).0: u32);
5654
StorageLive(_9);
5755
_9 = (((_3.1: std::option::Option<bool>) as Some).0: bool);
56+
StorageLive(_8);
57+
_8 = (((_3.0: std::option::Option<u32>) as Some).0: u32);
5858
_0 = const 0_u32;
59-
StorageDead(_9);
6059
StorageDead(_8);
60+
StorageDead(_9);
6161
- goto -> bb4;
6262
+ goto -> bb3;
6363
}

tests/mir-opt/early_otherwise_branch_3_element_tuple.opt1.EarlyOtherwiseBranch.diff

+6-6
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,16 @@
6969

7070
- bb4: {
7171
+ bb3: {
72-
StorageLive(_11);
73-
_11 = (((_4.0: std::option::Option<u32>) as Some).0: u32);
74-
StorageLive(_12);
75-
_12 = (((_4.1: std::option::Option<u32>) as Some).0: u32);
7672
StorageLive(_13);
7773
_13 = (((_4.2: std::option::Option<u32>) as Some).0: u32);
74+
StorageLive(_12);
75+
_12 = (((_4.1: std::option::Option<u32>) as Some).0: u32);
76+
StorageLive(_11);
77+
_11 = (((_4.0: std::option::Option<u32>) as Some).0: u32);
7878
_0 = const 0_u32;
79-
StorageDead(_13);
80-
StorageDead(_12);
8179
StorageDead(_11);
80+
StorageDead(_12);
81+
StorageDead(_13);
8282
- goto -> bb5;
8383
+ goto -> bb4;
8484
}

tests/mir-opt/early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.diff

+24-24
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,12 @@
116116
}
117117

118118
bb6: {
119-
StorageLive(_12);
120-
_39 = deref_copy (_4.0: &ViewportPercentageLength);
121-
_12 = (((*_39) as Vw).0: f32);
122119
StorageLive(_13);
123-
_40 = deref_copy (_4.1: &ViewportPercentageLength);
124-
_13 = (((*_40) as Vw).0: f32);
120+
_39 = deref_copy (_4.1: &ViewportPercentageLength);
121+
_13 = (((*_39) as Vw).0: f32);
122+
StorageLive(_12);
123+
_40 = deref_copy (_4.0: &ViewportPercentageLength);
124+
_12 = (((*_40) as Vw).0: f32);
125125
StorageLive(_14);
126126
StorageLive(_15);
127127
_15 = _12;
@@ -132,18 +132,18 @@
132132
StorageDead(_15);
133133
_3 = ViewportPercentageLength::Vw(move _14);
134134
StorageDead(_14);
135-
StorageDead(_13);
136135
StorageDead(_12);
136+
StorageDead(_13);
137137
goto -> bb10;
138138
}
139139

140140
bb7: {
141-
StorageLive(_17);
142-
_41 = deref_copy (_4.0: &ViewportPercentageLength);
143-
_17 = (((*_41) as Vh).0: f32);
144141
StorageLive(_18);
145-
_42 = deref_copy (_4.1: &ViewportPercentageLength);
146-
_18 = (((*_42) as Vh).0: f32);
142+
_41 = deref_copy (_4.1: &ViewportPercentageLength);
143+
_18 = (((*_41) as Vh).0: f32);
144+
StorageLive(_17);
145+
_42 = deref_copy (_4.0: &ViewportPercentageLength);
146+
_17 = (((*_42) as Vh).0: f32);
147147
StorageLive(_19);
148148
StorageLive(_20);
149149
_20 = _17;
@@ -154,18 +154,18 @@
154154
StorageDead(_20);
155155
_3 = ViewportPercentageLength::Vh(move _19);
156156
StorageDead(_19);
157-
StorageDead(_18);
158157
StorageDead(_17);
158+
StorageDead(_18);
159159
goto -> bb10;
160160
}
161161

162162
bb8: {
163-
StorageLive(_22);
164-
_43 = deref_copy (_4.0: &ViewportPercentageLength);
165-
_22 = (((*_43) as Vmin).0: f32);
166163
StorageLive(_23);
167-
_44 = deref_copy (_4.1: &ViewportPercentageLength);
168-
_23 = (((*_44) as Vmin).0: f32);
164+
_43 = deref_copy (_4.1: &ViewportPercentageLength);
165+
_23 = (((*_43) as Vmin).0: f32);
166+
StorageLive(_22);
167+
_44 = deref_copy (_4.0: &ViewportPercentageLength);
168+
_22 = (((*_44) as Vmin).0: f32);
169169
StorageLive(_24);
170170
StorageLive(_25);
171171
_25 = _22;
@@ -176,18 +176,18 @@
176176
StorageDead(_25);
177177
_3 = ViewportPercentageLength::Vmin(move _24);
178178
StorageDead(_24);
179-
StorageDead(_23);
180179
StorageDead(_22);
180+
StorageDead(_23);
181181
goto -> bb10;
182182
}
183183

184184
bb9: {
185-
StorageLive(_27);
186-
_45 = deref_copy (_4.0: &ViewportPercentageLength);
187-
_27 = (((*_45) as Vmax).0: f32);
188185
StorageLive(_28);
189-
_46 = deref_copy (_4.1: &ViewportPercentageLength);
190-
_28 = (((*_46) as Vmax).0: f32);
186+
_45 = deref_copy (_4.1: &ViewportPercentageLength);
187+
_28 = (((*_45) as Vmax).0: f32);
188+
StorageLive(_27);
189+
_46 = deref_copy (_4.0: &ViewportPercentageLength);
190+
_27 = (((*_46) as Vmax).0: f32);
191191
StorageLive(_29);
192192
StorageLive(_30);
193193
_30 = _27;
@@ -198,8 +198,8 @@
198198
StorageDead(_30);
199199
_3 = ViewportPercentageLength::Vmax(move _29);
200200
StorageDead(_29);
201-
StorageDead(_28);
202201
StorageDead(_27);
202+
StorageDead(_28);
203203
goto -> bb10;
204204
}
205205

tests/mir-opt/early_otherwise_branch_noopt.noopt1.EarlyOtherwiseBranch.diff

+3-3
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@
5959
}
6060

6161
bb5: {
62-
StorageLive(_9);
63-
_9 = (((_3.0: std::option::Option<u32>) as Some).0: u32);
6462
StorageLive(_10);
6563
_10 = (((_3.1: std::option::Option<u32>) as Some).0: u32);
64+
StorageLive(_9);
65+
_9 = (((_3.0: std::option::Option<u32>) as Some).0: u32);
6666
_0 = const 0_u32;
67-
StorageDead(_10);
6867
StorageDead(_9);
68+
StorageDead(_10);
6969
goto -> bb8;
7070
}
7171

tests/ui/pattern/bindings-after-at/bind-by-copy.rs

+39-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,30 @@
11
// run-pass
2+
#![allow(unused)]
23

34
// Test copy
45

5-
struct A { a: i32, b: i32 }
6-
struct B { a: i32, b: C }
7-
struct D { a: i32, d: C }
8-
#[derive(Copy,Clone)]
9-
struct C { c: i32 }
6+
struct A {
7+
a: i32,
8+
b: i32,
9+
}
10+
struct B {
11+
a: i32,
12+
b: C,
13+
}
14+
struct D {
15+
a: i32,
16+
d: C,
17+
}
18+
#[derive(Copy, Clone)]
19+
struct C {
20+
c: i32,
21+
}
22+
enum E {
23+
E { a: i32, e: C },
24+
NotE,
25+
}
1026

27+
#[rustfmt::skip]
1128
pub fn main() {
1229
match (A {a: 10, b: 20}) {
1330
x@A {a, b: 20} => { assert!(x.a == 10); assert!(a == 10); }
@@ -23,6 +40,23 @@ pub fn main() {
2340
y.d.c = 30;
2441
assert_eq!(d.c, 20);
2542

43+
match (E::E { a: 10, e: C { c: 20 } }) {
44+
x @ E::E{ a, e: C { c } } => {
45+
assert!(matches!(x, E::E { a: 10, e: C { c: 20 } }));
46+
assert!(a == 10);
47+
assert!(c == 20);
48+
}
49+
_ => panic!(),
50+
}
51+
match (E::E { a: 10, e: C { c: 20 } }) {
52+
mut x @ E::E{ a, e: C { mut c } } => {
53+
x = E::NotE;
54+
c += 30;
55+
assert_eq!(c, 50);
56+
}
57+
_ => panic!(),
58+
}
59+
2660
let some_b = Some(B { a: 10, b: C { c: 20 } });
2761

2862
// in irrefutable pattern

tests/ui/pattern/bindings-after-at/borrowck-move-and-move.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ fn main() {
1515
let a @ (b, c) = (u(), u()); //~ ERROR use of partially moved value
1616

1717
match Ok(U) {
18-
a @ Ok(b) | a @ Err(b) => {} //~ ERROR use of moved value
19-
//~^ ERROR use of moved value
18+
a @ Ok(b) | a @ Err(b) => {} //~ ERROR use of partially moved value
19+
//~^ ERROR use of partially moved value
2020
}
2121

2222
fn fun(a @ b: U) {} //~ ERROR use of moved value

0 commit comments

Comments
 (0)