diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index e94e93158c47a..d3864539c75a8 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -30,7 +30,7 @@ use ty::subst::{Subst, Substs}; use ty::walk::TypeWalker; use util::common::MemoizationMap; use util::nodemap::NodeSet; -use util::nodemap::FxHashMap; +use util::nodemap::{FxHashMap, FxHashSet}; use serialize::{self, Encodable, Encoder}; use std::borrow::Cow; @@ -1389,6 +1389,22 @@ impl<'tcx> serialize::UseSpecializedEncodable for AdtDef<'tcx> { impl<'tcx> serialize::UseSpecializedDecodable for AdtDef<'tcx> {} +impl<'a, 'gcx, 'tcx> AdtDefData<'tcx, 'static> { + #[inline] + pub fn is_uninhabited_recurse(&'tcx self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + block: Option, + cx: 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, cx, substs, self.is_union()) + }) + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum AdtKind { Struct, Union, Enum } @@ -1531,11 +1547,6 @@ impl<'a, 'gcx, 'tcx, 'container> AdtDefData<'gcx, 'container> { self.variants.iter().flat_map(VariantDefData::fields_iter) } - #[inline] - pub fn is_empty(&self) -> bool { - self.variants.is_empty() - } - #[inline] pub fn is_univariant(&self) -> bool { self.variants.len() == 1 @@ -1795,6 +1806,22 @@ impl<'tcx, 'container> VariantDefData<'tcx, 'container> { } } +impl<'a, 'gcx, 'tcx> VariantDefData<'tcx, 'static> { + #[inline] + pub fn is_uninhabited_recurse(&'tcx self, + visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>, + block: Option, + cx: 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, cx, substs)) + } else { + self.fields.iter().any(|f| f.is_uninhabited_recurse(visited, block, cx, substs)) + } + } +} + impl<'a, 'gcx, 'tcx, 'container> FieldDefData<'tcx, 'container> { pub fn new(did: DefId, name: Name, @@ -1820,6 +1847,18 @@ impl<'a, 'gcx, 'tcx, 'container> FieldDefData<'tcx, 'container> { } } +impl<'a, 'gcx, 'tcx> FieldDefData<'tcx, 'static> { + #[inline] + pub fn is_uninhabited_recurse(&'tcx 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| self.vis.is_accessible_from(b, &tcx.map)) && + self.ty(tcx, substs).is_uninhabited_recurse(visited, block, tcx) + } +} + /// Records the substitutions used to translate the polytype for an /// item into the monotype of an item reference. #[derive(Clone, RustcEncodable, RustcDecodable)] diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 81896ecfb5341..cb3176cce10bd 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -22,8 +22,9 @@ use collections::enum_set::{self, EnumSet, CLike}; use std::fmt; use std::ops; use syntax::abi; -use syntax::ast::{self, Name}; +use syntax::ast::{self, Name, NodeId}; use syntax::symbol::{keywords, InternedString}; +use util::nodemap::FxHashSet; use serialize; @@ -929,19 +930,27 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } } - pub fn is_uninhabited(&self, _cx: TyCtxt) -> bool { - // FIXME(#24885): be smarter here, the AdtDefData::is_empty method could easily be made - // more complete. + /// 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 { + 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, _) => def.is_empty(), + TyAdt(def, substs) => { + def.is_uninhabited_recurse(visited, block, cx, substs) + }, - // FIXME(canndrew): There's no reason why these can't be uncommented, they're tested - // and they don't break anything. But I'm keeping my changes small for now. - //TyNever => true, - //TyTuple(ref tys) => tys.iter().any(|ty| ty.is_uninhabited(cx)), + 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), - // FIXME(canndrew): this line breaks core::fmt - //TyRef(_, ref tm) => tm.ty.is_uninhabited(cx), _ => false, } } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index f63a27e0d7563..3c94d7d6fd5d1 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -204,7 +204,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { // 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(self.tcx) { + 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 {} \