Skip to content

Commit 347bc77

Browse files
committed
Use global recursion limit when evaluating inhabitedness
1 parent 2bb9c58 commit 347bc77

File tree

5 files changed

+55
-47
lines changed

5 files changed

+55
-47
lines changed

src/librustc/ty/inhabitedness/mod.rs

+44-37
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use util::nodemap::FxHashSet;
11+
use util::nodemap::{FxHashMap, FxHashSet};
1212
use ty::context::TyCtxt;
1313
use ty::{AdtDef, VariantDef, FieldDef, TyS};
1414
use ty::{DefId, Substs};
@@ -62,53 +62,43 @@ mod def_id_forest;
6262
// This code should only compile in modules where the uninhabitedness of Foo is
6363
// visible.
6464

65-
const ARBITRARY_RECURSION_LIMIT: u32 = 24;
66-
6765
impl<'a, 'gcx, 'tcx> AdtDef {
6866
/// Calculate the forest of DefIds from which this adt is visibly uninhabited.
6967
pub fn uninhabited_from(
7068
&self,
71-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
72-
recursion_depth: u32,
69+
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
7370
tcx: TyCtxt<'a, 'gcx, 'tcx>,
7471
substs: &'tcx Substs<'tcx>) -> DefIdForest
7572
{
76-
if !visited.insert((self.did, substs)) {
77-
return DefIdForest::empty();
78-
}
79-
80-
let ret = DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
81-
v.uninhabited_from(visited, recursion_depth, tcx, substs, self.adt_kind())
82-
}));
83-
visited.remove(&(self.did, substs));
84-
ret
73+
DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
74+
v.uninhabited_from(visited, tcx, substs, self.adt_kind())
75+
}))
8576
}
8677
}
8778

8879
impl<'a, 'gcx, 'tcx> VariantDef {
8980
/// Calculate the forest of DefIds from which this variant is visibly uninhabited.
9081
pub fn uninhabited_from(
9182
&self,
92-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
93-
recursion_depth: u32,
83+
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
9484
tcx: TyCtxt<'a, 'gcx, 'tcx>,
9585
substs: &'tcx Substs<'tcx>,
9686
adt_kind: AdtKind) -> DefIdForest
9787
{
9888
match adt_kind {
9989
AdtKind::Union => {
10090
DefIdForest::intersection(tcx, self.fields.iter().map(|f| {
101-
f.uninhabited_from(visited, recursion_depth, tcx, substs, false)
91+
f.uninhabited_from(visited, tcx, substs, false)
10292
}))
10393
},
10494
AdtKind::Struct => {
10595
DefIdForest::union(tcx, self.fields.iter().map(|f| {
106-
f.uninhabited_from(visited, recursion_depth, tcx, substs, false)
96+
f.uninhabited_from(visited, tcx, substs, false)
10797
}))
10898
},
10999
AdtKind::Enum => {
110100
DefIdForest::union(tcx, self.fields.iter().map(|f| {
111-
f.uninhabited_from(visited, recursion_depth, tcx, substs, true)
101+
f.uninhabited_from(visited, tcx, substs, true)
112102
}))
113103
},
114104
}
@@ -119,14 +109,13 @@ impl<'a, 'gcx, 'tcx> FieldDef {
119109
/// Calculate the forest of DefIds from which this field is visibly uninhabited.
120110
pub fn uninhabited_from(
121111
&self,
122-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
123-
recursion_depth: u32,
112+
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
124113
tcx: TyCtxt<'a, 'gcx, 'tcx>,
125114
substs: &'tcx Substs<'tcx>,
126115
is_enum: bool) -> DefIdForest
127116
{
128117
let mut data_uninhabitedness = move || {
129-
self.ty(tcx, substs).uninhabited_from(visited, recursion_depth, tcx)
118+
self.ty(tcx, substs).uninhabited_from(visited, tcx)
130119
};
131120
// FIXME(canndrew): Currently enum fields are (incorrectly) stored with
132121
// Visibility::Invisible so we need to override self.vis if we're
@@ -151,15 +140,9 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
151140
/// Calculate the forest of DefIds from which this type is visibly uninhabited.
152141
pub fn uninhabited_from(
153142
&self,
154-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
155-
mut recursion_depth: u32,
143+
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
156144
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
157145
{
158-
recursion_depth += 1;
159-
if recursion_depth >= ARBITRARY_RECURSION_LIMIT {
160-
return DefIdForest::empty();
161-
}
162-
163146
match tcx.lift_to_global(&self) {
164147
Some(global_ty) => {
165148
{
@@ -168,44 +151,68 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
168151
return forest.clone();
169152
}
170153
}
171-
let forest = global_ty.uninhabited_from_inner(visited, recursion_depth, tcx);
154+
let forest = global_ty.uninhabited_from_inner(visited, tcx);
172155
let mut cache = tcx.inhabitedness_cache.borrow_mut();
173156
cache.insert(global_ty, forest.clone());
174157
forest
175158
},
176159
None => {
177-
let forest = self.uninhabited_from_inner(visited, recursion_depth, tcx);
160+
let forest = self.uninhabited_from_inner(visited, tcx);
178161
forest
179162
},
180163
}
181164
}
182165

183166
fn uninhabited_from_inner(
184167
&self,
185-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
186-
recursion_depth: u32,
168+
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
187169
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
188170
{
189171
match self.sty {
190172
TyAdt(def, substs) => {
191-
def.uninhabited_from(visited, recursion_depth, tcx, substs)
173+
{
174+
let mut substs_set = visited.entry(def.did).or_insert(FxHashSet::default());
175+
if !substs_set.insert(substs) {
176+
// We are already calculating the inhabitedness of this type.
177+
// The type must contain a reference to itself. Break the
178+
// infinite loop.
179+
return DefIdForest::empty();
180+
}
181+
if substs_set.len() >= tcx.sess.recursion_limit.get() / 4 {
182+
// We have gone very deep, reinstantiating this ADT inside
183+
// itself with different type arguments. We are probably
184+
// hitting an infinite loop. For example, it's possible to write:
185+
// a type Foo<T>
186+
// which contains a Foo<(T, T)>
187+
// which contains a Foo<((T, T), (T, T))>
188+
// which contains a Foo<(((T, T), (T, T)), ((T, T), (T, T)))>
189+
// etc.
190+
let error = format!("reached recursion limit while checking
191+
inhabitedness of `{}`", self);
192+
tcx.sess.fatal(&error);
193+
}
194+
}
195+
let ret = def.uninhabited_from(visited, tcx, substs);
196+
let mut substs_set = visited.get_mut(&def.did).unwrap();
197+
substs_set.remove(substs);
198+
ret
192199
},
193200

194201
TyNever => DefIdForest::full(tcx),
195202
TyTuple(ref tys, _) => {
196203
DefIdForest::union(tcx, tys.iter().map(|ty| {
197-
ty.uninhabited_from(visited, recursion_depth, tcx)
204+
ty.uninhabited_from(visited, tcx)
198205
}))
199206
},
200207
TyArray(ty, len) => {
201208
if len == 0 {
202209
DefIdForest::empty()
203210
} else {
204-
ty.uninhabited_from(visited, recursion_depth, tcx)
211+
ty.uninhabited_from(visited, tcx)
205212
}
206213
}
207214
TyRef(_, ref tm) => {
208-
tm.ty.uninhabited_from(visited, recursion_depth, tcx)
215+
tm.ty.uninhabited_from(visited, tcx)
209216
}
210217

211218
_ => DefIdForest::empty(),

src/librustc/ty/sty.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use std::cmp::Ordering;
2424
use syntax::abi;
2525
use syntax::ast::{self, Name};
2626
use syntax::symbol::{keywords, InternedString};
27-
use util::nodemap::FxHashSet;
27+
use util::nodemap::FxHashMap;
2828

2929
use serialize;
3030

@@ -1018,8 +1018,8 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
10181018
/// This code should only compile in modules where the uninhabitedness of Foo is
10191019
/// visible.
10201020
pub fn is_uninhabited_from(&self, module: DefId, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
1021-
let mut visited = FxHashSet::default();
1022-
let forest = self.uninhabited_from(&mut visited, 0, tcx);
1021+
let mut visited = FxHashMap::default();
1022+
let forest = self.uninhabited_from(&mut visited, tcx);
10231023

10241024
// To check whether this type is uninhabited at all (not just from the
10251025
// given node) you could check whether the forest is empty.

src/librustc_const_eval/_match.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use eval::{compare_const_vals};
1717

1818
use rustc_const_math::ConstInt;
1919

20-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
20+
use rustc_data_structures::fx::FxHashMap;
2121
use rustc_data_structures::indexed_vec::Idx;
2222

2323
use pattern::{FieldPattern, Pattern, PatternKind};
@@ -404,8 +404,8 @@ fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
404404
}
405405
ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
406406
def.variants.iter().filter_map(|v| {
407-
let mut visited = FxHashSet::default();
408-
let forest = v.uninhabited_from(&mut visited, 0,
407+
let mut visited = FxHashMap::default();
408+
let forest = v.uninhabited_from(&mut visited,
409409
cx.tcx, substs,
410410
AdtKind::Enum);
411411
if forest.contains(cx.tcx, cx.module)

src/librustc_mir/build/matches/simplify.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use build::{BlockAnd, BlockAndExtension, Builder};
2626
use build::matches::{Binding, MatchPair, Candidate};
2727
use hair::*;
2828
use rustc::mir::*;
29-
use rustc_data_structures::fx::FxHashSet;
29+
use rustc_data_structures::fx::FxHashMap;
3030

3131
use std::mem;
3232

@@ -102,8 +102,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
102102
if self.hir.tcx().sess.features.borrow().never_type {
103103
let irrefutable = adt_def.variants.iter().enumerate().all(|(i, v)| {
104104
i == variant_index || {
105-
let mut visited = FxHashSet::default();
106-
let node_set = v.uninhabited_from(&mut visited, 0,
105+
let mut visited = FxHashMap::default();
106+
let node_set = v.uninhabited_from(&mut visited,
107107
self.hir.tcx(),
108108
substs,
109109
adt_def.adt_kind());

src/test/compile-fail/inhabitedness-infinite-loop.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// error-pattern:reached recursion limit
12+
1113
#![feature(never_type)]
1214

1315
struct Foo<'a, T: 'a> {
@@ -17,7 +19,6 @@ struct Foo<'a, T: 'a> {
1719

1820
fn wub(f: Foo<!>) {
1921
match f {}
20-
//~^ ERROR non-exhaustive
2122
}
2223

2324
fn main() {}

0 commit comments

Comments
 (0)