Auto merge of #14426 - HKalbasi:master, r=HKalbasi
fix stack overflow in `is_ty_uninhabited_from` fix #14421
This commit is contained in:
commit
e7337fc9c3
@ -9,6 +9,7 @@ use hir_def::{
|
|||||||
adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup,
|
adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup,
|
||||||
ModuleId, VariantId,
|
ModuleId, VariantId,
|
||||||
};
|
};
|
||||||
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
|
consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
|
||||||
@ -16,7 +17,8 @@ use crate::{
|
|||||||
|
|
||||||
/// Checks whether a type is visibly uninhabited from a particular module.
|
/// Checks whether a type is visibly uninhabited from a particular module.
|
||||||
pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
|
pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
|
||||||
let mut uninhabited_from = UninhabitedFrom { target_mod, db };
|
let mut uninhabited_from =
|
||||||
|
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
|
||||||
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
|
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
|
||||||
inhabitedness == BREAK_VISIBLY_UNINHABITED
|
inhabitedness == BREAK_VISIBLY_UNINHABITED
|
||||||
}
|
}
|
||||||
@ -32,7 +34,8 @@ pub(crate) fn is_enum_variant_uninhabited_from(
|
|||||||
let vars_attrs = db.variants_attrs(variant.parent);
|
let vars_attrs = db.variants_attrs(variant.parent);
|
||||||
let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate();
|
let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate();
|
||||||
|
|
||||||
let mut uninhabited_from = UninhabitedFrom { target_mod, db };
|
let mut uninhabited_from =
|
||||||
|
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
|
||||||
let inhabitedness = uninhabited_from.visit_variant(
|
let inhabitedness = uninhabited_from.visit_variant(
|
||||||
variant.into(),
|
variant.into(),
|
||||||
&enum_data.variants[variant.local_id].variant_data,
|
&enum_data.variants[variant.local_id].variant_data,
|
||||||
@ -45,6 +48,9 @@ pub(crate) fn is_enum_variant_uninhabited_from(
|
|||||||
|
|
||||||
struct UninhabitedFrom<'a> {
|
struct UninhabitedFrom<'a> {
|
||||||
target_mod: ModuleId,
|
target_mod: ModuleId,
|
||||||
|
recursive_ty: FxHashSet<Ty>,
|
||||||
|
// guard for preventing stack overflow in non trivial non terminating types
|
||||||
|
max_depth: usize,
|
||||||
db: &'a dyn HirDatabase,
|
db: &'a dyn HirDatabase,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +71,14 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
|
|||||||
ty: &Ty,
|
ty: &Ty,
|
||||||
outer_binder: DebruijnIndex,
|
outer_binder: DebruijnIndex,
|
||||||
) -> ControlFlow<VisiblyUninhabited> {
|
) -> ControlFlow<VisiblyUninhabited> {
|
||||||
match ty.kind(Interner) {
|
if self.recursive_ty.contains(ty) || self.max_depth == 0 {
|
||||||
|
// rustc considers recursive types always inhabited. I think it is valid to consider
|
||||||
|
// recursive types as always uninhabited, but we should do what rustc is doing.
|
||||||
|
return CONTINUE_OPAQUELY_INHABITED;
|
||||||
|
}
|
||||||
|
self.recursive_ty.insert(ty.clone());
|
||||||
|
self.max_depth -= 1;
|
||||||
|
let r = match ty.kind(Interner) {
|
||||||
TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
|
TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
|
||||||
TyKind::Never => BREAK_VISIBLY_UNINHABITED,
|
TyKind::Never => BREAK_VISIBLY_UNINHABITED,
|
||||||
TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
|
TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
|
||||||
@ -75,7 +88,10 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
|
|||||||
},
|
},
|
||||||
|
|
||||||
TyKind::Ref(..) | _ => CONTINUE_OPAQUELY_INHABITED,
|
TyKind::Ref(..) | _ => CONTINUE_OPAQUELY_INHABITED,
|
||||||
}
|
};
|
||||||
|
self.recursive_ty.remove(ty);
|
||||||
|
self.max_depth += 1;
|
||||||
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interner(&self) -> Interner {
|
fn interner(&self) -> Interner {
|
||||||
|
@ -692,6 +692,41 @@ fn f(inp: (Foo, Foo, Foo, Foo)) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// FIXME: We should have tests for `is_ty_uninhabited_from`
|
||||||
|
fn regression_14421() {
|
||||||
|
check_diagnostics(
|
||||||
|
r#"
|
||||||
|
pub enum Tree {
|
||||||
|
Node(TreeNode),
|
||||||
|
Leaf(TreeLeaf),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Box<T>(&T);
|
||||||
|
|
||||||
|
pub struct TreeNode {
|
||||||
|
pub depth: usize,
|
||||||
|
pub children: [Box<Tree>; 8]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TreeLeaf {
|
||||||
|
pub depth: usize,
|
||||||
|
pub data: u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test() {
|
||||||
|
let mut tree = Tree::Leaf(
|
||||||
|
//^^^^^^^^ 💡 weak: variable does not need to be mutable
|
||||||
|
TreeLeaf {
|
||||||
|
depth: 0,
|
||||||
|
data: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fn_traits() {
|
fn fn_traits() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user