Skip to content

Commit

Permalink
Auto merge of #50912 - varkor:exhaustive-integer-matching, r=arielb1
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bors committed Aug 22, 2018
2 parents 1cbf339 + 6971c5d commit a79cffb
Show file tree
Hide file tree
Showing 10 changed files with 927 additions and 84 deletions.
2 changes: 1 addition & 1 deletion src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2228,7 +2228,7 @@ pub fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ty::Const) -> fmt::Resul
}
}

pub fn print_miri_value<W: Write>(value: Value, ty: Ty, f: &mut W) -> fmt::Result {
pub fn print_miri_value<'tcx, W: Write>(value: Value, ty: Ty<'tcx>, f: &mut W) -> fmt::Result {
use ty::TypeVariants::*;
// print some primitives
if let Value::Scalar(ScalarMaybeUndef::Scalar(Scalar::Bits { bits, .. })) = value {
Expand Down
700 changes: 625 additions & 75 deletions src/librustc_mir/hair/pattern/_match.rs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/librustc_mir/hair/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
self.tables);
let pattern = patcx.lower_pattern(pat);
let pattern_ty = pattern.ty;
let pats : Matrix = vec![vec![
let pats: Matrix = vec![vec![
expand_pattern(cx, pattern)
]].into_iter().collect();

Expand Down Expand Up @@ -391,7 +391,7 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
printed_if_let_err = true;
}
}
},
}

hir::MatchSource::WhileLetDesugar => {
// check which arm we're on.
Expand Down
16 changes: 10 additions & 6 deletions src/librustc_mir/hair/pattern/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ impl<'tcx> fmt::Display for Pattern<'tcx> {
PatternKind::Range { lo, hi, end } => {
fmt_const_val(f, lo)?;
match end {
RangeEnd::Included => write!(f, "...")?,
RangeEnd::Included => write!(f, "..=")?,
RangeEnd::Excluded => write!(f, "..")?,
}
fmt_const_val(f, hi)
Expand Down Expand Up @@ -368,9 +368,14 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
"lower range bound must be less than upper",
);
PatternKind::Wild
},
(RangeEnd::Included, None) |
(RangeEnd::Included, Some(Ordering::Greater)) => {
}
(RangeEnd::Included, Some(Ordering::Equal)) => {
PatternKind::Constant { value: lo }
}
(RangeEnd::Included, Some(Ordering::Less)) => {
PatternKind::Range { lo, hi, end }
}
(RangeEnd::Included, _) => {
let mut err = struct_span_err!(
self.tcx.sess,
lo_expr.span,
Expand All @@ -390,8 +395,7 @@ impl<'a, 'tcx> PatternContext<'a, 'tcx> {
}
err.emit();
PatternKind::Wild
},
(RangeEnd::Included, Some(_)) => PatternKind::Range { lo, hi, end },
}
}
}
_ => PatternKind::Wild
Expand Down
1 change: 1 addition & 0 deletions src/librustc_mir/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
#![feature(unicode_internals)]
#![feature(step_trait)]
#![feature(slice_concat_ext)]
#![feature(if_while_or_patterns)]

#![recursion_limit="256"]

Expand Down
3 changes: 3 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,9 @@ declare_features! (
// 'a: { break 'a; }
(active, label_break_value, "1.28.0", Some(48594), None),

// Integer match exhaustiveness checking
(active, exhaustive_integer_patterns, "1.30.0", Some(50907), None),

// #[panic_implementation]
(active, panic_implementation, "1.28.0", Some(44489), None),

Expand Down
173 changes: 173 additions & 0 deletions src/test/ui/exhaustive_integer_patterns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(exhaustive_integer_patterns)]
#![feature(exclusive_range_pattern)]
#![deny(unreachable_patterns)]

use std::{char, usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128};

fn main() {
let x: u8 = 0;

// A single range covering the entire domain.
match x {
0 ..= 255 => {} // ok
}

// A combination of ranges and values.
// These are currently allowed to be overlapping.
match x {
0 ..= 32 => {}
33 => {}
34 .. 128 => {}
100 ..= 200 => {}
200 => {} //~ ERROR unreachable pattern
201 ..= 255 => {}
}

// An incomplete set of values.
match x { //~ ERROR non-exhaustive patterns
0 .. 128 => {}
}

// A more incomplete set of values.
match x { //~ ERROR non-exhaustive patterns
0 ..= 10 => {}
20 ..= 30 => {}
35 => {}
70 .. 255 => {}
}

let x: i8 = 0;
match x { //~ ERROR non-exhaustive patterns
-7 => {}
-5..=120 => {}
-2..=20 => {} //~ ERROR unreachable pattern
125 => {}
}

// Let's test other types too!
let c: char = '\u{0}';
match c {
'\u{0}' ..= char::MAX => {} // ok
}

// We can actually get away with just covering the
// following two ranges, which correspond to all
// valid Unicode Scalar Values.
match c {
'\u{0000}' ..= '\u{D7FF}' => {}
'\u{E000}' ..= '\u{10_FFFF}' => {}
}

match 0usize {
0 ..= usize::MAX => {} // ok
}

match 0u16 {
0 ..= u16::MAX => {} // ok
}

match 0u32 {
0 ..= u32::MAX => {} // ok
}

match 0u64 {
0 ..= u64::MAX => {} // ok
}

match 0u128 {
0 ..= u128::MAX => {} // ok
}

match 0isize {
isize::MIN ..= isize::MAX => {} // ok
}

match 0i8 {
-128 ..= 127 => {} // ok
}

match 0i8 { //~ ERROR non-exhaustive patterns
-127 ..= 127 => {}
}

match 0i16 {
i16::MIN ..= i16::MAX => {} // ok
}

match 0i16 { //~ ERROR non-exhaustive patterns
i16::MIN ..= -1 => {}
1 ..= i16::MAX => {}
}

match 0i32 {
i32::MIN ..= i32::MAX => {} // ok
}

match 0i64 {
i64::MIN ..= i64::MAX => {} // ok
}

match 0i128 {
i128::MIN ..= i128::MAX => {} // ok
}

// Make sure that guards don't factor into the exhaustiveness checks.
match 0u8 { //~ ERROR non-exhaustive patterns
0 .. 128 => {}
128 ..= 255 if true => {}
}

match 0u8 {
0 .. 128 => {}
128 ..= 255 if false => {}
128 ..= 255 => {} // ok, because previous arm was guarded
}

// Now things start getting a bit more interesting. Testing products!
match (0u8, Some(())) { //~ ERROR non-exhaustive patterns
(1, _) => {}
(_, None) => {}
}

match (0u8, true) { //~ ERROR non-exhaustive patterns
(0 ..= 125, false) => {}
(128 ..= 255, false) => {}
(0 ..= 255, true) => {}
}

match (0u8, true) { // ok
(0 ..= 125, false) => {}
(128 ..= 255, false) => {}
(0 ..= 255, true) => {}
(125 .. 128, false) => {}
}

match 0u8 { // ok
0 .. 2 => {}
1 ..= 2 => {}
_ => {}
}

const LIM: u128 = u128::MAX - 1;
match 0u128 { //~ ERROR non-exhaustive patterns
0 ..= LIM => {}
}

match 0u128 { //~ ERROR non-exhaustive patterns
0 ..= 4 => {}
}

match 0u128 { //~ ERROR non-exhaustive patterns
4 ..= u128::MAX => {}
}
}
87 changes: 87 additions & 0 deletions src/test/ui/exhaustive_integer_patterns.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
error: unreachable pattern
--> $DIR/exhaustive_integer_patterns.rs:32:9
|
LL | 200 => {} //~ ERROR unreachable pattern
| ^^^
|
note: lint level defined here
--> $DIR/exhaustive_integer_patterns.rs:13:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^

error[E0004]: non-exhaustive patterns: `128u8..=255u8` not covered
--> $DIR/exhaustive_integer_patterns.rs:37:11
|
LL | match x { //~ ERROR non-exhaustive patterns
| ^ pattern `128u8..=255u8` not covered

error[E0004]: non-exhaustive patterns: `11u8..=19u8`, `31u8..=34u8`, `36u8..=69u8` and 1 more not covered
--> $DIR/exhaustive_integer_patterns.rs:42:11
|
LL | match x { //~ ERROR non-exhaustive patterns
| ^ patterns `11u8..=19u8`, `31u8..=34u8`, `36u8..=69u8` and 1 more not covered

error: unreachable pattern
--> $DIR/exhaustive_integer_patterns.rs:53:9
|
LL | -2..=20 => {} //~ ERROR unreachable pattern
| ^^^^^^^

error[E0004]: non-exhaustive patterns: `-128i8..=-8i8`, `-6i8`, `121i8..=124i8` and 1 more not covered
--> $DIR/exhaustive_integer_patterns.rs:50:11
|
LL | match x { //~ ERROR non-exhaustive patterns
| ^ patterns `-128i8..=-8i8`, `-6i8`, `121i8..=124i8` and 1 more not covered

error[E0004]: non-exhaustive patterns: `-128i8` not covered
--> $DIR/exhaustive_integer_patterns.rs:99:11
|
LL | match 0i8 { //~ ERROR non-exhaustive patterns
| ^^^ pattern `-128i8` not covered

error[E0004]: non-exhaustive patterns: `0i16` not covered
--> $DIR/exhaustive_integer_patterns.rs:107:11
|
LL | match 0i16 { //~ ERROR non-exhaustive patterns
| ^^^^ pattern `0i16` not covered

error[E0004]: non-exhaustive patterns: `128u8..=255u8` not covered
--> $DIR/exhaustive_integer_patterns.rs:125:11
|
LL | match 0u8 { //~ ERROR non-exhaustive patterns
| ^^^ pattern `128u8..=255u8` not covered

error[E0004]: non-exhaustive patterns: `(0u8, Some(_))` and `(2u8..=255u8, Some(_))` not covered
--> $DIR/exhaustive_integer_patterns.rs:137:11
|
LL | match (0u8, Some(())) { //~ ERROR non-exhaustive patterns
| ^^^^^^^^^^^^^^^ patterns `(0u8, Some(_))` and `(2u8..=255u8, Some(_))` not covered

error[E0004]: non-exhaustive patterns: `(126u8..=127u8, false)` not covered
--> $DIR/exhaustive_integer_patterns.rs:142:11
|
LL | match (0u8, true) { //~ ERROR non-exhaustive patterns
| ^^^^^^^^^^^ pattern `(126u8..=127u8, false)` not covered

error[E0004]: non-exhaustive patterns: `340282366920938463463374607431768211455u128` not covered
--> $DIR/exhaustive_integer_patterns.rs:162:11
|
LL | match 0u128 { //~ ERROR non-exhaustive patterns
| ^^^^^ pattern `340282366920938463463374607431768211455u128` not covered

error[E0004]: non-exhaustive patterns: `5u128..=340282366920938463463374607431768211455u128` not covered
--> $DIR/exhaustive_integer_patterns.rs:166:11
|
LL | match 0u128 { //~ ERROR non-exhaustive patterns
| ^^^^^ pattern `5u128..=340282366920938463463374607431768211455u128` not covered

error[E0004]: non-exhaustive patterns: `0u128..=3u128` not covered
--> $DIR/exhaustive_integer_patterns.rs:170:11
|
LL | match 0u128 { //~ ERROR non-exhaustive patterns
| ^^^^^ pattern `0u128..=3u128` not covered

error: aborting due to 13 previous errors

For more information about this error, try `rustc --explain E0004`.
16 changes: 16 additions & 0 deletions src/test/ui/feature-gate-exhaustive_integer_patterns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn main() {
let x: u8 = 0;
match x { //~ ERROR non-exhaustive patterns: `_` not covered
0 ..= 255 => {}
}
}
9 changes: 9 additions & 0 deletions src/test/ui/feature-gate-exhaustive_integer_patterns.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0004]: non-exhaustive patterns: `_` not covered
--> $DIR/feature-gate-exhaustive_integer_patterns.rs:13:11
|
LL | match x { //~ ERROR non-exhaustive patterns: `_` not covered
| ^ pattern `_` not covered

error: aborting due to previous error

For more information about this error, try `rustc --explain E0004`.

0 comments on commit a79cffb

Please sign in to comment.