Skip to content

Add recursion limit to inhabitedness check #39680

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 12, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 25 additions & 11 deletions src/librustc/ty/inhabitedness/mod.rs
Original file line number Diff line number Diff line change
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We normally use the tcx.sess.recursion_limit and emit a fatal error when it is exceeded (grep for that for usage examples).

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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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() {}