Skip to content

Commit 282fa87

Browse files
committed
Auto merge of #39680 - canndrew:uninhabited_from-infinite-loop, r=arielb1
Add recursion limit to inhabitedness check Fixes #39489. Add test aswell.
2 parents 410d807 + 347bc77 commit 282fa87

File tree

5 files changed

+68
-22
lines changed

5 files changed

+68
-22
lines changed

src/librustc/ty/inhabitedness/mod.rs

+37-16
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};
@@ -66,27 +66,21 @@ impl<'a, 'gcx, 'tcx> AdtDef {
6666
/// Calculate the forest of DefIds from which this adt is visibly uninhabited.
6767
pub fn uninhabited_from(
6868
&self,
69-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
69+
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
7070
tcx: TyCtxt<'a, 'gcx, 'tcx>,
7171
substs: &'tcx Substs<'tcx>) -> DefIdForest
7272
{
73-
if !visited.insert((self.did, substs)) {
74-
return DefIdForest::empty();
75-
}
76-
77-
let ret = DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
73+
DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
7874
v.uninhabited_from(visited, tcx, substs, self.adt_kind())
79-
}));
80-
visited.remove(&(self.did, substs));
81-
ret
75+
}))
8276
}
8377
}
8478

8579
impl<'a, 'gcx, 'tcx> VariantDef {
8680
/// Calculate the forest of DefIds from which this variant is visibly uninhabited.
8781
pub fn uninhabited_from(
8882
&self,
89-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
83+
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
9084
tcx: TyCtxt<'a, 'gcx, 'tcx>,
9185
substs: &'tcx Substs<'tcx>,
9286
adt_kind: AdtKind) -> DefIdForest
@@ -115,12 +109,14 @@ impl<'a, 'gcx, 'tcx> FieldDef {
115109
/// Calculate the forest of DefIds from which this field is visibly uninhabited.
116110
pub fn uninhabited_from(
117111
&self,
118-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
112+
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
119113
tcx: TyCtxt<'a, 'gcx, 'tcx>,
120114
substs: &'tcx Substs<'tcx>,
121115
is_enum: bool) -> DefIdForest
122116
{
123-
let mut data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(visited, tcx);
117+
let mut data_uninhabitedness = move || {
118+
self.ty(tcx, substs).uninhabited_from(visited, tcx)
119+
};
124120
// FIXME(canndrew): Currently enum fields are (incorrectly) stored with
125121
// Visibility::Invisible so we need to override self.vis if we're
126122
// dealing with an enum.
@@ -144,7 +140,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
144140
/// Calculate the forest of DefIds from which this type is visibly uninhabited.
145141
pub fn uninhabited_from(
146142
&self,
147-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
143+
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
148144
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
149145
{
150146
match tcx.lift_to_global(&self) {
@@ -169,12 +165,37 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
169165

170166
fn uninhabited_from_inner(
171167
&self,
172-
visited: &mut FxHashSet<(DefId, &'tcx Substs<'tcx>)>,
168+
visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
173169
tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
174170
{
175171
match self.sty {
176172
TyAdt(def, substs) => {
177-
def.uninhabited_from(visited, 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
178199
},
179200

180201
TyNever => DefIdForest::full(tcx),

src/librustc/ty/sty.rs

+2-2
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,7 +1018,7 @@ 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();
1021+
let mut visited = FxHashMap::default();
10221022
let forest = self.uninhabited_from(&mut visited, tcx);
10231023

10241024
// To check whether this type is uninhabited at all (not just from the

src/librustc_const_eval/_match.rs

+2-2
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,7 +404,7 @@ 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();
407+
let mut visited = FxHashMap::default();
408408
let forest = v.uninhabited_from(&mut visited,
409409
cx.tcx, substs,
410410
AdtKind::Enum);

src/librustc_mir/build/matches/simplify.rs

+2-2
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,7 +102,7 @@ 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();
105+
let mut visited = FxHashMap::default();
106106
let node_set = v.uninhabited_from(&mut visited,
107107
self.hir.tcx(),
108108
substs,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// error-pattern:reached recursion limit
12+
13+
#![feature(never_type)]
14+
15+
struct Foo<'a, T: 'a> {
16+
ph: std::marker::PhantomData<T>,
17+
foo: &'a Foo<'a, (T, T)>,
18+
}
19+
20+
fn wub(f: Foo<!>) {
21+
match f {}
22+
}
23+
24+
fn main() {}
25+

0 commit comments

Comments
 (0)