Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3b30da3

Browse files
committedMar 26, 2025
Auto merge of #138961 - meithecatte:expr-use-visitor, r=<try>
ExprUseVisitor: properly report discriminant reads This PR fixes #137467. In order to do so, it needs to introduce a small breaking change surrounding the interaction of closure captures with matching against enums with uninhabited variants. Yes – to fix an ICE! ## Background The current upvar inference code handles patterns in two parts: - `ExprUseVisitor::walk_pat` finds the *bindings* being done by the pattern and captures the relevant parts - `ExprUseVisitor::maybe_read_scrutinee` determines whether matching against the pattern will at any point require inspecting a discriminant, and if so, captures *the entire scrutinee*. It also has some weird logic around bindings, deciding to also capture the entire scrutinee if *pretty much any binding exists in the pattern*, with some weird behavior like #137553. Nevertheless, something like `|| let (a, _) = x;` will only capture `x.0`, because `maybe_read_scrutinee` does not run for irrefutable patterns at all. This causes issues like #137467, where the closure wouldn't be capturing enough, because an irrefutable or-pattern can still require inspecting a discriminant, and the match lowering would then panic, because it couldn't find an appropriate upvar in the closure. My thesis is that this is not a reasonable implementation. To that end, I intend to merge the functionality of both these parts into `walk_pat`, which will bring upvar inference closer to what the MIR lowering actually needs – both in making sure that necessary variables get captured, fixing #137467, and in reducing the cases where redundant variables do – fixing #137553. This PR introduces the necessary logic into `walk_pat`, fixing #137467. A subsequent PR will remove `maybe_read_scrutinee` entirely, which should now be redundant, fixing #137553. The latter is still pending, as my current revision doesn't handle opaque types correctly for some reason I haven't looked into yet. ## The breaking change The following example, adapted from the testsuite, compiles on current stable, but will not compile with this PR: ```rust #[derive(Clone, Copy, PartialEq, Eq, Debug)] enum Void {} pub fn main() { let mut r = Result::<Void, (u32, u32)>::Err((0, 0)); let mut f = || { let Err((ref mut a, _)) = r; *a = 1; }; let mut g = || { //~^ ERROR: cannot borrow `r` as mutable more than once at a time let Err((_, ref mut b)) = r; *b = 2; }; f(); g(); assert_eq!(r, Err((1, 2))); } ``` The issue is that, to determine that matching against `Err` here doesn't require inspecting the discriminant, we need to query the `InhabitedPredicate` of the types involved. However, as upvar inference is done during typechecking, the relevant type might not yet be fully inferred. Because of this, performing such a check hits this assertion: https://github.com/rust-lang/rust/blob/43f0014ef0f242418674f49052ed39b70f73bc1c/compiler/rustc_middle/src/ty/inhabitedness/mod.rs#L121 The code used to compile fine, but only because the compiler incorrectly assumed that patterns used within a `let` cannot possibly be inspecting any discriminants. ## Is the breaking change necessary? One other option would be to double down, and introduce a deliberate semantics difference between `let $pat = $expr;` and `match $expr { $pat => ... }`, that syntactically determines whether the pattern is in an irrefutable position, instead of querying the types. **This would not eliminate the breaking change,** but it would limit it to more contrived examples, such as ```rust let ((true, Err((ref mut a, _, _))) | (false, Err((_, ref mut a, _)))) = x; ``` The cost here, would be the complexity added with very little benefit. ## Other notes - I performed various cleanups while working on this. The last commit of the PR is the interesting one. - Due to the temporary duplication of logic between `maybe_read_scrutinee` and `walk_pat`, some of the `#[rustc_capture_analysis]` tests report duplicate messages before deduplication. This is harmless.
2 parents 19cab6b + 8ed61e4 commit 3b30da3

16 files changed

+487
-192
lines changed
 

‎compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+139-104
Large diffs are not rendered by default.

‎compiler/rustc_hir_typeck/src/upvar.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
//! from there).
1919
//!
2020
//! The fact that we are inferring borrow kinds as we go results in a
21-
//! semi-hacky interaction with mem-categorization. In particular,
22-
//! mem-categorization will query the current borrow kind as it
23-
//! categorizes, and we'll return the *current* value, but this may get
21+
//! semi-hacky interaction with the way `ExprUseVisitor` is computing
22+
//! `Place`s. In particular, it will query the current borrow kind as it
23+
//! goes, and we'll return the *current* value, but this may get
2424
//! adjusted later. Therefore, in this module, we generally ignore the
25-
//! borrow kind (and derived mutabilities) that are returned from
26-
//! mem-categorization, since they may be inaccurate. (Another option
25+
//! borrow kind (and derived mutabilities) that `ExprUseVisitor` returns
26+
//! within `Place`s, since they may be inaccurate. (Another option
2727
//! would be to use a unification scheme, where instead of returning a
2828
//! concrete borrow kind like `ty::ImmBorrow`, we return a
2929
//! `ty::InferBorrow(upvar_id)` or something like that, but this would

‎compiler/rustc_middle/src/hir/place.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ pub struct Projection<'tcx> {
5353
pub kind: ProjectionKind,
5454
}
5555

56-
/// A `Place` represents how a value is located in memory.
56+
/// A `Place` represents how a value is located in memory. This does not
57+
/// always correspond to a syntactic place expression. For example, when
58+
/// processing a pattern, a `Place` can be used to refer to the sub-value
59+
/// currently being inspected.
5760
///
5861
/// This is an HIR version of [`rustc_middle::mir::Place`].
5962
#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
@@ -67,7 +70,10 @@ pub struct Place<'tcx> {
6770
pub projections: Vec<Projection<'tcx>>,
6871
}
6972

70-
/// A `PlaceWithHirId` represents how a value is located in memory.
73+
/// A `PlaceWithHirId` represents how a value is located in memory. This does not
74+
/// always correspond to a syntactic place expression. For example, when
75+
/// processing a pattern, a `Place` can be used to refer to the sub-value
76+
/// currently being inspected.
7177
///
7278
/// This is an HIR version of [`rustc_middle::mir::Place`].
7379
#[derive(Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]

‎tests/crashes/137467-1.rs

-17
This file was deleted.

‎tests/crashes/137467-2.rs

-18
This file was deleted.

‎tests/crashes/137467-3.rs

-8
This file was deleted.

‎tests/ui/closures/2229_closure_analysis/capture-enums.rs

+2
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ fn multi_variant_enum() {
2222
//~| Min Capture analysis includes:
2323
if let Info::Point(_, _, str) = point {
2424
//~^ NOTE: Capturing point[] -> Immutable
25+
//~| NOTE: Capturing point[] -> Immutable
2526
//~| NOTE: Capturing point[(2, 0)] -> ByValue
2627
//~| NOTE: Min Capture point[] -> ByValue
2728
println!("{}", str);
2829
}
2930

3031
if let Info::Meta(_, v) = meta {
3132
//~^ NOTE: Capturing meta[] -> Immutable
33+
//~| NOTE: Capturing meta[] -> Immutable
3234
//~| NOTE: Capturing meta[(1, 1)] -> ByValue
3335
//~| NOTE: Min Capture meta[] -> ByValue
3436
println!("{:?}", v);

‎tests/ui/closures/2229_closure_analysis/capture-enums.stderr

+18-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ LL | let c = #[rustc_capture_analysis]
99
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
1010

1111
error[E0658]: attributes on expressions are experimental
12-
--> $DIR/capture-enums.rs:48:13
12+
--> $DIR/capture-enums.rs:50:13
1313
|
1414
LL | let c = #[rustc_capture_analysis]
1515
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -34,18 +34,28 @@ note: Capturing point[] -> Immutable
3434
|
3535
LL | if let Info::Point(_, _, str) = point {
3636
| ^^^^^
37+
note: Capturing point[] -> Immutable
38+
--> $DIR/capture-enums.rs:23:41
39+
|
40+
LL | if let Info::Point(_, _, str) = point {
41+
| ^^^^^
3742
note: Capturing point[(2, 0)] -> ByValue
3843
--> $DIR/capture-enums.rs:23:41
3944
|
4045
LL | if let Info::Point(_, _, str) = point {
4146
| ^^^^^
4247
note: Capturing meta[] -> Immutable
43-
--> $DIR/capture-enums.rs:30:35
48+
--> $DIR/capture-enums.rs:31:35
49+
|
50+
LL | if let Info::Meta(_, v) = meta {
51+
| ^^^^
52+
note: Capturing meta[] -> Immutable
53+
--> $DIR/capture-enums.rs:31:35
4454
|
4555
LL | if let Info::Meta(_, v) = meta {
4656
| ^^^^
4757
note: Capturing meta[(1, 1)] -> ByValue
48-
--> $DIR/capture-enums.rs:30:35
58+
--> $DIR/capture-enums.rs:31:35
4959
|
5060
LL | if let Info::Meta(_, v) = meta {
5161
| ^^^^
@@ -67,13 +77,13 @@ note: Min Capture point[] -> ByValue
6777
LL | if let Info::Point(_, _, str) = point {
6878
| ^^^^^
6979
note: Min Capture meta[] -> ByValue
70-
--> $DIR/capture-enums.rs:30:35
80+
--> $DIR/capture-enums.rs:31:35
7181
|
7282
LL | if let Info::Meta(_, v) = meta {
7383
| ^^^^
7484

7585
error: First Pass analysis includes:
76-
--> $DIR/capture-enums.rs:52:5
86+
--> $DIR/capture-enums.rs:54:5
7787
|
7888
LL | / || {
7989
LL | |
@@ -85,13 +95,13 @@ LL | | };
8595
| |_____^
8696
|
8797
note: Capturing point[(2, 0)] -> ByValue
88-
--> $DIR/capture-enums.rs:55:47
98+
--> $DIR/capture-enums.rs:57:47
8999
|
90100
LL | let SingleVariant::Point(_, _, str) = point;
91101
| ^^^^^
92102

93103
error: Min Capture analysis includes:
94-
--> $DIR/capture-enums.rs:52:5
104+
--> $DIR/capture-enums.rs:54:5
95105
|
96106
LL | / || {
97107
LL | |
@@ -103,7 +113,7 @@ LL | | };
103113
| |_____^
104114
|
105115
note: Min Capture point[(2, 0)] -> ByValue
106-
--> $DIR/capture-enums.rs:55:47
116+
--> $DIR/capture-enums.rs:57:47
107117
|
108118
LL | let SingleVariant::Point(_, _, str) = point;
109119
| ^^^^^

‎tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs

+5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ fn test_1_should_capture() {
1414
//~| Min Capture analysis includes:
1515
match variant {
1616
//~^ NOTE: Capturing variant[] -> Immutable
17+
//~| NOTE: Capturing variant[] -> Immutable
1718
//~| NOTE: Min Capture variant[] -> Immutable
1819
Some(_) => {}
1920
_ => {}
@@ -132,6 +133,7 @@ fn test_5_should_capture_multi_variant() {
132133
//~| Min Capture analysis includes:
133134
match variant {
134135
//~^ NOTE: Capturing variant[] -> Immutable
136+
//~| NOTE: Capturing variant[] -> Immutable
135137
//~| NOTE: Min Capture variant[] -> Immutable
136138
MVariant::A => {}
137139
_ => {}
@@ -150,6 +152,7 @@ fn test_7_should_capture_slice_len() {
150152
//~| Min Capture analysis includes:
151153
match slice {
152154
//~^ NOTE: Capturing slice[] -> Immutable
155+
//~| NOTE: Capturing slice[Deref] -> Immutable
153156
//~| NOTE: Min Capture slice[] -> Immutable
154157
[_,_,_] => {},
155158
_ => {}
@@ -162,6 +165,7 @@ fn test_7_should_capture_slice_len() {
162165
//~| Min Capture analysis includes:
163166
match slice {
164167
//~^ NOTE: Capturing slice[] -> Immutable
168+
//~| NOTE: Capturing slice[Deref] -> Immutable
165169
//~| NOTE: Min Capture slice[] -> Immutable
166170
[] => {},
167171
_ => {}
@@ -174,6 +178,7 @@ fn test_7_should_capture_slice_len() {
174178
//~| Min Capture analysis includes:
175179
match slice {
176180
//~^ NOTE: Capturing slice[] -> Immutable
181+
//~| NOTE: Capturing slice[Deref] -> Immutable
177182
//~| NOTE: Min Capture slice[] -> Immutable
178183
[_, .. ,_] => {},
179184
_ => {}

‎tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr

+52-27
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ note: Capturing variant[] -> Immutable
1414
|
1515
LL | match variant {
1616
| ^^^^^^^
17+
note: Capturing variant[] -> Immutable
18+
--> $DIR/patterns-capture-analysis.rs:15:15
19+
|
20+
LL | match variant {
21+
| ^^^^^^^
1722

1823
error: Min Capture analysis includes:
1924
--> $DIR/patterns-capture-analysis.rs:12:5
@@ -33,7 +38,7 @@ LL | match variant {
3338
| ^^^^^^^
3439

3540
error: First Pass analysis includes:
36-
--> $DIR/patterns-capture-analysis.rs:30:5
41+
--> $DIR/patterns-capture-analysis.rs:31:5
3742
|
3843
LL | / || {
3944
LL | |
@@ -44,7 +49,7 @@ LL | | };
4449
| |_____^
4550

4651
error: First Pass analysis includes:
47-
--> $DIR/patterns-capture-analysis.rs:49:5
52+
--> $DIR/patterns-capture-analysis.rs:50:5
4853
|
4954
LL | / || {
5055
LL | |
@@ -55,7 +60,7 @@ LL | | };
5560
| |_____^
5661

5762
error: First Pass analysis includes:
58-
--> $DIR/patterns-capture-analysis.rs:63:5
63+
--> $DIR/patterns-capture-analysis.rs:64:5
5964
|
6065
LL | / || {
6166
LL | |
@@ -66,18 +71,18 @@ LL | | };
6671
| |_____^
6772
|
6873
note: Capturing variant[] -> Immutable
69-
--> $DIR/patterns-capture-analysis.rs:66:15
74+
--> $DIR/patterns-capture-analysis.rs:67:15
7075
|
7176
LL | match variant {
7277
| ^^^^^^^
7378
note: Capturing variant[(0, 0)] -> Immutable
74-
--> $DIR/patterns-capture-analysis.rs:66:15
79+
--> $DIR/patterns-capture-analysis.rs:67:15
7580
|
7681
LL | match variant {
7782
| ^^^^^^^
7883

7984
error: Min Capture analysis includes:
80-
--> $DIR/patterns-capture-analysis.rs:63:5
85+
--> $DIR/patterns-capture-analysis.rs:64:5
8186
|
8287
LL | / || {
8388
LL | |
@@ -88,13 +93,13 @@ LL | | };
8893
| |_____^
8994
|
9095
note: Min Capture variant[] -> Immutable
91-
--> $DIR/patterns-capture-analysis.rs:66:15
96+
--> $DIR/patterns-capture-analysis.rs:67:15
9297
|
9398
LL | match variant {
9499
| ^^^^^^^
95100

96101
error: First Pass analysis includes:
97-
--> $DIR/patterns-capture-analysis.rs:83:5
102+
--> $DIR/patterns-capture-analysis.rs:84:5
98103
|
99104
LL | / || {
100105
LL | |
@@ -105,7 +110,7 @@ LL | | };
105110
| |_____^
106111

107112
error: First Pass analysis includes:
108-
--> $DIR/patterns-capture-analysis.rs:95:5
113+
--> $DIR/patterns-capture-analysis.rs:96:5
109114
|
110115
LL | / || {
111116
LL | |
@@ -116,7 +121,7 @@ LL | | };
116121
| |_____^
117122

118123
error: First Pass analysis includes:
119-
--> $DIR/patterns-capture-analysis.rs:108:5
124+
--> $DIR/patterns-capture-analysis.rs:109:5
120125
|
121126
LL | / || {
122127
LL | |
@@ -127,7 +132,7 @@ LL | | };
127132
| |_____^
128133

129134
error: First Pass analysis includes:
130-
--> $DIR/patterns-capture-analysis.rs:130:5
135+
--> $DIR/patterns-capture-analysis.rs:131:5
131136
|
132137
LL | / || {
133138
LL | |
@@ -138,13 +143,18 @@ LL | | };
138143
| |_____^
139144
|
140145
note: Capturing variant[] -> Immutable
141-
--> $DIR/patterns-capture-analysis.rs:133:15
146+
--> $DIR/patterns-capture-analysis.rs:134:15
147+
|
148+
LL | match variant {
149+
| ^^^^^^^
150+
note: Capturing variant[] -> Immutable
151+
--> $DIR/patterns-capture-analysis.rs:134:15
142152
|
143153
LL | match variant {
144154
| ^^^^^^^
145155

146156
error: Min Capture analysis includes:
147-
--> $DIR/patterns-capture-analysis.rs:130:5
157+
--> $DIR/patterns-capture-analysis.rs:131:5
148158
|
149159
LL | / || {
150160
LL | |
@@ -155,13 +165,13 @@ LL | | };
155165
| |_____^
156166
|
157167
note: Min Capture variant[] -> Immutable
158-
--> $DIR/patterns-capture-analysis.rs:133:15
168+
--> $DIR/patterns-capture-analysis.rs:134:15
159169
|
160170
LL | match variant {
161171
| ^^^^^^^
162172

163173
error: First Pass analysis includes:
164-
--> $DIR/patterns-capture-analysis.rs:148:5
174+
--> $DIR/patterns-capture-analysis.rs:150:5
165175
|
166176
LL | / || {
167177
LL | |
@@ -172,13 +182,18 @@ LL | | };
172182
| |_____^
173183
|
174184
note: Capturing slice[] -> Immutable
175-
--> $DIR/patterns-capture-analysis.rs:151:15
185+
--> $DIR/patterns-capture-analysis.rs:153:15
186+
|
187+
LL | match slice {
188+
| ^^^^^
189+
note: Capturing slice[Deref] -> Immutable
190+
--> $DIR/patterns-capture-analysis.rs:153:15
176191
|
177192
LL | match slice {
178193
| ^^^^^
179194

180195
error: Min Capture analysis includes:
181-
--> $DIR/patterns-capture-analysis.rs:148:5
196+
--> $DIR/patterns-capture-analysis.rs:150:5
182197
|
183198
LL | / || {
184199
LL | |
@@ -189,13 +204,13 @@ LL | | };
189204
| |_____^
190205
|
191206
note: Min Capture slice[] -> Immutable
192-
--> $DIR/patterns-capture-analysis.rs:151:15
207+
--> $DIR/patterns-capture-analysis.rs:153:15
193208
|
194209
LL | match slice {
195210
| ^^^^^
196211

197212
error: First Pass analysis includes:
198-
--> $DIR/patterns-capture-analysis.rs:160:5
213+
--> $DIR/patterns-capture-analysis.rs:163:5
199214
|
200215
LL | / || {
201216
LL | |
@@ -206,13 +221,18 @@ LL | | };
206221
| |_____^
207222
|
208223
note: Capturing slice[] -> Immutable
209-
--> $DIR/patterns-capture-analysis.rs:163:15
224+
--> $DIR/patterns-capture-analysis.rs:166:15
225+
|
226+
LL | match slice {
227+
| ^^^^^
228+
note: Capturing slice[Deref] -> Immutable
229+
--> $DIR/patterns-capture-analysis.rs:166:15
210230
|
211231
LL | match slice {
212232
| ^^^^^
213233

214234
error: Min Capture analysis includes:
215-
--> $DIR/patterns-capture-analysis.rs:160:5
235+
--> $DIR/patterns-capture-analysis.rs:163:5
216236
|
217237
LL | / || {
218238
LL | |
@@ -223,13 +243,13 @@ LL | | };
223243
| |_____^
224244
|
225245
note: Min Capture slice[] -> Immutable
226-
--> $DIR/patterns-capture-analysis.rs:163:15
246+
--> $DIR/patterns-capture-analysis.rs:166:15
227247
|
228248
LL | match slice {
229249
| ^^^^^
230250

231251
error: First Pass analysis includes:
232-
--> $DIR/patterns-capture-analysis.rs:172:5
252+
--> $DIR/patterns-capture-analysis.rs:176:5
233253
|
234254
LL | / || {
235255
LL | |
@@ -240,13 +260,18 @@ LL | | };
240260
| |_____^
241261
|
242262
note: Capturing slice[] -> Immutable
243-
--> $DIR/patterns-capture-analysis.rs:175:15
263+
--> $DIR/patterns-capture-analysis.rs:179:15
264+
|
265+
LL | match slice {
266+
| ^^^^^
267+
note: Capturing slice[Deref] -> Immutable
268+
--> $DIR/patterns-capture-analysis.rs:179:15
244269
|
245270
LL | match slice {
246271
| ^^^^^
247272

248273
error: Min Capture analysis includes:
249-
--> $DIR/patterns-capture-analysis.rs:172:5
274+
--> $DIR/patterns-capture-analysis.rs:176:5
250275
|
251276
LL | / || {
252277
LL | |
@@ -257,13 +282,13 @@ LL | | };
257282
| |_____^
258283
|
259284
note: Min Capture slice[] -> Immutable
260-
--> $DIR/patterns-capture-analysis.rs:175:15
285+
--> $DIR/patterns-capture-analysis.rs:179:15
261286
|
262287
LL | match slice {
263288
| ^^^^^
264289

265290
error: First Pass analysis includes:
266-
--> $DIR/patterns-capture-analysis.rs:189:5
291+
--> $DIR/patterns-capture-analysis.rs:194:5
267292
|
268293
LL | / || {
269294
LL | |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// This example used to compile, but the fact that it should was never properly
2+
// discussed. With further experience, we concluded that capture precision
3+
// depending on whether some types are inhabited goes too far, introducing a
4+
// bunch of headaches without much benefit.
5+
//@ edition:2021
6+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
7+
enum Void {}
8+
9+
pub fn main() {
10+
let mut r = Result::<Void, (u32, u32)>::Err((0, 0));
11+
let mut f = || {
12+
let Err((ref mut a, _)) = r;
13+
*a = 1;
14+
};
15+
let mut g = || {
16+
//~^ ERROR: cannot borrow `r` as mutable more than once at a time
17+
let Err((_, ref mut b)) = r;
18+
*b = 2;
19+
};
20+
f();
21+
g();
22+
assert_eq!(r, Err((1, 2)));
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0499]: cannot borrow `r` as mutable more than once at a time
2+
--> $DIR/only-inhabited-variant-stable.rs:15:17
3+
|
4+
LL | let mut f = || {
5+
| -- first mutable borrow occurs here
6+
LL | let Err((ref mut a, _)) = r;
7+
| - first borrow occurs due to use of `r` in closure
8+
...
9+
LL | let mut g = || {
10+
| ^^ second mutable borrow occurs here
11+
LL |
12+
LL | let Err((_, ref mut b)) = r;
13+
| - second borrow occurs due to use of `r` in closure
14+
...
15+
LL | f();
16+
| - first borrow later used here
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0499`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0499]: cannot borrow `r` as mutable more than once at a time
2+
--> $DIR/only-inhabited-variant.rs:16:17
3+
|
4+
LL | let mut f = || {
5+
| -- first mutable borrow occurs here
6+
LL | let Err((ref mut a, _)) = r;
7+
| - first borrow occurs due to use of `r` in closure
8+
...
9+
LL | let mut g = || {
10+
| ^^ second mutable borrow occurs here
11+
LL |
12+
LL | let Err((_, ref mut b)) = r;
13+
| - second borrow occurs due to use of `r` in closure
14+
...
15+
LL | f();
16+
| - first borrow later used here
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0499`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0499]: cannot borrow `r` as mutable more than once at a time
2+
--> $DIR/only-inhabited-variant.rs:16:17
3+
|
4+
LL | let mut f = || {
5+
| -- first mutable borrow occurs here
6+
LL | let Err((ref mut a, _)) = r;
7+
| - first borrow occurs due to use of `r` in closure
8+
...
9+
LL | let mut g = || {
10+
| ^^ second mutable borrow occurs here
11+
LL |
12+
LL | let Err((_, ref mut b)) = r;
13+
| - second borrow occurs due to use of `r` in closure
14+
...
15+
LL | f();
16+
| - first borrow later used here
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0499`.

‎tests/ui/closures/2229_closure_analysis/run_pass/multivariant.rs renamed to ‎tests/ui/closures/2229_closure_analysis/only-inhabited-variant.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
// Test precise capture of a multi-variant enum (when remaining variants are
2-
// visibly uninhabited).
1+
// This example used to compile, but the fact that it should was never properly
2+
// discussed. With further experience, we concluded that capture precision
3+
// depending on whether some types are inhabited goes too far, introducing a
4+
// bunch of headaches without much benefit.
35
//@ revisions: normal exhaustive_patterns
46
//@ edition:2021
5-
//@ run-pass
67
#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
78
#![feature(never_type)]
89

@@ -13,6 +14,7 @@ pub fn main() {
1314
*a = 1;
1415
};
1516
let mut g = || {
17+
//~^ ERROR: cannot borrow `r` as mutable more than once at a time
1618
let Err((_, ref mut b)) = r;
1719
*b = 2;
1820
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
//@ edition:2024
2+
//@ check-pass
3+
4+
const X: u32 = 0;
5+
6+
fn match_literal(x: (u32, u32, u32)) {
7+
let _ = || {
8+
let ((0, a, _) | (_, _, a)) = x;
9+
a
10+
};
11+
}
12+
13+
fn match_range(x: (u32, u32, u32)) {
14+
let _ = || {
15+
let ((0..5, a, _) | (_, _, a)) = x;
16+
a
17+
};
18+
}
19+
20+
fn match_const(x: (u32, u32, u32)) {
21+
let _ = || {
22+
let ((X, a, _) | (_, _, a)) = x;
23+
a
24+
};
25+
}
26+
27+
enum Choice { A, B }
28+
29+
fn match_unit_variant(x: (Choice, u32, u32)) {
30+
let _ = || {
31+
let ((Choice::A, a, _) | (Choice::B, _, a)) = x;
32+
a
33+
};
34+
}
35+
36+
struct Unit;
37+
38+
fn match_unit_struct(mut x: (Unit, u32)) {
39+
let r = &mut x.0;
40+
let _ = || {
41+
let (Unit, a) = x;
42+
a
43+
};
44+
45+
let _ = *r;
46+
}
47+
48+
enum Also { Unit }
49+
50+
fn match_unit_enum(mut x: (Also, u32)) {
51+
let r = &mut x.0;
52+
let _ = || {
53+
let (Also::Unit, a) = x;
54+
a
55+
};
56+
57+
let _ = *r;
58+
}
59+
60+
enum TEnum {
61+
A(u32),
62+
B(u32),
63+
}
64+
65+
enum SEnum {
66+
A { a: u32 },
67+
B { a: u32 },
68+
}
69+
70+
fn match_tuple_enum(x: TEnum) {
71+
let _ = || {
72+
let (TEnum::A(a) | TEnum::B(a)) = x;
73+
a
74+
};
75+
}
76+
77+
fn match_struct_enum(x: SEnum) {
78+
let _ = || {
79+
let (SEnum::A { a } | SEnum::B { a }) = x;
80+
a
81+
};
82+
}
83+
84+
enum TSingle {
85+
A(u32, u32),
86+
}
87+
88+
enum SSingle {
89+
A { a: u32, b: u32 },
90+
}
91+
92+
struct TStruct(u32, u32);
93+
struct SStruct { a: u32, b: u32 }
94+
95+
fn match_struct(mut x: SStruct) {
96+
let r = &mut x.a;
97+
let _ = || {
98+
let SStruct { b, .. } = x;
99+
b
100+
};
101+
102+
let _ = *r;
103+
}
104+
105+
fn match_tuple_struct(mut x: TStruct) {
106+
let r = &mut x.0;
107+
let _ = || {
108+
let TStruct(_, a) = x;
109+
a
110+
};
111+
112+
let _ = *r;
113+
}
114+
115+
fn match_singleton(mut x: SSingle) {
116+
let SSingle::A { a: ref mut r, .. } = x;
117+
let _ = || {
118+
let SSingle::A { b, .. } = x;
119+
b
120+
};
121+
122+
let _ = *r;
123+
}
124+
125+
fn match_tuple_singleton(mut x: TSingle) {
126+
let TSingle::A(ref mut r, _) = x;
127+
let _ = || {
128+
let TSingle::A(_, a) = x;
129+
a
130+
};
131+
132+
let _ = *r;
133+
}
134+
135+
fn match_slice(x: (&[u32], u32, u32)) {
136+
let _ = || {
137+
let (([], a, _) | ([_, ..], _, a)) = x;
138+
a
139+
};
140+
}
141+
142+
// Original testcase, for completeness
143+
enum Camera {
144+
Normal { base_transform: i32 },
145+
Volume { transform: i32 },
146+
}
147+
148+
fn draw_ui(camera: &mut Camera) {
149+
|| {
150+
let (Camera::Normal {
151+
base_transform: _transform,
152+
}
153+
| Camera::Volume {
154+
transform: _transform,
155+
}) = camera;
156+
};
157+
}
158+
159+
fn draw_ui2(camera: &mut Camera) {
160+
|| {
161+
let (Camera::Normal {
162+
base_transform: _,
163+
}
164+
| Camera::Volume {
165+
transform: _,
166+
}) = camera;
167+
};
168+
}
169+
170+
fn main() {}

0 commit comments

Comments
 (0)
Please sign in to comment.