Skip to content

Commit 1978168

Browse files
committed
Detect cycles in InhabitedPredicate::apply
This is for post-monomorphization cycles. These are only caught later (in drop elaboration for the example that I saw), so we need to handle them here. This issue wasn't noticed before because exhaustiveness only checked inhabitedness when `exhaustive_patterns` was on. The preceding commit now check inhabitedness always, which revealed the problem.
1 parent 2186f98 commit 1978168

File tree

2 files changed

+31
-10
lines changed

2 files changed

+31
-10
lines changed

Diff for: compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs

+30-10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use smallvec::SmallVec;
2+
13
use crate::ty::context::TyCtxt;
24
use crate::ty::{self, DefId, ParamEnv, Ty};
35

@@ -31,27 +33,31 @@ impl<'tcx> InhabitedPredicate<'tcx> {
3133
/// Returns true if the corresponding type is inhabited in the given
3234
/// `ParamEnv` and module
3335
pub fn apply(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, module_def_id: DefId) -> bool {
34-
let Ok(result) = self
35-
.apply_inner::<!>(tcx, param_env, &|id| Ok(tcx.is_descendant_of(module_def_id, id)));
36+
let Ok(result) = self.apply_inner::<!>(tcx, param_env, &mut Default::default(), &|id| {
37+
Ok(tcx.is_descendant_of(module_def_id, id))
38+
});
3639
result
3740
}
3841

3942
/// Same as `apply`, but returns `None` if self contains a module predicate
4043
pub fn apply_any_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
41-
self.apply_inner(tcx, param_env, &|_| Err(())).ok()
44+
self.apply_inner(tcx, param_env, &mut Default::default(), &|_| Err(())).ok()
4245
}
4346

4447
/// Same as `apply`, but `NotInModule(_)` predicates yield `false`. That is,
4548
/// privately uninhabited types are considered always uninhabited.
4649
pub fn apply_ignore_module(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> bool {
47-
let Ok(result) = self.apply_inner::<!>(tcx, param_env, &|_| Ok(true));
50+
let Ok(result) =
51+
self.apply_inner::<!>(tcx, param_env, &mut Default::default(), &|_| Ok(true));
4852
result
4953
}
5054

51-
fn apply_inner<E>(
55+
#[instrument(level = "debug", skip(tcx, param_env, in_module), ret)]
56+
fn apply_inner<E: std::fmt::Debug>(
5257
self,
5358
tcx: TyCtxt<'tcx>,
5459
param_env: ParamEnv<'tcx>,
60+
eval_stack: &mut SmallVec<[Ty<'tcx>; 1]>, // for cycle detection
5561
in_module: &impl Fn(DefId) -> Result<bool, E>,
5662
) -> Result<bool, E> {
5763
match self {
@@ -71,11 +77,25 @@ impl<'tcx> InhabitedPredicate<'tcx> {
7177
match normalized_pred {
7278
// We don't have more information than we started with, so consider inhabited.
7379
Self::GenericType(_) => Ok(true),
74-
pred => pred.apply_inner(tcx, param_env, in_module),
80+
pred => {
81+
// A type which is cyclic when monomorphized can happen here since the
82+
// layout error would only trigger later. See e.g. `tests/ui/sized/recursive-type-2.rs`.
83+
if eval_stack.contains(&t) {
84+
return Ok(true); // Recover; this will error later.
85+
}
86+
eval_stack.push(t);
87+
let ret = pred.apply_inner(tcx, param_env, eval_stack, in_module);
88+
eval_stack.pop();
89+
ret
90+
}
7591
}
7692
}
77-
Self::And([a, b]) => try_and(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
78-
Self::Or([a, b]) => try_or(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
93+
Self::And([a, b]) => {
94+
try_and(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module))
95+
}
96+
Self::Or([a, b]) => {
97+
try_or(a, b, |x| x.apply_inner(tcx, param_env, eval_stack, in_module))
98+
}
7999
}
80100
}
81101

@@ -197,7 +217,7 @@ impl<'tcx> InhabitedPredicate<'tcx> {
197217

198218
// this is basically like `f(a)? && f(b)?` but different in the case of
199219
// `Ok(false) && Err(_) -> Ok(false)`
200-
fn try_and<T, E>(a: T, b: T, f: impl Fn(T) -> Result<bool, E>) -> Result<bool, E> {
220+
fn try_and<T, E>(a: T, b: T, mut f: impl FnMut(T) -> Result<bool, E>) -> Result<bool, E> {
201221
let a = f(a);
202222
if matches!(a, Ok(false)) {
203223
return Ok(false);
@@ -209,7 +229,7 @@ fn try_and<T, E>(a: T, b: T, f: impl Fn(T) -> Result<bool, E>) -> Result<bool, E
209229
}
210230
}
211231

212-
fn try_or<T, E>(a: T, b: T, f: impl Fn(T) -> Result<bool, E>) -> Result<bool, E> {
232+
fn try_or<T, E>(a: T, b: T, mut f: impl FnMut(T) -> Result<bool, E>) -> Result<bool, E> {
213233
let a = f(a);
214234
if matches!(a, Ok(true)) {
215235
return Ok(true);

Diff for: compiler/rustc_middle/src/ty/inhabitedness/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ impl<'tcx> VariantDef {
103103
}
104104

105105
impl<'tcx> Ty<'tcx> {
106+
#[instrument(level = "debug", skip(tcx), ret)]
106107
pub fn inhabited_predicate(self, tcx: TyCtxt<'tcx>) -> InhabitedPredicate<'tcx> {
107108
match self.kind() {
108109
// For now, unions are always considered inhabited

0 commit comments

Comments
 (0)