Skip to content

Commit bbce06e

Browse files
authored
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
2 parents b250e0f + b4a23bb commit bbce06e

File tree

2 files changed

+47
-10
lines changed

2 files changed

+47
-10
lines changed

src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,32 @@ impl<'a> InferenceTable<'a> {
916916

917917
/// Check if given type is `Sized` or not
918918
pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool {
919+
let mut ty = ty.clone();
920+
{
921+
let mut structs = SmallVec::<[_; 8]>::new();
922+
// Must use a loop here and not recursion because otherwise users will conduct completely
923+
// artificial examples of structs that have themselves as the tail field and complain r-a crashes.
924+
while let Some((AdtId::StructId(id), subst)) = ty.as_adt() {
925+
let struct_data = self.db.struct_data(id);
926+
if let Some((last_field, _)) = struct_data.variant_data.fields().iter().next_back()
927+
{
928+
let last_field_ty = self.db.field_types(id.into())[last_field]
929+
.clone()
930+
.substitute(Interner, subst);
931+
if structs.contains(&ty) {
932+
// A struct recursively contains itself as a tail field somewhere.
933+
return true; // Don't overload the users with too many errors.
934+
}
935+
structs.push(ty);
936+
// Structs can have DST as its last field and such cases are not handled
937+
// as unsized by the chalk, so we do this manually.
938+
ty = last_field_ty;
939+
} else {
940+
break;
941+
};
942+
}
943+
}
944+
919945
// Early return for some obvious types
920946
if matches!(
921947
ty.kind(Interner),
@@ -930,16 +956,6 @@ impl<'a> InferenceTable<'a> {
930956
return true;
931957
}
932958

933-
if let Some((AdtId::StructId(id), subst)) = ty.as_adt() {
934-
let struct_data = self.db.struct_data(id);
935-
if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() {
936-
let last_field_ty =
937-
self.db.field_types(id.into())[last_field].clone().substitute(Interner, subst);
938-
// Structs can have DST as its last field and such cases are not handled
939-
// as unsized by the chalk, so we do this manually
940-
return self.is_sized(&last_field_ty);
941-
}
942-
}
943959
let Some(sized) = self
944960
.db
945961
.lang_item(self.trait_env.krate, LangItem::Sized)

src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4790,3 +4790,24 @@ fn allowed3(baz: impl Baz<Assoc = Qux<impl Foo>>) {}
47904790
"#]],
47914791
)
47924792
}
4793+
4794+
#[test]
4795+
fn recursive_tail_sized() {
4796+
check_infer(
4797+
r#"
4798+
struct WeirdFoo(WeirdBar);
4799+
struct WeirdBar(WeirdFoo);
4800+
4801+
fn bar(v: *const ()) {
4802+
let _ = v as *const WeirdFoo;
4803+
}
4804+
"#,
4805+
expect![[r#"
4806+
62..63 'v': *const ()
4807+
76..113 '{ ...Foo; }': ()
4808+
86..87 '_': *const WeirdFoo
4809+
90..91 'v': *const ()
4810+
90..110 'v as *...irdFoo': *const WeirdFoo
4811+
"#]],
4812+
);
4813+
}

0 commit comments

Comments
 (0)