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/mir/mod.rs b/src/librustc/mir/mod.rs index 3cd3580473292..1d0e95245ea2d 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: &'tcx AdtDef, 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/context.rs b/src/librustc/ty/context.rs index f58d7dcb45f30..644df8741e853 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::DefIdForest; 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, DefIdForest>>, + 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/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/mod.rs b/src/librustc/ty/inhabitedness/mod.rs new file mode 100644 index 0000000000000..c5b75839e99b7 --- /dev/null +++ b/src/librustc/ty/inhabitedness/mod.rs @@ -0,0 +1,199 @@ +// 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 util::nodemap::FxHashSet; +use ty::context::TyCtxt; +use ty::{AdtDef, VariantDef, FieldDef, TyS}; +use ty::{DefId, Substs}; +use ty::{AdtKind, Visibility}; +use ty::TypeVariants::*; + +pub use self::def_id_forest::DefIdForest; + +mod def_id_forest; + +// 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. + pub fn uninhabited_from( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>, + substs: &'tcx Substs<'tcx>) -> DefIdForest + { + if !visited.insert((self.did, substs)) { + return DefIdForest::empty(); + } + + let ret = DefIdForest::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 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) -> DefIdForest + { + match adt_kind { + AdtKind::Union => { + DefIdForest::intersection(tcx, self.fields.iter().map(|f| { + f.uninhabited_from(visited, tcx, substs, false) + })) + }, + AdtKind::Struct => { + DefIdForest::union(tcx, self.fields.iter().map(|f| { + f.uninhabited_from(visited, tcx, substs, false) + })) + }, + AdtKind::Enum => { + DefIdForest::union(tcx, self.fields.iter().map(|f| { + f.uninhabited_from(visited, tcx, substs, true) + })) + }, + } + } +} + +impl<'a, 'gcx, 'tcx> FieldDef { + /// 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) -> 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 { + match self.vis { + Visibility::Invisible => DefIdForest::empty(), + Visibility::Restricted(from) => { + let forest = DefIdForest::from_id(from); + let iter = Some(forest).into_iter().chain(Some(data_uninhabitedness())); + DefIdForest::intersection(tcx, iter) + }, + Visibility::Public => data_uninhabitedness(), + } + } + } +} + +impl<'a, 'gcx, 'tcx> TyS<'tcx> { + /// 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>) -> DefIdForest + { + match tcx.lift_to_global(&self) { + Some(global_ty) => { + { + let cache = tcx.inhabitedness_cache.borrow(); + if let Some(forest) = cache.get(&global_ty) { + return forest.clone(); + } + } + let forest = global_ty.uninhabited_from_inner(visited, tcx); + let mut cache = tcx.inhabitedness_cache.borrow_mut(); + cache.insert(global_ty, forest.clone()); + forest + }, + None => { + let forest = self.uninhabited_from_inner(visited, tcx); + forest + }, + } + } + + fn uninhabited_from_inner( + &self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest + { + match self.sty { + TyAdt(def, substs) => { + def.uninhabited_from(visited, tcx, substs) + }, + + TyNever => DefIdForest::full(tcx), + TyTuple(ref tys) => { + DefIdForest::union(tcx, tys.iter().map(|ty| { + ty.uninhabited_from(visited, tcx) + })) + }, + TyArray(ty, len) => { + if len == 0 { + DefIdForest::empty() + } else { + ty.uninhabited_from(visited, tcx) + } + } + TyRef(_, ref tm) => tm.ty.uninhabited_from(visited, tcx), + + _ => DefIdForest::empty(), + } + } +} + diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 3e33d246b6d18..fa62e893a2875 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; @@ -226,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> { @@ -252,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, @@ -263,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 @@ -1406,20 +1414,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.is_union()) - }) - } - #[inline] pub fn is_struct(&self) -> bool { !self.is_union() && !self.is_enum() @@ -1754,36 +1748,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>, - 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)) - } - } } 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>) -> bool { - block.map_or(true, |b| tcx.vis_is_accessible_from(self.vis, b)) && - 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..81b0a55841ad8 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,29 +979,52 @@ 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 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(); - self.is_uninhabited_recurse(&mut visited, block, cx) - } - - 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, - } + let forest = self.uninhabited_from(&mut visited, tcx); + + // 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 { diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index 6d04975f533da..f4b3646fce02c 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -17,14 +17,14 @@ 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}; 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; @@ -144,9 +144,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>, - /// 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>, + /// 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: ! }` 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>>>, } @@ -154,27 +158,24 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> { impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { pub fn create_and_enter( tcx: TyCtxt<'a, 'tcx, 'tcx>, + module: DefId, 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, - wild_pattern: &wild_pattern, + module: module, 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(|| { @@ -269,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) } @@ -310,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 } @@ -356,51 +364,65 @@ 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(cx: &mut MatchCheckCtxt, - matrix: &Matrix, - pcx: PatternContext) -> 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 +/// 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. -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 +{ + debug!("all_constructors({:?})", pcx.ty); 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_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.module, 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(); + 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)) + } + }).collect() + } + _ => { + if pcx.ty.is_uninhabited_from(cx.module, cx.tcx) { + vec![] + } else { + vec![Single] + } + } } } -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 @@ -491,6 +513,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` @@ -500,29 +528,30 @@ 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; 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())); @@ -541,10 +570,28 @@ pub fn is_useful<'a, 'tcx>(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 { @@ -558,11 +605,25 @@ pub fn is_useful<'a, 'tcx>(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(Pattern { + ty: pcx.ty, + span: DUMMY_SP, + kind: box PatternKind::Wild, + }); + 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 } @@ -570,19 +631,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() @@ -657,6 +726,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) => { + adt.variants[ctor.variant_index_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], @@ -708,19 +804,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 } @@ -732,35 +827,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 @@ -796,11 +897,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 { @@ -824,11 +928,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 53e83815b4652..2949cf0d535bf 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -24,8 +24,9 @@ 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_errors::DiagnosticBuilder; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::lint; +use rustc_errors::{Diagnostic, Level, DiagnosticBuilder}; use rustc::hir::def::*; use rustc::hir::intravisit::{self, Visitor, FnKind, NestedVisitorMap}; @@ -35,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> } @@ -80,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); } _ => {} } @@ -131,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. @@ -150,7 +150,8 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { } } - MatchCheckCtxt::create_and_enter(self.tcx, |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| ( @@ -174,32 +175,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); }) } @@ -210,13 +193,21 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { "local binding" }; - MatchCheckCtxt::create_and_enter(self.tcx, |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; 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!() @@ -324,14 +315,16 @@ 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"); + 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 { - err.span_note(catchall, "this pattern matches any value"); + diagnostic.span_label(pat.span, &"this is an unreachable pattern"); + diagnostic.span_note(catchall, "this pattern matches any value"); } - err.emit(); + cx.tcx.sess.add_lint_diagnostic(lint::builtin::UNREACHABLE_PATTERNS, + hir_pat.id, diagnostic); }, hir::MatchSource::TryDesugar => { @@ -353,13 +346,19 @@ 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] + vec![&wild_pattern] } else { pats.iter().map(|w| w.single_pattern()).collect() }; diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index b24cd261dd584..8c8b2b5da36dc 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -16,6 +16,8 @@ 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. @@ -25,10 +27,10 @@ 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) => {/* ... */} - None => {/* ... */} + x => {/* ... */} // This handles the `None` case _ => {/* ... */} // All possible cases have already been handled } ``` diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index b122d97a702f6..42394f4745f66 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>, }, @@ -391,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"), }; @@ -406,13 +407,12 @@ 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, _) => { 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!( @@ -439,7 +439,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) } }; @@ -529,6 +529,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { fn lower_variant_or_leaf( &mut self, def: Def, + ty: Ty<'tcx>, subpatterns: Vec>) -> PatternKind<'tcx> { @@ -537,8 +538,14 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { 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, variant_index: adt_def.variant_index_with_id(variant_id), subpatterns: subpatterns, } @@ -562,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) => { @@ -578,12 +586,12 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { } } } - _ => self.lower_variant_or_leaf(def, 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), } } @@ -651,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), @@ -661,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) } } } @@ -696,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) => { @@ -776,8 +785,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 +838,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_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)] 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_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs index 71282dcf0ba07..b071834122367 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,30 @@ 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)| { + 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); + 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/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..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), + &IntList::Cons(val, box IntList::Nil) => IntList::Cons(val, box IntList::Nil), //~^ ERROR unreachable pattern -//~^^ 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-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-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-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 4997a6fee195b..e2b80215caf61 100644 --- a/src/test/compile-fail/issue-31221.rs +++ b/src/test/compile-fail/issue-31221.rs @@ -8,6 +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, @@ -41,13 +50,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-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/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..472b054b08777 100644 --- a/src/test/compile-fail/match-vec-unreachable.rs +++ b/src/test/compile-fail/match-vec-unreachable.rs @@ -9,6 +9,8 @@ // except according to those terms. #![feature(slice_patterns)] +#![deny(unreachable_patterns)] +#![allow(unused_variables)] fn main() { let x: Vec<(isize, isize)> = Vec::new(); @@ -24,7 +26,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/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/compile-fail/unreachable-arm.rs b/src/test/compile-fail/unreachable-arm.rs index bc93b86a39119..df827d2c78421 100644 --- a/src/test/compile-fail/unreachable-arm.rs +++ b/src/test/compile-fail/unreachable-arm.rs @@ -8,11 +8,18 @@ // 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)] +#![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) => { } //~ ERROR unreachable pattern + _ => { } + } +} -fn main() { match foo::b(1) { foo::b(_) | foo::a(box _, 1) => { } foo::a(_, 1) => { } } } 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(&[]); +} + 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