From 9ad20442e8af42815fde41c03188764ac516cf18 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Mon, 28 Nov 2016 18:38:27 +0800 Subject: [PATCH 01/22] Start enabling empty types in pattern matching. Remove the assumption at the start of is_useful that any suitably-long array of wildcard patterns is useful relative the any empty vector. Instead we just continue to recurse column-wise over the matrix. This assumption is false in the presence of empty types. eg. in the simplest case: let x: ! = ...; match x { // This pattern should not be considered useful by the algorithm _ => ... } --- src/librustc_const_eval/_match.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index 6d04975f533da..e18945446572c 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -510,19 +510,24 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, -> Usefulness<'tcx> { let &Matrix(ref rows) = matrix; debug!("is_useful({:?}, {:?})", matrix, v); - if rows.is_empty() { - return match witness { - ConstructWitness => UsefulWithWitness(vec![Witness( - repeat(cx.wild_pattern).take(v.len()).cloned().collect() - )]), - LeaveOutWitness => Useful - }; - } - if rows[0].is_empty() { - return NotUseful; - } - let &Matrix(ref rows) = matrix; + // The base case. We are pattern-matching on () and the return value is + // based on whether our matrix has a row or not. + // NOTE: This could potentially be optimized by checking rows.is_empty() + // first and then, if v is non-empty, the return value is based on whether + // the type of the tuple we're checking is inhabited or not. + if v.is_empty() { + return if rows.is_empty() { + match witness { + ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), + LeaveOutWitness => Useful, + } + } + else { + NotUseful + } + }; + assert!(rows.iter().all(|r| r.len() == v.len())); From bcdbe942e108e47ffa712fa44ad9b251c53c105b Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Tue, 29 Nov 2016 15:10:26 +0800 Subject: [PATCH 02/22] Make is_useful handle empty types properly --- src/librustc/lint/builtin.rs | 7 ++ src/librustc_const_eval/_match.rs | 63 +++++++++++++--- src/librustc_const_eval/check_match.rs | 20 ++++-- src/librustc_const_eval/diagnostics.rs | 2 +- src/librustc_lint/lib.rs | 1 + src/librustc_typeck/check/_match.rs | 8 ++- src/test/compile-fail/E0001.rs | 6 +- src/test/compile-fail/issue-12116.rs | 2 +- src/test/compile-fail/issue-12369.rs | 2 + src/test/compile-fail/issue-13727.rs | 3 + src/test/compile-fail/issue-30240-b.rs | 26 +++++++ src/test/compile-fail/issue-30240.rs | 1 - src/test/compile-fail/issue-31221.rs | 11 +-- src/test/compile-fail/issue-3601.rs | 1 - src/test/compile-fail/match-argm-statics-2.rs | 71 +++++++++++++++++++ src/test/compile-fail/match-arm-statics.rs | 42 ++--------- .../match-byte-array-patterns-2.rs | 26 +++++++ .../compile-fail/match-byte-array-patterns.rs | 9 +-- .../compile-fail/match-range-fail-dominate.rs | 2 + src/test/compile-fail/match-ref-ice.rs | 3 +- src/test/compile-fail/match-vec-fixed.rs | 1 + .../compile-fail/match-vec-unreachable.rs | 7 +- .../struct-pattern-match-useless.rs | 4 +- src/test/compile-fail/unreachable-arm.rs | 13 +++- 24 files changed, 246 insertions(+), 85 deletions(-) create mode 100644 src/test/compile-fail/issue-30240-b.rs create mode 100644 src/test/compile-fail/match-argm-statics-2.rs create mode 100644 src/test/compile-fail/match-byte-array-patterns-2.rs diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 667c2590fa996..96fb168581b21 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -70,6 +70,12 @@ declare_lint! { "detects unreachable code paths" } +declare_lint! { + pub UNREACHABLE_PATTERNS, + Warn, + "detects unreachable patterns" +} + declare_lint! { pub WARNINGS, Warn, @@ -239,6 +245,7 @@ impl LintPass for HardwiredLints { UNUSED_ASSIGNMENTS, DEAD_CODE, UNREACHABLE_CODE, + UNREACHABLE_PATTERNS, WARNINGS, UNUSED_FEATURES, STABLE_FEATURES, diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index e18945446572c..b517a77255e2f 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -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}; @@ -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; @@ -144,6 +145,14 @@ impl<'a, 'tcx> FromIterator>> 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>, @@ -154,6 +163,7 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> { impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { pub fn create_and_enter( tcx: TyCtxt<'a, 'tcx, 'tcx>, + node: NodeId, f: F) -> R where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R { @@ -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(), @@ -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 { +fn missing_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, + matrix: &Matrix, + pcx: PatternContext<'tcx>) -> Vec { let used_constructors: Vec = matrix.0.iter() .flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![])) @@ -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 { +/// +/// 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 +{ 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] + } + } } } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 53e83815b4652..d6187f40b7f3e 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -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::*; @@ -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| ( @@ -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)) @@ -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 => { diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index b24cd261dd584..139443a1719c1 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -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 } ``` diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index a24edfaaac1c2..efccc4abd43b8 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -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, diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 9b86196b3ece2..0fa9062df4591 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -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. diff --git a/src/test/compile-fail/E0001.rs b/src/test/compile-fail/E0001.rs index 906642d855580..b72b0d661901f 100644 --- a/src/test/compile-fail/E0001.rs +++ b/src/test/compile-fail/E0001.rs @@ -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 } } diff --git a/src/test/compile-fail/issue-12116.rs b/src/test/compile-fail/issue-12116.rs index 1978068575b95..24765cfc2a671 100644 --- a/src/test/compile-fail/issue-12116.rs +++ b/src/test/compile-fail/issue-12116.rs @@ -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!() } diff --git a/src/test/compile-fail/issue-12369.rs b/src/test/compile-fail/issue-12369.rs index 978d6f59b2df4..4df1e24dcfbd5 100644 --- a/src/test/compile-fail/issue-12369.rs +++ b/src/test/compile-fail/issue-12369.rs @@ -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]; diff --git a/src/test/compile-fail/issue-13727.rs b/src/test/compile-fail/issue-13727.rs index 28c2c7bc0e2e7..2e815548e8913 100644 --- a/src/test/compile-fail/issue-13727.rs +++ b/src/test/compile-fail/issue-13727.rs @@ -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"), diff --git a/src/test/compile-fail/issue-30240-b.rs b/src/test/compile-fail/issue-30240-b.rs new file mode 100644 index 0000000000000..cf6935b9ba6d4 --- /dev/null +++ b/src/test/compile-fail/issue-30240-b.rs @@ -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 or the MIT license +// , 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 + _ => {}, + } +} + diff --git a/src/test/compile-fail/issue-30240.rs b/src/test/compile-fail/issue-30240.rs index 9b105e7ec159d..60fb307d4e1a4 100644 --- a/src/test/compile-fail/issue-30240.rs +++ b/src/test/compile-fail/issue-30240.rs @@ -16,6 +16,5 @@ fn main() { match "world" { //~ ERROR non-exhaustive patterns: `&_` ref _x if false => {} "hello" => {} - "hello" => {} //~ ERROR unreachable pattern } } diff --git a/src/test/compile-fail/issue-31221.rs b/src/test/compile-fail/issue-31221.rs index 4997a6fee195b..8cf6725cec454 100644 --- a/src/test/compile-fail/issue-31221.rs +++ b/src/test/compile-fail/issue-31221.rs @@ -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, @@ -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 - }; } diff --git a/src/test/compile-fail/issue-3601.rs b/src/test/compile-fail/issue-3601.rs index b25e683db0906..cc69a76e04331 100644 --- a/src/test/compile-fail/issue-3601.rs +++ b/src/test/compile-fail/issue-3601.rs @@ -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 }; } diff --git a/src/test/compile-fail/match-argm-statics-2.rs b/src/test/compile-fail/match-argm-statics-2.rs new file mode 100644 index 0000000000000..40dcf3d0f12cc --- /dev/null +++ b/src/test/compile-fail/match-argm-statics-2.rs @@ -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 or the MIT license +// , 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 = 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, + 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(); +} + diff --git a/src/test/compile-fail/match-arm-statics.rs b/src/test/compile-fail/match-arm-statics.rs index 9b313f248fcbb..40d73ab51c762 100644 --- a/src/test/compile-fail/match-arm-statics.rs +++ b/src/test/compile-fail/match-arm-statics.rs @@ -7,10 +7,16 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. + +#![allow(dead_code)] +#![deny(unreachable_patterns)] + use self::Direction::{North, East, South, West}; +#[derive(PartialEq, Eq)] struct NewBool(bool); +#[derive(PartialEq, Eq)] enum Direction { North, East, @@ -20,15 +26,6 @@ enum Direction { 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) => () - } -} - fn unreachable_1() { match (true, false) { TRUE_TRUE => (), @@ -43,17 +40,6 @@ fn unreachable_1() { const NONE: Option = 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 => () - } -} - fn unreachable_2() { match Some(Some(North)) { Some(NONE) => (), @@ -73,19 +59,6 @@ struct Foo { 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 unreachable_3() { match (Foo { bar: Some(EAST), baz: NewBool(true) }) { Foo { bar: None, baz: NewBool(true) } => (), @@ -100,9 +73,6 @@ fn unreachable_3() { } fn main() { - nonexhaustive_1(); - nonexhaustive_2(); - nonexhaustive_3(); unreachable_1(); unreachable_2(); unreachable_3(); diff --git a/src/test/compile-fail/match-byte-array-patterns-2.rs b/src/test/compile-fail/match-byte-array-patterns-2.rs new file mode 100644 index 0000000000000..ad7e931a0ec97 --- /dev/null +++ b/src/test/compile-fail/match-byte-array-patterns-2.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(advanced_slice_patterns, slice_patterns)] + +fn main() { + let buf = &[0, 1, 2, 3]; + + match buf { //~ ERROR non-exhaustive + b"AAAA" => {} + } + + let buf: &[u8] = buf; + + match buf { //~ ERROR non-exhaustive + b"AAAA" => {} + } +} + diff --git a/src/test/compile-fail/match-byte-array-patterns.rs b/src/test/compile-fail/match-byte-array-patterns.rs index 86323656b873e..1ff07eae1c9c0 100644 --- a/src/test/compile-fail/match-byte-array-patterns.rs +++ b/src/test/compile-fail/match-byte-array-patterns.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(advanced_slice_patterns, slice_patterns)] +#![deny(unreachable_patterns)] fn main() { let buf = &[0, 1, 2, 3]; @@ -37,10 +38,6 @@ fn main() { _ => {} } - match buf { //~ ERROR non-exhaustive - b"AAAA" => {} - } - let buf: &[u8] = buf; match buf { @@ -66,8 +63,4 @@ fn main() { b"AAAA" => {}, //~ ERROR unreachable pattern _ => {} } - - match buf { //~ ERROR non-exhaustive - b"AAAA" => {} - } } diff --git a/src/test/compile-fail/match-range-fail-dominate.rs b/src/test/compile-fail/match-range-fail-dominate.rs index 825a485d52956..256aa180f4a59 100644 --- a/src/test/compile-fail/match-range-fail-dominate.rs +++ b/src/test/compile-fail/match-range-fail-dominate.rs @@ -14,6 +14,8 @@ //error-pattern: unreachable //error-pattern: unreachable +#![deny(unreachable_patterns)] + fn main() { match 5 { 1 ... 10 => { } diff --git a/src/test/compile-fail/match-ref-ice.rs b/src/test/compile-fail/match-ref-ice.rs index 042ec95f7e753..1cdbba17f658a 100644 --- a/src/test/compile-fail/match-ref-ice.rs +++ b/src/test/compile-fail/match-ref-ice.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(slice_patterns)] +#![deny(unreachable_patterns)] // The arity of `ref x` is always 1. If the pattern is compared to some non-structural type whose // arity is always 0, an ICE occurs. @@ -19,7 +20,7 @@ fn main() { let homura = [1, 2, 3]; match homura { - [1, ref madoka, 3] => (), + [1, ref _madoka, 3] => (), [1, 2, 3] => (), //~ ERROR unreachable pattern [_, _, _] => (), } diff --git a/src/test/compile-fail/match-vec-fixed.rs b/src/test/compile-fail/match-vec-fixed.rs index 60d0c24bb3d36..dd9379c756d12 100644 --- a/src/test/compile-fail/match-vec-fixed.rs +++ b/src/test/compile-fail/match-vec-fixed.rs @@ -9,6 +9,7 @@ // except according to those terms. #![feature(slice_patterns)] +#![deny(unreachable_patterns)] fn a() { let v = [1, 2, 3]; diff --git a/src/test/compile-fail/match-vec-unreachable.rs b/src/test/compile-fail/match-vec-unreachable.rs index 4d9b3aea1124b..6b8111ac31307 100644 --- a/src/test/compile-fail/match-vec-unreachable.rs +++ b/src/test/compile-fail/match-vec-unreachable.rs @@ -9,13 +9,14 @@ // except according to those terms. #![feature(slice_patterns)] +#![deny(unreachable_patterns)] fn main() { let x: Vec<(isize, isize)> = Vec::new(); let x: &[(isize, isize)] = &x; match *x { - [a, (2, 3), _] => (), - [(1, 2), (2, 3), b] => (), //~ ERROR unreachable pattern + [_, (2, 3), _] => (), + [(1, 2), (2, 3), _] => (), //~ ERROR unreachable pattern _ => () } @@ -24,7 +25,7 @@ fn main() { "baz".to_string()]; let x: &[String] = &x; match *x { - [a, _, _, ..] => { println!("{}", a); } + [ref a, _, _, ..] => { println!("{}", a); } [_, _, _, _, _] => { } //~ ERROR unreachable pattern _ => { } } diff --git a/src/test/compile-fail/struct-pattern-match-useless.rs b/src/test/compile-fail/struct-pattern-match-useless.rs index 9f7ebc261ad2e..dda30141b4a06 100644 --- a/src/test/compile-fail/struct-pattern-match-useless.rs +++ b/src/test/compile-fail/struct-pattern-match-useless.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![deny(unreachable_patterns)] + struct Foo { x: isize, y: isize, @@ -16,7 +18,7 @@ struct Foo { pub fn main() { let a = Foo { x: 1, y: 2 }; match a { - Foo { x: x, y: y } => (), + Foo { x: _x, y: _y } => (), Foo { .. } => () //~ ERROR unreachable pattern } diff --git a/src/test/compile-fail/unreachable-arm.rs b/src/test/compile-fail/unreachable-arm.rs index bc93b86a39119..461f092b98b54 100644 --- a/src/test/compile-fail/unreachable-arm.rs +++ b/src/test/compile-fail/unreachable-arm.rs @@ -12,7 +12,16 @@ #![feature(box_patterns)] #![feature(box_syntax)] +#![allow(dead_code)] +#![deny(unreachable_patterns)] -enum foo { a(Box, isize), b(usize), } +enum Foo { A(Box, isize), B(usize), } + +fn main() { + match Foo::B(1) { + Foo::B(_) | Foo::A(box _, 1) => { } + Foo::A(_, 1) => { } + _ => { } + } +} -fn main() { match foo::b(1) { foo::b(_) | foo::a(box _, 1) => { } foo::a(_, 1) => { } } } From 9c5e86d0cd0185bb1030b95196394adb6c2c7a7a Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 1 Dec 2016 01:12:03 +0800 Subject: [PATCH 03/22] More pattern matching for empty types changes Fix is_uninhabited for enum types. It used to assume that an enums variant's fields were all private. Fix MIR generation for irrefutable Variant pattern matches. This allows code like this to work: let x: Result<32, !> = Ok(123); let Ok(y) = x; Carry type information on dummy wildcard patterns. Sometimes we need to expand these patterns into their constructors and we don't want to be expanding a TyError into a Constructor::Single. --- src/librustc/mir/mod.rs | 4 + src/librustc/ty/mod.rs | 33 +++-- src/librustc_const_eval/_match.rs | 143 ++++++++++++------- src/librustc_const_eval/check_match.rs | 50 +++---- src/librustc_const_eval/pattern.rs | 19 ++- src/librustc_mir/build/matches/simplify.rs | 20 ++- src/librustc_mir/build/matches/test.rs | 4 +- src/test/run-pass/empty-types-in-patterns.rs | 60 ++++++++ 8 files changed, 237 insertions(+), 96 deletions(-) create mode 100644 src/test/run-pass/empty-types-in-patterns.rs diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 3cd3580473292..f244306a0cf4f 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -888,6 +888,10 @@ impl<'tcx> Lvalue<'tcx> { self.elem(ProjectionElem::Deref) } + pub fn downcast(self, adt_def: AdtDef<'tcx>, variant_index: usize) -> Lvalue<'tcx> { + self.elem(ProjectionElem::Downcast(adt_def, variant_index)) + } + pub fn index(self, index: Operand<'tcx>) -> Lvalue<'tcx> { self.elem(ProjectionElem::Index(index)) } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 3e33d246b6d18..ba389b98b8c4c 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1416,7 +1416,7 @@ impl<'a, 'gcx, 'tcx> AdtDef { return false; }; self.variants.iter().all(|v| { - v.is_uninhabited_recurse(visited, block, tcx, substs, self.is_union()) + v.is_uninhabited_recurse(visited, block, tcx, substs, self.adt_kind()) }) } @@ -1761,11 +1761,23 @@ impl<'a, 'gcx, 'tcx> VariantDef { block: Option, tcx: TyCtxt<'a, 'gcx, 'tcx>, substs: &'tcx Substs<'tcx>, - is_union: bool) -> bool { - if is_union { - self.fields.iter().all(|f| f.is_uninhabited_recurse(visited, block, tcx, substs)) - } else { - self.fields.iter().any(|f| f.is_uninhabited_recurse(visited, block, tcx, substs)) + adt_kind: AdtKind) -> bool { + match adt_kind { + AdtKind::Union => { + self.fields.iter().all(|f| { + f.is_uninhabited_recurse(visited, block, tcx, substs, false) + }) + }, + AdtKind::Struct => { + self.fields.iter().any(|f| { + f.is_uninhabited_recurse(visited, block, tcx, substs, false) + }) + }, + AdtKind::Enum => { + self.fields.iter().any(|f| { + f.is_uninhabited_recurse(visited, block, tcx, substs, true) + }) + }, } } } @@ -1780,9 +1792,12 @@ impl<'a, 'gcx, 'tcx> FieldDef { visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, block: Option, tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>) -> bool { - block.map_or(true, |b| tcx.vis_is_accessible_from(self.vis, b)) && - self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx) + substs: &'tcx Substs<'tcx>, + is_enum: bool) -> bool { + let visible = is_enum || block.map_or(true, |b| { + tcx.vis_is_accessible_from(self.vis, b) + }); + visible && self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx) } } diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index b517a77255e2f..1577e87049cc3 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -24,7 +24,7 @@ use pattern::{FieldPattern, Pattern, PatternKind}; use pattern::{PatternFoldable, PatternFolder}; use rustc::hir::def_id::DefId; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable}; use rustc::mir::Field; use rustc::util::common::ErrorReported; @@ -153,9 +153,6 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> { /// 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>, pub pattern_arena: &'a TypedArena>, pub byte_array_map: FxHashMap<*const Pattern<'tcx>, Vec<&'a Pattern<'tcx>>>, } @@ -167,25 +164,20 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { f: F) -> R where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R { - let wild_pattern = Pattern { - ty: tcx.types.err, - span: DUMMY_SP, - kind: box PatternKind::Wild - }; - let pattern_arena = TypedArena::new(); f(MatchCheckCtxt { tcx: tcx, node: node, - wild_pattern: &wild_pattern, pattern_arena: &pattern_arena, byte_array_map: FxHashMap(), }) } // convert a byte-string pattern to a list of u8 patterns. - fn lower_byte_str_pattern(&mut self, pat: &'a Pattern<'tcx>) -> Vec<&'a Pattern<'tcx>> { + fn lower_byte_str_pattern<'p>(&mut self, pat: &'p Pattern<'tcx>) -> Vec<&'p Pattern<'tcx>> + where 'a: 'p + { let pattern_arena = &*self.pattern_arena; let tcx = self.tcx; self.byte_array_map.entry(pat).or_insert_with(|| { @@ -401,6 +393,7 @@ fn missing_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, pcx: PatternContext<'tcx>) -> Vec { + debug!("all_constructors({:?})", pcx.ty); match pcx.ty.sty { ty::TyBool => [true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(), @@ -421,7 +414,10 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, 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) { + if v.is_uninhabited_recurse(&mut visited, + Some(cx.node), + cx.tcx, substs, + AdtKind::Enum) { None } else { Some(Variant(v.did)) @@ -438,10 +434,10 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, } } -fn max_slice_length<'a, 'tcx, I>( +fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>( _cx: &mut MatchCheckCtxt<'a, 'tcx>, patterns: I) -> usize - where I: Iterator> + where I: Iterator> { // The exhaustiveness-checking paper does not include any details on // checking variable-length slice patterns. However, they are matched @@ -532,6 +528,12 @@ fn max_slice_length<'a, 'tcx, I>( } /// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html +/// The algorithm from the paper has been modified to correctly handle empty +/// types. The changes are: +/// (0) We don't exit early if the pattern matrix has zero rows. We just +/// continue to recurse over columns. +/// (1) all_constructors will only return constructors that are statically +/// possible. eg. it will only return Ok for Result /// /// Whether a vector `v` of patterns is 'useful' in relation to a set of such /// vectors `m` is defined as there being a set of inputs that will match `v` @@ -541,12 +543,9 @@ fn max_slice_length<'a, 'tcx, I>( /// relation to preceding patterns, it is not reachable) and exhaustiveness /// checking (if a wildcard pattern is useful in relation to a matrix, the /// matrix isn't exhaustive). -/// -/// Note: is_useful doesn't work on empty types, as the paper notes. -/// So it assumes that v is non-empty. -pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, - matrix: &Matrix<'a, 'tcx>, - v: &[&'a Pattern<'tcx>], +pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, + matrix: &Matrix<'p, 'tcx>, + v: &[&'p Pattern<'tcx>], witness: WitnessPreference) -> Usefulness<'tcx> { let &Matrix(ref rows) = matrix; @@ -616,19 +615,27 @@ pub fn is_useful<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, } } -fn is_useful_specialized<'a, 'tcx>( +fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>( cx: &mut MatchCheckCtxt<'a, 'tcx>, - &Matrix(ref m): &Matrix<'a, 'tcx>, - v: &[&'a Pattern<'tcx>], + &Matrix(ref m): &Matrix<'p, 'tcx>, + v: &[&'p Pattern<'tcx>], ctor: Constructor, lty: Ty<'tcx>, witness: WitnessPreference) -> Usefulness<'tcx> { - let arity = constructor_arity(cx, &ctor, lty); + let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty); + let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| { + Pattern { + ty: ty, + span: DUMMY_SP, + kind: box PatternKind::Wild, + } + }).collect(); + let wild_patterns: Vec<_> = wild_patterns_owned.iter().collect(); let matrix = Matrix(m.iter().flat_map(|r| { - specialize(cx, &r[..], &ctor, 0, arity) + specialize(cx, &r[..], &ctor, &wild_patterns) }).collect()); - match specialize(cx, v, &ctor, 0, arity) { + match specialize(cx, v, &ctor, &wild_patterns) { Some(v) => match is_useful(cx, &matrix, &v[..], witness) { UsefulWithWitness(witnesses) => UsefulWithWitness( witnesses.into_iter() @@ -703,6 +710,33 @@ fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> usize } } +/// This computes the types of the sub patterns that a constructor should be +/// expanded to. +/// +/// For instance, a tuple pattern (43u32, 'a') has sub pattern types [u32, char]. +fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, + ctor: &Constructor, + ty: Ty<'tcx>) -> Vec> +{ + debug!("constructor_sub_pattern_tys({:?}, {:?})", ctor, ty); + match ty.sty { + ty::TyTuple(ref fs) => fs.into_iter().map(|t| *t).collect(), + ty::TyBox(ty) => vec![ty], + ty::TySlice(ty) | ty::TyArray(ty, _) => match *ctor { + Slice(length) => repeat(ty).take(length).collect(), + ConstantValue(_) => vec![], + _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) + }, + ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty], + ty::TyAdt(adt, substs) => { + ctor.variant_for_adt(adt).fields.iter().map(|field| { + field.ty(cx.tcx, substs) + }).collect() + } + _ => vec![], + } +} + fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span, ctor: &Constructor, prefix: &[Pattern], @@ -754,19 +788,18 @@ fn range_covered_by_constructor(tcx: TyCtxt, span: Span, Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater) } -fn patterns_for_variant<'a, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, - subpatterns: &'a [FieldPattern<'tcx>], - arity: usize) - -> Vec<&'a Pattern<'tcx>> +fn patterns_for_variant<'p, 'a: 'p, 'tcx: 'a>( + subpatterns: &'p [FieldPattern<'tcx>], + wild_patterns: &[&'p Pattern<'tcx>]) + -> Vec<&'p Pattern<'tcx>> { - let mut result = vec![cx.wild_pattern; arity]; + let mut result = wild_patterns.to_owned(); for subpat in subpatterns { result[subpat.field.index()] = &subpat.pattern; } - debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, arity, result); + debug!("patterns_for_variant({:?}, {:?}) = {:?}", subpatterns, wild_patterns, result); result } @@ -778,35 +811,41 @@ fn patterns_for_variant<'a, 'tcx>( /// different patterns. /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing /// fields filled with wild patterns. -fn specialize<'a, 'tcx>( +fn specialize<'p, 'a: 'p, 'tcx: 'a>( cx: &mut MatchCheckCtxt<'a, 'tcx>, - r: &[&'a Pattern<'tcx>], - constructor: &Constructor, col: usize, arity: usize) - -> Option>> + r: &[&'p Pattern<'tcx>], + constructor: &Constructor, + wild_patterns: &[&'p Pattern<'tcx>]) + -> Option>> { - let pat = &r[col]; + let pat = &r[0]; let head: Option> = match *pat.kind { - PatternKind::Binding { .. } | PatternKind::Wild => - Some(vec![cx.wild_pattern; arity]), + PatternKind::Binding { .. } | PatternKind::Wild => { + Some(wild_patterns.to_owned()) + }, - PatternKind::Variant { adt_def, variant_index, ref subpatterns } => { + PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; if *constructor == Variant(variant.did) { - Some(patterns_for_variant(cx, subpatterns, arity)) + Some(patterns_for_variant(subpatterns, wild_patterns)) } else { None } } - PatternKind::Leaf { ref subpatterns } => Some(patterns_for_variant(cx, subpatterns, arity)), - PatternKind::Deref { ref subpattern } => Some(vec![subpattern]), + PatternKind::Leaf { ref subpatterns } => { + Some(patterns_for_variant(subpatterns, wild_patterns)) + } + PatternKind::Deref { ref subpattern } => { + Some(vec![subpattern]) + } PatternKind::Constant { ref value } => { match *constructor { Slice(..) => match *value { ConstVal::ByteStr(ref data) => { - if arity == data.len() { + if wild_patterns.len() == data.len() { Some(cx.lower_byte_str_pattern(pat)) } else { None @@ -842,11 +881,14 @@ fn specialize<'a, 'tcx>( match *constructor { Slice(..) => { let pat_len = prefix.len() + suffix.len(); - if let Some(slice_count) = arity.checked_sub(pat_len) { + if let Some(slice_count) = wild_patterns.len().checked_sub(pat_len) { if slice_count == 0 || slice.is_some() { Some( prefix.iter().chain( - repeat(cx.wild_pattern).take(slice_count).chain( + wild_patterns.iter().map(|p| *p) + .skip(prefix.len()) + .take(slice_count) + .chain( suffix.iter() )).collect()) } else { @@ -870,11 +912,10 @@ fn specialize<'a, 'tcx>( } } }; - debug!("specialize({:?}, {:?}) = {:?}", r[col], arity, head); + debug!("specialize({:?}, {:?}) = {:?}", r[0], wild_patterns, head); head.map(|mut head| { - head.extend_from_slice(&r[..col]); - head.extend_from_slice(&r[col + 1..]); + head.extend_from_slice(&r[1 ..]); head }) } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index d6187f40b7f3e..c3a033f4aa793 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -24,7 +24,7 @@ use rustc::middle::expr_use_visitor as euv; use rustc::middle::mem_categorization::{cmt}; use rustc::session::Session; use rustc::traits::Reveal; -use rustc::ty::{self, TyCtxt}; +use rustc::ty::{self, Ty, TyCtxt}; use rustc::lint; use rustc_errors::DiagnosticBuilder; @@ -36,7 +36,7 @@ use rustc_back::slice; use syntax::ast; use syntax::ptr::P; -use syntax_pos::Span; +use syntax_pos::{Span, DUMMY_SP}; struct OuterVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> } @@ -81,7 +81,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchVisitor<'a, 'tcx> { match ex.node { hir::ExprMatch(ref scrut, ref arms, source) => { - self.check_match(scrut, arms, source, ex.span); + self.check_match(scrut, arms, source); } _ => {} } @@ -132,8 +132,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { &self, scrut: &hir::Expr, arms: &[hir::Arm], - source: hir::MatchSource, - span: Span) + source: hir::MatchSource) { for arm in arms { // First, check legality of move bindings. @@ -175,32 +174,14 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { // Fourth, check for unreachable arms. check_arms(cx, &inlined_arms, source); - // Finally, check if the whole match expression is exhaustive. - // Check for empty enum, because is_useful only works on inhabited types. - let pat_ty = self.tcx.tables().node_id_to_type(scrut.id); - if inlined_arms.is_empty() { - if !pat_ty.is_uninhabited(Some(scrut.id), self.tcx) { - // We know the type is inhabited, so this must be wrong - let mut err = create_e0004(self.tcx.sess, span, - format!("non-exhaustive patterns: type {} \ - is non-empty", - pat_ty)); - span_help!(&mut err, span, - "Please ensure that all possible cases are being handled; \ - possibly adding wildcards or more match arms."); - err.emit(); - } - // If the type *is* uninhabited, it's vacuously exhaustive - return; - } - let matrix: Matrix = inlined_arms .iter() .filter(|&&(_, guard)| guard.is_none()) .flat_map(|arm| &arm.0) .map(|pat| vec![pat.0]) .collect(); - check_exhaustive(cx, scrut.span, &matrix, source); + let scrut_ty = cx.tcx.tables().node_id_to_type(scrut.id); + check_exhaustive(cx, scrut_ty, scrut.span, &matrix, source); }) } @@ -213,11 +194,18 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { MatchCheckCtxt::create_and_enter(self.tcx, pat.id, |ref mut cx| { let mut patcx = PatternContext::new(self.tcx); + let pattern = patcx.lower_pattern(pat); + let pattern_ty = pattern.ty; let pats : Matrix = vec![vec![ - expand_pattern(cx, patcx.lower_pattern(pat)) + expand_pattern(cx, pattern) ]].into_iter().collect(); - let witness = match is_useful(cx, &pats, &[cx.wild_pattern], ConstructWitness) { + let wild_pattern = Pattern { + ty: pattern_ty, + span: DUMMY_SP, + kind: box PatternKind::Wild, + }; + let witness = match is_useful(cx, &pats, &[&wild_pattern], ConstructWitness) { UsefulWithWitness(witness) => witness, NotUseful => return, Useful => bug!() @@ -359,10 +347,16 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, } fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, + scrut_ty: Ty<'tcx>, sp: Span, matrix: &Matrix<'a, 'tcx>, source: hir::MatchSource) { - match is_useful(cx, matrix, &[cx.wild_pattern], ConstructWitness) { + let wild_pattern = Pattern { + ty: scrut_ty, + span: DUMMY_SP, + kind: box PatternKind::Wild, + }; + match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) { UsefulWithWitness(pats) => { let witnesses = if pats.is_empty() { vec![cx.wild_pattern] diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index b122d97a702f6..ce2cd47572f5d 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -13,7 +13,8 @@ use eval; use rustc::lint; use rustc::middle::const_val::ConstVal; use rustc::mir::{Field, BorrowKind, Mutability}; -use rustc::ty::{self, TyCtxt, AdtDef, Ty, Region}; +use rustc::ty::{self, TyCtxt, AdtDef, Ty, TypeVariants, Region}; +use rustc::ty::subst::{Substs, Kind}; use rustc::hir::{self, PatKind}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::pat_util::EnumerateAndAdjustIterator; @@ -67,6 +68,7 @@ pub enum PatternKind<'tcx> { /// Foo(...) or Foo{...} or Foo, where `Foo` is a variant name from an adt with >1 variants Variant { adt_def: &'tcx AdtDef, + substs: &'tcx Substs<'tcx>, variant_index: usize, subpatterns: Vec>, }, @@ -534,11 +536,15 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { { match def { Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => { - let enum_id = self.tcx.parent_def_id(variant_id).unwrap(); - let adt_def = self.tcx.lookup_adt_def(enum_id); + let ty = self.tcx.tables().node_id_to_type(pat.id); + let (adt_def, substs) = match ty.sty { + TypeVariants::TyAdt(adt_def, substs) => (adt_def, substs), + _ => span_bug!(pat.span, "inappropriate type for def"), + }; if adt_def.variants.len() > 1 { PatternKind::Variant { adt_def: adt_def, + substs: substs, variant_index: adt_def.variant_index_with_id(variant_id), subpatterns: subpatterns, } @@ -776,8 +782,9 @@ macro_rules! CloneImpls { } CloneImpls!{ <'tcx> - Span, Field, Mutability, ast::Name, ast::NodeId, usize, ConstVal, - Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef + Span, Field, Mutability, ast::Name, ast::NodeId, usize, ConstVal, Region, + Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef, + &'tcx Substs<'tcx>, &'tcx Kind<'tcx> } impl<'tcx> PatternFoldable<'tcx> for FieldPattern<'tcx> { @@ -828,10 +835,12 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> { }, PatternKind::Variant { adt_def, + substs, variant_index, ref subpatterns, } => PatternKind::Variant { adt_def: adt_def.fold_with(folder), + substs: substs.fold_with(folder), variant_index: variant_index.fold_with(folder), subpatterns: subpatterns.fold_with(folder) }, diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index 71282dcf0ba07..c3414c591abb2 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -26,6 +26,7 @@ use build::{BlockAnd, BlockAndExtension, Builder}; use build::matches::{Binding, MatchPair, Candidate}; use hair::*; use rustc::mir::*; +use rustc_data_structures::fx::FxHashSet; use std::mem; @@ -93,11 +94,28 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } PatternKind::Range { .. } | - PatternKind::Variant { .. } | PatternKind::Slice { .. } => { Err(match_pair) } + PatternKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { + let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| { + let mut visited = FxHashSet::default(); + i == variant_index || v.is_uninhabited_recurse(&mut visited, + None, + self.hir.tcx(), + substs, + adt_def.adt_kind()) + }); + if irrefutable { + let lvalue = match_pair.lvalue.downcast(adt_def, variant_index); + candidate.match_pairs.extend(self.field_match_pairs(lvalue, subpatterns)); + Ok(()) + } else { + Err(match_pair) + } + }, + PatternKind::Array { ref prefix, ref slice, ref suffix } => { self.prefix_slice_suffix(&mut candidate.match_pairs, &match_pair.lvalue, diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index cb449037aeba3..8b4a013bad0a3 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -32,7 +32,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// It is a bug to call this with a simplifyable pattern. pub fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { match *match_pair.pattern.kind { - PatternKind::Variant { ref adt_def, variant_index: _, subpatterns: _ } => { + PatternKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => { Test { span: match_pair.pattern.span, kind: TestKind::Switch { @@ -451,7 +451,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // If we are performing a variant switch, then this // informs variant patterns, but nothing else. (&TestKind::Switch { adt_def: tested_adt_def, .. }, - &PatternKind::Variant { adt_def, variant_index, ref subpatterns }) => { + &PatternKind::Variant { adt_def, variant_index, ref subpatterns, .. }) => { assert_eq!(adt_def, tested_adt_def); let new_candidate = self.candidate_after_variant_switch(match_pair_index, diff --git a/src/test/run-pass/empty-types-in-patterns.rs b/src/test/run-pass/empty-types-in-patterns.rs new file mode 100644 index 0000000000000..23705d36e3de2 --- /dev/null +++ b/src/test/run-pass/empty-types-in-patterns.rs @@ -0,0 +1,60 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] +#![feature(slice_patterns)] +#![allow(unreachable_patterns)] +#![allow(unreachable_code)] + +#[allow(dead_code)] +fn foo(z: !) { + let x: Result = Ok(z); + + let Ok(_y) = x; + let Err(_y) = x; + + let x = [z; 1]; + + match x {}; + match x { + [q] => q, + }; +} + +fn bar(nevers: &[!]) { + match nevers { + &[] => (), + }; + + match nevers { + &[] => (), + &[_] => (), + &[_, _, _, ..] => (), + }; +} + +fn main() { + let x: Result = Ok(123); + let Ok(y) = x; + + assert_eq!(123, y); + + match x { + Ok(y) => y, + }; + + match x { + Ok(y) => y, + Err(e) => match e {}, + }; + + bar(&[]); +} + From 9ba9cd5fd5df75d12225429f5831fb968672c79d Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 1 Dec 2016 11:37:03 +0800 Subject: [PATCH 04/22] Improve error message, fix and add tests. Changes the non-exhaustive match error message to generate more general witnesses. --- src/librustc_const_eval/_match.rs | 69 +++++++++++-------- src/test/compile-fail/match-slice-patterns.rs | 2 +- .../compile-fail/uninhabited-irrefutable.rs | 38 ++++++++++ src/test/compile-fail/uninhabited-patterns.rs | 49 +++++++++++++ src/test/ui/check_match/issue-35609.stderr | 4 +- 5 files changed, 131 insertions(+), 31 deletions(-) create mode 100644 src/test/compile-fail/uninhabited-irrefutable.rs create mode 100644 src/test/compile-fail/uninhabited-patterns.rs diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index 1577e87049cc3..b6d1d22015e04 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -359,25 +359,6 @@ impl<'tcx> Witness<'tcx> { } } -/// Return the set of constructors from the same type as the first column of `matrix`, -/// that are matched only by wildcard patterns from that first column. -/// -/// 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<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, - matrix: &Matrix, - pcx: PatternContext<'tcx>) -> Vec { - let used_constructors: Vec = - matrix.0.iter() - .flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![])) - .collect(); - debug!("used_constructors = {:?}", used_constructors); - all_constructors(cx, pcx).into_iter() - .filter(|c| !used_constructors.contains(c)) - .collect() -} - /// This determines the set of all possible constructors of a pattern matching /// values of type `left_ty`. For vectors, this would normally be an infinite set /// @@ -586,10 +567,28 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, ).find(|result| result.is_useful()).unwrap_or(NotUseful) } else { debug!("is_useful - expanding wildcard"); - let constructors = missing_constructors(cx, matrix, pcx); - debug!("is_useful - missing_constructors = {:?}", constructors); - if constructors.is_empty() { - all_constructors(cx, pcx).into_iter().map(|c| { + + let used_ctors: Vec = rows.iter().flat_map(|row| { + pat_constructors(cx, row[0], pcx).unwrap_or(vec![]) + }).collect(); + debug!("used_ctors = {:?}", used_ctors); + let all_ctors = all_constructors(cx, pcx); + debug!("all_ctors = {:?}", all_ctors); + let missing_ctors: Vec = all_ctors.iter().filter(|c| { + !used_ctors.contains(*c) + }).cloned().collect(); + debug!("missing_ctors = {:?}", missing_ctors); + + // `missing_ctors` is the set of constructors from the same type as the + // first column of `matrix` that are matched only by wildcard patterns + // from the first column. + // + // 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 `missing_ctors` + + if missing_ctors.is_empty() { + all_ctors.into_iter().map(|c| { is_useful_specialized(cx, matrix, v, c.clone(), pcx.ty, witness) }).find(|result| result.is_useful()).unwrap_or(NotUseful) } else { @@ -603,11 +602,25 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, match is_useful(cx, &matrix, &v[1..], witness) { UsefulWithWitness(pats) => { let cx = &*cx; - UsefulWithWitness(pats.into_iter().flat_map(|witness| { - constructors.iter().map(move |ctor| { - witness.clone().push_wild_constructor(cx, ctor, pcx.ty) - }) - }).collect()) + let new_witnesses = if used_ctors.is_empty() { + // All constructors are unused. Add wild patterns + // rather than each individual constructor + pats.into_iter().map(|mut witness| { + witness.0.push(P(hir::Pat { + id: DUMMY_NODE_ID, + node: PatKind::Wild, + span: DUMMY_SP, + })); + witness + }).collect() + } else { + pats.into_iter().flat_map(|witness| { + missing_ctors.iter().map(move |ctor| { + witness.clone().push_wild_constructor(cx, ctor, pcx.ty) + }) + }).collect() + }; + UsefulWithWitness(new_witnesses) } result => result } diff --git a/src/test/compile-fail/match-slice-patterns.rs b/src/test/compile-fail/match-slice-patterns.rs index c0fc75f9713a8..fd4bd1c7b944b 100644 --- a/src/test/compile-fail/match-slice-patterns.rs +++ b/src/test/compile-fail/match-slice-patterns.rs @@ -12,7 +12,7 @@ fn check(list: &[Option<()>]) { match list { - //~^ ERROR `&[None, Some(_), None, _]` and `&[Some(_), Some(_), None, _]` not covered + //~^ ERROR `&[_, Some(_), None, _]` not covered &[] => {}, &[_] => {}, &[_, _] => {}, diff --git a/src/test/compile-fail/uninhabited-irrefutable.rs b/src/test/compile-fail/uninhabited-irrefutable.rs new file mode 100644 index 0000000000000..4755fdd4fd5e8 --- /dev/null +++ b/src/test/compile-fail/uninhabited-irrefutable.rs @@ -0,0 +1,38 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(never_type)] + +mod foo { + pub struct SecretlyEmpty { + _priv: !, + } + + pub struct NotSoSecretlyEmpty { + pub _pub: !, + } +} + +struct NotSoSecretlyEmpty { + _priv: !, +} + +enum Foo { + A(foo::SecretlyEmpty), + B(foo::NotSoSecretlyEmpty), + C(NotSoSecretlyEmpty), + D(u32), +} + +fn main() { + let x: Foo = Foo::D(123); + let Foo::D(_y) = x; //~ ERROR refutable pattern in local binding: `A(_)` not covered +} + diff --git a/src/test/compile-fail/uninhabited-patterns.rs b/src/test/compile-fail/uninhabited-patterns.rs new file mode 100644 index 0000000000000..0de29f3a8d737 --- /dev/null +++ b/src/test/compile-fail/uninhabited-patterns.rs @@ -0,0 +1,49 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(box_patterns)] +#![feature(slice_patterns)] +#![feature(box_syntax)] +#![feature(never_type)] +#![deny(unreachable_patterns)] + +mod foo { + pub struct SecretlyEmpty { + _priv: !, + } +} + +struct NotSoSecretlyEmpty { + _priv: !, +} + +fn main() { + let x: &[!] = &[]; + + match x { + &[] => (), + &[..] => (), //~ ERROR unreachable pattern + }; + + let x: Result, &[Result]> = Err(&[]); + match x { + Ok(box _) => (), //~ ERROR unreachable pattern + Err(&[]) => (), + Err(&[..]) => (), //~ ERROR unreachable pattern + } + + let x: Result> = Err(Err(123)); + match x { + Ok(_y) => (), + Err(Err(_y)) => (), + Err(Ok(_y)) => (), //~ ERROR unreachable pattern + } +} + diff --git a/src/test/ui/check_match/issue-35609.stderr b/src/test/ui/check_match/issue-35609.stderr index 66069c7a86a34..0aafe3f17b3d0 100644 --- a/src/test/ui/check_match/issue-35609.stderr +++ b/src/test/ui/check_match/issue-35609.stderr @@ -4,11 +4,11 @@ error[E0004]: non-exhaustive patterns: `(B, _)`, `(C, _)`, `(D, _)` and 2 more n 20 | match (A, ()) { | ^^^^^^^ patterns `(B, _)`, `(C, _)`, `(D, _)` and 2 more not covered -error[E0004]: non-exhaustive patterns: `(A, B)`, `(B, B)`, `(C, B)` and 27 more not covered +error[E0004]: non-exhaustive patterns: `(_, B)`, `(_, C)`, `(_, D)` and 2 more not covered --> $DIR/issue-35609.rs:24:11 | 24 | match (A, A) { - | ^^^^^^ patterns `(A, B)`, `(B, B)`, `(C, B)` and 27 more not covered + | ^^^^^^ patterns `(_, B)`, `(_, C)`, `(_, D)` and 2 more not covered error[E0004]: non-exhaustive patterns: `((B, _), _)`, `((C, _), _)`, `((D, _), _)` and 2 more not covered --> $DIR/issue-35609.rs:28:11 From cfc45d52bb90a1b893ea964b1b91d7dd125ae36d Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 1 Dec 2016 11:56:55 +0800 Subject: [PATCH 05/22] Style fix --- src/librustc_const_eval/_match.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index b6d1d22015e04..f5408aa2ce26e 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -543,8 +543,7 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), LeaveOutWitness => Useful, } - } - else { + } else { NotUseful } }; From d2827aa9bc58383c20d92fc1c5a5003160565884 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 1 Dec 2016 12:41:21 +0800 Subject: [PATCH 06/22] Fix build after rebase --- src/librustc_const_eval/pattern.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index ce2cd47572f5d..b92558680af8c 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -408,7 +408,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { pattern: self.lower_pattern(field), }) .collect(); - self.lower_variant_or_leaf(def, subpatterns) + self.lower_variant_or_leaf(def, ty, subpatterns) } PatKind::Struct(ref qpath, ref fields, _) => { @@ -441,7 +441,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { }) .collect(); - self.lower_variant_or_leaf(def, subpatterns) + self.lower_variant_or_leaf(def, ty, subpatterns) } }; @@ -531,15 +531,15 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { fn lower_variant_or_leaf( &mut self, def: Def, + ty: Ty<'tcx>, subpatterns: Vec>) -> PatternKind<'tcx> { match def { Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => { - let ty = self.tcx.tables().node_id_to_type(pat.id); let (adt_def, substs) = match ty.sty { TypeVariants::TyAdt(adt_def, substs) => (adt_def, substs), - _ => span_bug!(pat.span, "inappropriate type for def"), + _ => bug!("inappropriate type for def"), }; if adt_def.variants.len() > 1 { PatternKind::Variant { @@ -584,7 +584,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { } } } - _ => self.lower_variant_or_leaf(def, vec![]) + _ => self.lower_variant_or_leaf(def, ty, vec![]) }; Pattern { From 56f355c83a29a366b3a2cd30922e695f3ba62a71 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sat, 3 Dec 2016 12:56:20 +0800 Subject: [PATCH 07/22] Fix build after rebase --- src/librustc/mir/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index f244306a0cf4f..1d0e95245ea2d 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -888,7 +888,7 @@ impl<'tcx> Lvalue<'tcx> { self.elem(ProjectionElem::Deref) } - pub fn downcast(self, adt_def: AdtDef<'tcx>, variant_index: usize) -> Lvalue<'tcx> { + pub fn downcast(self, adt_def: &'tcx AdtDef, variant_index: usize) -> Lvalue<'tcx> { self.elem(ProjectionElem::Downcast(adt_def, variant_index)) } From f8c4d10e955e36e886e8674affa58b133d1c9a51 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sun, 11 Dec 2016 22:23:18 +0800 Subject: [PATCH 08/22] Fix test I broke --- src/test/compile-fail/issue-12116.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/compile-fail/issue-12116.rs b/src/test/compile-fail/issue-12116.rs index 24765cfc2a671..a8d2c55255340 100644 --- a/src/test/compile-fail/issue-12116.rs +++ b/src/test/compile-fail/issue-12116.rs @@ -10,6 +10,9 @@ #![feature(box_patterns)] #![feature(box_syntax)] +#![allow(dead_code)] +#![allow(unused_variables)] +#![deny(unreachable_patterns)] enum IntList { Cons(isize, Box), @@ -19,9 +22,8 @@ enum IntList { 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 cannot move out of borrowed content -//~^^ WARN pattern binding `Nil` is named the same as one of the variants of the type `IntList` + &IntList::Cons(val, box IntList::Nil) => IntList::Cons(val, box IntList::Nil), +//~^ ERROR unreachable pattern _ => panic!() } } From 9482492ab65820773ba0b5bd1ad81da5f5858c6c Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sun, 11 Dec 2016 22:25:26 +0800 Subject: [PATCH 09/22] Add drain method to AccumulateVec/ArrayVec You can now call .drain(..) on SmallVec, AccumulateVec and ArrayVec --- .../accumulate_vec.rs | 39 +++++++++ src/librustc_data_structures/array_vec.rs | 86 ++++++++++++++++++- src/librustc_data_structures/lib.rs | 2 + 3 files changed, 126 insertions(+), 1 deletion(-) diff --git a/src/librustc_data_structures/accumulate_vec.rs b/src/librustc_data_structures/accumulate_vec.rs index 937cb3f600746..78af655852d1b 100644 --- a/src/librustc_data_structures/accumulate_vec.rs +++ b/src/librustc_data_structures/accumulate_vec.rs @@ -19,6 +19,7 @@ use std::ops::{Deref, DerefMut}; use std::iter::{self, IntoIterator, FromIterator}; use std::slice; use std::vec; +use std::collections::range::RangeArgument; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; @@ -71,6 +72,19 @@ impl AccumulateVec { AccumulateVec::Heap(ref mut vec) => vec.pop(), } } + + pub fn drain(&mut self, range: R) -> Drain + where R: RangeArgument + { + match *self { + AccumulateVec::Array(ref mut v) => { + Drain::Array(v.drain(range)) + }, + AccumulateVec::Heap(ref mut v) => { + Drain::Heap(v.drain(range)) + }, + } + } } impl Deref for AccumulateVec { @@ -132,6 +146,31 @@ impl Iterator for IntoIter { } } +pub enum Drain<'a, A: Array> + where A::Element: 'a +{ + Array(array_vec::Drain<'a, A>), + Heap(vec::Drain<'a, A::Element>), +} + +impl<'a, A: Array> Iterator for Drain<'a, A> { + type Item = A::Element; + + fn next(&mut self) -> Option { + match *self { + Drain::Array(ref mut drain) => drain.next(), + Drain::Heap(ref mut drain) => drain.next(), + } + } + + fn size_hint(&self) -> (usize, Option) { + match *self { + Drain::Array(ref drain) => drain.size_hint(), + Drain::Heap(ref drain) => drain.size_hint(), + } + } +} + impl IntoIterator for AccumulateVec { type Item = A::Element; type IntoIter = IntoIter; diff --git a/src/librustc_data_structures/array_vec.rs b/src/librustc_data_structures/array_vec.rs index 631cf2cfcf6db..844e9041d2029 100644 --- a/src/librustc_data_structures/array_vec.rs +++ b/src/librustc_data_structures/array_vec.rs @@ -12,12 +12,13 @@ use std::marker::Unsize; use std::iter::Extend; -use std::ptr::{self, drop_in_place}; +use std::ptr::{self, drop_in_place, Shared}; use std::ops::{Deref, DerefMut, Range}; use std::hash::{Hash, Hasher}; use std::slice; use std::fmt; use std::mem; +use std::collections::range::RangeArgument; pub unsafe trait Array { type Element; @@ -103,6 +104,44 @@ impl ArrayVec { None } } + + pub fn drain(&mut self, range: R) -> Drain + where R: RangeArgument + { + // Memory safety + // + // When the Drain is first created, it shortens the length of + // the source vector to make sure no uninitalized or moved-from elements + // are accessible at all if the Drain's destructor never gets to run. + // + // Drain will ptr::read out the values to remove. + // When finished, remaining tail of the vec is copied back to cover + // the hole, and the vector length is restored to the new length. + // + let len = self.len(); + let start = *range.start().unwrap_or(&0); + let end = *range.end().unwrap_or(&len); + assert!(start <= end); + assert!(end <= len); + + unsafe { + // set self.vec length's to start, to be safe in case Drain is leaked + self.set_len(start); + // Use the borrow in the IterMut to indicate borrowing behavior of the + // whole Drain iterator (like &mut T). + let range_slice = { + let arr = &mut self.values as &mut [ManuallyDrop<_>]; + slice::from_raw_parts_mut(arr.as_mut_ptr().offset(start as isize), + end - start) + }; + Drain { + tail_start: end, + tail_len: len - end, + iter: range_slice.iter(), + array_vec: Shared::new(self as *mut _), + } + } + } } impl Default for ArrayVec @@ -179,6 +218,51 @@ impl Iterator for Iter { } } +pub struct Drain<'a, A: Array> + where A::Element: 'a +{ + tail_start: usize, + tail_len: usize, + iter: slice::Iter<'a, ManuallyDrop>, + array_vec: Shared>, +} + +impl<'a, A: Array> Iterator for Drain<'a, A> { + type Item = A::Element; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(|elt| unsafe { ptr::read(elt as *const ManuallyDrop<_>).value }) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a, A: Array> Drop for Drain<'a, A> { + fn drop(&mut self) { + // exhaust self first + while let Some(_) = self.next() {} + + if self.tail_len > 0 { + unsafe { + let source_array_vec = &mut **self.array_vec; + // memmove back untouched tail, update to new length + let start = source_array_vec.len(); + let tail = self.tail_start; + { + let mut arr = &mut source_array_vec.values as &mut [ManuallyDrop<_>]; + let src = arr.as_ptr().offset(tail as isize); + let dst = arr.as_mut_ptr().offset(start as isize); + ptr::copy(src, dst, self.tail_len); + }; + source_array_vec.set_len(start + self.tail_len); + } + } + } +} + impl IntoIterator for ArrayVec { type Item = A::Element; type IntoIter = Iter; diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index d3ec674daed4d..ee75a3596e18a 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -25,6 +25,8 @@ html_root_url = "https://doc.rust-lang.org/nightly/")] #![cfg_attr(not(stage0), deny(warnings))] +#![feature(shared)] +#![feature(collections_range)] #![feature(nonzero)] #![feature(rustc_private)] #![feature(staged_api)] From 7946597f7556dc8e1ad05e02d7e82b4ff800a5ac Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sun, 11 Dec 2016 22:30:14 +0800 Subject: [PATCH 10/22] Refactor is_uninhabited We now cache the inhabitedness of types in the GlobalCtxt. Rather than calculating whether a type is visibly uninhabited from a given NodeId we calculate the full set of NodeIds from which a type is visibly uninhabited then cache that set. We can then use that to answer queries about the inhabitedness of a type relative to any given node. --- src/librustc/ty/context.rs | 4 + src/librustc/ty/inhabitedness.rs | 261 +++++++++++++++++++++ src/librustc/ty/mod.rs | 56 +---- src/librustc/ty/sty.rs | 32 +-- src/librustc_const_eval/_match.rs | 14 +- src/librustc_mir/build/matches/simplify.rs | 14 +- 6 files changed, 294 insertions(+), 87 deletions(-) create mode 100644 src/librustc/ty/inhabitedness.rs diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index f58d7dcb45f30..6c7946a528e04 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -33,6 +33,7 @@ use ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, ExistentialPredicate}; use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid}; use ty::TypeVariants::*; use ty::layout::{Layout, TargetDataLayout}; +use ty::inhabitedness::NodeForrest; use ty::maps; use util::common::MemoizationMap; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; @@ -459,6 +460,8 @@ pub struct GlobalCtxt<'tcx> { // FIXME dep tracking -- should be harmless enough pub normalized_cache: RefCell, Ty<'tcx>>>, + pub inhabitedness_cache: RefCell, NodeForrest>>, + pub lang_items: middle::lang_items::LanguageItems, /// Maps from def-id of a type or region parameter to its @@ -760,6 +763,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { associated_item_def_ids: RefCell::new(DepTrackingMap::new(dep_graph.clone())), ty_param_defs: RefCell::new(NodeMap()), normalized_cache: RefCell::new(FxHashMap()), + inhabitedness_cache: RefCell::new(FxHashMap()), lang_items: lang_items, inherent_impls: RefCell::new(DepTrackingMap::new(dep_graph.clone())), used_unsafe: RefCell::new(NodeSet()), diff --git a/src/librustc/ty/inhabitedness.rs b/src/librustc/ty/inhabitedness.rs new file mode 100644 index 0000000000000..54fdbe1a34af8 --- /dev/null +++ b/src/librustc/ty/inhabitedness.rs @@ -0,0 +1,261 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::mem; +use rustc_data_structures::small_vec::SmallVec; +use syntax::ast::{CRATE_NODE_ID, NodeId}; +use util::nodemap::FxHashSet; +use ty::context::TyCtxt; +use ty::{AdtDef, VariantDef, FieldDef, TyS}; +use ty::{DefId, Substs}; +use ty::{AdtKind, Visibility, NodeIdTree}; +use ty::TypeVariants::*; + +/// Represents a set of nodes closed under the ancestor relation. That is, if a +/// node is in this set then so are all its descendants. +#[derive(Clone)] +pub struct NodeForrest { + /// The minimal set of nodes required to represent the whole set. + /// If A and B are nodes in the NodeForrest, and A is a desecendant + /// of B, then only B will be in root_nodes. + /// We use a SmallVec here because (for its use in this module) its rare + /// that this will contain more than one or two nodes. + root_nodes: SmallVec<[NodeId; 1]>, +} + +impl<'a, 'gcx, 'tcx> NodeForrest { + /// Create an empty set. + pub fn empty() -> NodeForrest { + NodeForrest { + root_nodes: SmallVec::new(), + } + } + + /// Create a set containing every node. + #[inline] + pub fn full() -> NodeForrest { + NodeForrest::from_node(CRATE_NODE_ID) + } + + /// Create a set containing a node and all its descendants. + pub fn from_node(node: NodeId) -> NodeForrest { + let mut root_nodes = SmallVec::new(); + root_nodes.push(node); + NodeForrest { + root_nodes: root_nodes, + } + } + + /// Test whether the set is empty. + pub fn is_empty(&self) -> bool { + self.root_nodes.is_empty() + } + + /// Test whether the set conains a node. + pub fn contains(&self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + node: NodeId) -> bool + { + for root_node in self.root_nodes.iter() { + if tcx.map.is_descendant_of(node, *root_node) { + return true; + } + } + false + } + + /// Calculate the intersection of a collection of sets. + pub fn intersection(tcx: TyCtxt<'a, 'gcx, 'tcx>, + iter: I) -> NodeForrest + where I: IntoIterator + { + let mut ret = NodeForrest::full(); + let mut next_ret = SmallVec::new(); + let mut old_ret: SmallVec<[NodeId; 1]> = SmallVec::new(); + for next_set in iter { + for node in ret.root_nodes.drain(..) { + if next_set.contains(tcx, node) { + next_ret.push(node); + } else { + old_ret.push(node); + } + } + ret.root_nodes.extend(old_ret.drain(..)); + + for node in next_set.root_nodes { + if ret.contains(tcx, node) { + next_ret.push(node); + } + } + + mem::swap(&mut next_ret, &mut ret.root_nodes); + next_ret.drain(..); + } + ret + } + + /// Calculate the union of a collection of sets. + pub fn union(tcx: TyCtxt<'a, 'gcx, 'tcx>, + iter: I) -> NodeForrest + where I: IntoIterator + { + let mut ret = NodeForrest::empty(); + let mut next_ret = SmallVec::new(); + for next_set in iter { + for node in ret.root_nodes.drain(..) { + if !next_set.contains(tcx, node) { + next_ret.push(node); + } + } + + for node in next_set.root_nodes { + if !next_ret.contains(&node) { + next_ret.push(node); + } + } + + mem::swap(&mut next_ret, &mut ret.root_nodes); + next_ret.drain(..); + } + ret + } +} + +impl<'a, 'gcx, 'tcx> AdtDef { + /// Calculate the set of nodes from which this adt is visibly uninhabited. + pub fn uninhabited_from( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + substs: &'tcx Substs<'tcx>) -> NodeForrest + { + if !visited.insert((self.did, substs)) { + return NodeForrest::empty(); + } + + let ret = NodeForrest::intersection(tcx, self.variants.iter().map(|v| { + v.uninhabited_from(visited, tcx, substs, self.adt_kind()) + })); + visited.remove(&(self.did, substs)); + ret + } +} + +impl<'a, 'gcx, 'tcx> VariantDef { + /// Calculate the set of nodes from which this variant is visibly uninhabited. + pub fn uninhabited_from( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + substs: &'tcx Substs<'tcx>, + adt_kind: AdtKind) -> NodeForrest + { + match adt_kind { + AdtKind::Union => { + NodeForrest::intersection(tcx, self.fields.iter().map(|f| { + f.uninhabited_from(visited, tcx, substs, false) + })) + }, + AdtKind::Struct => { + NodeForrest::union(tcx, self.fields.iter().map(|f| { + f.uninhabited_from(visited, tcx, substs, false) + })) + }, + AdtKind::Enum => { + NodeForrest::union(tcx, self.fields.iter().map(|f| { + f.uninhabited_from(visited, tcx, substs, true) + })) + }, + } + } +} + +impl<'a, 'gcx, 'tcx> FieldDef { + /// Calculate the set of nodes from which this field is visibly uninhabited. + pub fn uninhabited_from( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + substs: &'tcx Substs<'tcx>, + is_enum: bool) -> NodeForrest + { + if let Visibility::PrivateExternal = self.vis { + return NodeForrest::empty(); + } + + let data_inhabitedness = self.ty(tcx, substs).uninhabited_from(visited, tcx); + match self.vis { + Visibility::Restricted(from) if !is_enum => { + let node_set = NodeForrest::from_node(from); + let iter = Some(node_set).into_iter().chain(Some(data_inhabitedness)); + NodeForrest::intersection(tcx, iter) + }, + _ => data_inhabitedness, + } + } +} + +impl<'a, 'gcx, 'tcx> TyS<'tcx> { + /// Calculate the set of nodes from which this type is visibly uninhabited. + pub fn uninhabited_from( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>) -> NodeForrest + { + match tcx.lift_to_global(&self) { + Some(global_ty) => { + { + let cache = tcx.inhabitedness_cache.borrow(); + if let Some(closed_node_set) = cache.get(&global_ty) { + return closed_node_set.clone(); + } + } + let node_set = global_ty.uninhabited_from_inner(visited, tcx); + let mut cache = tcx.inhabitedness_cache.borrow_mut(); + cache.insert(global_ty, node_set.clone()); + node_set + }, + None => { + let node_set = self.uninhabited_from_inner(visited, tcx); + node_set + }, + } + } + + fn uninhabited_from_inner( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>) -> NodeForrest + { + match self.sty { + TyAdt(def, substs) => { + def.uninhabited_from(visited, tcx, substs) + }, + + TyNever => NodeForrest::full(), + TyTuple(ref tys) => { + NodeForrest::union(tcx, tys.iter().map(|ty| { + ty.uninhabited_from(visited, tcx) + })) + }, + TyArray(ty, len) => { + if len == 0 { + NodeForrest::empty() + } else { + ty.uninhabited_from(visited, tcx) + } + } + TyRef(_, ref tm) => tm.ty.uninhabited_from(visited, tcx), + + _ => NodeForrest::empty(), + } + } +} + diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index ba389b98b8c4c..7cfc0f74214da 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -29,7 +29,7 @@ use ty; use ty::subst::{Subst, Substs}; use ty::walk::TypeWalker; use util::common::MemoizationMap; -use util::nodemap::{NodeSet, NodeMap, FxHashMap, FxHashSet}; +use util::nodemap::{NodeSet, NodeMap, FxHashMap}; use serialize::{self, Encodable, Encoder}; use std::borrow::Cow; @@ -78,6 +78,7 @@ pub mod cast; pub mod error; pub mod fast_reject; pub mod fold; +pub mod inhabitedness; pub mod item_path; pub mod layout; pub mod _match; @@ -1406,20 +1407,6 @@ impl<'a, 'gcx, 'tcx> AdtDef { self.flags.set(self.flags.get() | AdtFlags::IS_DTORCK_VALID) } - #[inline] - pub fn is_uninhabited_recurse(&self, - visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - block: Option, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>) -> bool { - if !visited.insert((self.did, substs)) { - return false; - }; - self.variants.iter().all(|v| { - v.is_uninhabited_recurse(visited, block, tcx, substs, self.adt_kind()) - }) - } - #[inline] pub fn is_struct(&self) -> bool { !self.is_union() && !self.is_enum() @@ -1754,51 +1741,12 @@ impl<'a, 'gcx, 'tcx> VariantDef { pub fn field_named(&self, name: ast::Name) -> &FieldDef { self.find_field_named(name).unwrap() } - - #[inline] - pub fn is_uninhabited_recurse(&self, - visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - block: Option, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>, - adt_kind: AdtKind) -> bool { - match adt_kind { - AdtKind::Union => { - self.fields.iter().all(|f| { - f.is_uninhabited_recurse(visited, block, tcx, substs, false) - }) - }, - AdtKind::Struct => { - self.fields.iter().any(|f| { - f.is_uninhabited_recurse(visited, block, tcx, substs, false) - }) - }, - AdtKind::Enum => { - self.fields.iter().any(|f| { - f.is_uninhabited_recurse(visited, block, tcx, substs, true) - }) - }, - } - } } impl<'a, 'gcx, 'tcx> FieldDef { pub fn ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, subst: &Substs<'tcx>) -> Ty<'tcx> { tcx.item_type(self.did).subst(tcx, subst) } - - #[inline] - pub fn is_uninhabited_recurse(&self, - visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - block: Option, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>, - is_enum: bool) -> bool { - let visible = is_enum || block.map_or(true, |b| { - tcx.vis_is_accessible_from(self.vis, b) - }); - visible && self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx) - } } /// Records the substitutions used to translate the polytype for an diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 638345608c2f5..340b7415f5cb3 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -979,29 +979,21 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } - /// Checks whether a type is uninhabited. - /// If `block` is `Some(id)` it also checks that the uninhabited-ness is visible from `id`. - pub fn is_uninhabited(&self, block: Option, cx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { + /// Checks whether a type is visibly uninhabited from a particular node. + pub fn is_uninhabited_from(&self, block: NodeId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { let mut visited = FxHashSet::default(); - self.is_uninhabited_recurse(&mut visited, block, cx) + let node_set = self.uninhabited_from(&mut visited, tcx); + node_set.contains(tcx, block) } - pub fn is_uninhabited_recurse(&self, - visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - block: Option, - cx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { - match self.sty { - TyAdt(def, substs) => { - def.is_uninhabited_recurse(visited, block, cx, substs) - }, - - TyNever => true, - TyTuple(ref tys) => tys.iter().any(|ty| ty.is_uninhabited_recurse(visited, block, cx)), - TyArray(ty, len) => len > 0 && ty.is_uninhabited_recurse(visited, block, cx), - TyRef(_, ref tm) => tm.ty.is_uninhabited_recurse(visited, block, cx), - - _ => false, - } + /// Checks whether a type is uninhabited. + /// Note: just because a type is uninhabited, that doesn't mean that it's + /// *visibly* uninhabited outside its module. You sometimes may want + /// `is_uninhabited_from` instead. + pub fn is_uninhabited_anywhere(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { + let mut visited = FxHashSet::default(); + let node_set = self.uninhabited_from(&mut visited, tcx); + !node_set.is_empty() } pub fn is_primitive(&self) -> bool { diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index f5408aa2ce26e..ea01857745e56 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -379,14 +379,14 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, ty::TyBool => [true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(), ty::TySlice(ref sub_ty) => { - if sub_ty.is_uninhabited(Some(cx.node), cx.tcx) { + if sub_ty.is_uninhabited_from(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) { + if length == 0 || !sub_ty.is_uninhabited_from(cx.node, cx.tcx) { vec![Slice(length)] } else { vec![] @@ -395,10 +395,10 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, 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, - AdtKind::Enum) { + let node_set = v.uninhabited_from(&mut visited, + cx.tcx, substs, + AdtKind::Enum); + if node_set.contains(cx.tcx, cx.node) { None } else { Some(Variant(v.did)) @@ -406,7 +406,7 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, }).collect() } _ => { - if pcx.ty.is_uninhabited(Some(cx.node), cx.tcx) { + if pcx.ty.is_uninhabited_from(cx.node, cx.tcx) { vec![] } else { vec![Single] diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index c3414c591abb2..b071834122367 100644 --- a/src/librustc_mir/build/matches/simplify.rs +++ b/src/librustc_mir/build/matches/simplify.rs @@ -100,12 +100,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { PatternKind::Variant { adt_def, substs, variant_index, ref subpatterns } => { let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| { - let mut visited = FxHashSet::default(); - i == variant_index || v.is_uninhabited_recurse(&mut visited, - None, - self.hir.tcx(), - substs, - adt_def.adt_kind()) + i == variant_index || { + let mut visited = FxHashSet::default(); + let node_set = v.uninhabited_from(&mut visited, + self.hir.tcx(), + substs, + adt_def.adt_kind()); + !node_set.is_empty() + } }); if irrefutable { let lvalue = match_pair.lvalue.downcast(adt_def, variant_index); From 44a70f0221819f3db2c845baf555835d9532f29f Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sun, 11 Dec 2016 23:47:51 +0800 Subject: [PATCH 11/22] Fix inhabitedness bug --- src/librustc/ty/inhabitedness.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/librustc/ty/inhabitedness.rs b/src/librustc/ty/inhabitedness.rs index 54fdbe1a34af8..5acffca26790c 100644 --- a/src/librustc/ty/inhabitedness.rs +++ b/src/librustc/ty/inhabitedness.rs @@ -186,18 +186,19 @@ impl<'a, 'gcx, 'tcx> FieldDef { substs: &'tcx Substs<'tcx>, is_enum: bool) -> NodeForrest { - if let Visibility::PrivateExternal = self.vis { - return NodeForrest::empty(); - } - - let data_inhabitedness = self.ty(tcx, substs).uninhabited_from(visited, tcx); - match self.vis { - Visibility::Restricted(from) if !is_enum => { - let node_set = NodeForrest::from_node(from); - let iter = Some(node_set).into_iter().chain(Some(data_inhabitedness)); - NodeForrest::intersection(tcx, iter) - }, - _ => data_inhabitedness, + let mut data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(visited, tcx); + if is_enum { + data_uninhabitedness() + } else { + match self.vis { + Visibility::PrivateExternal => NodeForrest::empty(), + Visibility::Restricted(from) => { + let node_set = NodeForrest::from_node(from); + let iter = Some(node_set).into_iter().chain(Some(data_uninhabitedness())); + NodeForrest::intersection(tcx, iter) + }, + Visibility::Public => data_uninhabitedness(), + } } } } From 5ba61edbd038edebb9e3f61a14cd4777ecc399be Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Mon, 12 Dec 2016 00:01:12 +0800 Subject: [PATCH 12/22] Disable unreachable patterns error entirely --- src/librustc_const_eval/check_match.rs | 21 +++++++++------------ src/test/compile-fail/issue-14221.rs | 4 ++++ src/test/compile-fail/issue-30302.rs | 5 +++++ src/test/compile-fail/issue-31221.rs | 7 +++++++ 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index c3a033f4aa793..4b79cd7695ed2 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -26,7 +26,7 @@ use rustc::session::Session; use rustc::traits::Reveal; use rustc::ty::{self, Ty, TyCtxt}; use rustc::lint; -use rustc_errors::DiagnosticBuilder; +use rustc_errors::{Diagnostic, Level, DiagnosticBuilder}; use rustc::hir::def::*; use rustc::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap}; @@ -313,19 +313,16 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, }, hir::MatchSource::Normal => { - // if we had a catchall pattern, raise an error. - // Otherwise an unreachable pattern raises a warning. + let mut diagnostic = Diagnostic::new(Level::Warning, + "unreachable pattern"); + diagnostic.set_span(pat.span); + // if we had a catchall pattern, hint at that 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")); + diagnostic.span_label(pat.span, &"this is an unreachable pattern"); + diagnostic.span_note(catchall, "this pattern matches any value"); } + cx.tcx.sess.add_lint_diagnostic(lint::builtin::UNREACHABLE_PATTERNS, + hir_pat.id, diagnostic); }, hir::MatchSource::TryDesugar => { diff --git a/src/test/compile-fail/issue-14221.rs b/src/test/compile-fail/issue-14221.rs index e79be99a346fa..d11fe99c07f6b 100644 --- a/src/test/compile-fail/issue-14221.rs +++ b/src/test/compile-fail/issue-14221.rs @@ -8,6 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![deny(unreachable_patterns)] +#![allow(unused_variables)] +#![allow(non_snake_case)] + pub enum E { A, B, diff --git a/src/test/compile-fail/issue-30302.rs b/src/test/compile-fail/issue-30302.rs index 26508a4722425..01150ff13740f 100644 --- a/src/test/compile-fail/issue-30302.rs +++ b/src/test/compile-fail/issue-30302.rs @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(non_snake_case)] +#![deny(unreachable_patterns)] + enum Stack { Nil, Cons(T, Box>) diff --git a/src/test/compile-fail/issue-31221.rs b/src/test/compile-fail/issue-31221.rs index 8cf6725cec454..e2b80215caf61 100644 --- a/src/test/compile-fail/issue-31221.rs +++ b/src/test/compile-fail/issue-31221.rs @@ -8,8 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(non_snake_case)] #![deny(unreachable_patterns)] +//~^ NOTE lint level defined here +//~^^ NOTE lint level defined here +//~^^^ NOTE lint level defined here +#[derive(Clone, Copy)] enum Enum { Var1, Var2, From a1570828b2b158afa755a5836b20c0fa5abc764a Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 29 Dec 2016 12:17:40 +0800 Subject: [PATCH 13/22] Amend compile-fail tests --- src/test/compile-fail/match-vec-unreachable.rs | 5 +++-- src/test/compile-fail/unreachable-arm.rs | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/test/compile-fail/match-vec-unreachable.rs b/src/test/compile-fail/match-vec-unreachable.rs index 6b8111ac31307..472b054b08777 100644 --- a/src/test/compile-fail/match-vec-unreachable.rs +++ b/src/test/compile-fail/match-vec-unreachable.rs @@ -10,13 +10,14 @@ #![feature(slice_patterns)] #![deny(unreachable_patterns)] +#![allow(unused_variables)] fn main() { let x: Vec<(isize, isize)> = Vec::new(); let x: &[(isize, isize)] = &x; match *x { - [_, (2, 3), _] => (), - [(1, 2), (2, 3), _] => (), //~ ERROR unreachable pattern + [a, (2, 3), _] => (), + [(1, 2), (2, 3), b] => (), //~ ERROR unreachable pattern _ => () } diff --git a/src/test/compile-fail/unreachable-arm.rs b/src/test/compile-fail/unreachable-arm.rs index 461f092b98b54..df827d2c78421 100644 --- a/src/test/compile-fail/unreachable-arm.rs +++ b/src/test/compile-fail/unreachable-arm.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:unreachable pattern - #![feature(box_patterns)] #![feature(box_syntax)] #![allow(dead_code)] @@ -20,7 +18,7 @@ enum Foo { A(Box, isize), B(usize), } fn main() { match Foo::B(1) { Foo::B(_) | Foo::A(box _, 1) => { } - Foo::A(_, 1) => { } + Foo::A(_, 1) => { } //~ ERROR unreachable pattern _ => { } } } From 4136ba072e1b8ff5c06de2c3dc4b72773ae1b3e5 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 29 Dec 2016 12:38:02 +0800 Subject: [PATCH 14/22] Remove E0001 diagnostic --- src/librustc_const_eval/diagnostics.rs | 25 ------------------- .../feature-gate-rustc-diagnostic-macros.rs | 4 +-- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index 139443a1719c1..c809eef917fe5 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -15,31 +15,6 @@ // In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable. register_long_diagnostics! { -E0001: r##" -This error suggests that the expression arm corresponding to the noted pattern -will never be reached as for all possible values of the expression being -matched, one of the preceding patterns will match. - -This means that perhaps some of the preceding patterns are too general, this -one is too specific or the ordering is incorrect. - -For example, the following `match` block has too many arms: - -```compile_fail,E0001 -match Some(0) { - Some(bar) => {/* ... */} - x => {/* ... */} // This handles the `None` case - _ => {/* ... */} // All possible cases have already been handled -} -``` - -`match` blocks have their patterns matched in order, so, for example, putting -a wildcard arm above a more specific arm will make the latter arm irrelevant. - -Ensure the ordering of the match arm is correct and remove any superfluous -arms. -"##, - E0002: r##" ## Note: this error code is no longer emitted by the compiler. diff --git a/src/test/compile-fail/feature-gate-rustc-diagnostic-macros.rs b/src/test/compile-fail/feature-gate-rustc-diagnostic-macros.rs index 8286d833e8d22..04e95584407aa 100644 --- a/src/test/compile-fail/feature-gate-rustc-diagnostic-macros.rs +++ b/src/test/compile-fail/feature-gate-rustc-diagnostic-macros.rs @@ -11,11 +11,11 @@ // Test that diagnostic macros are gated by `rustc_diagnostic_macros` feature // gate -__register_diagnostic!(E0001); +__register_diagnostic!(E0002); //~^ ERROR macro undefined: '__register_diagnostic!' fn main() { - __diagnostic_used!(E0001); + __diagnostic_used!(E0002); //~^ ERROR macro undefined: '__diagnostic_used!' } From 9f83e962de29dec0eea7c8ae4ac403bbc6ad1f16 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 29 Dec 2016 17:08:33 +0800 Subject: [PATCH 15/22] Fix build after rebase. Mostly just rename stuff. Visibility checks use DefIds rather than NodeIds now. --- src/librustc/ty/context.rs | 4 +- src/librustc/ty/inhabitedness.rs | 174 +++++++++++++------------ src/librustc/ty/mod.rs | 25 ++-- src/librustc/ty/sty.rs | 10 +- src/librustc_const_eval/_match.rs | 28 ++-- src/librustc_const_eval/check_match.rs | 6 +- 6 files changed, 129 insertions(+), 118 deletions(-) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 6c7946a528e04..6450ddaa53296 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -33,7 +33,7 @@ use ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, ExistentialPredicate}; use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid}; use ty::TypeVariants::*; use ty::layout::{Layout, TargetDataLayout}; -use ty::inhabitedness::NodeForrest; +use ty::inhabitedness::DefIdForrest; use ty::maps; use util::common::MemoizationMap; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; @@ -460,7 +460,7 @@ pub struct GlobalCtxt<'tcx> { // FIXME dep tracking -- should be harmless enough pub normalized_cache: RefCell, Ty<'tcx>>>, - pub inhabitedness_cache: RefCell, NodeForrest>>, + pub inhabitedness_cache: RefCell, DefIdForrest>>, pub lang_items: middle::lang_items::LanguageItems, diff --git a/src/librustc/ty/inhabitedness.rs b/src/librustc/ty/inhabitedness.rs index 5acffca26790c..dc21e84871133 100644 --- a/src/librustc/ty/inhabitedness.rs +++ b/src/librustc/ty/inhabitedness.rs @@ -10,118 +10,120 @@ use std::mem; use rustc_data_structures::small_vec::SmallVec; -use syntax::ast::{CRATE_NODE_ID, NodeId}; +use syntax::ast::CRATE_NODE_ID; use util::nodemap::FxHashSet; use ty::context::TyCtxt; use ty::{AdtDef, VariantDef, FieldDef, TyS}; use ty::{DefId, Substs}; -use ty::{AdtKind, Visibility, NodeIdTree}; +use ty::{AdtKind, Visibility, DefIdTree}; use ty::TypeVariants::*; -/// Represents a set of nodes closed under the ancestor relation. That is, if a -/// node is in this set then so are all its descendants. +/// Represents a set of DefIds closed under the ancestor relation. That is, if +/// a DefId is in this set then so are all its descendants. #[derive(Clone)] -pub struct NodeForrest { - /// The minimal set of nodes required to represent the whole set. - /// If A and B are nodes in the NodeForrest, and A is a desecendant - /// of B, then only B will be in root_nodes. +pub struct DefIdForrest { + /// The minimal set of DefIds required to represent the whole set. + /// If A and B are DefIds in the DefIdForrest, and A is a desecendant + /// of B, then only B will be in root_ids. /// We use a SmallVec here because (for its use in this module) its rare - /// that this will contain more than one or two nodes. - root_nodes: SmallVec<[NodeId; 1]>, + /// that this will contain even two ids. + root_ids: SmallVec<[DefId; 1]>, } -impl<'a, 'gcx, 'tcx> NodeForrest { - /// Create an empty set. - pub fn empty() -> NodeForrest { - NodeForrest { - root_nodes: SmallVec::new(), +impl<'a, 'gcx, 'tcx> DefIdForrest { + /// Create an empty forrest. + pub fn empty() -> DefIdForrest { + DefIdForrest { + root_ids: SmallVec::new(), } } - /// Create a set containing every node. + /// Create a forrest consisting of a single tree representing the entire + /// crate. #[inline] - pub fn full() -> NodeForrest { - NodeForrest::from_node(CRATE_NODE_ID) + pub fn full(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForrest { + let crate_id = tcx.map.local_def_id(CRATE_NODE_ID); + DefIdForrest::from_id(crate_id) } - /// Create a set containing a node and all its descendants. - pub fn from_node(node: NodeId) -> NodeForrest { - let mut root_nodes = SmallVec::new(); - root_nodes.push(node); - NodeForrest { - root_nodes: root_nodes, + /// Create a forrest containing a DefId and all its descendants. + pub fn from_id(id: DefId) -> DefIdForrest { + let mut root_ids = SmallVec::new(); + root_ids.push(id); + DefIdForrest { + root_ids: root_ids, } } - /// Test whether the set is empty. + /// Test whether the forrest is empty. pub fn is_empty(&self) -> bool { - self.root_nodes.is_empty() + self.root_ids.is_empty() } - /// Test whether the set conains a node. + /// Test whether the forrest conains a given DefId. pub fn contains(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, - node: NodeId) -> bool + id: DefId) -> bool { - for root_node in self.root_nodes.iter() { - if tcx.map.is_descendant_of(node, *root_node) { + for root_id in self.root_ids.iter() { + if tcx.is_descendant_of(id, *root_id) { return true; } } false } - /// Calculate the intersection of a collection of sets. + /// Calculate the intersection of a collection of forrests. pub fn intersection(tcx: TyCtxt<'a, 'gcx, 'tcx>, - iter: I) -> NodeForrest - where I: IntoIterator + iter: I) -> DefIdForrest + where I: IntoIterator { - let mut ret = NodeForrest::full(); + let mut ret = DefIdForrest::full(tcx); let mut next_ret = SmallVec::new(); - let mut old_ret: SmallVec<[NodeId; 1]> = SmallVec::new(); - for next_set in iter { - for node in ret.root_nodes.drain(..) { - if next_set.contains(tcx, node) { - next_ret.push(node); + let mut old_ret: SmallVec<[DefId; 1]> = SmallVec::new(); + for next_forrest in iter { + for id in ret.root_ids.drain(..) { + if next_forrest.contains(tcx, id) { + next_ret.push(id); } else { - old_ret.push(node); + old_ret.push(id); } } - ret.root_nodes.extend(old_ret.drain(..)); + ret.root_ids.extend(old_ret.drain(..)); - for node in next_set.root_nodes { - if ret.contains(tcx, node) { - next_ret.push(node); + for id in next_forrest.root_ids { + if ret.contains(tcx, id) { + next_ret.push(id); } } - mem::swap(&mut next_ret, &mut ret.root_nodes); + mem::swap(&mut next_ret, &mut ret.root_ids); next_ret.drain(..); } ret } - /// Calculate the union of a collection of sets. + /// Calculate the union of a collection of forrests. pub fn union(tcx: TyCtxt<'a, 'gcx, 'tcx>, - iter: I) -> NodeForrest - where I: IntoIterator + iter: I) -> DefIdForrest + where I: IntoIterator { - let mut ret = NodeForrest::empty(); + let mut ret = DefIdForrest::empty(); let mut next_ret = SmallVec::new(); - for next_set in iter { - for node in ret.root_nodes.drain(..) { - if !next_set.contains(tcx, node) { - next_ret.push(node); + for next_forrest in iter { + for id in ret.root_ids.drain(..) { + if !next_forrest.contains(tcx, id) { + next_ret.push(id); } } - for node in next_set.root_nodes { - if !next_ret.contains(&node) { - next_ret.push(node); + for id in next_forrest.root_ids { + if !next_ret.contains(&id) { + next_ret.push(id); } } - mem::swap(&mut next_ret, &mut ret.root_nodes); + mem::swap(&mut next_ret, &mut ret.root_ids); next_ret.drain(..); } ret @@ -129,18 +131,18 @@ impl<'a, 'gcx, 'tcx> NodeForrest { } impl<'a, 'gcx, 'tcx> AdtDef { - /// Calculate the set of nodes from which this adt is visibly uninhabited. + /// Calculate the forrest of DefIds from which this adt is visibly uninhabited. pub fn uninhabited_from( &self, visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>) -> NodeForrest + substs: &'tcx Substs<'tcx>) -> DefIdForrest { if !visited.insert((self.did, substs)) { - return NodeForrest::empty(); + return DefIdForrest::empty(); } - let ret = NodeForrest::intersection(tcx, self.variants.iter().map(|v| { + let ret = DefIdForrest::intersection(tcx, self.variants.iter().map(|v| { v.uninhabited_from(visited, tcx, substs, self.adt_kind()) })); visited.remove(&(self.did, substs)); @@ -149,27 +151,27 @@ impl<'a, 'gcx, 'tcx> AdtDef { } impl<'a, 'gcx, 'tcx> VariantDef { - /// Calculate the set of nodes from which this variant is visibly uninhabited. + /// Calculate the forrest of DefIds from which this variant is visibly uninhabited. pub fn uninhabited_from( &self, visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, tcx: TyCtxt<'a, 'gcx, 'tcx>, substs: &'tcx Substs<'tcx>, - adt_kind: AdtKind) -> NodeForrest + adt_kind: AdtKind) -> DefIdForrest { match adt_kind { AdtKind::Union => { - NodeForrest::intersection(tcx, self.fields.iter().map(|f| { + DefIdForrest::intersection(tcx, self.fields.iter().map(|f| { f.uninhabited_from(visited, tcx, substs, false) })) }, AdtKind::Struct => { - NodeForrest::union(tcx, self.fields.iter().map(|f| { + DefIdForrest::union(tcx, self.fields.iter().map(|f| { f.uninhabited_from(visited, tcx, substs, false) })) }, AdtKind::Enum => { - NodeForrest::union(tcx, self.fields.iter().map(|f| { + DefIdForrest::union(tcx, self.fields.iter().map(|f| { f.uninhabited_from(visited, tcx, substs, true) })) }, @@ -178,24 +180,24 @@ impl<'a, 'gcx, 'tcx> VariantDef { } impl<'a, 'gcx, 'tcx> FieldDef { - /// Calculate the set of nodes from which this field is visibly uninhabited. + /// Calculate the forrest of DefIds from which this field is visibly uninhabited. pub fn uninhabited_from( &self, visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, tcx: TyCtxt<'a, 'gcx, 'tcx>, substs: &'tcx Substs<'tcx>, - is_enum: bool) -> NodeForrest + is_enum: bool) -> DefIdForrest { let mut data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(visited, tcx); if is_enum { data_uninhabitedness() } else { match self.vis { - Visibility::PrivateExternal => NodeForrest::empty(), + Visibility::Invisible => DefIdForrest::empty(), Visibility::Restricted(from) => { - let node_set = NodeForrest::from_node(from); - let iter = Some(node_set).into_iter().chain(Some(data_uninhabitedness())); - NodeForrest::intersection(tcx, iter) + let forrest = DefIdForrest::from_id(from); + let iter = Some(forrest).into_iter().chain(Some(data_uninhabitedness())); + DefIdForrest::intersection(tcx, iter) }, Visibility::Public => data_uninhabitedness(), } @@ -204,28 +206,28 @@ impl<'a, 'gcx, 'tcx> FieldDef { } impl<'a, 'gcx, 'tcx> TyS<'tcx> { - /// Calculate the set of nodes from which this type is visibly uninhabited. + /// Calculate the forrest of DefIds from which this type is visibly uninhabited. pub fn uninhabited_from( &self, visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - tcx: TyCtxt<'a, 'gcx, 'tcx>) -> NodeForrest + tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForrest { match tcx.lift_to_global(&self) { Some(global_ty) => { { let cache = tcx.inhabitedness_cache.borrow(); - if let Some(closed_node_set) = cache.get(&global_ty) { - return closed_node_set.clone(); + if let Some(forrest) = cache.get(&global_ty) { + return forrest.clone(); } } - let node_set = global_ty.uninhabited_from_inner(visited, tcx); + let forrest = global_ty.uninhabited_from_inner(visited, tcx); let mut cache = tcx.inhabitedness_cache.borrow_mut(); - cache.insert(global_ty, node_set.clone()); - node_set + cache.insert(global_ty, forrest.clone()); + forrest }, None => { - let node_set = self.uninhabited_from_inner(visited, tcx); - node_set + let forrest = self.uninhabited_from_inner(visited, tcx); + forrest }, } } @@ -233,29 +235,29 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { fn uninhabited_from_inner( &self, visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - tcx: TyCtxt<'a, 'gcx, 'tcx>) -> NodeForrest + tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForrest { match self.sty { TyAdt(def, substs) => { def.uninhabited_from(visited, tcx, substs) }, - TyNever => NodeForrest::full(), + TyNever => DefIdForrest::full(tcx), TyTuple(ref tys) => { - NodeForrest::union(tcx, tys.iter().map(|ty| { + DefIdForrest::union(tcx, tys.iter().map(|ty| { ty.uninhabited_from(visited, tcx) })) }, TyArray(ty, len) => { if len == 0 { - NodeForrest::empty() + DefIdForrest::empty() } else { ty.uninhabited_from(visited, tcx) } } TyRef(_, ref tm) => tm.ty.uninhabited_from(visited, tcx), - _ => NodeForrest::empty(), + _ => DefIdForrest::empty(), } } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 7cfc0f74214da..fa62e893a2875 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -227,6 +227,20 @@ pub enum Visibility { pub trait DefIdTree: Copy { fn parent(self, id: DefId) -> Option; + + fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool { + if descendant.krate != ancestor.krate { + return false; + } + + while descendant != ancestor { + match self.parent(descendant) { + Some(parent) => descendant = parent, + None => return false, + } + } + true + } } impl<'a, 'gcx, 'tcx> DefIdTree for TyCtxt<'a, 'gcx, 'tcx> { @@ -253,7 +267,7 @@ impl Visibility { } /// Returns true if an item with this visibility is accessible from the given block. - pub fn is_accessible_from(self, mut module: DefId, tree: T) -> bool { + pub fn is_accessible_from(self, module: DefId, tree: T) -> bool { let restriction = match self { // Public items are visible everywhere. Visibility::Public => return true, @@ -264,14 +278,7 @@ impl Visibility { Visibility::Restricted(module) => module, }; - while module != restriction { - match tree.parent(module) { - Some(parent) => module = parent, - None => return false, - } - } - - true + tree.is_descendant_of(module, restriction) } /// Returns true if this visibility is at least as accessible as the given visibility diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 340b7415f5cb3..92c616b8c71f1 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -22,7 +22,7 @@ use std::fmt; use std::iter; use std::cmp::Ordering; use syntax::abi; -use syntax::ast::{self, Name, NodeId}; +use syntax::ast::{self, Name}; use syntax::symbol::{keywords, InternedString}; use util::nodemap::FxHashSet; @@ -979,11 +979,11 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } - /// Checks whether a type is visibly uninhabited from a particular node. - pub fn is_uninhabited_from(&self, block: NodeId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { + /// Checks whether a type is visibly uninhabited from a particular module. + pub fn is_uninhabited_from(&self, module: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { let mut visited = FxHashSet::default(); - let node_set = self.uninhabited_from(&mut visited, tcx); - node_set.contains(tcx, block) + let forrest = self.uninhabited_from(&mut visited, tcx); + forrest.contains(tcx, module) } /// Checks whether a type is uninhabited. diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index ea01857745e56..f1bd659bd2d39 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -29,7 +29,8 @@ use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable}; use rustc::mir::Field; use rustc::util::common::ErrorReported; -use syntax::ast::NodeId; +use syntax::ast::DUMMY_NODE_ID; +use syntax::ptr::P; use syntax_pos::{Span, DUMMY_SP}; use arena::TypedArena; @@ -145,14 +146,13 @@ impl<'a, 'tcx> FromIterator>> 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 + /// The module in which 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, + /// not. eg. `struct Foo { _private: ! }` cannot be seen to be empty + /// outside it's module and should not be matchable with an empty match + /// statement. + pub module: DefId, pub pattern_arena: &'a TypedArena>, pub byte_array_map: FxHashMap<*const Pattern<'tcx>, Vec<&'a Pattern<'tcx>>>, } @@ -160,7 +160,7 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> { impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { pub fn create_and_enter( tcx: TyCtxt<'a, 'tcx, 'tcx>, - node: NodeId, + module: DefId, f: F) -> R where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R { @@ -168,7 +168,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { f(MatchCheckCtxt { tcx: tcx, - node: node, + module: module, pattern_arena: &pattern_arena, byte_array_map: FxHashMap(), }) @@ -379,14 +379,14 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, ty::TyBool => [true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(), ty::TySlice(ref sub_ty) => { - if sub_ty.is_uninhabited_from(cx.node, cx.tcx) { + if sub_ty.is_uninhabited_from(cx.module, 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_from(cx.node, cx.tcx) { + if length == 0 || !sub_ty.is_uninhabited_from(cx.module, cx.tcx) { vec![Slice(length)] } else { vec![] @@ -395,10 +395,10 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => { def.variants.iter().filter_map(|v| { let mut visited = FxHashSet::default(); - let node_set = v.uninhabited_from(&mut visited, + let forrest = v.uninhabited_from(&mut visited, cx.tcx, substs, AdtKind::Enum); - if node_set.contains(cx.tcx, cx.node) { + if forrest.contains(cx.tcx, cx.module) { None } else { Some(Variant(v.did)) @@ -406,7 +406,7 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, }).collect() } _ => { - if pcx.ty.is_uninhabited_from(cx.node, cx.tcx) { + if pcx.ty.is_uninhabited_from(cx.module, cx.tcx) { vec![] } else { vec![Single] diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 4b79cd7695ed2..824f1e3c975bd 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -150,7 +150,8 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { } } - MatchCheckCtxt::create_and_enter(self.tcx, scrut.id, |ref mut cx| { + let module = self.tcx.map.local_def_id(self.tcx.map.get_module_parent(scrut.id)); + MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| { let mut have_errors = false; let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| ( @@ -192,7 +193,8 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { "local binding" }; - MatchCheckCtxt::create_and_enter(self.tcx, pat.id, |ref mut cx| { + let module = self.tcx.map.local_def_id(self.tcx.map.get_module_parent(pat.id)); + MatchCheckCtxt::create_and_enter(self.tcx, module, |ref mut cx| { let mut patcx = PatternContext::new(self.tcx); let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; From 699b25ff3a86213b806387c3806adc3af9858205 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 30 Dec 2016 10:51:32 -0500 Subject: [PATCH 16/22] fix comment that got split in two --- src/librustc_const_eval/_match.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index f1bd659bd2d39..36e9b1b490020 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -361,14 +361,13 @@ impl<'tcx> Witness<'tcx> { /// This determines the set of all possible constructors of a pattern matching /// values of type `left_ty`. For vectors, this would normally be an infinite set +/// but is instead bounded by the maximum fixed length of slice patterns in +/// the column of patterns being analyzed. /// /// This intentionally does not list ConstantValue specializations for /// non-booleans, because we currently assume that there is always a /// "non-standard constant" that matches. See issue #12483. /// -/// but is instead bounded by the maximum fixed length of slice patterns in -/// the column of patterns being analyzed. -/// /// 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>, From e9ffc409bc7a8a8b11c326136209d720671a8dcd Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sat, 31 Dec 2016 07:35:43 +0800 Subject: [PATCH 17/22] Spelling. s/forrest/forest --- src/librustc/ty/context.rs | 4 +- src/librustc/ty/inhabitedness.rs | 114 +++++++++++++++--------------- src/librustc/ty/sty.rs | 4 +- src/librustc_const_eval/_match.rs | 8 +-- 4 files changed, 65 insertions(+), 65 deletions(-) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 6450ddaa53296..644df8741e853 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -33,7 +33,7 @@ use ty::{BareFnTy, InferTy, ParamTy, ProjectionTy, ExistentialPredicate}; use ty::{TyVar, TyVid, IntVar, IntVid, FloatVar, FloatVid}; use ty::TypeVariants::*; use ty::layout::{Layout, TargetDataLayout}; -use ty::inhabitedness::DefIdForrest; +use ty::inhabitedness::DefIdForest; use ty::maps; use util::common::MemoizationMap; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; @@ -460,7 +460,7 @@ pub struct GlobalCtxt<'tcx> { // FIXME dep tracking -- should be harmless enough pub normalized_cache: RefCell, Ty<'tcx>>>, - pub inhabitedness_cache: RefCell, DefIdForrest>>, + pub inhabitedness_cache: RefCell, DefIdForest>>, pub lang_items: middle::lang_items::LanguageItems, diff --git a/src/librustc/ty/inhabitedness.rs b/src/librustc/ty/inhabitedness.rs index dc21e84871133..762fb11ba05e9 100644 --- a/src/librustc/ty/inhabitedness.rs +++ b/src/librustc/ty/inhabitedness.rs @@ -21,46 +21,46 @@ use ty::TypeVariants::*; /// Represents a set of DefIds closed under the ancestor relation. That is, if /// a DefId is in this set then so are all its descendants. #[derive(Clone)] -pub struct DefIdForrest { +pub struct DefIdForest { /// The minimal set of DefIds required to represent the whole set. - /// If A and B are DefIds in the DefIdForrest, and A is a desecendant + /// If A and B are DefIds in the DefIdForest, and A is a desecendant /// of B, then only B will be in root_ids. /// We use a SmallVec here because (for its use in this module) its rare /// that this will contain even two ids. root_ids: SmallVec<[DefId; 1]>, } -impl<'a, 'gcx, 'tcx> DefIdForrest { - /// Create an empty forrest. - pub fn empty() -> DefIdForrest { - DefIdForrest { +impl<'a, 'gcx, 'tcx> DefIdForest { + /// Create an empty forest. + pub fn empty() -> DefIdForest { + DefIdForest { root_ids: SmallVec::new(), } } - /// Create a forrest consisting of a single tree representing the entire + /// Create a forest consisting of a single tree representing the entire /// crate. #[inline] - pub fn full(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForrest { + pub fn full(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest { let crate_id = tcx.map.local_def_id(CRATE_NODE_ID); - DefIdForrest::from_id(crate_id) + DefIdForest::from_id(crate_id) } - /// Create a forrest containing a DefId and all its descendants. - pub fn from_id(id: DefId) -> DefIdForrest { + /// Create a forest containing a DefId and all its descendants. + pub fn from_id(id: DefId) -> DefIdForest { let mut root_ids = SmallVec::new(); root_ids.push(id); - DefIdForrest { + DefIdForest { root_ids: root_ids, } } - /// Test whether the forrest is empty. + /// Test whether the forest is empty. pub fn is_empty(&self) -> bool { self.root_ids.is_empty() } - /// Test whether the forrest conains a given DefId. + /// Test whether the forest conains a given DefId. pub fn contains(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, id: DefId) -> bool @@ -73,17 +73,17 @@ impl<'a, 'gcx, 'tcx> DefIdForrest { false } - /// Calculate the intersection of a collection of forrests. + /// Calculate the intersection of a collection of forests. pub fn intersection(tcx: TyCtxt<'a, 'gcx, 'tcx>, - iter: I) -> DefIdForrest - where I: IntoIterator + iter: I) -> DefIdForest + where I: IntoIterator { - let mut ret = DefIdForrest::full(tcx); + let mut ret = DefIdForest::full(tcx); let mut next_ret = SmallVec::new(); let mut old_ret: SmallVec<[DefId; 1]> = SmallVec::new(); - for next_forrest in iter { + for next_forest in iter { for id in ret.root_ids.drain(..) { - if next_forrest.contains(tcx, id) { + if next_forest.contains(tcx, id) { next_ret.push(id); } else { old_ret.push(id); @@ -91,7 +91,7 @@ impl<'a, 'gcx, 'tcx> DefIdForrest { } ret.root_ids.extend(old_ret.drain(..)); - for id in next_forrest.root_ids { + for id in next_forest.root_ids { if ret.contains(tcx, id) { next_ret.push(id); } @@ -103,21 +103,21 @@ impl<'a, 'gcx, 'tcx> DefIdForrest { ret } - /// Calculate the union of a collection of forrests. + /// Calculate the union of a collection of forests. pub fn union(tcx: TyCtxt<'a, 'gcx, 'tcx>, - iter: I) -> DefIdForrest - where I: IntoIterator + iter: I) -> DefIdForest + where I: IntoIterator { - let mut ret = DefIdForrest::empty(); + let mut ret = DefIdForest::empty(); let mut next_ret = SmallVec::new(); - for next_forrest in iter { + for next_forest in iter { for id in ret.root_ids.drain(..) { - if !next_forrest.contains(tcx, id) { + if !next_forest.contains(tcx, id) { next_ret.push(id); } } - for id in next_forrest.root_ids { + for id in next_forest.root_ids { if !next_ret.contains(&id) { next_ret.push(id); } @@ -131,18 +131,18 @@ impl<'a, 'gcx, 'tcx> DefIdForrest { } impl<'a, 'gcx, 'tcx> AdtDef { - /// Calculate the forrest of DefIds from which this adt is visibly uninhabited. + /// Calculate the forest of DefIds from which this adt is visibly uninhabited. pub fn uninhabited_from( &self, visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &'tcx Substs<'tcx>) -> DefIdForrest + substs: &'tcx Substs<'tcx>) -> DefIdForest { if !visited.insert((self.did, substs)) { - return DefIdForrest::empty(); + return DefIdForest::empty(); } - let ret = DefIdForrest::intersection(tcx, self.variants.iter().map(|v| { + let ret = DefIdForest::intersection(tcx, self.variants.iter().map(|v| { v.uninhabited_from(visited, tcx, substs, self.adt_kind()) })); visited.remove(&(self.did, substs)); @@ -151,27 +151,27 @@ impl<'a, 'gcx, 'tcx> AdtDef { } impl<'a, 'gcx, 'tcx> VariantDef { - /// Calculate the forrest of DefIds from which this variant is visibly uninhabited. + /// Calculate the forest of DefIds from which this variant is visibly uninhabited. pub fn uninhabited_from( &self, visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, tcx: TyCtxt<'a, 'gcx, 'tcx>, substs: &'tcx Substs<'tcx>, - adt_kind: AdtKind) -> DefIdForrest + adt_kind: AdtKind) -> DefIdForest { match adt_kind { AdtKind::Union => { - DefIdForrest::intersection(tcx, self.fields.iter().map(|f| { + DefIdForest::intersection(tcx, self.fields.iter().map(|f| { f.uninhabited_from(visited, tcx, substs, false) })) }, AdtKind::Struct => { - DefIdForrest::union(tcx, self.fields.iter().map(|f| { + DefIdForest::union(tcx, self.fields.iter().map(|f| { f.uninhabited_from(visited, tcx, substs, false) })) }, AdtKind::Enum => { - DefIdForrest::union(tcx, self.fields.iter().map(|f| { + DefIdForest::union(tcx, self.fields.iter().map(|f| { f.uninhabited_from(visited, tcx, substs, true) })) }, @@ -180,24 +180,24 @@ impl<'a, 'gcx, 'tcx> VariantDef { } impl<'a, 'gcx, 'tcx> FieldDef { - /// Calculate the forrest of DefIds from which this field is visibly uninhabited. + /// Calculate the forest of DefIds from which this field is visibly uninhabited. pub fn uninhabited_from( &self, visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, tcx: TyCtxt<'a, 'gcx, 'tcx>, substs: &'tcx Substs<'tcx>, - is_enum: bool) -> DefIdForrest + is_enum: bool) -> DefIdForest { let mut data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(visited, tcx); if is_enum { data_uninhabitedness() } else { match self.vis { - Visibility::Invisible => DefIdForrest::empty(), + Visibility::Invisible => DefIdForest::empty(), Visibility::Restricted(from) => { - let forrest = DefIdForrest::from_id(from); - let iter = Some(forrest).into_iter().chain(Some(data_uninhabitedness())); - DefIdForrest::intersection(tcx, iter) + let forest = DefIdForest::from_id(from); + let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness())); + DefIdForest::intersection(tcx, iter) }, Visibility::Public => data_uninhabitedness(), } @@ -206,28 +206,28 @@ impl<'a, 'gcx, 'tcx> FieldDef { } impl<'a, 'gcx, 'tcx> TyS<'tcx> { - /// Calculate the forrest of DefIds from which this type is visibly uninhabited. + /// Calculate the forest of DefIds from which this type is visibly uninhabited. pub fn uninhabited_from( &self, visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForrest + tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest { match tcx.lift_to_global(&self) { Some(global_ty) => { { let cache = tcx.inhabitedness_cache.borrow(); - if let Some(forrest) = cache.get(&global_ty) { - return forrest.clone(); + if let Some(forest) = cache.get(&global_ty) { + return forest.clone(); } } - let forrest = global_ty.uninhabited_from_inner(visited, tcx); + let forest = global_ty.uninhabited_from_inner(visited, tcx); let mut cache = tcx.inhabitedness_cache.borrow_mut(); - cache.insert(global_ty, forrest.clone()); - forrest + cache.insert(global_ty, forest.clone()); + forest }, None => { - let forrest = self.uninhabited_from_inner(visited, tcx); - forrest + let forest = self.uninhabited_from_inner(visited, tcx); + forest }, } } @@ -235,29 +235,29 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { fn uninhabited_from_inner( &self, visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, - tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForrest + tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest { match self.sty { TyAdt(def, substs) => { def.uninhabited_from(visited, tcx, substs) }, - TyNever => DefIdForrest::full(tcx), + TyNever => DefIdForest::full(tcx), TyTuple(ref tys) => { - DefIdForrest::union(tcx, tys.iter().map(|ty| { + DefIdForest::union(tcx, tys.iter().map(|ty| { ty.uninhabited_from(visited, tcx) })) }, TyArray(ty, len) => { if len == 0 { - DefIdForrest::empty() + DefIdForest::empty() } else { ty.uninhabited_from(visited, tcx) } } TyRef(_, ref tm) => tm.ty.uninhabited_from(visited, tcx), - _ => DefIdForrest::empty(), + _ => DefIdForest::empty(), } } } diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 92c616b8c71f1..1890bb4335aac 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -982,8 +982,8 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { /// Checks whether a type is visibly uninhabited from a particular module. pub fn is_uninhabited_from(&self, module: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { let mut visited = FxHashSet::default(); - let forrest = self.uninhabited_from(&mut visited, tcx); - forrest.contains(tcx, module) + let forest = self.uninhabited_from(&mut visited, tcx); + forest.contains(tcx, module) } /// Checks whether a type is uninhabited. diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index 36e9b1b490020..90e861c3f57c7 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -394,10 +394,10 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => { def.variants.iter().filter_map(|v| { let mut visited = FxHashSet::default(); - let forrest = v.uninhabited_from(&mut visited, - cx.tcx, substs, - AdtKind::Enum); - if forrest.contains(cx.tcx, cx.module) { + let forest = v.uninhabited_from(&mut visited, + cx.tcx, substs, + AdtKind::Enum); + if forest.contains(cx.tcx, cx.module) { None } else { Some(Variant(v.did)) From f9478902267ea4ff57e95f0620f066445078db4b Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sat, 31 Dec 2016 08:51:25 +0800 Subject: [PATCH 18/22] Change file structure, add comments for inhabitedness.rs --- .../ty/inhabitedness/def_id_forest.rs | 133 +++++++++++++++ .../mod.rs} | 161 ++++++------------ src/librustc/ty/sty.rs | 51 ++++-- 3 files changed, 222 insertions(+), 123 deletions(-) create mode 100644 src/librustc/ty/inhabitedness/def_id_forest.rs rename src/librustc/ty/{inhabitedness.rs => inhabitedness/mod.rs} (59%) diff --git a/src/librustc/ty/inhabitedness/def_id_forest.rs b/src/librustc/ty/inhabitedness/def_id_forest.rs new file mode 100644 index 0000000000000..16bc65603f135 --- /dev/null +++ b/src/librustc/ty/inhabitedness/def_id_forest.rs @@ -0,0 +1,133 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::mem; +use rustc_data_structures::small_vec::SmallVec; +use syntax::ast::CRATE_NODE_ID; +use ty::context::TyCtxt; +use ty::{DefId, DefIdTree}; + +/// Represents a forest of DefIds closed under the ancestor relation. That is, +/// if a DefId representing a module is contained in the forest then all +/// DefIds defined in that module or submodules are also implicitly contained +/// in the forest. +/// +/// This is used to represent a set of modules in which a type is visibly +/// uninhabited. +#[derive(Clone)] +pub struct DefIdForest { + /// The minimal set of DefIds required to represent the whole set. + /// If A and B are DefIds in the DefIdForest, and A is a desecendant + /// of B, then only B will be in root_ids. + /// We use a SmallVec here because (for its use for cacheing inhabitedness) + /// its rare that this will contain even two ids. + root_ids: SmallVec<[DefId; 1]>, +} + +impl<'a, 'gcx, 'tcx> DefIdForest { + /// Create an empty forest. + pub fn empty() -> DefIdForest { + DefIdForest { + root_ids: SmallVec::new(), + } + } + + /// Create a forest consisting of a single tree representing the entire + /// crate. + #[inline] + pub fn full(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest { + let crate_id = tcx.map.local_def_id(CRATE_NODE_ID); + DefIdForest::from_id(crate_id) + } + + /// Create a forest containing a DefId and all its descendants. + pub fn from_id(id: DefId) -> DefIdForest { + let mut root_ids = SmallVec::new(); + root_ids.push(id); + DefIdForest { + root_ids: root_ids, + } + } + + /// Test whether the forest is empty. + pub fn is_empty(&self) -> bool { + self.root_ids.is_empty() + } + + /// Test whether the forest conains a given DefId. + pub fn contains(&self, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + id: DefId) -> bool + { + for root_id in self.root_ids.iter() { + if tcx.is_descendant_of(id, *root_id) { + return true; + } + } + false + } + + /// Calculate the intersection of a collection of forests. + pub fn intersection(tcx: TyCtxt<'a, 'gcx, 'tcx>, + iter: I) -> DefIdForest + where I: IntoIterator + { + let mut ret = DefIdForest::full(tcx); + let mut next_ret = SmallVec::new(); + let mut old_ret: SmallVec<[DefId; 1]> = SmallVec::new(); + for next_forest in iter { + for id in ret.root_ids.drain(..) { + if next_forest.contains(tcx, id) { + next_ret.push(id); + } else { + old_ret.push(id); + } + } + ret.root_ids.extend(old_ret.drain(..)); + + for id in next_forest.root_ids { + if ret.contains(tcx, id) { + next_ret.push(id); + } + } + + mem::swap(&mut next_ret, &mut ret.root_ids); + next_ret.drain(..); + } + ret + } + + /// Calculate the union of a collection of forests. + pub fn union(tcx: TyCtxt<'a, 'gcx, 'tcx>, + iter: I) -> DefIdForest + where I: IntoIterator + { + let mut ret = DefIdForest::empty(); + let mut next_ret = SmallVec::new(); + for next_forest in iter { + for id in ret.root_ids.drain(..) { + if !next_forest.contains(tcx, id) { + next_ret.push(id); + } + } + + for id in next_forest.root_ids { + if !next_ret.contains(&id) { + next_ret.push(id); + } + } + + mem::swap(&mut next_ret, &mut ret.root_ids); + next_ret.drain(..); + } + ret + } +} + diff --git a/src/librustc/ty/inhabitedness.rs b/src/librustc/ty/inhabitedness/mod.rs similarity index 59% rename from src/librustc/ty/inhabitedness.rs rename to src/librustc/ty/inhabitedness/mod.rs index 762fb11ba05e9..1cc31b20ba95d 100644 --- a/src/librustc/ty/inhabitedness.rs +++ b/src/librustc/ty/inhabitedness/mod.rs @@ -8,127 +8,59 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::mem; -use rustc_data_structures::small_vec::SmallVec; -use syntax::ast::CRATE_NODE_ID; use util::nodemap::FxHashSet; use ty::context::TyCtxt; use ty::{AdtDef, VariantDef, FieldDef, TyS}; use ty::{DefId, Substs}; -use ty::{AdtKind, Visibility, DefIdTree}; +use ty::{AdtKind, Visibility}; use ty::TypeVariants::*; -/// Represents a set of DefIds closed under the ancestor relation. That is, if -/// a DefId is in this set then so are all its descendants. -#[derive(Clone)] -pub struct DefIdForest { - /// The minimal set of DefIds required to represent the whole set. - /// If A and B are DefIds in the DefIdForest, and A is a desecendant - /// of B, then only B will be in root_ids. - /// We use a SmallVec here because (for its use in this module) its rare - /// that this will contain even two ids. - root_ids: SmallVec<[DefId; 1]>, -} - -impl<'a, 'gcx, 'tcx> DefIdForest { - /// Create an empty forest. - pub fn empty() -> DefIdForest { - DefIdForest { - root_ids: SmallVec::new(), - } - } - - /// Create a forest consisting of a single tree representing the entire - /// crate. - #[inline] - pub fn full(tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest { - let crate_id = tcx.map.local_def_id(CRATE_NODE_ID); - DefIdForest::from_id(crate_id) - } - - /// Create a forest containing a DefId and all its descendants. - pub fn from_id(id: DefId) -> DefIdForest { - let mut root_ids = SmallVec::new(); - root_ids.push(id); - DefIdForest { - root_ids: root_ids, - } - } - - /// Test whether the forest is empty. - pub fn is_empty(&self) -> bool { - self.root_ids.is_empty() - } - - /// Test whether the forest conains a given DefId. - pub fn contains(&self, - tcx: TyCtxt<'a, 'gcx, 'tcx>, - id: DefId) -> bool - { - for root_id in self.root_ids.iter() { - if tcx.is_descendant_of(id, *root_id) { - return true; - } - } - false - } - - /// Calculate the intersection of a collection of forests. - pub fn intersection(tcx: TyCtxt<'a, 'gcx, 'tcx>, - iter: I) -> DefIdForest - where I: IntoIterator - { - let mut ret = DefIdForest::full(tcx); - let mut next_ret = SmallVec::new(); - let mut old_ret: SmallVec<[DefId; 1]> = SmallVec::new(); - for next_forest in iter { - for id in ret.root_ids.drain(..) { - if next_forest.contains(tcx, id) { - next_ret.push(id); - } else { - old_ret.push(id); - } - } - ret.root_ids.extend(old_ret.drain(..)); +pub use self::def_id_forest::DefIdForest; - for id in next_forest.root_ids { - if ret.contains(tcx, id) { - next_ret.push(id); - } - } - - mem::swap(&mut next_ret, &mut ret.root_ids); - next_ret.drain(..); - } - ret - } +mod def_id_forest; - /// Calculate the union of a collection of forests. - pub fn union(tcx: TyCtxt<'a, 'gcx, 'tcx>, - iter: I) -> DefIdForest - where I: IntoIterator - { - let mut ret = DefIdForest::empty(); - let mut next_ret = SmallVec::new(); - for next_forest in iter { - for id in ret.root_ids.drain(..) { - if !next_forest.contains(tcx, id) { - next_ret.push(id); - } - } - - for id in next_forest.root_ids { - if !next_ret.contains(&id) { - next_ret.push(id); - } - } - - mem::swap(&mut next_ret, &mut ret.root_ids); - next_ret.drain(..); - } - ret - } -} +// The methods in this module calculate DefIdForests of modules in which a +// AdtDef/VariantDef/FieldDef is visibly uninhabited. +// +// # Example +// ```rust +// enum Void {} +// mod a { +// pub mod b { +// pub struct SecretlyUninhabited { +// _priv: !, +// } +// } +// } +// +// mod c { +// pub struct AlsoSecretlyUninhabited { +// _priv: Void, +// } +// mod d { +// } +// } +// +// struct Foo { +// x: a::b::SecretlyUninhabited, +// y: c::AlsoSecretlyUninhabited, +// } +// ``` +// In this code, the type Foo will only be visibly uninhabited inside the +// modules b, c and d. Calling uninhabited_from on Foo or its AdtDef will +// return the forest of modules {b, c->d} (represented in a DefIdForest by the +// set {b, c}) +// +// We need this information for pattern-matching on Foo or types that contain +// Foo. +// +// # Example +// ```rust +// let foo_result: Result = ... ; +// let Ok(t) = foo_result; +// ``` +// This code should only compile in modules where the uninhabitedness of Foo is +// visible. impl<'a, 'gcx, 'tcx> AdtDef { /// Calculate the forest of DefIds from which this adt is visibly uninhabited. @@ -189,6 +121,9 @@ impl<'a, 'gcx, 'tcx> FieldDef { is_enum: bool) -> DefIdForest { let mut data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(visited, tcx); + // FIXME(canndrew): Currently enum fields are (incorrectly) stored with + // Visibility::Invisible so we need to override self.vis if we're + // dealing with an enum. if is_enum { data_uninhabitedness() } else { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 1890bb4335aac..319b11d8031e9 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -980,20 +980,51 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } /// Checks whether a type is visibly uninhabited from a particular module. + /// # Example + /// ```rust + /// enum Void {} + /// mod a { + /// pub mod b { + /// pub struct SecretlyUninhabited { + /// _priv: !, + /// } + /// } + /// } + /// + /// mod c { + /// pub struct AlsoSecretlyUninhabited { + /// _priv: Void, + /// } + /// mod d { + /// } + /// } + /// + /// struct Foo { + /// x: a::b::SecretlyUninhabited, + /// y: c::AlsoSecretlyUninhabited, + /// } + /// ``` + /// In this code, the type `Foo` will only be visibly uninhabited inside the + /// modules b, c and d. This effects pattern-matching on `Foo` or types that + /// contain `Foo`. + /// + /// # Example + /// ```rust + /// let foo_result: Result = ... ; + /// let Ok(t) = foo_result; + /// ``` + /// This code should only compile in modules where the uninhabitedness of Foo is + /// visible. pub fn is_uninhabited_from(&self, module: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { let mut visited = FxHashSet::default(); let forest = self.uninhabited_from(&mut visited, tcx); - forest.contains(tcx, module) - } - /// Checks whether a type is uninhabited. - /// Note: just because a type is uninhabited, that doesn't mean that it's - /// *visibly* uninhabited outside its module. You sometimes may want - /// `is_uninhabited_from` instead. - pub fn is_uninhabited_anywhere(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool { - let mut visited = FxHashSet::default(); - let node_set = self.uninhabited_from(&mut visited, tcx); - !node_set.is_empty() + // To check whether this type is uninhabited at all (not just from the + // given node) you could check whether the forest is empty. + // ``` + // forest.is_empty() + // ``` + forest.contains(tcx, module) } pub fn is_primitive(&self) -> bool { From c0cd145c1f6c29b2ef1f08b666394eb28bef0de4 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sat, 31 Dec 2016 09:17:51 +0800 Subject: [PATCH 19/22] Fix make tidy --- src/librustc/ty/inhabitedness/mod.rs | 2 +- src/librustc/ty/sty.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs index 1cc31b20ba95d..c5b75839e99b7 100644 --- a/src/librustc/ty/inhabitedness/mod.rs +++ b/src/librustc/ty/inhabitedness/mod.rs @@ -53,7 +53,7 @@ mod def_id_forest; // // We need this information for pattern-matching on Foo or types that contain // Foo. -// +// // # Example // ```rust // let foo_result: Result = ... ; diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 319b11d8031e9..81b0a55841ad8 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1007,7 +1007,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { /// In this code, the type `Foo` will only be visibly uninhabited inside the /// modules b, c and d. This effects pattern-matching on `Foo` or types that /// contain `Foo`. - /// + /// /// # Example /// ```rust /// let foo_result: Result = ... ; From 70b7bd94cc90c9e7a67a367859c1c2b66d989fda Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Wed, 4 Jan 2017 11:07:32 +0800 Subject: [PATCH 20/22] Fix build after rebase --- src/librustc_const_eval/_match.rs | 25 ++++++++++++++---------- src/librustc_const_eval/check_match.rs | 2 +- src/librustc_const_eval/pattern.rs | 27 ++++++++++++++------------ 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index 90e861c3f57c7..f4b3646fce02c 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -29,8 +29,6 @@ use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable}; use rustc::mir::Field; use rustc::util::common::ErrorReported; -use syntax::ast::DUMMY_NODE_ID; -use syntax::ptr::P; use syntax_pos::{Span, DUMMY_SP}; use arena::TypedArena; @@ -272,8 +270,14 @@ impl<'tcx> Witness<'tcx> { ty: Ty<'tcx>) -> Self { - let arity = constructor_arity(cx, ctor, ty); - self.0.extend(repeat(cx.wild_pattern).take(arity).cloned()); + let sub_pattern_tys = constructor_sub_pattern_tys(cx, ctor, ty); + self.0.extend(sub_pattern_tys.into_iter().map(|ty| { + Pattern { + ty: ty, + span: DUMMY_SP, + kind: box PatternKind::Wild, + } + })); self.apply_constructor(cx, ctor, ty) } @@ -313,10 +317,11 @@ impl<'tcx> Witness<'tcx> { } }).collect(); - if let ty::TyAdt(adt, _) = ty.sty { + if let ty::TyAdt(adt, substs) = ty.sty { if adt.variants.len() > 1 { PatternKind::Variant { adt_def: adt, + substs: substs, variant_index: ctor.variant_index_for_adt(adt), subpatterns: pats } @@ -604,11 +609,11 @@ pub fn is_useful<'p, 'a: 'p, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, // All constructors are unused. Add wild patterns // rather than each individual constructor pats.into_iter().map(|mut witness| { - witness.0.push(P(hir::Pat { - id: DUMMY_NODE_ID, - node: PatKind::Wild, + witness.0.push(Pattern { + ty: pcx.ty, span: DUMMY_SP, - })); + kind: box PatternKind::Wild, + }); witness }).collect() } else { @@ -740,7 +745,7 @@ fn constructor_sub_pattern_tys<'a, 'tcx: 'a>(cx: &MatchCheckCtxt<'a, 'tcx>, }, ty::TyRef(_, ref ty_and_mut) => vec![ty_and_mut.ty], ty::TyAdt(adt, substs) => { - ctor.variant_for_adt(adt).fields.iter().map(|field| { + adt.variants[ctor.variant_index_for_adt(adt)].fields.iter().map(|field| { field.ty(cx.tcx, substs) }).collect() } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 824f1e3c975bd..2949cf0d535bf 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -358,7 +358,7 @@ fn check_exhaustive<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>, match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) { UsefulWithWitness(pats) => { let witnesses = if pats.is_empty() { - vec![cx.wild_pattern] + vec![&wild_pattern] } else { pats.iter().map(|w| w.single_pattern()).collect() }; diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index b92558680af8c..42394f4745f66 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -393,8 +393,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { PatKind::TupleStruct(ref qpath, ref subpatterns, ddpos) => { let def = self.tcx.tables().qpath_def(qpath, pat.id); - let pat_ty = self.tcx.tables().node_id_to_type(pat.id); - let adt_def = match pat_ty.sty { + let adt_def = match ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => span_bug!(pat.span, "tuple struct pattern not applied to an ADT"), }; @@ -413,8 +412,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { PatKind::Struct(ref qpath, ref fields, _) => { let def = self.tcx.tables().qpath_def(qpath, pat.id); - let pat_ty = self.tcx.tables().node_id_to_type(pat.id); - let adt_def = match pat_ty.sty { + let adt_def = match ty.sty { ty::TyAdt(adt_def, _) => adt_def, _ => { span_bug!( @@ -537,11 +535,14 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { { match def { Def::Variant(variant_id) | Def::VariantCtor(variant_id, ..) => { - let (adt_def, substs) = match ty.sty { - TypeVariants::TyAdt(adt_def, substs) => (adt_def, substs), - _ => bug!("inappropriate type for def"), - }; + let enum_id = self.tcx.parent_def_id(variant_id).unwrap(); + let adt_def = self.tcx.lookup_adt_def(enum_id); if adt_def.variants.len() > 1 { + let substs = match ty.sty { + TypeVariants::TyAdt(_, substs) => substs, + TypeVariants::TyFnDef(_, substs, _) => substs, + _ => bug!("inappropriate type for def: {:?}", ty.sty), + }; PatternKind::Variant { adt_def: adt_def, substs: substs, @@ -568,6 +569,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { pat_id: ast::NodeId, span: Span) -> Pattern<'tcx> { + let ty = self.tcx.tables().node_id_to_type(id); let def = self.tcx.tables().qpath_def(qpath, id); let kind = match def { Def::Const(def_id) | Def::AssociatedConst(def_id) => { @@ -584,12 +586,12 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { } } } - _ => self.lower_variant_or_leaf(def, ty, vec![]) + _ => self.lower_variant_or_leaf(def, ty, vec![]), }; Pattern { span: span, - ty: self.tcx.tables().node_id_to_type(id), + ty: ty, kind: Box::new(kind), } } @@ -657,6 +659,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { hir::ExprPath(ref qpath) => qpath, _ => bug!() }; + let ty = self.tcx.tables().node_id_to_type(callee.id); let def = self.tcx.tables().qpath_def(qpath, callee.id); match def { Def::Fn(..) | Def::Method(..) => self.lower_lit(expr), @@ -667,7 +670,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { pattern: self.lower_const_expr(expr, pat_id, span) } }).collect(); - self.lower_variant_or_leaf(def, subpatterns) + self.lower_variant_or_leaf(def, ty, subpatterns) } } } @@ -702,7 +705,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { }) .collect(); - self.lower_variant_or_leaf(def, subpatterns) + self.lower_variant_or_leaf(def, pat_ty, subpatterns) } hir::ExprArray(ref exprs) => { From 291c84aad48d09e31e84045c1ffccf82e7c2d443 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Thu, 5 Jan 2017 23:48:02 +0800 Subject: [PATCH 21/22] Un-remove E0001, put a notice on it instead --- src/librustc_const_eval/diagnostics.rs | 27 +++++++++++++++++++ .../feature-gate-rustc-diagnostic-macros.rs | 4 +-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index c809eef917fe5..ff0976afc0ce7 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -15,6 +15,33 @@ // In vim you can `:set tw=80` and use `gq` to wrap paragraphs. Use `:set tw=0` to disable. register_long_diagnostics! { +E0001: r##" +## Note: this error code is no longer emitted by the compiler. + +This error suggests that the expression arm corresponding to the noted pattern +will never be reached as for all possible values of the expression being +matched, one of the preceding patterns will match. + +This means that perhaps some of the preceding patterns are too general, this +one is too specific or the ordering is incorrect. + +For example, the following `match` block has too many arms: + +```compile_fail,E0001 +match Some(0) { + Some(bar) => {/* ... */} + x => {/* ... */} // This handles the `None` case + _ => {/* ... */} // All possible cases have already been handled +} +``` + +`match` blocks have their patterns matched in order, so, for example, putting +a wildcard arm above a more specific arm will make the latter arm irrelevant. + +Ensure the ordering of the match arm is correct and remove any superfluous +arms. +"##, + E0002: r##" ## Note: this error code is no longer emitted by the compiler. diff --git a/src/test/compile-fail/feature-gate-rustc-diagnostic-macros.rs b/src/test/compile-fail/feature-gate-rustc-diagnostic-macros.rs index 04e95584407aa..8286d833e8d22 100644 --- a/src/test/compile-fail/feature-gate-rustc-diagnostic-macros.rs +++ b/src/test/compile-fail/feature-gate-rustc-diagnostic-macros.rs @@ -11,11 +11,11 @@ // Test that diagnostic macros are gated by `rustc_diagnostic_macros` feature // gate -__register_diagnostic!(E0002); +__register_diagnostic!(E0001); //~^ ERROR macro undefined: '__register_diagnostic!' fn main() { - __diagnostic_used!(E0002); + __diagnostic_used!(E0001); //~^ ERROR macro undefined: '__diagnostic_used!' } From 275c19d5b6a0f5eceb93e60ee314e73909a12faf Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Fri, 6 Jan 2017 01:00:03 +0800 Subject: [PATCH 22/22] fix doc test for E0001 --- src/librustc_const_eval/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index ff0976afc0ce7..8c8b2b5da36dc 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -27,7 +27,7 @@ one is too specific or the ordering is incorrect. For example, the following `match` block has too many arms: -```compile_fail,E0001 +``` match Some(0) { Some(bar) => {/* ... */} x => {/* ... */} // This handles the `None` case