Skip to content

Commit d5c7ec4

Browse files
committed
fix stack overflow in is_ty_uninhabited_from
1 parent a375ad6 commit d5c7ec4

File tree

2 files changed

+59
-5
lines changed

2 files changed

+59
-5
lines changed

crates/hir-ty/src/inhabitedness.rs

+24-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//! Type inhabitedness logic.
2-
use std::ops::ControlFlow::{self, Break, Continue};
2+
use std::{
3+
collections::HashMap,
4+
ops::ControlFlow::{self, Break, Continue},
5+
};
36

47
use chalk_ir::{
58
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
@@ -9,14 +12,16 @@ use hir_def::{
912
adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup,
1013
ModuleId, VariantId,
1114
};
15+
use rustc_hash::FxHashMap;
1216

1317
use crate::{
1418
consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
1519
};
1620

1721
/// Checks whether a type is visibly uninhabited from a particular module.
1822
pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
19-
let mut uninhabited_from = UninhabitedFrom { target_mod, db };
23+
let mut uninhabited_from =
24+
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: HashMap::default() };
2025
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
2126
inhabitedness == BREAK_VISIBLY_UNINHABITED
2227
}
@@ -32,7 +37,8 @@ pub(crate) fn is_enum_variant_uninhabited_from(
3237
let vars_attrs = db.variants_attrs(variant.parent);
3338
let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate();
3439

35-
let mut uninhabited_from = UninhabitedFrom { target_mod, db };
40+
let mut uninhabited_from =
41+
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: HashMap::default() };
3642
let inhabitedness = uninhabited_from.visit_variant(
3743
variant.into(),
3844
&enum_data.variants[variant.local_id].variant_data,
@@ -45,6 +51,9 @@ pub(crate) fn is_enum_variant_uninhabited_from(
4551

4652
struct UninhabitedFrom<'a> {
4753
target_mod: ModuleId,
54+
recursive_ty: FxHashMap<Ty, ()>,
55+
// guard for preventing stack overflow in non trivial non terminating types
56+
max_depth: usize,
4857
db: &'a dyn HirDatabase,
4958
}
5059

@@ -65,7 +74,14 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
6574
ty: &Ty,
6675
outer_binder: DebruijnIndex,
6776
) -> ControlFlow<VisiblyUninhabited> {
68-
match ty.kind(Interner) {
77+
if self.recursive_ty.contains_key(ty) || self.max_depth == 0 {
78+
// rustc considers recursive types always inhabited. I think it is valid to consider
79+
// recursive types as always uninhabited, but we should do what rustc is doing.
80+
return CONTINUE_OPAQUELY_INHABITED;
81+
}
82+
self.recursive_ty.insert(ty.clone(), ());
83+
self.max_depth -= 1;
84+
let r = match ty.kind(Interner) {
6985
TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
7086
TyKind::Never => BREAK_VISIBLY_UNINHABITED,
7187
TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
@@ -75,7 +91,10 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
7591
},
7692

7793
TyKind::Ref(..) | _ => CONTINUE_OPAQUELY_INHABITED,
78-
}
94+
};
95+
self.recursive_ty.remove(ty);
96+
self.max_depth += 1;
97+
r
7998
}
8099

81100
fn interner(&self) -> Interner {

crates/ide-diagnostics/src/handlers/mutability_errors.rs

+35
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,41 @@ fn f(inp: (Foo, Foo, Foo, Foo)) {
692692
);
693693
}
694694

695+
#[test]
696+
// FIXME: We should have tests for `is_ty_uninhabited_from`
697+
fn regression_14421() {
698+
check_diagnostics(
699+
r#"
700+
pub enum Tree {
701+
Node(TreeNode),
702+
Leaf(TreeLeaf),
703+
}
704+
705+
struct Box<T>(&T);
706+
707+
pub struct TreeNode {
708+
pub depth: usize,
709+
pub children: [Box<Tree>; 8]
710+
}
711+
712+
pub struct TreeLeaf {
713+
pub depth: usize,
714+
pub data: u8
715+
}
716+
717+
pub fn test() {
718+
let mut tree = Tree::Leaf(
719+
//^^^^^^^^ 💡 weak: variable does not need to be mutable
720+
TreeLeaf {
721+
depth: 0,
722+
data: 0
723+
}
724+
);
725+
}
726+
"#,
727+
);
728+
}
729+
695730
#[test]
696731
fn fn_traits() {
697732
check_diagnostics(

0 commit comments

Comments
 (0)