Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exhaustive integer matching #50912

Merged
merged 47 commits into from
Aug 22, 2018
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
e3357d9
Implement interval checking
varkor May 19, 2018
b3d2baf
Give correct suggestions
varkor May 19, 2018
384db4f
Add support for all integer types
varkor May 19, 2018
ed5a4d5
Add feature gate and refactor
varkor May 19, 2018
b8702a0
Add feature gate test
varkor May 19, 2018
121fa8d
Fix handling of signed integers
varkor May 20, 2018
7476ba4
Add semi-exhaustive tests for exhaustiveness
varkor May 20, 2018
9778a81
Improve macros with reduced repetition
varkor May 20, 2018
a20cb10
Require just the Unicode Scalar Values to be matched for a char
varkor May 21, 2018
7f72030
Fix range splitting
varkor May 21, 2018
c00fd8f
Refactor interval conditions
varkor May 21, 2018
7695bd0
Use bit operators for min_max_ty
varkor May 21, 2018
a553fa7
Fix integer overflow
varkor May 22, 2018
8389972
Add singleton patterns to test
varkor May 22, 2018
f4af3b0
Refactor to remove explicit integer type matching
varkor May 22, 2018
a9f2c5a
Fix sign conversion arithmetic errors
varkor May 23, 2018
effb3d0
Improve the comments
varkor May 23, 2018
c388c11
Special-case (RangeEnd::Included, Ordering::Equal) in lower_pattern_u…
varkor May 23, 2018
97a032e
Simplify bitwise operations
varkor May 24, 2018
be12b24
Fix print_miri_value for signed integers
varkor May 24, 2018
72cc4bd
Inline encode and decode methods
varkor May 24, 2018
1aa7494
Introduce signed_bias method
varkor May 24, 2018
732d638
Replace ... with ..= in suggestions
varkor May 25, 2018
6c21a03
Refactor after miri api changes
varkor May 25, 2018
d27c21c
Refactor for less allocation
varkor May 28, 2018
07064de
No longer return value_constructors for all_constructors
varkor May 29, 2018
25ba911
Add guarded arms to tests
varkor Jun 2, 2018
af366b0
Refactor condition
varkor Jun 22, 2018
bfc0807
Add some comments
varkor Aug 12, 2018
4aa929c
Move witnesses inside push_wild_constructor
varkor Aug 12, 2018
5959a35
Move logic from push_wild_constructor to apply_constructor
varkor Aug 12, 2018
bfc8ce3
Add a test for integer products
varkor Aug 12, 2018
99754ad
Some reformatting
varkor Aug 12, 2018
400cb14
Add a summary of the algorithm to the file
varkor Aug 13, 2018
9e9e023
More formatting improvements
varkor Aug 13, 2018
527cccb
Add some more compound exhaustiveness tests
varkor Aug 14, 2018
e9c8361
Add equivalence class splitting for range constructors
varkor Aug 14, 2018
1dbc781
Handle equivalence classes of length-1 ranges
varkor Aug 14, 2018
0383539
Fix handling of floating-point ranges
varkor Aug 14, 2018
798b9ff
Tweak comments
varkor Aug 19, 2018
87463c3
Improve some comments
varkor Aug 20, 2018
6e8a625
Remove pattern consideration from split_grouped_constructors
varkor Aug 20, 2018
c421af9
Add assertion to constructor_intersects_pattern
varkor Aug 20, 2018
61b6363
Add more detail to the split_grouped_constructors comment
varkor Aug 20, 2018
6a957e1
Add a test case for u128::MAX - 1
varkor Aug 21, 2018
dec5563
Use a boundary method instead of an endpoint method for split_grouped…
varkor Aug 21, 2018
6971c5d
Add some extra edge case tests
varkor Aug 21, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
727 changes: 652 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 },
}

This comment was marked as resolved.

}
}
_ => 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 @@ -487,6 +487,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
160 changes: 160 additions & 0 deletions src/test/ui/exhaustive_integer_patterns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since char is a USV, it doesn't actually need to cover that entire range. If it's feasible, could this handle the example in rust-lang/rfcs#1550 (comment) too? (Feel free to punt if hard.)

}

// 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 => {}
_ => {}
}
}
69 changes: 69 additions & 0 deletions src/test/ui/exhaustive_integer_patterns.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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: aborting due to 10 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`.