Add recursion limit to inhabitedness check
Fixes #39489. Add test aswell.
This commit is contained in:
parent
a3da24bba9
commit
2bb9c5875d
@ -62,11 +62,14 @@ mod def_id_forest;
|
|||||||
// This code should only compile in modules where the uninhabitedness of Foo is
|
// This code should only compile in modules where the uninhabitedness of Foo is
|
||||||
// visible.
|
// visible.
|
||||||
|
|
||||||
|
const ARBITRARY_RECURSION_LIMIT: u32 = 24;
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> AdtDef {
|
impl<'a, 'gcx, 'tcx> AdtDef {
|
||||||
/// Calculate the forest of DefIds from which this adt is visibly uninhabited.
|
/// Calculate the forest of DefIds from which this adt is visibly uninhabited.
|
||||||
pub fn uninhabited_from(
|
pub fn uninhabited_from(
|
||||||
&self,
|
&self,
|
||||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||||
|
recursion_depth: u32,
|
||||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
substs: &'tcx Substs<'tcx>) -> DefIdForest
|
substs: &'tcx Substs<'tcx>) -> DefIdForest
|
||||||
{
|
{
|
||||||
@ -75,7 +78,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let ret = DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
|
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));
|
visited.remove(&(self.did, substs));
|
||||||
ret
|
ret
|
||||||
@ -87,6 +90,7 @@ impl<'a, 'gcx, 'tcx> VariantDef {
|
|||||||
pub fn uninhabited_from(
|
pub fn uninhabited_from(
|
||||||
&self,
|
&self,
|
||||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||||
|
recursion_depth: u32,
|
||||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
substs: &'tcx Substs<'tcx>,
|
substs: &'tcx Substs<'tcx>,
|
||||||
adt_kind: AdtKind) -> DefIdForest
|
adt_kind: AdtKind) -> DefIdForest
|
||||||
@ -94,17 +98,17 @@ impl<'a, 'gcx, 'tcx> VariantDef {
|
|||||||
match adt_kind {
|
match adt_kind {
|
||||||
AdtKind::Union => {
|
AdtKind::Union => {
|
||||||
DefIdForest::intersection(tcx, self.fields.iter().map(|f| {
|
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 => {
|
AdtKind::Struct => {
|
||||||
DefIdForest::union(tcx, self.fields.iter().map(|f| {
|
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 => {
|
AdtKind::Enum => {
|
||||||
DefIdForest::union(tcx, self.fields.iter().map(|f| {
|
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(
|
pub fn uninhabited_from(
|
||||||
&self,
|
&self,
|
||||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||||
|
recursion_depth: u32,
|
||||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
substs: &'tcx Substs<'tcx>,
|
substs: &'tcx Substs<'tcx>,
|
||||||
is_enum: bool) -> DefIdForest
|
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
|
// FIXME(canndrew): Currently enum fields are (incorrectly) stored with
|
||||||
// Visibility::Invisible so we need to override self.vis if we're
|
// Visibility::Invisible so we need to override self.vis if we're
|
||||||
// dealing with an enum.
|
// dealing with an enum.
|
||||||
@ -145,8 +152,14 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
|
|||||||
pub fn uninhabited_from(
|
pub fn uninhabited_from(
|
||||||
&self,
|
&self,
|
||||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||||
|
mut recursion_depth: u32,
|
||||||
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
|
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
|
||||||
{
|
{
|
||||||
|
recursion_depth += 1;
|
||||||
|
if recursion_depth >= ARBITRARY_RECURSION_LIMIT {
|
||||||
|
return DefIdForest::empty();
|
||||||
|
}
|
||||||
|
|
||||||
match tcx.lift_to_global(&self) {
|
match tcx.lift_to_global(&self) {
|
||||||
Some(global_ty) => {
|
Some(global_ty) => {
|
||||||
{
|
{
|
||||||
@ -155,13 +168,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
|
|||||||
return forest.clone();
|
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();
|
let mut cache = tcx.inhabitedness_cache.borrow_mut();
|
||||||
cache.insert(global_ty, forest.clone());
|
cache.insert(global_ty, forest.clone());
|
||||||
forest
|
forest
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let forest = self.uninhabited_from_inner(visited, tcx);
|
let forest = self.uninhabited_from_inner(visited, recursion_depth, tcx);
|
||||||
forest
|
forest
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -170,28 +183,29 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
|
|||||||
fn uninhabited_from_inner(
|
fn uninhabited_from_inner(
|
||||||
&self,
|
&self,
|
||||||
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
|
||||||
|
recursion_depth: u32,
|
||||||
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
|
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
|
||||||
{
|
{
|
||||||
match self.sty {
|
match self.sty {
|
||||||
TyAdt(def, substs) => {
|
TyAdt(def, substs) => {
|
||||||
def.uninhabited_from(visited, tcx, substs)
|
def.uninhabited_from(visited, recursion_depth, tcx, substs)
|
||||||
},
|
},
|
||||||
|
|
||||||
TyNever => DefIdForest::full(tcx),
|
TyNever => DefIdForest::full(tcx),
|
||||||
TyTuple(ref tys, _) => {
|
TyTuple(ref tys, _) => {
|
||||||
DefIdForest::union(tcx, tys.iter().map(|ty| {
|
DefIdForest::union(tcx, tys.iter().map(|ty| {
|
||||||
ty.uninhabited_from(visited, tcx)
|
ty.uninhabited_from(visited, recursion_depth, tcx)
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
TyArray(ty, len) => {
|
TyArray(ty, len) => {
|
||||||
if len == 0 {
|
if len == 0 {
|
||||||
DefIdForest::empty()
|
DefIdForest::empty()
|
||||||
} else {
|
} else {
|
||||||
ty.uninhabited_from(visited, tcx)
|
ty.uninhabited_from(visited, recursion_depth, tcx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TyRef(_, ref tm) => {
|
TyRef(_, ref tm) => {
|
||||||
tm.ty.uninhabited_from(visited, tcx)
|
tm.ty.uninhabited_from(visited, recursion_depth, tcx)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => DefIdForest::empty(),
|
_ => DefIdForest::empty(),
|
||||||
|
@ -1019,7 +1019,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
|
|||||||
/// visible.
|
/// visible.
|
||||||
pub fn is_uninhabited_from(&self, module: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
|
pub fn is_uninhabited_from(&self, module: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
|
||||||
let mut visited = FxHashSet::default();
|
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
|
// To check whether this type is uninhabited at all (not just from the
|
||||||
// given node) you could check whether the forest is empty.
|
// given node) you could check whether the forest is empty.
|
||||||
|
@ -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 => {
|
ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
|
||||||
def.variants.iter().filter_map(|v| {
|
def.variants.iter().filter_map(|v| {
|
||||||
let mut visited = FxHashSet::default();
|
let mut visited = FxHashSet::default();
|
||||||
let forest = v.uninhabited_from(&mut visited,
|
let forest = v.uninhabited_from(&mut visited, 0,
|
||||||
cx.tcx, substs,
|
cx.tcx, substs,
|
||||||
AdtKind::Enum);
|
AdtKind::Enum);
|
||||||
if forest.contains(cx.tcx, cx.module)
|
if forest.contains(cx.tcx, cx.module)
|
||||||
|
@ -103,7 +103,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
|||||||
let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| {
|
let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| {
|
||||||
i == variant_index || {
|
i == variant_index || {
|
||||||
let mut visited = FxHashSet::default();
|
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(),
|
self.hir.tcx(),
|
||||||
substs,
|
substs,
|
||||||
adt_def.adt_kind());
|
adt_def.adt_kind());
|
||||||
|
24
src/test/compile-fail/inhabitedness-infinite-loop.rs
Normal file
24
src/test/compile-fail/inhabitedness-infinite-loop.rs
Normal file
@ -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() {}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user