Skip to content

Commit bcdbe94

Browse files
committed
Make is_useful handle empty types properly
1 parent 9ad2044 commit bcdbe94

24 files changed

+246
-85
lines changed

src/librustc/lint/builtin.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ declare_lint! {
7070
"detects unreachable code paths"
7171
}
7272

73+
declare_lint! {
74+
pub UNREACHABLE_PATTERNS,
75+
Warn,
76+
"detects unreachable patterns"
77+
}
78+
7379
declare_lint! {
7480
pub WARNINGS,
7581
Warn,
@@ -239,6 +245,7 @@ impl LintPass for HardwiredLints {
239245
UNUSED_ASSIGNMENTS,
240246
DEAD_CODE,
241247
UNREACHABLE_CODE,
248+
UNREACHABLE_PATTERNS,
242249
WARNINGS,
243250
UNUSED_FEATURES,
244251
STABLE_FEATURES,

src/librustc_const_eval/_match.rs

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use eval::{compare_const_vals};
1717

1818
use rustc_const_math::ConstInt;
1919

20-
use rustc_data_structures::fx::FxHashMap;
20+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2121
use rustc_data_structures::indexed_vec::Idx;
2222

2323
use pattern::{FieldPattern, Pattern, PatternKind};
@@ -29,6 +29,7 @@ use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
2929
use rustc::mir::Field;
3030
use rustc::util::common::ErrorReported;
3131

32+
use syntax::ast::NodeId;
3233
use syntax_pos::{Span, DUMMY_SP};
3334

3435
use arena::TypedArena;
@@ -144,6 +145,14 @@ impl<'a, 'tcx> FromIterator<Vec<&'a Pattern<'tcx>>> for Matrix<'a, 'tcx> {
144145
//NOTE: appears to be the only place other then InferCtxt to contain a ParamEnv
145146
pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
146147
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
148+
/// (roughly) where in the code the match occurs. This is necessary for
149+
/// checking inhabited-ness of types because whether a type is (visibly)
150+
/// inhabited can depend on whether it was defined in the current module or
151+
/// not. eg.
152+
/// struct Foo { _private: ! }
153+
/// can not be seen to be empty outside it's module and should not
154+
/// be matchable with an empty match statement.
155+
pub node: NodeId,
147156
/// A wild pattern with an error type - it exists to avoid having to normalize
148157
/// associated types to get field types.
149158
pub wild_pattern: &'a Pattern<'tcx>,
@@ -154,6 +163,7 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
154163
impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
155164
pub fn create_and_enter<F, R>(
156165
tcx: TyCtxt<'a, 'tcx, 'tcx>,
166+
node: NodeId,
157167
f: F) -> R
158168
where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R
159169
{
@@ -167,6 +177,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
167177

168178
f(MatchCheckCtxt {
169179
tcx: tcx,
180+
node: node,
170181
wild_pattern: &wild_pattern,
171182
pattern_arena: &pattern_arena,
172183
byte_array_map: FxHashMap(),
@@ -362,9 +373,9 @@ impl<'tcx> Witness<'tcx> {
362373
/// Therefore, if there is some pattern that is unmatched by `matrix`, it will
363374
/// still be unmatched if the first constructor is replaced by any of the constructors
364375
/// in the return value.
365-
fn missing_constructors(cx: &mut MatchCheckCtxt,
366-
matrix: &Matrix,
367-
pcx: PatternContext) -> Vec<Constructor> {
376+
fn missing_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
377+
matrix: &Matrix,
378+
pcx: PatternContext<'tcx>) -> Vec<Constructor> {
368379
let used_constructors: Vec<Constructor> =
369380
matrix.0.iter()
370381
.flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![]))
@@ -384,16 +395,46 @@ fn missing_constructors(cx: &mut MatchCheckCtxt,
384395
///
385396
/// but is instead bounded by the maximum fixed length of slice patterns in
386397
/// the column of patterns being analyzed.
387-
fn all_constructors(_cx: &mut MatchCheckCtxt, pcx: PatternContext) -> Vec<Constructor> {
398+
///
399+
/// We make sure to omit constructors that are statically impossible. eg for
400+
/// Option<!> we do not include Some(_) in the returned list of constructors.
401+
fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
402+
pcx: PatternContext<'tcx>) -> Vec<Constructor>
403+
{
388404
match pcx.ty.sty {
389405
ty::TyBool =>
390406
[true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
391-
ty::TySlice(_) =>
392-
(0..pcx.max_slice_length+1).map(|length| Slice(length)).collect(),
393-
ty::TyArray(_, length) => vec![Slice(length)],
394-
ty::TyAdt(def, _) if def.is_enum() && def.variants.len() > 1 =>
395-
def.variants.iter().map(|v| Variant(v.did)).collect(),
396-
_ => vec![Single]
407+
ty::TySlice(ref sub_ty) => {
408+
if sub_ty.is_uninhabited(Some(cx.node), cx.tcx) {
409+
vec![Slice(0)]
410+
} else {
411+
(0..pcx.max_slice_length+1).map(|length| Slice(length)).collect()
412+
}
413+
}
414+
ty::TyArray(ref sub_ty, length) => {
415+
if length == 0 || !sub_ty.is_uninhabited(Some(cx.node), cx.tcx) {
416+
vec![Slice(length)]
417+
} else {
418+
vec![]
419+
}
420+
}
421+
ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
422+
def.variants.iter().filter_map(|v| {
423+
let mut visited = FxHashSet::default();
424+
if v.is_uninhabited_recurse(&mut visited, Some(cx.node), cx.tcx, substs, false) {
425+
None
426+
} else {
427+
Some(Variant(v.did))
428+
}
429+
}).collect()
430+
}
431+
_ => {
432+
if pcx.ty.is_uninhabited(Some(cx.node), cx.tcx) {
433+
vec![]
434+
} else {
435+
vec![Single]
436+
}
437+
}
397438
}
398439
}
399440

src/librustc_const_eval/check_match.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use rustc::middle::mem_categorization::{cmt};
2525
use rustc::session::Session;
2626
use rustc::traits::Reveal;
2727
use rustc::ty::{self, TyCtxt};
28+
use rustc::lint;
2829
use rustc_errors::DiagnosticBuilder;
2930

3031
use rustc::hir::def::*;
@@ -150,7 +151,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
150151
}
151152
}
152153

153-
MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| {
154+
MatchCheckCtxt::create_and_enter(self.tcx, scrut.id, |ref mut cx| {
154155
let mut have_errors = false;
155156

156157
let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| (
@@ -210,7 +211,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
210211
"local binding"
211212
};
212213

213-
MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| {
214+
MatchCheckCtxt::create_and_enter(self.tcx, pat.id, |ref mut cx| {
214215
let mut patcx = PatternContext::new(self.tcx);
215216
let pats : Matrix = vec![vec![
216217
expand_pattern(cx, patcx.lower_pattern(pat))
@@ -324,14 +325,19 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
324325
},
325326

326327
hir::MatchSource::Normal => {
327-
let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001,
328-
"unreachable pattern");
329-
err.span_label(pat.span, &"this is an unreachable pattern");
330-
// if we had a catchall pattern, hint at that
328+
// if we had a catchall pattern, raise an error.
329+
// Otherwise an unreachable pattern raises a warning.
331330
if let Some(catchall) = catchall {
331+
let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001,
332+
"unreachable pattern");
333+
err.span_label(pat.span, &"this is an unreachable pattern");
332334
err.span_note(catchall, "this pattern matches any value");
335+
err.emit();
336+
} else {
337+
cx.tcx.sess.add_lint(lint::builtin::UNREACHABLE_PATTERNS,
338+
hir_pat.id, pat.span,
339+
String::from("unreachable pattern"));
333340
}
334-
err.emit();
335341
},
336342

337343
hir::MatchSource::TryDesugar => {

src/librustc_const_eval/diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ For example, the following `match` block has too many arms:
2828
```compile_fail,E0001
2929
match Some(0) {
3030
Some(bar) => {/* ... */}
31-
None => {/* ... */}
31+
x => {/* ... */} // This handles the `None` case
3232
_ => {/* ... */} // All possible cases have already been handled
3333
}
3434
```

src/librustc_lint/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
165165
DEAD_CODE,
166166
UNUSED_MUT,
167167
UNREACHABLE_CODE,
168+
UNREACHABLE_PATTERNS,
168169
UNUSED_MUST_USE,
169170
UNUSED_UNSAFE,
170171
PATH_STATEMENTS,

src/librustc_typeck/check/_match.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
399399
self.check_pat(&p, discrim_ty);
400400
all_pats_diverge &= self.diverges.get();
401401
}
402-
all_pats_diverge
402+
// As discussed with @eddyb, this is for disabling unreachable_code
403+
// warnings on patterns (they're now subsumed by unreachable_patterns
404+
// warnings).
405+
match all_pats_diverge {
406+
Diverges::Maybe => Diverges::Maybe,
407+
Diverges::Always | Diverges::WarnedAlways => Diverges::WarnedAlways,
408+
}
403409
}).collect();
404410

405411
// Now typecheck the blocks.

src/test/compile-fail/E0001.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![deny(unreachable_patterns)]
12+
1113
fn main() {
1214
let foo = Some(1);
1315
match foo {
14-
Some(bar) => {/* ... */}
16+
Some(_) => {/* ... */}
1517
None => {/* ... */}
16-
_ => {/* ... */} //~ ERROR E0001
18+
_ => {/* ... */} //~ ERROR unreachable pattern
1719
}
1820
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fn tail(source_list: &IntList) -> IntList {
2020
match source_list {
2121
&IntList::Cons(val, box ref next_list) => tail(next_list),
2222
&IntList::Cons(val, box Nil) => IntList::Cons(val, box Nil),
23-
//~^ ERROR unreachable pattern
23+
//~^ ERROR cannot move out of borrowed content
2424
//~^^ WARN pattern binding `Nil` is named the same as one of the variants of the type `IntList`
2525
_ => panic!()
2626
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
// except according to those terms.
1010

1111
#![feature(slice_patterns)]
12+
#![allow(unused_variables)]
13+
#![deny(unreachable_patterns)]
1214

1315
fn main() {
1416
let sl = vec![1,2,3];

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![allow(overflowing_literals)]
12+
#![deny(unreachable_patterns)]
13+
1114
fn test(val: u8) {
1215
match val {
1316
256 => print!("0b1110\n"),
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2016 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+
#![deny(unreachable_patterns)]
12+
13+
fn main() {
14+
match "world" {
15+
"hello" => {}
16+
_ => {},
17+
}
18+
19+
match "world" {
20+
ref _x if false => {}
21+
"hello" => {}
22+
"hello" => {} //~ ERROR unreachable pattern
23+
_ => {},
24+
}
25+
}
26+

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,5 @@ fn main() {
1616
match "world" { //~ ERROR non-exhaustive patterns: `&_`
1717
ref _x if false => {}
1818
"hello" => {}
19-
"hello" => {} //~ ERROR unreachable pattern
2019
}
2120
}

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

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![deny(unreachable_patterns)]
12+
1113
enum Enum {
1214
Var1,
1315
Var2,
@@ -41,13 +43,4 @@ fn main() {
4143
//~^ ERROR unreachable pattern
4244
//~^^ NOTE this is an unreachable pattern
4345
};
44-
// `_` need not emit a note, it is pretty obvious already.
45-
let t = (Var1, Var1);
46-
match t {
47-
(Var1, b) => (),
48-
_ => (),
49-
anything => ()
50-
//~^ ERROR unreachable pattern
51-
//~^^ NOTE this is an unreachable pattern
52-
};
5346
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,5 @@ fn main() {
4040
box NodeKind::Element(ed) => match ed.kind { //~ ERROR non-exhaustive patterns
4141
box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true }
4242
},
43-
_ => panic!("WAT") //~ ERROR unreachable pattern
4443
};
4544
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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+
use self::Direction::{North, East, South, West};
12+
13+
struct NewBool(bool);
14+
15+
enum Direction {
16+
North,
17+
East,
18+
South,
19+
West
20+
}
21+
22+
const TRUE_TRUE: (bool, bool) = (true, true);
23+
24+
fn nonexhaustive_1() {
25+
match (true, false) {
26+
//~^ ERROR non-exhaustive patterns: `(true, false)` not covered
27+
TRUE_TRUE => (),
28+
(false, false) => (),
29+
(false, true) => ()
30+
}
31+
}
32+
33+
const NONE: Option<Direction> = None;
34+
const EAST: Direction = East;
35+
36+
fn nonexhaustive_2() {
37+
match Some(Some(North)) {
38+
//~^ ERROR non-exhaustive patterns: `Some(Some(West))` not covered
39+
Some(NONE) => (),
40+
Some(Some(North)) => (),
41+
Some(Some(EAST)) => (),
42+
Some(Some(South)) => (),
43+
None => ()
44+
}
45+
}
46+
47+
const NEW_FALSE: NewBool = NewBool(false);
48+
struct Foo {
49+
bar: Option<Direction>,
50+
baz: NewBool
51+
}
52+
53+
const STATIC_FOO: Foo = Foo { bar: None, baz: NEW_FALSE };
54+
55+
fn nonexhaustive_3() {
56+
match (Foo { bar: Some(North), baz: NewBool(true) }) {
57+
//~^ ERROR non-exhaustive patterns: `Foo { bar: Some(North), baz: NewBool(true) }`
58+
Foo { bar: None, baz: NewBool(true) } => (),
59+
Foo { bar: _, baz: NEW_FALSE } => (),
60+
Foo { bar: Some(West), baz: NewBool(true) } => (),
61+
Foo { bar: Some(South), .. } => (),
62+
Foo { bar: Some(EAST), .. } => ()
63+
}
64+
}
65+
66+
fn main() {
67+
nonexhaustive_1();
68+
nonexhaustive_2();
69+
nonexhaustive_3();
70+
}
71+

0 commit comments

Comments
 (0)