about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/ty/mod.rs51
-rw-r--r--src/librustc/ty/sty.rs31
-rw-r--r--src/librustc_const_eval/check_match.rs2
3 files changed, 66 insertions, 18 deletions
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index ebf8c96def7..7982c641ede 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;
@@ -1390,6 +1390,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<NodeId>,
+                                  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 }
 
@@ -1533,11 +1549,6 @@ impl<'a, 'gcx, 'tcx, 'container> AdtDefData<'gcx, 'container> {
     }
 
     #[inline]
-    pub fn is_empty(&self) -> bool {
-        self.variants.is_empty()
-    }
-
-    #[inline]
     pub fn is_univariant(&self) -> bool {
         self.variants.len() == 1
     }
@@ -1796,6 +1807,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<NodeId>,
+                                  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,
@@ -1821,6 +1848,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<NodeId>,
+                                  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 81896ecfb53..cb3176cce10 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<NodeId>, 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<NodeId>,
+                                  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 f63a27e0d75..3c94d7d6fd5 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 {} \