Skip to content

Commit 14c4d2f

Browse files
committed
Auto merge of #117611 - Nadrieril:linear-pass-take-4, r=<try>
Rewrite exhaustiveness in one pass This is at least my 4th attempt at this in as many years x) Previous attempts were all too complicated or too slow. But we're finally here! The previous version of the exhaustiveness algorithm computed reachability for each arm then exhaustiveness of the whole match. Since each of these steps does roughly the same things, this rewrites the algorithm to do them all in one go. I also think this makes things much simpler. I also rewrote the documentation of the algorithm in depth. Hopefully it's up-to-date and easier to follow now. Plz comment if anything's unclear. r? `@oli-obk` I think you're one of the rare other people to understand the exhaustiveness algorithm? cc `@varkor` I know you're not active anymore, but if you feel like having a look you might enjoy this :D
2 parents 04817ff + ab918af commit 14c4d2f

18 files changed

+1230
-1001
lines changed

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,10 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
279279
} else {
280280
// Check the pattern for some things unrelated to exhaustiveness.
281281
let refutable = if cx.refutable { Refutable } else { Irrefutable };
282-
pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat));
283-
pat.walk_always(|pat| check_for_bindings_named_same_as_variants(self, pat, refutable));
282+
pat.walk_always(|pat| {
283+
check_borrow_conflicts_in_at_patterns(self, pat);
284+
check_for_bindings_named_same_as_variants(self, pat, refutable);
285+
});
284286
Ok(cx.pattern_arena.alloc(DeconstructedPat::from_pat(cx, pat)))
285287
}
286288
}
@@ -289,6 +291,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
289291
&self,
290292
refutability: RefutableFlag,
291293
match_span: Option<Span>,
294+
scrut_span: Span,
292295
) -> MatchCheckCtxt<'p, 'tcx> {
293296
let refutable = match refutability {
294297
Irrefutable => false,
@@ -299,7 +302,9 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
299302
param_env: self.param_env,
300303
module: self.tcx.parent_module(self.lint_level).to_def_id(),
301304
pattern_arena: &self.pattern_arena,
305+
match_lint_level: self.lint_level,
302306
match_span,
307+
scrut_span,
303308
refutable,
304309
}
305310
}
@@ -330,7 +335,8 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
330335
source: hir::MatchSource,
331336
expr_span: Span,
332337
) {
333-
let cx = self.new_cx(Refutable, Some(expr_span));
338+
let scrut = &self.thir[scrut];
339+
let cx = self.new_cx(Refutable, Some(expr_span), scrut.span);
334340

335341
let mut tarms = Vec::with_capacity(arms.len());
336342
for &arm in arms {
@@ -346,9 +352,8 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
346352
}
347353
}
348354

349-
let scrut = &self.thir[scrut];
350355
let scrut_ty = scrut.ty;
351-
let report = compute_match_usefulness(&cx, &tarms, self.lint_level, scrut_ty, scrut.span);
356+
let report = compute_match_usefulness(&cx, &tarms, scrut_ty);
352357

353358
match source {
354359
// Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
@@ -453,10 +458,10 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
453458
pat: &Pat<'tcx>,
454459
refutability: RefutableFlag,
455460
) -> Result<(MatchCheckCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
456-
let cx = self.new_cx(refutability, None);
461+
let cx = self.new_cx(refutability, None, pat.span);
457462
let pat = self.lower_pattern(&cx, pat)?;
458463
let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }];
459-
let report = compute_match_usefulness(&cx, &arms, self.lint_level, pat.ty(), pat.span());
464+
let report = compute_match_usefulness(&cx, &arms, pat.ty());
460465
Ok((cx, report))
461466
}
462467

compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs

+181-191
Large diffs are not rendered by default.

compiler/rustc_mir_build/src/thir/pattern/usefulness.rs

+756-565
Large diffs are not rendered by default.

tests/ui/or-patterns/exhaustiveness-pass.rs

+11
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,17 @@ fn main() {
3535
((0, 0) | (1, 0),) => {}
3636
_ => {}
3737
}
38+
match ((0, 0),) {
39+
// Note how the second one would be redundant without the guard.
40+
((x, y) | (y, x),) if x == 0 => {}
41+
_ => {}
42+
}
43+
match 0 {
44+
// We don't warn the second one as redundant in general because of cases like the one above.
45+
// We could technically do it if there are no bindings.
46+
0 | 0 if 0 == 0 => {}
47+
_ => {}
48+
}
3849

3950
// This one caused ICE https://github.com/rust-lang/rust/issues/117378
4051
match (0u8, 0) {

tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![deny(unreachable_patterns)]
22

33
// We wrap patterns in a tuple because top-level or-patterns were special-cased.
4+
#[rustfmt::skip]
45
fn main() {
56
match (0u8,) {
67
(1 | 2,) => {}
@@ -73,6 +74,11 @@ fn main() {
7374
| 0] => {} //~ ERROR unreachable
7475
_ => {}
7576
}
77+
match (true, 0) {
78+
(true, 0 | 0) => {} //~ ERROR unreachable
79+
(_, 0 | 0) => {} //~ ERROR unreachable
80+
_ => {}
81+
}
7682
match &[][..] {
7783
[0] => {}
7884
[0, _] => {}
@@ -149,4 +155,8 @@ fn main() {
149155
| true, //~ ERROR unreachable
150156
false | true) => {}
151157
}
158+
match (true, true) {
159+
(x, y)
160+
| (y, x) => {} //~ ERROR unreachable
161+
}
152162
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: unreachable pattern
2-
--> $DIR/exhaustiveness-unreachable-pattern.rs:7:9
2+
--> $DIR/exhaustiveness-unreachable-pattern.rs:8:9
33
|
44
LL | (1,) => {}
55
| ^^^^
@@ -11,128 +11,140 @@ LL | #![deny(unreachable_patterns)]
1111
| ^^^^^^^^^^^^^^^^^^^^
1212

1313
error: unreachable pattern
14-
--> $DIR/exhaustiveness-unreachable-pattern.rs:12:9
14+
--> $DIR/exhaustiveness-unreachable-pattern.rs:13:9
1515
|
1616
LL | (2,) => {}
1717
| ^^^^
1818

1919
error: unreachable pattern
20-
--> $DIR/exhaustiveness-unreachable-pattern.rs:18:9
20+
--> $DIR/exhaustiveness-unreachable-pattern.rs:19:9
2121
|
2222
LL | (1 | 2,) => {}
2323
| ^^^^^^^^
2424

2525
error: unreachable pattern
26-
--> $DIR/exhaustiveness-unreachable-pattern.rs:23:9
26+
--> $DIR/exhaustiveness-unreachable-pattern.rs:24:9
2727
|
2828
LL | (1, 3) => {}
2929
| ^^^^^^
3030

3131
error: unreachable pattern
32-
--> $DIR/exhaustiveness-unreachable-pattern.rs:24:9
32+
--> $DIR/exhaustiveness-unreachable-pattern.rs:25:9
3333
|
3434
LL | (1, 4) => {}
3535
| ^^^^^^
3636

3737
error: unreachable pattern
38-
--> $DIR/exhaustiveness-unreachable-pattern.rs:25:9
38+
--> $DIR/exhaustiveness-unreachable-pattern.rs:26:9
3939
|
4040
LL | (2, 4) => {}
4141
| ^^^^^^
4242

4343
error: unreachable pattern
44-
--> $DIR/exhaustiveness-unreachable-pattern.rs:26:9
44+
--> $DIR/exhaustiveness-unreachable-pattern.rs:27:9
4545
|
4646
LL | (2 | 1, 4) => {}
4747
| ^^^^^^^^^^
4848

4949
error: unreachable pattern
50-
--> $DIR/exhaustiveness-unreachable-pattern.rs:28:9
50+
--> $DIR/exhaustiveness-unreachable-pattern.rs:29:9
5151
|
5252
LL | (1, 4 | 5) => {}
5353
| ^^^^^^^^^^
5454

5555
error: unreachable pattern
56-
--> $DIR/exhaustiveness-unreachable-pattern.rs:36:9
56+
--> $DIR/exhaustiveness-unreachable-pattern.rs:37:9
5757
|
5858
LL | (Some(1),) => {}
5959
| ^^^^^^^^^^
6060

6161
error: unreachable pattern
62-
--> $DIR/exhaustiveness-unreachable-pattern.rs:37:9
62+
--> $DIR/exhaustiveness-unreachable-pattern.rs:38:9
6363
|
6464
LL | (None,) => {}
6565
| ^^^^^^^
6666

6767
error: unreachable pattern
68-
--> $DIR/exhaustiveness-unreachable-pattern.rs:42:9
68+
--> $DIR/exhaustiveness-unreachable-pattern.rs:43:9
6969
|
7070
LL | ((1..=4,),) => {}
7171
| ^^^^^^^^^^^
7272

7373
error: unreachable pattern
74-
--> $DIR/exhaustiveness-unreachable-pattern.rs:47:14
74+
--> $DIR/exhaustiveness-unreachable-pattern.rs:48:14
7575
|
7676
LL | (1 | 1,) => {}
7777
| ^
7878

7979
error: unreachable pattern
80-
--> $DIR/exhaustiveness-unreachable-pattern.rs:51:19
80+
--> $DIR/exhaustiveness-unreachable-pattern.rs:52:19
8181
|
8282
LL | (0 | 1) | 1 => {}
8383
| ^
8484

8585
error: unreachable pattern
86-
--> $DIR/exhaustiveness-unreachable-pattern.rs:57:14
86+
--> $DIR/exhaustiveness-unreachable-pattern.rs:58:14
8787
|
8888
LL | 0 | (0 | 0) => {}
8989
| ^
9090

9191
error: unreachable pattern
92-
--> $DIR/exhaustiveness-unreachable-pattern.rs:57:18
92+
--> $DIR/exhaustiveness-unreachable-pattern.rs:58:18
9393
|
9494
LL | 0 | (0 | 0) => {}
9595
| ^
9696

9797
error: unreachable pattern
98-
--> $DIR/exhaustiveness-unreachable-pattern.rs:65:13
98+
--> $DIR/exhaustiveness-unreachable-pattern.rs:66:13
9999
|
100100
LL | / Some(
101101
LL | | 0 | 0) => {}
102102
| |______________________^
103103

104104
error: unreachable pattern
105-
--> $DIR/exhaustiveness-unreachable-pattern.rs:71:15
105+
--> $DIR/exhaustiveness-unreachable-pattern.rs:72:15
106106
|
107107
LL | | 0
108108
| ^
109109

110110
error: unreachable pattern
111-
--> $DIR/exhaustiveness-unreachable-pattern.rs:73:15
111+
--> $DIR/exhaustiveness-unreachable-pattern.rs:74:15
112112
|
113113
LL | | 0] => {}
114114
| ^
115115

116116
error: unreachable pattern
117-
--> $DIR/exhaustiveness-unreachable-pattern.rs:81:10
117+
--> $DIR/exhaustiveness-unreachable-pattern.rs:78:20
118+
|
119+
LL | (true, 0 | 0) => {}
120+
| ^
121+
122+
error: unreachable pattern
123+
--> $DIR/exhaustiveness-unreachable-pattern.rs:79:17
124+
|
125+
LL | (_, 0 | 0) => {}
126+
| ^
127+
128+
error: unreachable pattern
129+
--> $DIR/exhaustiveness-unreachable-pattern.rs:87:10
118130
|
119131
LL | [1
120132
| ^
121133

122134
error: unreachable pattern
123-
--> $DIR/exhaustiveness-unreachable-pattern.rs:93:10
135+
--> $DIR/exhaustiveness-unreachable-pattern.rs:99:10
124136
|
125137
LL | [true
126138
| ^^^^
127139

128140
error: unreachable pattern
129-
--> $DIR/exhaustiveness-unreachable-pattern.rs:100:36
141+
--> $DIR/exhaustiveness-unreachable-pattern.rs:106:36
130142
|
131143
LL | (true | false, None | Some(true
132144
| ^^^^
133145

134146
error: unreachable pattern
135-
--> $DIR/exhaustiveness-unreachable-pattern.rs:105:14
147+
--> $DIR/exhaustiveness-unreachable-pattern.rs:111:14
136148
|
137149
LL | (true
138150
| ^^^^
@@ -143,28 +155,34 @@ LL | (true | false, None | Some(t_or_f!())) => {}
143155
= note: this error originates in the macro `t_or_f` (in Nightly builds, run with -Z macro-backtrace for more info)
144156

145157
error: unreachable pattern
146-
--> $DIR/exhaustiveness-unreachable-pattern.rs:116:14
158+
--> $DIR/exhaustiveness-unreachable-pattern.rs:122:14
147159
|
148160
LL | Some(0
149161
| ^
150162

151163
error: unreachable pattern
152-
--> $DIR/exhaustiveness-unreachable-pattern.rs:135:19
164+
--> $DIR/exhaustiveness-unreachable-pattern.rs:141:19
153165
|
154166
LL | | false) => {}
155167
| ^^^^^
156168

157169
error: unreachable pattern
158-
--> $DIR/exhaustiveness-unreachable-pattern.rs:143:15
170+
--> $DIR/exhaustiveness-unreachable-pattern.rs:149:15
159171
|
160172
LL | | true) => {}
161173
| ^^^^
162174

163175
error: unreachable pattern
164-
--> $DIR/exhaustiveness-unreachable-pattern.rs:149:15
176+
--> $DIR/exhaustiveness-unreachable-pattern.rs:155:15
165177
|
166178
LL | | true,
167179
| ^^^^
168180

169-
error: aborting due to 26 previous errors
181+
error: unreachable pattern
182+
--> $DIR/exhaustiveness-unreachable-pattern.rs:160:15
183+
|
184+
LL | | (y, x) => {}
185+
| ^^^^^^
186+
187+
error: aborting due to 29 previous errors
170188

tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.allow.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0004]: non-exhaustive patterns: type `usize` is non-empty
2-
--> $DIR/pointer-sized-int.rs:54:11
2+
--> $DIR/pointer-sized-int.rs:59:11
33
|
44
LL | match 7usize {}
55
| ^^^^^^

0 commit comments

Comments
 (0)