From 4fe57190074ec713d801c22c2269c03838d8fa56 Mon Sep 17 00:00:00 2001 From: Senthilnathan Date: Mon, 17 Aug 2026 12:48:41 +0530 Subject: [PATCH] Fix SIGBUS/segfault when compiling recursive #soa slice/dynamic array types --- src/llvm_backend_general.cpp | 24 ++++++++++++++++- tests/issues/run.sh | 1 + tests/issues/test_issue_7356.odin | 43 +++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/issues/test_issue_7356.odin diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 7af1c52be..ecb961cc3 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2464,6 +2464,22 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { return struct_type; } + bool is_soa_struct = type->Struct.soa_kind != StructSoa_None; + LLVMTypeRef named_struct_type = nullptr; + if (is_soa_struct) { + // NOTE(bill): SOA structs are anonymous and may be recursive + // (e.g. `#soa[]T` where `T` itself contains a field of type + // `#soa[]T`). Register an opaque named struct up front so that any + // recursive field references resolve to it instead of recursing + // infinitely. + gbString soa_name = temp_canonical_string(type); + named_struct_type = LLVMGetTypeByName(m->mod, soa_name); + if (named_struct_type == nullptr) { + named_struct_type = LLVMStructCreateNamed(ctx, soa_name); + } + map_set(&m->types, type, named_struct_type); + } + lbStructFieldRemapping field_remapping = {}; slice_init(&field_remapping, permanent_allocator(), type->Struct.fields.count); @@ -2516,7 +2532,13 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { GB_ASSERT(fields[i] != nullptr); } - LLVMTypeRef struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, requires_packing); + LLVMTypeRef struct_type = nullptr; + if (is_soa_struct) { + struct_type = named_struct_type; + LLVMStructSetBody(struct_type, fields.data, cast(unsigned)fields.count, requires_packing); + } else { + struct_type = LLVMStructTypeInContext(ctx, fields.data, cast(unsigned)fields.count, requires_packing); + } map_set(&m->struct_field_remapping, cast(void *)struct_type, field_remapping); map_set(&m->struct_field_remapping, cast(void *)type, field_remapping); #if 0 diff --git a/tests/issues/run.sh b/tests/issues/run.sh index ce97a570a..55c7cc649 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -82,6 +82,7 @@ else fi $ODIN check ../test_issue_6979.odin -no-entry-point $COMMON $ODIN build ../test_issue_7037.odin $COMMON -o:none +$ODIN test ../test_issue_7356.odin $COMMON if [[ $($ODIN build ../test_issue_7108.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then echo "SUCCESSFUL 1/1" diff --git a/tests/issues/test_issue_7356.odin b/tests/issues/test_issue_7356.odin new file mode 100644 index 000000000..c2dca833d --- /dev/null +++ b/tests/issues/test_issue_7356.odin @@ -0,0 +1,43 @@ +// Tests issue #7356 https://github.com/odin-lang/Odin/issues/7356 +// The compiler used to terminate with a SIGBUS/SIGSEGV when compiling a +// recursive `#soa` slice/dynamic array contained within its own element type. + +package test_issues + +import "core:testing" + +Bookmark :: struct { + name: string, + children: #soa[]Bookmark, +} + +DynamicBookmark :: struct { + name: string, + children: #soa[dynamic]Bookmark, +} + +@(test) +test_recursive_soa_slice :: proc(t: ^testing.T) { + b: Bookmark + b.name = "root" + b.children = make(#soa[]Bookmark, 2) + defer delete(b.children) + + b.children[0].name = "child0" + b.children[1].name = "child1" + + testing.expect_value(t, len(b.children), 2) + testing.expect_value(t, b.children[0].name, "child0") + testing.expect_value(t, b.children[1].name, "child1") +} + +@(test) +test_recursive_soa_dynamic :: proc(t: ^testing.T) { + b: DynamicBookmark + b.name = "root" + append_soa(&b.children, Bookmark{name = "child0"}) + defer delete(b.children) + + testing.expect_value(t, len(b.children), 1) + testing.expect_value(t, b.children[0].name, "child0") +}