Skip to content

Commit b1646cb

Browse files
committed
auto merge of #14731 : jakub-/rust/pattern-matching-refactor, r=alexcrichton
This PR is changing the error messages for non-exhaustive pattern matching to include a more accurate witness, i.e. a pattern that is not covered by any of the ones provided by the user. Example: ```rust fn main() { match (true, (Some("foo"), [true, true]), Some(42u)) { (false, _, _) => (), (true, (None, [true, _]), None) => (), (true, (None, [false, _]), Some(1u)) => () } } ``` ```sh /tmp/witness.rs:2:2: 6:3 error: non-exhaustive patterns: (true, (core::option::Some(_), _), _) not covered /tmp/witness.rs:2 match (true, (Some("foo"), [true, true]), Some(42u)) { /tmp/witness.rs:3 (false, _, _) => (), /tmp/witness.rs:4 (true, (None, [true, _]), None) => (), /tmp/witness.rs:5 (true, (None, [false, _]), Some(1u)) => () /tmp/witness.rs:6 } ``` As part of that, I refactored some of the relevant code and carried over the changes to fixed vectors from the previous PR. I'm putting it out there for now but the tests will be red.
2 parents 2563481 + a88819a commit b1646cb

14 files changed

+728
-646
lines changed

src/librustc/middle/check_match.rs

+490-583
Large diffs are not rendered by default.

src/librustc/middle/trans/_match.rs

+18-7
Original file line numberDiff line numberDiff line change
@@ -988,8 +988,7 @@ fn extract_vec_elems<'a>(
988988
pat_id: ast::NodeId,
989989
elem_count: uint,
990990
slice: Option<uint>,
991-
val: ValueRef,
992-
count: ValueRef)
991+
val: ValueRef)
993992
-> ExtractedBlock<'a> {
994993
let _icx = push_ctxt("match::extract_vec_elems");
995994
let vec_datum = match_datum(bcx, val, pat_id);
@@ -1003,7 +1002,7 @@ fn extract_vec_elems<'a>(
10031002
Some(n) if i < n => GEPi(bcx, base, [i]),
10041003
Some(n) if i > n => {
10051004
InBoundsGEP(bcx, base, [
1006-
Sub(bcx, count,
1005+
Sub(bcx, len,
10071006
C_int(bcx.ccx(), (elem_count - i) as int))])
10081007
}
10091008
_ => unsafe { llvm::LLVMGetUndef(vt.llunit_ty.to_ref()) }
@@ -1765,7 +1764,7 @@ fn compile_submatch_continue<'a, 'b>(
17651764
vec_len_eq => (n, None)
17661765
};
17671766
let args = extract_vec_elems(opt_cx, pat_id, n,
1768-
slice, val, test_val);
1767+
slice, val);
17691768
size = args.vals.len();
17701769
unpacked = args.vals.clone();
17711770
opt_cx = args.bcx;
@@ -2264,9 +2263,21 @@ fn bind_irrefutable_pat<'a>(
22642263
let loaded_val = Load(bcx, val);
22652264
bcx = bind_irrefutable_pat(bcx, inner, loaded_val, binding_mode, cleanup_scope);
22662265
}
2267-
ast::PatVec(..) => {
2268-
bcx.sess().span_bug(pat.span,
2269-
"vector patterns are never irrefutable!");
2266+
ast::PatVec(ref before, ref slice, ref after) => {
2267+
let extracted = extract_vec_elems(
2268+
bcx, pat.id, before.len() + 1u + after.len(),
2269+
slice.map(|_| before.len()), val
2270+
);
2271+
bcx = before
2272+
.iter().map(|v| Some(*v))
2273+
.chain(Some(*slice).move_iter())
2274+
.chain(after.iter().map(|v| Some(*v)))
2275+
.zip(extracted.vals.iter())
2276+
.fold(bcx, |bcx, (inner, elem)| {
2277+
inner.map_or(bcx, |inner| {
2278+
bind_irrefutable_pat(bcx, inner, *elem, binding_mode, cleanup_scope)
2279+
})
2280+
});
22702281
}
22712282
ast::PatMac(..) => {
22722283
bcx.sess().span_bug(pat.span, "unexpanded macro");

src/librustc/middle/typeck/check/_match.rs

+24-11
Original file line numberDiff line numberDiff line change
@@ -632,9 +632,9 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) {
632632
fcx.infcx().next_region_var(
633633
infer::PatternRegion(pat.span));
634634

635-
let check_err = || {
636-
for elt in before.iter() {
637-
check_pat(pcx, &**elt, ty::mk_err());
635+
let check_err = |found: String| {
636+
for &elt in before.iter() {
637+
check_pat(pcx, &*elt, ty::mk_err());
638638
}
639639
for elt in slice.iter() {
640640
check_pat(pcx, &**elt, ty::mk_err());
@@ -653,15 +653,16 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) {
653653
})
654654
},
655655
Some(expected),
656-
"a vector pattern".to_string(),
656+
found,
657657
None);
658658
fcx.write_error(pat.id);
659659
};
660660

661-
let (elt_type, region_var, mutbl) = match *structure_of(fcx,
661+
let (elt_type, region_var, mutbl, fixed) = match *structure_of(fcx,
662662
pat.span,
663663
expected) {
664-
ty::ty_vec(mt, Some(_)) => (mt.ty, default_region_var, ast::MutImmutable),
664+
ty::ty_vec(mt, Some(fixed)) =>
665+
(mt.ty, default_region_var, ast::MutImmutable, Some(fixed)),
665666
ty::ty_uniq(t) => match ty::get(t).sty {
666667
ty::ty_vec(mt, None) => {
667668
fcx.type_error_message(pat.span,
@@ -671,25 +672,37 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) {
671672
},
672673
expected,
673674
None);
674-
(mt.ty, default_region_var, ast::MutImmutable)
675+
(mt.ty, default_region_var, ast::MutImmutable, None)
675676
}
676677
_ => {
677-
check_err();
678+
check_err("a vector pattern".to_string());
678679
return;
679680
}
680681
},
681682
ty::ty_rptr(r, mt) => match ty::get(mt.ty).sty {
682-
ty::ty_vec(mt, None) => (mt.ty, r, mt.mutbl),
683+
ty::ty_vec(mt, None) => (mt.ty, r, mt.mutbl, None),
683684
_ => {
684-
check_err();
685+
check_err("a vector pattern".to_string());
685686
return;
686687
}
687688
},
688689
_ => {
689-
check_err();
690+
check_err("a vector pattern".to_string());
690691
return;
691692
}
692693
};
694+
695+
let min_len = before.len() + after.len();
696+
fixed.and_then(|count| match slice {
697+
Some(_) if count < min_len =>
698+
Some(format!("a fixed vector pattern of size at least {}", min_len)),
699+
700+
None if count != min_len =>
701+
Some(format!("a fixed vector pattern of size {}", min_len)),
702+
703+
_ => None
704+
}).map(check_err);
705+
693706
for elt in before.iter() {
694707
check_pat(pcx, &**elt, elt_type);
695708
}

src/test/compile-fail/issue-13482.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 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 = [1,2];
13+
let y = match x {
14+
[] => None,
15+
//~^ ERROR expected `[<generic integer #1>, .. 2]` but found a fixed vector pattern of size 0
16+
[a,_] => Some(a)
17+
};
18+
}

src/test/compile-fail/issue-2111.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
// except according to those terms.
1010

1111
fn foo(a: Option<uint>, b: Option<uint>) {
12-
match (a,b) { //~ ERROR: non-exhaustive patterns: None not covered
12+
match (a,b) {
13+
//~^ ERROR: non-exhaustive patterns: `(None, None)` not covered
1314
(Some(a), Some(b)) if a == b => { }
1415
(Some(_), None) |
1516
(None, Some(_)) => { }

src/test/compile-fail/issue-4321.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 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 tup = (true, true);
13+
println!("foo {:}", match tup { //~ ERROR non-exhaustive patterns: `(true, false)` not covered
14+
(false, false) => "foo",
15+
(false, true) => "bar",
16+
(true, true) => "baz"
17+
});
18+
}

src/test/compile-fail/non-exhaustive-match-nested.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// error-pattern: non-exhaustive patterns
1211
enum t { a(u), b }
1312
enum u { c, d }
1413

1514
fn main() {
1615
let x = a(c);
17-
match x {
16+
match x { //~ ERROR non-exhaustive patterns: `a(c)` not covered
1817
a(d) => { fail!("hello"); }
1918
b => { fail!("goodbye"); }
2019
}

src/test/compile-fail/non-exhaustive-match.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,21 @@ enum t { a, b, }
1212

1313
fn main() {
1414
let x = a;
15-
match x { b => { } } //~ ERROR non-exhaustive patterns
16-
match true { //~ ERROR non-exhaustive patterns
15+
match x { b => { } } //~ ERROR non-exhaustive patterns: `a` not covered
16+
match true { //~ ERROR non-exhaustive patterns: `false` not covered
1717
true => {}
1818
}
19-
match Some(10) { //~ ERROR non-exhaustive patterns
19+
match Some(10) { //~ ERROR non-exhaustive patterns: `Some(_)` not covered
2020
None => {}
2121
}
22-
match (2, 3, 4) { //~ ERROR non-exhaustive patterns
22+
match (2, 3, 4) { //~ ERROR non-exhaustive patterns: `(_, _, _)` not covered
2323
(_, _, 4) => {}
2424
}
25-
match (a, a) { //~ ERROR non-exhaustive patterns
25+
match (a, a) { //~ ERROR non-exhaustive patterns: `(a, a)` not covered
2626
(a, b) => {}
2727
(b, a) => {}
2828
}
29-
match a { //~ ERROR b not covered
29+
match a { //~ ERROR non-exhaustive patterns: `b` not covered
3030
a => {}
3131
}
3232
// This is exhaustive, though the algorithm got it wrong at one point
@@ -37,8 +37,7 @@ fn main() {
3737
}
3838
let vec = vec!(Some(42), None, Some(21));
3939
let vec: &[Option<int>] = vec.as_slice();
40-
match vec {
41-
//~^ ERROR non-exhaustive patterns: vectors of length 0 not covered
40+
match vec { //~ ERROR non-exhaustive patterns: `[]` not covered
4241
[Some(..), None, ..tail] => {}
4342
[Some(..), Some(..), ..tail] => {}
4443
[None] => {}
@@ -51,7 +50,7 @@ fn main() {
5150
}
5251
let vec = vec!(0.5);
5352
let vec: &[f32] = vec.as_slice();
54-
match vec { //~ ERROR non-exhaustive patterns: vectors of length 4 not covered
53+
match vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _]` not covered
5554
[0.1, 0.2, 0.3] => (),
5655
[0.1, 0.2] => (),
5756
[0.1] => (),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2014 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(struct_variant)]
12+
13+
struct Foo {
14+
first: bool,
15+
second: Option<[uint, ..4]>
16+
}
17+
18+
enum Color {
19+
Red,
20+
Green,
21+
CustomRGBA { a: bool, r: u8, g: u8, b: u8 }
22+
}
23+
24+
fn struct_with_a_nested_enum_and_vector() {
25+
match Foo { first: true, second: None } {
26+
//~^ ERROR non-exhaustive patterns: `Foo{first: false, second: Some([_, _, _, _])}` not covered
27+
Foo { first: true, second: None } => (),
28+
Foo { first: true, second: Some(_) } => (),
29+
Foo { first: false, second: None } => (),
30+
Foo { first: false, second: Some([1u, 2u, 3u, 4u]) } => ()
31+
}
32+
}
33+
34+
fn enum_with_multiple_missing_variants() {
35+
match Red {
36+
//~^ ERROR non-exhaustive patterns: `Red` not covered
37+
CustomRGBA { .. } => ()
38+
}
39+
}
40+
41+
fn enum_struct_variant() {
42+
match Red {
43+
//~^ ERROR non-exhaustive patterns: `CustomRGBA{a: true, r: _, g: _, b: _}` not covered
44+
Red => (),
45+
Green => (),
46+
CustomRGBA { a: false, r: _, g: _, b: 0 } => (),
47+
CustomRGBA { a: false, r: _, g: _, b: _ } => ()
48+
}
49+
}
50+
51+
enum Enum {
52+
First,
53+
Second(bool)
54+
}
55+
56+
fn vectors_with_nested_enums() {
57+
let x: &'static [Enum] = [First, Second(false)];
58+
match x {
59+
//~^ ERROR non-exhaustive patterns: `[Second(true), Second(false)]` not covered
60+
[] => (),
61+
[_] => (),
62+
[First, _] => (),
63+
[Second(true), First] => (),
64+
[Second(true), Second(true)] => (),
65+
[Second(false), _] => (),
66+
[_, _, ..tail, _] => ()
67+
}
68+
}
69+
70+
fn main() {
71+
struct_with_a_nested_enum_and_vector();
72+
enum_with_multiple_missing_variants();
73+
enum_struct_variant();
74+
}

src/test/compile-fail/precise-refutable-pattern-errors.rs

-32
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 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+
12+
fn func((1, (Some(1), 2..3)): (int, (Option<int>, int))) { }
13+
//~^ ERROR refutable pattern in function argument: `(_, _)` not covered
14+
15+
fn main() {
16+
let (1, (Some(1), 2..3)) = (1, (None, 2));
17+
//~^ ERROR refutable pattern in local binding: `(_, _)` not covered
18+
}

src/test/compile-fail/refutable-pattern-in-fn-arg.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
fn main() {
12-
let f = |3: int| println!("hello"); //~ ERROR refutable pattern
12+
let f = |3: int| println!("hello");
13+
//~^ ERROR refutable pattern in function argument: `_` not covered
1314
f(4);
1415
}

src/test/run-pass/issue-14393.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2014 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+
// ignore-win32: FIXME #13793
12+
13+
fn main() {
14+
match ("", 1u) {
15+
(_, 42u) => (),
16+
("", _) => (),
17+
_ => ()
18+
}
19+
}

0 commit comments

Comments
 (0)