Skip to content

Commit f4780a9

Browse files
authored
Rollup merge of rust-lang#78072 - Nadrieril:cleanup-constant-matching, r=varkor
Cleanup constant matching in exhaustiveness checking This supercedes rust-lang#77390. I made the `Opaque` constructor work. I have opened two issues rust-lang#78071 and rust-lang#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 rust-lang#78071
2 parents dc30e64 + faf8710 commit f4780a9

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

Lines changed: 111 additions & 339 deletions
Large diffs are not rendered by default.

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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 7 additions & 5 deletions
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

Lines changed: 7 additions & 0 deletions
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
},
Lines changed: 11 additions & 0 deletions
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+
}
Lines changed: 17 additions & 0 deletions
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+
}
Lines changed: 20 additions & 0 deletions
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+
Lines changed: 114 additions & 0 deletions
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+
}

0 commit comments

Comments
 (0)