Skip to content

Commit

Permalink
Fix a stack overflow when computing the sizedness of a struct that in…
Browse files Browse the repository at this point in the history
…cludes itself as the tail field
  • Loading branch information
ChayimFriedman2 committed Nov 25, 2024
1 parent cc4ffa7 commit b4a23bb
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 10 deletions.
36 changes: 26 additions & 10 deletions src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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)
Expand Down
21 changes: 21 additions & 0 deletions src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
"#]],
);
}

0 comments on commit b4a23bb

Please sign in to comment.