Skip to content

Commit a79cffb

Browse files
committed
Auto merge of #50912 - varkor:exhaustive-integer-matching, r=arielb1
Exhaustive integer matching This adds a new feature flag `exhaustive_integer_patterns` that enables exhaustive matching of integer types by their values. For example, the following is now accepted: ```rust #![feature(exhaustive_integer_patterns)] #![feature(exclusive_range_pattern)] fn matcher(x: u8) { match x { // ok 0 .. 32 => { /* foo */ } 32 => { /* bar */ } 33 ..= 255 => { /* baz */ } } } ``` This matching is permitted on all integer (signed/unsigned and char) types. Sensible error messages are also provided. For example: ```rust fn matcher(x: u8) { match x { //~ ERROR 0 .. 32 => { /* foo */ } } } ``` results in: ``` error[E0004]: non-exhaustive patterns: `32u8...255u8` not covered --> matches.rs:3:9 | 6 | match x { | ^ pattern `32u8...255u8` not covered ``` This implements rust-lang/rfcs#1550 for #50907. While there hasn't been a full RFC for this feature, it was suggested that this might be a feature that obviously complements the existing exhaustiveness checks (e.g. for `bool`) and so a feature gate would be sufficient for now.
2 parents 1cbf339 + 6971c5d commit a79cffb

10 files changed

+927
-84
lines changed

src/librustc/mir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2228,7 +2228,7 @@ pub fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ty::Const) -> fmt::Resul
22282228
}
22292229
}
22302230

2231-
pub fn print_miri_value<W: Write>(value: Value, ty: Ty, f: &mut W) -> fmt::Result {
2231+
pub fn print_miri_value<'tcx, W: Write>(value: Value, ty: Ty<'tcx>, f: &mut W) -> fmt::Result {
22322232
use ty::TypeVariants::*;
22332233
// print some primitives
22342234
if let Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { bits, .. })) = value {

src/librustc_mir/hair/pattern/_match.rs

+625-75
Large diffs are not rendered by default.

src/librustc_mir/hair/pattern/check_match.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
272272
self.tables);
273273
let pattern = patcx.lower_pattern(pat);
274274
let pattern_ty = pattern.ty;
275-
let pats : Matrix = vec![vec![
275+
let pats: Matrix = vec![vec![
276276
expand_pattern(cx, pattern)
277277
]].into_iter().collect();
278278

@@ -391,7 +391,7 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
391391
printed_if_let_err = true;
392392
}
393393
}
394-
},
394+
}
395395

396396
hir::MatchSource::WhileLetDesugar => {
397397
// check which arm we're on.

src/librustc_mir/hair/pattern/mod.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ impl<'tcx> fmt::Display for Pattern<'tcx> {
233233
PatternKind::Range { lo, hi, end } => {
234234
fmt_const_val(f, lo)?;
235235
match end {
236-
RangeEnd::Included => write!(f, "...")?,
236+
RangeEnd::Included => write!(f, "..=")?,
237237
RangeEnd::Excluded => write!(f, "..")?,
238238
}
239239
fmt_const_val(f, hi)
@@ -368,9 +368,14 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
368368
"lower range bound must be less than upper",
369369
);
370370
PatternKind::Wild
371-
},
372-
(RangeEnd::Included, None) |
373-
(RangeEnd::Included, Some(Ordering::Greater)) => {
371+
}
372+
(RangeEnd::Included, Some(Ordering::Equal)) => {
373+
PatternKind::Constant { value: lo }
374+
}
375+
(RangeEnd::Included, Some(Ordering::Less)) => {
376+
PatternKind::Range { lo, hi, end }
377+
}
378+
(RangeEnd::Included, _) => {
374379
let mut err = struct_span_err!(
375380
self.tcx.sess,
376381
lo_expr.span,
@@ -390,8 +395,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
390395
}
391396
err.emit();
392397
PatternKind::Wild
393-
},
394-
(RangeEnd::Included, Some(_)) => PatternKind::Range { lo, hi, end },
398+
}
395399
}
396400
}
397401
_ => PatternKind::Wild

src/librustc_mir/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
3636
#![feature(unicode_internals)]
3737
#![feature(step_trait)]
3838
#![feature(slice_concat_ext)]
39+
#![feature(if_while_or_patterns)]
3940

4041
#![recursion_limit="256"]
4142

src/libsyntax/feature_gate.rs

+3
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,9 @@ declare_features! (
471471
// 'a: { break 'a; }
472472
(active, label_break_value, "1.28.0", Some(48594), None),
473473

474+
// Integer match exhaustiveness checking
475+
(active, exhaustive_integer_patterns, "1.30.0", Some(50907), None),
476+
474477
// #[panic_implementation]
475478
(active, panic_implementation, "1.28.0", Some(44489), None),
476479

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(exhaustive_integer_patterns)]
12+
#![feature(exclusive_range_pattern)]
13+
#![deny(unreachable_patterns)]
14+
15+
use std::{char, usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128};
16+
17+
fn main() {
18+
let x: u8 = 0;
19+
20+
// A single range covering the entire domain.
21+
match x {
22+
0 ..= 255 => {} // ok
23+
}
24+
25+
// A combination of ranges and values.
26+
// These are currently allowed to be overlapping.
27+
match x {
28+
0 ..= 32 => {}
29+
33 => {}
30+
34 .. 128 => {}
31+
100 ..= 200 => {}
32+
200 => {} //~ ERROR unreachable pattern
33+
201 ..= 255 => {}
34+
}
35+
36+
// An incomplete set of values.
37+
match x { //~ ERROR non-exhaustive patterns
38+
0 .. 128 => {}
39+
}
40+
41+
// A more incomplete set of values.
42+
match x { //~ ERROR non-exhaustive patterns
43+
0 ..= 10 => {}
44+
20 ..= 30 => {}
45+
35 => {}
46+
70 .. 255 => {}
47+
}
48+
49+
let x: i8 = 0;
50+
match x { //~ ERROR non-exhaustive patterns
51+
-7 => {}
52+
-5..=120 => {}
53+
-2..=20 => {} //~ ERROR unreachable pattern
54+
125 => {}
55+
}
56+
57+
// Let's test other types too!
58+
let c: char = '\u{0}';
59+
match c {
60+
'\u{0}' ..= char::MAX => {} // ok
61+
}
62+
63+
// We can actually get away with just covering the
64+
// following two ranges, which correspond to all
65+
// valid Unicode Scalar Values.
66+
match c {
67+
'\u{0000}' ..= '\u{D7FF}' => {}
68+
'\u{E000}' ..= '\u{10_FFFF}' => {}
69+
}
70+
71+
match 0usize {
72+
0 ..= usize::MAX => {} // ok
73+
}
74+
75+
match 0u16 {
76+
0 ..= u16::MAX => {} // ok
77+
}
78+
79+
match 0u32 {
80+
0 ..= u32::MAX => {} // ok
81+
}
82+
83+
match 0u64 {
84+
0 ..= u64::MAX => {} // ok
85+
}
86+
87+
match 0u128 {
88+
0 ..= u128::MAX => {} // ok
89+
}
90+
91+
match 0isize {
92+
isize::MIN ..= isize::MAX => {} // ok
93+
}
94+
95+
match 0i8 {
96+
-128 ..= 127 => {} // ok
97+
}
98+
99+
match 0i8 { //~ ERROR non-exhaustive patterns
100+
-127 ..= 127 => {}
101+
}
102+
103+
match 0i16 {
104+
i16::MIN ..= i16::MAX => {} // ok
105+
}
106+
107+
match 0i16 { //~ ERROR non-exhaustive patterns
108+
i16::MIN ..= -1 => {}
109+
1 ..= i16::MAX => {}
110+
}
111+
112+
match 0i32 {
113+
i32::MIN ..= i32::MAX => {} // ok
114+
}
115+
116+
match 0i64 {
117+
i64::MIN ..= i64::MAX => {} // ok
118+
}
119+
120+
match 0i128 {
121+
i128::MIN ..= i128::MAX => {} // ok
122+
}
123+
124+
// Make sure that guards don't factor into the exhaustiveness checks.
125+
match 0u8 { //~ ERROR non-exhaustive patterns
126+
0 .. 128 => {}
127+
128 ..= 255 if true => {}
128+
}
129+
130+
match 0u8 {
131+
0 .. 128 => {}
132+
128 ..= 255 if false => {}
133+
128 ..= 255 => {} // ok, because previous arm was guarded
134+
}
135+
136+
// Now things start getting a bit more interesting. Testing products!
137+
match (0u8, Some(())) { //~ ERROR non-exhaustive patterns
138+
(1, _) => {}
139+
(_, None) => {}
140+
}
141+
142+
match (0u8, true) { //~ ERROR non-exhaustive patterns
143+
(0 ..= 125, false) => {}
144+
(128 ..= 255, false) => {}
145+
(0 ..= 255, true) => {}
146+
}
147+
148+
match (0u8, true) { // ok
149+
(0 ..= 125, false) => {}
150+
(128 ..= 255, false) => {}
151+
(0 ..= 255, true) => {}
152+
(125 .. 128, false) => {}
153+
}
154+
155+
match 0u8 { // ok
156+
0 .. 2 => {}
157+
1 ..= 2 => {}
158+
_ => {}
159+
}
160+
161+
const LIM: u128 = u128::MAX - 1;
162+
match 0u128 { //~ ERROR non-exhaustive patterns
163+
0 ..= LIM => {}
164+
}
165+
166+
match 0u128 { //~ ERROR non-exhaustive patterns
167+
0 ..= 4 => {}
168+
}
169+
170+
match 0u128 { //~ ERROR non-exhaustive patterns
171+
4 ..= u128::MAX => {}
172+
}
173+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
error: unreachable pattern
2+
--> $DIR/exhaustive_integer_patterns.rs:32:9
3+
|
4+
LL | 200 => {} //~ ERROR unreachable pattern
5+
| ^^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/exhaustive_integer_patterns.rs:13:9
9+
|
10+
LL | #![deny(unreachable_patterns)]
11+
| ^^^^^^^^^^^^^^^^^^^^
12+
13+
error[E0004]: non-exhaustive patterns: `128u8..=255u8` not covered
14+
--> $DIR/exhaustive_integer_patterns.rs:37:11
15+
|
16+
LL | match x { //~ ERROR non-exhaustive patterns
17+
| ^ pattern `128u8..=255u8` not covered
18+
19+
error[E0004]: non-exhaustive patterns: `11u8..=19u8`, `31u8..=34u8`, `36u8..=69u8` and 1 more not covered
20+
--> $DIR/exhaustive_integer_patterns.rs:42:11
21+
|
22+
LL | match x { //~ ERROR non-exhaustive patterns
23+
| ^ patterns `11u8..=19u8`, `31u8..=34u8`, `36u8..=69u8` and 1 more not covered
24+
25+
error: unreachable pattern
26+
--> $DIR/exhaustive_integer_patterns.rs:53:9
27+
|
28+
LL | -2..=20 => {} //~ ERROR unreachable pattern
29+
| ^^^^^^^
30+
31+
error[E0004]: non-exhaustive patterns: `-128i8..=-8i8`, `-6i8`, `121i8..=124i8` and 1 more not covered
32+
--> $DIR/exhaustive_integer_patterns.rs:50:11
33+
|
34+
LL | match x { //~ ERROR non-exhaustive patterns
35+
| ^ patterns `-128i8..=-8i8`, `-6i8`, `121i8..=124i8` and 1 more not covered
36+
37+
error[E0004]: non-exhaustive patterns: `-128i8` not covered
38+
--> $DIR/exhaustive_integer_patterns.rs:99:11
39+
|
40+
LL | match 0i8 { //~ ERROR non-exhaustive patterns
41+
| ^^^ pattern `-128i8` not covered
42+
43+
error[E0004]: non-exhaustive patterns: `0i16` not covered
44+
--> $DIR/exhaustive_integer_patterns.rs:107:11
45+
|
46+
LL | match 0i16 { //~ ERROR non-exhaustive patterns
47+
| ^^^^ pattern `0i16` not covered
48+
49+
error[E0004]: non-exhaustive patterns: `128u8..=255u8` not covered
50+
--> $DIR/exhaustive_integer_patterns.rs:125:11
51+
|
52+
LL | match 0u8 { //~ ERROR non-exhaustive patterns
53+
| ^^^ pattern `128u8..=255u8` not covered
54+
55+
error[E0004]: non-exhaustive patterns: `(0u8, Some(_))` and `(2u8..=255u8, Some(_))` not covered
56+
--> $DIR/exhaustive_integer_patterns.rs:137:11
57+
|
58+
LL | match (0u8, Some(())) { //~ ERROR non-exhaustive patterns
59+
| ^^^^^^^^^^^^^^^ patterns `(0u8, Some(_))` and `(2u8..=255u8, Some(_))` not covered
60+
61+
error[E0004]: non-exhaustive patterns: `(126u8..=127u8, false)` not covered
62+
--> $DIR/exhaustive_integer_patterns.rs:142:11
63+
|
64+
LL | match (0u8, true) { //~ ERROR non-exhaustive patterns
65+
| ^^^^^^^^^^^ pattern `(126u8..=127u8, false)` not covered
66+
67+
error[E0004]: non-exhaustive patterns: `340282366920938463463374607431768211455u128` not covered
68+
--> $DIR/exhaustive_integer_patterns.rs:162:11
69+
|
70+
LL | match 0u128 { //~ ERROR non-exhaustive patterns
71+
| ^^^^^ pattern `340282366920938463463374607431768211455u128` not covered
72+
73+
error[E0004]: non-exhaustive patterns: `5u128..=340282366920938463463374607431768211455u128` not covered
74+
--> $DIR/exhaustive_integer_patterns.rs:166:11
75+
|
76+
LL | match 0u128 { //~ ERROR non-exhaustive patterns
77+
| ^^^^^ pattern `5u128..=340282366920938463463374607431768211455u128` not covered
78+
79+
error[E0004]: non-exhaustive patterns: `0u128..=3u128` not covered
80+
--> $DIR/exhaustive_integer_patterns.rs:170:11
81+
|
82+
LL | match 0u128 { //~ ERROR non-exhaustive patterns
83+
| ^^^^^ pattern `0u128..=3u128` not covered
84+
85+
error: aborting due to 13 previous errors
86+
87+
For more information about this error, try `rustc --explain E0004`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let x: u8 = 0;
13+
match x { //~ ERROR non-exhaustive patterns: `_` not covered
14+
0 ..= 255 => {}
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0004]: non-exhaustive patterns: `_` not covered
2+
--> $DIR/feature-gate-exhaustive_integer_patterns.rs:13:11
3+
|
4+
LL | match x { //~ ERROR non-exhaustive patterns: `_` not covered
5+
| ^ pattern `_` not covered
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0004`.

0 commit comments

Comments
 (0)