From 427ddef0e84384c075dd01e8f1b512922c814a1c Mon Sep 17 00:00:00 2001 From: Pavel Verigo Date: Tue, 20 Oct 2026 06:00:00 +0200 Subject: [PATCH] llvm: fix lowerType bug on struct/union with under aligned field --- src/codegen/llvm.zig | 16 ++++++++++++---- test/behavior/align.zig | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index f4198c2db8da81c76fcb743e8e5dd972ede77bef..0fd9224c206fb95e709d91b0e02aef2e894818d5 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2893,8 +2893,8 @@ pub const Object = struct { o.gpa, try o.builder.arrayType(padding_len, .i8), ); - if (@ctz(offset) < max_field_ty_align.toLog2Units()) { - struct_kind = .@"packed"; // prevent unexpected trailing padding + if (struct_type.alignment.compareStrict(.lt, max_field_ty_align)) { + struct_kind = .@"packed"; // prevent wrong alignment } } @@ -2973,6 +2973,14 @@ pub const Object = struct { const aligned_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[layout.most_aligned_field]); const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty, repr); + const struct_kind: Builder.Type.Structure.Kind = for (union_obj.field_types.get(ip)) |field_ty_ip| { + const field_ty = Type.fromInterned(field_ty_ip); + if (!field_ty.hasRuntimeBits(zcu)) continue; + if (layout.payload_align.compareStrict(.lt, field_ty.abiAlignment(zcu))) { + break .@"packed"; // prevent wrong alignment + } + } else .normal; + const payload_ty = ty: { if (layout.most_aligned_field_size == layout.payload_size) { break :ty aligned_field_llvm_ty; @@ -2993,7 +3001,7 @@ pub const Object = struct { o.builder.namedTypeSetBody( ty, - try o.builder.structType(.normal, &.{payload_ty}), + try o.builder.structType(struct_kind, &.{payload_ty}), ); return ty; } @@ -3021,7 +3029,7 @@ pub const Object = struct { o.builder.namedTypeSetBody( ty, - try o.builder.structType(.normal, llvm_fields[0..llvm_fields_len]), + try o.builder.structType(struct_kind, llvm_fields[0..llvm_fields_len]), ); return ty; }, diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 83a100533d77a81a0a77ef38def5e61cb2a29f79..599e8467a9616996dbc4159c5ba00c2c92a3ef93 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -495,6 +495,42 @@ test "struct field explicit alignment" { try expect(@intFromPtr(&node.massive_byte) % 64 == 0); } +test "nested struct field alignment" { + const S = struct { + fn doTheTest() !void { + const T = struct { + a: u8, + b: struct { c: u16 align(1) }, + }; + const init: T = .{ .a = 0, .b = .{ .c = ~@as(u16, 0) } }; + var t: T = init; // copy constant aggregate + _ = &t; + try expect(t.b.c == ~@as(u16, 0)); + } + }; + + try S.doTheTest(); + try comptime S.doTheTest(); +} + +test "nested union field alignment" { + const S = struct { + fn doTheTest() !void { + const T = struct { + a: u8, + b: union { c: u16 align(1) }, + }; + const init: T = .{ .a = 0, .b = .{ .c = ~@as(u16, 0) } }; + var t: T = init; // copy constant aggregate + _ = &t; + try expect(t.b.c == ~@as(u16, 0)); + } + }; + + try S.doTheTest(); + try comptime S.doTheTest(); +} + test "align(N) on functions" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO -- 2.54.0