about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAndrew Cann <shum@canndrew.org>2017-02-09 17:34:45 +0800
committerAndrew Cann <shum@canndrew.org>2017-02-09 17:34:45 +0800
commit2bb9c5875d0ab956f7d87c60de34bc3f2a427c1e (patch)
tree83ed7f19957a8fdff80593ae94d30864e35dc714
parenta3da24bba940831697a024b93569891c77675778 (diff)
downloadrust-2bb9c5875d0ab956f7d87c60de34bc3f2a427c1e.tar.gz
rust-2bb9c5875d0ab956f7d87c60de34bc3f2a427c1e.zip
Add recursion limit to inhabitedness check
Fixes #39489.
Add test aswell.
-rw-r--r--src/librustc/ty/inhabitedness/mod.rs36
-rw-r--r--src/librustc/ty/sty.rs2
-rw-r--r--src/librustc_const_eval/_match.rs2
-rw-r--r--src/librustc_mir/build/matches/simplify.rs2
-rw-r--r--src/test/compile-fail/inhabitedness-infinite-loop.rs24
5 files changed, 52 insertions, 14 deletions
diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs
index 18a3f1a218d..51d046728c2 100644
--- a/src/librustc/ty/inhabitedness/mod.rs
+++ b/src/librustc/ty/inhabitedness/mod.rs
@@ -62,11 +62,14 @@ mod def_id_forest;
 // This code should only compile in modules where the uninhabitedness of Foo is
 // visible.
 
+const ARBITRARY_RECURSION_LIMIT: u32 = 24;
+
 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>)>,
+                recursion_depth: u32,
                 tcx: TyCtxt<'a, 'gcx, 'tcx>,
                 substs: &'tcx Substs<'tcx>) -> DefIdForest
     {
@@ -75,7 +78,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
         }
 
         let ret = DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
-            v.uninhabited_from(visited, tcx, substs, self.adt_kind())
+            v.uninhabited_from(visited, recursion_depth, tcx, substs, self.adt_kind())
         }));
         visited.remove(&(self.did, substs));
         ret
@@ -87,6 +90,7 @@ impl<'a, 'gcx, 'tcx> VariantDef {
     pub fn uninhabited_from(
                 &self,
                 visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
+                recursion_depth: u32,
                 tcx: TyCtxt<'a, 'gcx, 'tcx>,
                 substs: &'tcx Substs<'tcx>,
                 adt_kind: AdtKind) -> DefIdForest
@@ -94,17 +98,17 @@ impl<'a, 'gcx, 'tcx> VariantDef {
         match adt_kind {
             AdtKind::Union => {
                 DefIdForest::intersection(tcx, self.fields.iter().map(|f| {
-                    f.uninhabited_from(visited, tcx, substs, false)
+                    f.uninhabited_from(visited, recursion_depth, tcx, substs, false)
                 }))
             },
             AdtKind::Struct => {
                 DefIdForest::union(tcx, self.fields.iter().map(|f| {
-                    f.uninhabited_from(visited, tcx, substs, false)
+                    f.uninhabited_from(visited, recursion_depth, tcx, substs, false)
                 }))
             },
             AdtKind::Enum => {
                 DefIdForest::union(tcx, self.fields.iter().map(|f| {
-                    f.uninhabited_from(visited, tcx, substs, true)
+                    f.uninhabited_from(visited, recursion_depth, tcx, substs, true)
                 }))
             },
         }
@@ -116,11 +120,14 @@ impl<'a, 'gcx, 'tcx> FieldDef {
     pub fn uninhabited_from(
                 &self,
                 visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
+                recursion_depth: u32,
                 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);
+        let mut data_uninhabitedness = move || {
+            self.ty(tcx, substs).uninhabited_from(visited, recursion_depth, 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.
@@ -145,8 +152,14 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
     pub fn uninhabited_from(
                 &self,
                 visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
+                mut recursion_depth: u32,
                 tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
     {
+        recursion_depth += 1;
+        if recursion_depth >= ARBITRARY_RECURSION_LIMIT {
+            return DefIdForest::empty();
+        }
+
         match tcx.lift_to_global(&self) {
             Some(global_ty) => {
                 {
@@ -155,13 +168,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
                         return forest.clone();
                     }
                 }
-                let forest = global_ty.uninhabited_from_inner(visited, tcx);
+                let forest = global_ty.uninhabited_from_inner(visited, recursion_depth, 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);
+                let forest = self.uninhabited_from_inner(visited, recursion_depth, tcx);
                 forest
             },
         }
@@ -170,28 +183,29 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
     fn uninhabited_from_inner(
                 &self,
                 visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
+                recursion_depth: u32,
                 tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
     {
         match self.sty {
             TyAdt(def, substs) => {
-                def.uninhabited_from(visited, tcx, substs)
+                def.uninhabited_from(visited, recursion_depth, tcx, substs)
             },
 
             TyNever => DefIdForest::full(tcx),
             TyTuple(ref tys, _) => {
                 DefIdForest::union(tcx, tys.iter().map(|ty| {
-                    ty.uninhabited_from(visited, tcx)
+                    ty.uninhabited_from(visited, recursion_depth, tcx)
                 }))
             },
             TyArray(ty, len) => {
                 if len == 0 {
                     DefIdForest::empty()
                 } else {
-                    ty.uninhabited_from(visited, tcx)
+                    ty.uninhabited_from(visited, recursion_depth, tcx)
                 }
             }
             TyRef(_, ref tm) => {
-                tm.ty.uninhabited_from(visited, tcx)
+                tm.ty.uninhabited_from(visited, recursion_depth, tcx)
             }
 
             _ => DefIdForest::empty(),
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 4ce1d7a9013..61b77492426 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -1019,7 +1019,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
     /// 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);
+        let forest = self.uninhabited_from(&mut visited, 0, tcx);
 
         // To check whether this type is uninhabited at all (not just from the
         // given node) you could check whether the forest is empty.
diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs
index 7a64ff7114a..101c43332a3 100644
--- a/src/librustc_const_eval/_match.rs
+++ b/src/librustc_const_eval/_match.rs
@@ -405,7 +405,7 @@ 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 forest = v.uninhabited_from(&mut visited,
+                let forest = v.uninhabited_from(&mut visited, 0,
                                                 cx.tcx, substs,
                                                 AdtKind::Enum);
                 if forest.contains(cx.tcx, cx.module)
diff --git a/src/librustc_mir/build/matches/simplify.rs b/src/librustc_mir/build/matches/simplify.rs
index e94d35195c2..f6bfed8c7ab 100644
--- a/src/librustc_mir/build/matches/simplify.rs
+++ b/src/librustc_mir/build/matches/simplify.rs
@@ -103,7 +103,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                     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,
+                            let node_set = v.uninhabited_from(&mut visited, 0,
                                                               self.hir.tcx(),
                                                               substs,
                                                               adt_def.adt_kind());
diff --git a/src/test/compile-fail/inhabitedness-infinite-loop.rs b/src/test/compile-fail/inhabitedness-infinite-loop.rs
new file mode 100644
index 00000000000..eba0da3a216
--- /dev/null
+++ b/src/test/compile-fail/inhabitedness-infinite-loop.rs
@@ -0,0 +1,24 @@
+// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(never_type)]
+
+struct Foo<'a, T: 'a> {
+    ph: std::marker::PhantomData<T>,
+    foo: &'a Foo<'a, (T, T)>,
+}
+
+fn wub(f: Foo<!>) {
+    match f {}
+    //~^ ERROR non-exhaustive
+}
+
+fn main() {}
+