Skip to content

Commit

Permalink
Add recursion limit to inhabitedness check
Browse files Browse the repository at this point in the history
Fixes #39489.
Add test aswell.
  • Loading branch information
canndrew committed Feb 9, 2017
1 parent a3da24b commit 2bb9c58
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 14 deletions.
36 changes: 25 additions & 11 deletions src/librustc/ty/inhabitedness/mod.rs
Expand Up @@ -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
{
Expand All @@ -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
Expand All @@ -87,24 +90,25 @@ 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
{
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)
}))
},
}
Expand All @@ -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.
Expand All @@ -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) => {
{
Expand All @@ -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
},
}
Expand All @@ -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(),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/sty.rs
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_const_eval/_match.rs
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/build/matches/simplify.rs
Expand Up @@ -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());
Expand Down
24 changes: 24 additions & 0 deletions 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() {}

0 comments on commit 2bb9c58

Please sign in to comment.