From 23bd3cd9270352600f865cc5799dd6b4e266f29e Mon Sep 17 00:00:00 2001 From: mo Date: Fri, 21 Aug 2026 21:57:08 +1200 Subject: [PATCH 1/2] detect invalid block size caused by a void type Fixes #7008 When a union variant includes a pointer to a forward type, `lb_sizeof` returns 0 for the size of the block. This causes an extra padding word to be added to the llvm type, causing a size_of and type_of mismatch. The fix detects the 0 size and falls back to using `type_size_of` on the first variant. --- src/llvm_backend_general.cpp | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index b0610e511..5c77e58df 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -2770,26 +2770,17 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) { if (is_type_union_maybe_pointer(type)) { LLVMTypeRef variant = lb_type(m, type->Union.variants[0]); array_add(&fields, variant); - } else if (type->Union.variants.count == 1) { - LLVMTypeRef block_type = lb_type(m, type->Union.variants[0]); - - LLVMTypeRef tag_type = lb_type(m, union_tag_type(type)); - array_add(&fields, block_type); - array_add(&fields, tag_type); - i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type); - i64 padding = size - used_size; - if (padding > 0) { - LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align); - array_add(&fields, padding_type); - } - is_packed = true; } else { LLVMTypeRef block_type = lb_type_internal_union_block_type(m, type); LLVMTypeRef tag_type = lb_type(m, union_tag_type(type)); array_add(&fields, block_type); array_add(&fields, tag_type); - i64 used_size = lb_sizeof(block_type) + lb_sizeof(tag_type); + i64 block_size = lb_sizeof(block_type); + if (block_size == 0) { + block_size = type_size_of(type->Union.variants[0]); + } + i64 used_size = block_size + lb_sizeof(tag_type); i64 padding = size - used_size; if (padding > 0) { LLVMTypeRef padding_type = lb_type_padding_filler(m, padding, align); From 7bb0bc353f990bc49c98420f450aecd43ba63325 Mon Sep 17 00:00:00 2001 From: mo Date: Sat, 22 Aug 2026 10:42:18 +1200 Subject: [PATCH 2/2] add regression test for incorrect offset_of --- tests/issues/run.bat | 1 + tests/issues/run.sh | 1 + tests/issues/test_issue_7008.odin | 27 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 tests/issues/test_issue_7008.odin diff --git a/tests/issues/run.bat b/tests/issues/run.bat index d01c85e14..c5de8434a 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -40,6 +40,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused ..\..\..\odin test ..\test_issue_6753.odin %COMMON% || exit /b ..\..\..\odin check ..\test_issue_6874.odin %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b ..\..\..\odin check ..\test_issue_6979.odin -no-entry-point %COMMON% || exit /b +..\..\..\odin test ..\test_issue_7008.odin %COMMON% || exit /b ..\..\..\odin check ..\test_issue_7012.odin -no-entry-point %COMMON% || exit /b ..\..\..\odin build ..\test_issue_7037.odin %COMMON% -o:none || exit /b ..\..\..\odin build ..\test_issue_7188.odin %COMMON% || exit /b diff --git a/tests/issues/run.sh b/tests/issues/run.sh index f0cbea3fd..c1d6ff2f1 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -88,6 +88,7 @@ else exit 1 fi $ODIN check ../test_issue_6979.odin -no-entry-point $COMMON_CHECK +$ODIN test ../test_issue_7008.odin $COMMON $ODIN check ../test_issue_7012.odin -no-entry-point $COMMON_CHECK $ODIN build ../test_issue_7037.odin $COMMON -o:none $ODIN test ../test_issue_7356.odin $COMMON diff --git a/tests/issues/test_issue_7008.odin b/tests/issues/test_issue_7008.odin new file mode 100644 index 000000000..5869f61e6 --- /dev/null +++ b/tests/issues/test_issue_7008.odin @@ -0,0 +1,27 @@ +// Tests issue #7008 https://github.com/odin-lang/Odin/issues/7008 +package test_issues + +import "core:testing" +import "core:slice" + +Bar :: struct { + p: ^Foo, +} + +Maybe_Bar :: union { + Bar, +} + +Foo :: struct { + bar: Maybe_Bar, + value: byte, +} + +@(test) +test_offset_of_is_correct :: proc(t: ^testing.T) { + foo: Foo + foo.value = 42 + + bytes := slice.bytes_from_ptr(&foo, size_of(Foo)) + testing.expect_value(t, bytes[offset_of(foo.value)], 42) +}