Skip to content

Commit e12e972

Browse files
Rollup merge of #78072 - Nadrieril:cleanup-constant-matching, r=varkor
Cleanup constant matching in exhaustiveness checking This supercedes #77390. I made the `Opaque` constructor work. I have opened two issues #78071 and #78057 from the discussion we had on the previous PR. They are not regressions nor directly related to the current PR so I thought we'd deal with them separately. I left a FIXME somewhere because I didn't know how to compare string constants for equality. There might even be some unicode things that need to happen there. In the meantime I preserved previous behavior. EDIT: I accidentally fixed #78071
2 parents 0a06d73 + faf8710 commit e12e972

File tree

12 files changed

+448
-347
lines changed

12 files changed

+448
-347
lines changed

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

+111-339
Large diffs are not rendered by default.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
137137
patcx.include_lint_checks();
138138
let pattern = patcx.lower_pattern(pat);
139139
let pattern_ty = pattern.ty;
140-
let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
140+
let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(pattern));
141141
if !patcx.errors.is_empty() {
142142
*have_errors = true;
143143
patcx.report_inlining_errors(pat.span);

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

+7-5
Original file line numberDiff line numberDiff line change
@@ -387,14 +387,16 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
387387
// `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this
388388
// optimization for now.
389389
ty::Str => PatKind::Constant { value: cv },
390-
ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
391390
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
392391
// matching against references, you can only use byte string literals.
393-
// FIXME: clean this up, likely by permitting array patterns when matching on slices
394-
ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
392+
// The typechecker has a special case for byte string literals, by treating them
393+
// as slices. This means we turn `&[T; N]` constants into slice patterns, which
394+
// has no negative effects on pattern matching, even if we're actually matching on
395+
// arrays.
396+
ty::Array(..) |
395397
// Cannot merge this with the catch all branch below, because the `const_deref`
396-
// changes the type from slice to array, and slice patterns behave differently from
397-
// array patterns.
398+
// changes the type from slice to array, we need to keep the original type in the
399+
// pattern.
398400
ty::Slice(..) => {
399401
let old = self.behind_reference.replace(true);
400402
let array = tcx.deref_const(self.param_env.and(cv));

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

+7
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,13 @@ crate enum PatKind<'tcx> {
158158
subpattern: Pat<'tcx>,
159159
},
160160

161+
/// One of the following:
162+
/// * `&str`, which will be handled as a string pattern and thus exhaustiveness
163+
/// checking will detect if you use the same string twice in different patterns.
164+
/// * integer, bool, char or float, which will be handled by exhaustivenes to cover exactly
165+
/// its own value, similar to `&str`, but these values are much simpler.
166+
/// * Opaque constants, that must not be matched structurally. So anything that does not derive
167+
/// `PartialEq` and `Eq`.
161168
Constant {
162169
value: &'tcx ty::Const<'tcx>,
163170
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// check-pass
2+
// https://github.com/rust-lang/rust/issues/53708
3+
#[derive(PartialEq, Eq)]
4+
struct S;
5+
6+
fn main() {
7+
const C: &S = &S;
8+
match C {
9+
C => {}
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![deny(unreachable_patterns)]
2+
3+
#[derive(PartialEq)]
4+
struct Opaque(i32);
5+
6+
impl Eq for Opaque {}
7+
8+
const FOO: Opaque = Opaque(42);
9+
10+
fn main() {
11+
match FOO {
12+
FOO => {},
13+
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
14+
_ => {}
15+
//~^ ERROR unreachable pattern
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: to use a constant of type `Opaque` in a pattern, `Opaque` must be annotated with `#[derive(PartialEq, Eq)]`
2+
--> $DIR/issue-78057.rs:12:9
3+
|
4+
LL | FOO => {},
5+
| ^^^
6+
7+
error: unreachable pattern
8+
--> $DIR/issue-78057.rs:14:9
9+
|
10+
LL | _ => {}
11+
| ^
12+
|
13+
note: the lint level is defined here
14+
--> $DIR/issue-78057.rs:1:9
15+
|
16+
LL | #![deny(unreachable_patterns)]
17+
| ^^^^^^^^^^^^^^^^^^^^
18+
19+
error: aborting due to 2 previous errors
20+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// This file tests the exhaustiveness algorithm on opaque constants. Most of the examples give
2+
// unnecessary warnings because const_to_pat.rs converts a constant pattern to a wildcard when the
3+
// constant is not allowed as a pattern. This is an edge case so we may not care to fix it.
4+
// See also https://github.com/rust-lang/rust/issues/78057
5+
6+
#![deny(unreachable_patterns)]
7+
8+
#[derive(PartialEq)]
9+
struct Foo(i32);
10+
impl Eq for Foo {}
11+
const FOO: Foo = Foo(42);
12+
const FOO_REF: &Foo = &Foo(42);
13+
const FOO_REF_REF: &&Foo = &&Foo(42);
14+
15+
#[derive(PartialEq)]
16+
struct Bar;
17+
impl Eq for Bar {}
18+
const BAR: Bar = Bar;
19+
20+
#[derive(PartialEq)]
21+
enum Baz {
22+
Baz1,
23+
Baz2
24+
}
25+
impl Eq for Baz {}
26+
const BAZ: Baz = Baz::Baz1;
27+
28+
type Quux = fn(usize, usize) -> usize;
29+
fn quux(a: usize, b: usize) -> usize { a + b }
30+
const QUUX: Quux = quux;
31+
32+
fn main() {
33+
match FOO {
34+
FOO => {}
35+
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
36+
_ => {} // should not be emitting unreachable warning
37+
//~^ ERROR unreachable pattern
38+
}
39+
40+
match FOO_REF {
41+
FOO_REF => {}
42+
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
43+
Foo(_) => {} // should not be emitting unreachable warning
44+
//~^ ERROR unreachable pattern
45+
}
46+
47+
// This used to cause an ICE (https://github.com/rust-lang/rust/issues/78071)
48+
match FOO_REF_REF {
49+
FOO_REF_REF => {}
50+
//~^ WARNING must be annotated with `#[derive(PartialEq, Eq)]`
51+
//~| WARNING this was previously accepted by the compiler but is being phased out
52+
Foo(_) => {}
53+
}
54+
55+
match BAR {
56+
Bar => {}
57+
BAR => {} // should not be emitting unreachable warning
58+
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
59+
//~| ERROR unreachable pattern
60+
_ => {}
61+
//~^ ERROR unreachable pattern
62+
}
63+
64+
match BAR {
65+
BAR => {}
66+
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
67+
Bar => {} // should not be emitting unreachable warning
68+
//~^ ERROR unreachable pattern
69+
_ => {}
70+
//~^ ERROR unreachable pattern
71+
}
72+
73+
match BAR {
74+
BAR => {}
75+
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
76+
BAR => {} // should not be emitting unreachable warning
77+
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
78+
//~| ERROR unreachable pattern
79+
_ => {} // should not be emitting unreachable warning
80+
//~^ ERROR unreachable pattern
81+
}
82+
83+
match BAZ {
84+
BAZ => {}
85+
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
86+
Baz::Baz1 => {} // should not be emitting unreachable warning
87+
//~^ ERROR unreachable pattern
88+
_ => {}
89+
//~^ ERROR unreachable pattern
90+
}
91+
92+
match BAZ {
93+
Baz::Baz1 => {}
94+
BAZ => {}
95+
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
96+
_ => {}
97+
//~^ ERROR unreachable pattern
98+
}
99+
100+
match BAZ {
101+
BAZ => {}
102+
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
103+
Baz::Baz2 => {} // should not be emitting unreachable warning
104+
//~^ ERROR unreachable pattern
105+
_ => {} // should not be emitting unreachable warning
106+
//~^ ERROR unreachable pattern
107+
}
108+
109+
match QUUX {
110+
QUUX => {}
111+
QUUX => {}
112+
_ => {}
113+
}
114+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
2+
--> $DIR/consts-opaque.rs:34:9
3+
|
4+
LL | FOO => {}
5+
| ^^^
6+
7+
error: unreachable pattern
8+
--> $DIR/consts-opaque.rs:36:9
9+
|
10+
LL | _ => {} // should not be emitting unreachable warning
11+
| ^
12+
|
13+
note: the lint level is defined here
14+
--> $DIR/consts-opaque.rs:6:9
15+
|
16+
LL | #![deny(unreachable_patterns)]
17+
| ^^^^^^^^^^^^^^^^^^^^
18+
19+
error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
20+
--> $DIR/consts-opaque.rs:41:9
21+
|
22+
LL | FOO_REF => {}
23+
| ^^^^^^^
24+
25+
error: unreachable pattern
26+
--> $DIR/consts-opaque.rs:43:9
27+
|
28+
LL | Foo(_) => {} // should not be emitting unreachable warning
29+
| ^^^^^^
30+
31+
warning: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
32+
--> $DIR/consts-opaque.rs:49:9
33+
|
34+
LL | FOO_REF_REF => {}
35+
| ^^^^^^^^^^^
36+
|
37+
= note: `#[warn(indirect_structural_match)]` on by default
38+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
39+
= note: for more information, see issue #62411 <https://github.com/rust-lang/rust/issues/62411>
40+
41+
error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
42+
--> $DIR/consts-opaque.rs:57:9
43+
|
44+
LL | BAR => {} // should not be emitting unreachable warning
45+
| ^^^
46+
47+
error: unreachable pattern
48+
--> $DIR/consts-opaque.rs:57:9
49+
|
50+
LL | Bar => {}
51+
| --- matches any value
52+
LL | BAR => {} // should not be emitting unreachable warning
53+
| ^^^ unreachable pattern
54+
55+
error: unreachable pattern
56+
--> $DIR/consts-opaque.rs:60:9
57+
|
58+
LL | Bar => {}
59+
| --- matches any value
60+
...
61+
LL | _ => {}
62+
| ^ unreachable pattern
63+
64+
error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
65+
--> $DIR/consts-opaque.rs:65:9
66+
|
67+
LL | BAR => {}
68+
| ^^^
69+
70+
error: unreachable pattern
71+
--> $DIR/consts-opaque.rs:67:9
72+
|
73+
LL | Bar => {} // should not be emitting unreachable warning
74+
| ^^^
75+
76+
error: unreachable pattern
77+
--> $DIR/consts-opaque.rs:69:9
78+
|
79+
LL | Bar => {} // should not be emitting unreachable warning
80+
| --- matches any value
81+
LL |
82+
LL | _ => {}
83+
| ^ unreachable pattern
84+
85+
error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
86+
--> $DIR/consts-opaque.rs:74:9
87+
|
88+
LL | BAR => {}
89+
| ^^^
90+
91+
error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
92+
--> $DIR/consts-opaque.rs:76:9
93+
|
94+
LL | BAR => {} // should not be emitting unreachable warning
95+
| ^^^
96+
97+
error: unreachable pattern
98+
--> $DIR/consts-opaque.rs:76:9
99+
|
100+
LL | BAR => {} // should not be emitting unreachable warning
101+
| ^^^
102+
103+
error: unreachable pattern
104+
--> $DIR/consts-opaque.rs:79:9
105+
|
106+
LL | _ => {} // should not be emitting unreachable warning
107+
| ^
108+
109+
error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
110+
--> $DIR/consts-opaque.rs:84:9
111+
|
112+
LL | BAZ => {}
113+
| ^^^
114+
115+
error: unreachable pattern
116+
--> $DIR/consts-opaque.rs:86:9
117+
|
118+
LL | Baz::Baz1 => {} // should not be emitting unreachable warning
119+
| ^^^^^^^^^
120+
121+
error: unreachable pattern
122+
--> $DIR/consts-opaque.rs:88:9
123+
|
124+
LL | _ => {}
125+
| ^
126+
127+
error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
128+
--> $DIR/consts-opaque.rs:94:9
129+
|
130+
LL | BAZ => {}
131+
| ^^^
132+
133+
error: unreachable pattern
134+
--> $DIR/consts-opaque.rs:96:9
135+
|
136+
LL | _ => {}
137+
| ^
138+
139+
error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
140+
--> $DIR/consts-opaque.rs:101:9
141+
|
142+
LL | BAZ => {}
143+
| ^^^
144+
145+
error: unreachable pattern
146+
--> $DIR/consts-opaque.rs:103:9
147+
|
148+
LL | Baz::Baz2 => {} // should not be emitting unreachable warning
149+
| ^^^^^^^^^
150+
151+
error: unreachable pattern
152+
--> $DIR/consts-opaque.rs:105:9
153+
|
154+
LL | _ => {} // should not be emitting unreachable warning
155+
| ^
156+
157+
error: aborting due to 22 previous errors; 1 warning emitted
158+

src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ LL | match buf {
77
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
88
= note: the matched value is of type `&[u8; 4]`
99

10-
error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 2 more not covered
10+
error[E0004]: non-exhaustive patterns: `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered
1111
--> $DIR/match-byte-array-patterns-2.rs:10:11
1212
|
1313
LL | match buf {
14-
| ^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 2 more not covered
14+
| ^^^ patterns `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered
1515
|
1616
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
1717
= note: the matched value is of type `&[u8]`

0 commit comments

Comments
 (0)