Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #18559 from ChayimFriedman2/recur-unsized
fix: Fix a stack overflow when computing the sizedness of a struct that includes itself as the tail field
Laurențiu Nicola 2024-11-26
parent 327ab29 · parent 0d43932 · commit 157c7d0
-rw-r--r--crates/hir-ty/src/infer/unify.rs36
-rw-r--r--crates/hir-ty/src/tests/traits.rs21
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<Assoc = Qux<impl Foo>>) {}
"#]],
)
}
+
+#[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
+ "#]],
+ );
+}