Skip to content

Commit e7337fc

Browse files
committed
Auto merge of #14426 - HKalbasi:master, r=HKalbasi
fix stack overflow in `is_ty_uninhabited_from` fix #14421
2 parents a375ad6 + d0d4245 commit e7337fc

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

crates/hir-ty/src/inhabitedness.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ use hir_def::{
99
adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup,
1010
ModuleId, VariantId,
1111
};
12+
use rustc_hash::FxHashSet;
1213

1314
use crate::{
1415
consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
1516
};
1617

1718
/// Checks whether a type is visibly uninhabited from a particular module.
1819
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 };
20+
let mut uninhabited_from =
21+
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
2022
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
2123
inhabitedness == BREAK_VISIBLY_UNINHABITED
2224
}
@@ -32,7 +34,8 @@ pub(crate) fn is_enum_variant_uninhabited_from(
3234
let vars_attrs = db.variants_attrs(variant.parent);
3335
let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate();
3436

35-
let mut uninhabited_from = UninhabitedFrom { target_mod, db };
37+
let mut uninhabited_from =
38+
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
3639
let inhabitedness = uninhabited_from.visit_variant(
3740
variant.into(),
3841
&enum_data.variants[variant.local_id].variant_data,
@@ -45,6 +48,9 @@ pub(crate) fn is_enum_variant_uninhabited_from(
4548

4649
struct UninhabitedFrom<'a> {
4750
target_mod: ModuleId,
51+
recursive_ty: FxHashSet<Ty>,
52+
// guard for preventing stack overflow in non trivial non terminating types
53+
max_depth: usize,
4854
db: &'a dyn HirDatabase,
4955
}
5056

@@ -65,7 +71,14 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
6571
ty: &Ty,
6672
outer_binder: DebruijnIndex,
6773
) -> ControlFlow<VisiblyUninhabited> {
68-
match ty.kind(Interner) {
74+
if self.recursive_ty.contains(ty) || self.max_depth == 0 {
75+
// rustc considers recursive types always inhabited. I think it is valid to consider
76+
// recursive types as always uninhabited, but we should do what rustc is doing.
77+
return CONTINUE_OPAQUELY_INHABITED;
78+
}
79+
self.recursive_ty.insert(ty.clone());
80+
self.max_depth -= 1;
81+
let r = match ty.kind(Interner) {
6982
TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
7083
TyKind::Never => BREAK_VISIBLY_UNINHABITED,
7184
TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
@@ -75,7 +88,10 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
7588
},
7689

7790
TyKind::Ref(..) | _ => CONTINUE_OPAQUELY_INHABITED,
78-
}
91+
};
92+
self.recursive_ty.remove(ty);
93+
self.max_depth += 1;
94+
r
7995
}
8096

8197
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)