From 0d43932235f1422dd1e97b899ab151e83c6d6946 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 25 Nov 2024 18:44:41 +0200 Subject: [PATCH] Fix a stack overflow when computing the sizedness of a struct that includes itself as the tail field --- crates/hir-ty/src/infer/unify.rs | 36 ++++++++++++++++++++++--------- crates/hir-ty/src/tests/traits.rs | 21 ++++++++++++++++++ 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index e4881d7520..54aa18ce20 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -916,6 +916,32 @@ impl<'a> InferenceTable<'a> { /// Check if given type is `Sized` or not pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool { + let mut ty = ty.clone(); + { + let mut structs = SmallVec::<[_; 8]>::new(); + // Must use a loop here and not recursion because otherwise users will conduct completely + // artificial examples of structs that have themselves as the tail field and complain r-a crashes. + while let Some((AdtId::StructId(id), subst)) = ty.as_adt() { + let struct_data = self.db.struct_data(id); + if let Some((last_field, _)) = struct_data.variant_data.fields().iter().next_back() + { + let last_field_ty = self.db.field_types(id.into())[last_field] + .clone() + .substitute(Interner, subst); + if structs.contains(&ty) { + // A struct recursively contains itself as a tail field somewhere. + return true; // Don't overload the users with too many errors. + } + structs.push(ty); + // Structs can have DST as its last field and such cases are not handled + // as unsized by the chalk, so we do this manually. + ty = last_field_ty; + } else { + break; + }; + } + } + // Early return for some obvious types if matches!( ty.kind(Interner), @@ -930,16 +956,6 @@ impl<'a> InferenceTable<'a> { return true; } - if let Some((AdtId::StructId(id), subst)) = ty.as_adt() { - let struct_data = self.db.struct_data(id); - if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() { - let last_field_ty = - self.db.field_types(id.into())[last_field].clone().substitute(Interner, subst); - // Structs can have DST as its last field and such cases are not handled - // as unsized by the chalk, so we do this manually - return self.is_sized(&last_field_ty); - } - } let Some(sized) = self .db .lang_item(self.trait_env.krate, LangItem::Sized) diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 9b982a124e..624148cab2 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4790,3 +4790,24 @@ fn allowed3(baz: impl Baz>) {} "#]], ) } + +#[test] +fn recursive_tail_sized() { + check_infer( + r#" +struct WeirdFoo(WeirdBar); +struct WeirdBar(WeirdFoo); + +fn bar(v: *const ()) { + let _ = v as *const WeirdFoo; +} + "#, + expect![[r#" + 62..63 'v': *const () + 76..113 '{ ...Foo; }': () + 86..87 '_': *const WeirdFoo + 90..91 'v': *const () + 90..110 'v as *...irdFoo': *const WeirdFoo + "#]], + ); +}