diff --git a/src/librustc/ty/inhabitedness/mod.rs b/src/librustc/ty/inhabitedness/mod.rs index 18a3f1a218d85..51d046728c2bf 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 4ce1d7a901362..61b774924260d 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 7a64ff7114a7e..101c43332a326 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 e94d35195c213..f6bfed8c7ab5c 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 0000000000000..eba0da3a216aa --- /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 or the MIT license +// , 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, + foo: &'a Foo<'a, (T, T)>, +} + +fn wub(f: Foo) { + match f {} + //~^ ERROR non-exhaustive +} + +fn main() {} +