Skip to content

Commit

Permalink
Make is_useful handle empty types properly
Browse files Browse the repository at this point in the history
  • Loading branch information
canndrew committed Jan 3, 2017
1 parent 9ad2044 commit bcdbe94
Show file tree
Hide file tree
Showing 24 changed files with 246 additions and 85 deletions.
7 changes: 7 additions & 0 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ declare_lint! {
"detects unreachable code paths"
}

declare_lint! {
pub UNREACHABLE_PATTERNS,
Warn,
"detects unreachable patterns"
}

declare_lint! {
pub WARNINGS,
Warn,
Expand Down Expand Up @@ -239,6 +245,7 @@ impl LintPass for HardwiredLints {
UNUSED_ASSIGNMENTS,
DEAD_CODE,
UNREACHABLE_CODE,
UNREACHABLE_PATTERNS,
WARNINGS,
UNUSED_FEATURES,
STABLE_FEATURES,
Expand Down
63 changes: 52 additions & 11 deletions src/librustc_const_eval/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use eval::{compare_const_vals};

use rustc_const_math::ConstInt;

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::indexed_vec::Idx;

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

use syntax::ast::NodeId;
use syntax_pos::{Span, DUMMY_SP};

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

f(MatchCheckCtxt {
tcx: tcx,
node: node,
wild_pattern: &wild_pattern,
pattern_arena: &pattern_arena,
byte_array_map: FxHashMap(),
Expand Down Expand Up @@ -362,9 +373,9 @@ impl<'tcx> Witness<'tcx> {
/// Therefore, if there is some pattern that is unmatched by `matrix`, it will
/// still be unmatched if the first constructor is replaced by any of the constructors
/// in the return value.
fn missing_constructors(cx: &mut MatchCheckCtxt,
matrix: &Matrix,
pcx: PatternContext) -> Vec<Constructor> {
fn missing_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
matrix: &Matrix,
pcx: PatternContext<'tcx>) -> Vec<Constructor> {
let used_constructors: Vec<Constructor> =
matrix.0.iter()
.flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![]))
Expand All @@ -384,16 +395,46 @@ fn missing_constructors(cx: &mut MatchCheckCtxt,
///
/// but is instead bounded by the maximum fixed length of slice patterns in
/// the column of patterns being analyzed.
fn all_constructors(_cx: &mut MatchCheckCtxt, pcx: PatternContext) -> Vec<Constructor> {
///
/// We make sure to omit constructors that are statically impossible. eg for
/// Option<!> we do not include Some(_) in the returned list of constructors.
fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
pcx: PatternContext<'tcx>) -> Vec<Constructor>
{
match pcx.ty.sty {
ty::TyBool =>
[true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
ty::TySlice(_) =>
(0..pcx.max_slice_length+1).map(|length| Slice(length)).collect(),
ty::TyArray(_, length) => vec![Slice(length)],
ty::TyAdt(def, _) if def.is_enum() && def.variants.len() > 1 =>
def.variants.iter().map(|v| Variant(v.did)).collect(),
_ => vec![Single]
ty::TySlice(ref sub_ty) => {
if sub_ty.is_uninhabited(Some(cx.node), cx.tcx) {
vec![Slice(0)]
} else {
(0..pcx.max_slice_length+1).map(|length| Slice(length)).collect()
}
}
ty::TyArray(ref sub_ty, length) => {
if length == 0 || !sub_ty.is_uninhabited(Some(cx.node), cx.tcx) {
vec![Slice(length)]
} else {
vec![]
}
}
ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
def.variants.iter().filter_map(|v| {
let mut visited = FxHashSet::default();
if v.is_uninhabited_recurse(&mut visited, Some(cx.node), cx.tcx, substs, false) {
None
} else {
Some(Variant(v.did))
}
}).collect()
}
_ => {
if pcx.ty.is_uninhabited(Some(cx.node), cx.tcx) {
vec![]
} else {
vec![Single]
}
}
}
}

Expand Down
20 changes: 13 additions & 7 deletions src/librustc_const_eval/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use rustc::middle::mem_categorization::{cmt};
use rustc::session::Session;
use rustc::traits::Reveal;
use rustc::ty::{self, TyCtxt};
use rustc::lint;
use rustc_errors::DiagnosticBuilder;

use rustc::hir::def::*;
Expand Down Expand Up @@ -150,7 +151,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
}
}

MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| {
MatchCheckCtxt::create_and_enter(self.tcx, scrut.id, |ref mut cx| {
let mut have_errors = false;

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

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

hir::MatchSource::Normal => {
let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001,
"unreachable pattern");
err.span_label(pat.span, &"this is an unreachable pattern");
// if we had a catchall pattern, hint at that
// if we had a catchall pattern, raise an error.
// Otherwise an unreachable pattern raises a warning.
if let Some(catchall) = catchall {
let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001,
"unreachable pattern");
err.span_label(pat.span, &"this is an unreachable pattern");
err.span_note(catchall, "this pattern matches any value");
err.emit();
} else {
cx.tcx.sess.add_lint(lint::builtin::UNREACHABLE_PATTERNS,
hir_pat.id, pat.span,
String::from("unreachable pattern"));
}
err.emit();
},

hir::MatchSource::TryDesugar => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_const_eval/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ For example, the following `match` block has too many arms:
```compile_fail,E0001
match Some(0) {
Some(bar) => {/* ... */}
None => {/* ... */}
x => {/* ... */} // This handles the `None` case
_ => {/* ... */} // All possible cases have already been handled
}
```
Expand Down
1 change: 1 addition & 0 deletions src/librustc_lint/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
DEAD_CODE,
UNUSED_MUT,
UNREACHABLE_CODE,
UNREACHABLE_PATTERNS,
UNUSED_MUST_USE,
UNUSED_UNSAFE,
PATH_STATEMENTS,
Expand Down
8 changes: 7 additions & 1 deletion src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.check_pat(&p, discrim_ty);
all_pats_diverge &= self.diverges.get();
}
all_pats_diverge
// As discussed with @eddyb, this is for disabling unreachable_code
// warnings on patterns (they're now subsumed by unreachable_patterns
// warnings).
match all_pats_diverge {
Diverges::Maybe => Diverges::Maybe,
Diverges::Always | Diverges::WarnedAlways => Diverges::WarnedAlways,
}
}).collect();

// Now typecheck the blocks.
Expand Down
6 changes: 4 additions & 2 deletions src/test/compile-fail/E0001.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(unreachable_patterns)]

fn main() {
let foo = Some(1);
match foo {
Some(bar) => {/* ... */}
Some(_) => {/* ... */}
None => {/* ... */}
_ => {/* ... */} //~ ERROR E0001
_ => {/* ... */} //~ ERROR unreachable pattern
}
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/issue-12116.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn tail(source_list: &IntList) -> IntList {
match source_list {
&IntList::Cons(val, box ref next_list) => tail(next_list),
&IntList::Cons(val, box Nil) => IntList::Cons(val, box Nil),
//~^ ERROR unreachable pattern
//~^ ERROR cannot move out of borrowed content
//~^^ WARN pattern binding `Nil` is named the same as one of the variants of the type `IntList`
_ => panic!()
}
Expand Down
2 changes: 2 additions & 0 deletions src/test/compile-fail/issue-12369.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
// except according to those terms.

#![feature(slice_patterns)]
#![allow(unused_variables)]
#![deny(unreachable_patterns)]

fn main() {
let sl = vec![1,2,3];
Expand Down
3 changes: 3 additions & 0 deletions src/test/compile-fail/issue-13727.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(overflowing_literals)]
#![deny(unreachable_patterns)]

fn test(val: u8) {
match val {
256 => print!("0b1110\n"),
Expand Down
26 changes: 26 additions & 0 deletions src/test/compile-fail/issue-30240-b.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2016 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.

#![deny(unreachable_patterns)]

fn main() {
match "world" {
"hello" => {}
_ => {},
}

match "world" {
ref _x if false => {}
"hello" => {}
"hello" => {} //~ ERROR unreachable pattern
_ => {},
}
}

1 change: 0 additions & 1 deletion src/test/compile-fail/issue-30240.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,5 @@ fn main() {
match "world" { //~ ERROR non-exhaustive patterns: `&_`
ref _x if false => {}
"hello" => {}
"hello" => {} //~ ERROR unreachable pattern
}
}
11 changes: 2 additions & 9 deletions src/test/compile-fail/issue-31221.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(unreachable_patterns)]

enum Enum {
Var1,
Var2,
Expand Down Expand Up @@ -41,13 +43,4 @@ fn main() {
//~^ ERROR unreachable pattern
//~^^ NOTE this is an unreachable pattern
};
// `_` need not emit a note, it is pretty obvious already.
let t = (Var1, Var1);
match t {
(Var1, b) => (),
_ => (),
anything => ()
//~^ ERROR unreachable pattern
//~^^ NOTE this is an unreachable pattern
};
}
1 change: 0 additions & 1 deletion src/test/compile-fail/issue-3601.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,5 @@ fn main() {
box NodeKind::Element(ed) => match ed.kind { //~ ERROR non-exhaustive patterns
box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true }
},
_ => panic!("WAT") //~ ERROR unreachable pattern
};
}
71 changes: 71 additions & 0 deletions src/test/compile-fail/match-argm-statics-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2014 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.

use self::Direction::{North, East, South, West};

struct NewBool(bool);

enum Direction {
North,
East,
South,
West
}

const TRUE_TRUE: (bool, bool) = (true, true);

fn nonexhaustive_1() {
match (true, false) {
//~^ ERROR non-exhaustive patterns: `(true, false)` not covered
TRUE_TRUE => (),
(false, false) => (),
(false, true) => ()
}
}

const NONE: Option<Direction> = None;
const EAST: Direction = East;

fn nonexhaustive_2() {
match Some(Some(North)) {
//~^ ERROR non-exhaustive patterns: `Some(Some(West))` not covered
Some(NONE) => (),
Some(Some(North)) => (),
Some(Some(EAST)) => (),
Some(Some(South)) => (),
None => ()
}
}

const NEW_FALSE: NewBool = NewBool(false);
struct Foo {
bar: Option<Direction>,
baz: NewBool
}

const STATIC_FOO: Foo = Foo { bar: None, baz: NEW_FALSE };

fn nonexhaustive_3() {
match (Foo { bar: Some(North), baz: NewBool(true) }) {
//~^ ERROR non-exhaustive patterns: `Foo { bar: Some(North), baz: NewBool(true) }`
Foo { bar: None, baz: NewBool(true) } => (),
Foo { bar: _, baz: NEW_FALSE } => (),
Foo { bar: Some(West), baz: NewBool(true) } => (),
Foo { bar: Some(South), .. } => (),
Foo { bar: Some(EAST), .. } => ()
}
}

fn main() {
nonexhaustive_1();
nonexhaustive_2();
nonexhaustive_3();
}

Loading

0 comments on commit bcdbe94

Please sign in to comment.