authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-06-09 02:34:30-04:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-06-12 17:51:30+01:00
log746137034e244d863a022b821ae0a1952d9d93c1
tree62ad6883d960d3f995202eb4901aecddbf0fb957
parentd312dfc1f21a9194dd06c1d45f653bdaf823d2f6
signaturelock-open Commit is signed but in an unrecognized format.

Sema: fix union layout logic to match struct layout logic


2 files changed, 26 insertions(+), 17 deletions(-)

src/Sema.zig+9-1
...@@ -35054,7 +35054,7 @@ pub fn resolveUnionAlignment(...@@ -35054,7 +35054,7 @@ pub fn resolveUnionAlignment(
35054 union_type.setAlignment(ip, max_align);35054 union_type.setAlignment(ip, max_align);
35055}35055}
3505635056
35057/// This logic must be kept in sync with `Zcu.getUnionLayout`.35057/// This logic must be kept in sync with `Type.getUnionLayout`.
35058pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {35058pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
35059 const pt = sema.pt;35059 const pt = sema.pt;
35060 const ip = &pt.zcu.intern_pool;35060 const ip = &pt.zcu.intern_pool;
...@@ -35090,6 +35090,14 @@ pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {...@@ -35090,6 +35090,14 @@ pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
35090 const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]);35090 const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]);
35091 if (field_ty.isNoReturn(pt.zcu)) continue;35091 if (field_ty.isNoReturn(pt.zcu)) continue;
3509235092
35093 // We need to call `hasRuntimeBits` before calling `abiSize` to prevent reachable `unreachable`s,
35094 // but `hasRuntimeBits` only resolves field types and so may infinite recurse on a layout wip type,
35095 // so we must resolve the layout manually first, instead of waiting for `abiSize` to do it for us.
35096 // This is arguably just hacking around bugs in both `abiSize` for not allowing arbitrary types to
35097 // be queried, enabling failures to be handled with the emission of a compile error, and also in
35098 // `hasRuntimeBits` for ever being able to infinite recurse in the first place.
35099 try field_ty.resolveLayout(pt);
35100
35093 if (try field_ty.hasRuntimeBitsSema(pt)) {35101 if (try field_ty.hasRuntimeBitsSema(pt)) {
35094 max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) {35102 max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) {
35095 error.AnalysisFail => {35103 error.AnalysisFail => {
src/Type.zig+17-16
...@@ -3915,29 +3915,30 @@ fn resolveUnionInner(...@@ -3915,29 +3915,30 @@ fn resolveUnionInner(
3915pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {3915pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {
3916 const ip = &zcu.intern_pool;3916 const ip = &zcu.intern_pool;
3917 assert(loaded_union.haveLayout(ip));3917 assert(loaded_union.haveLayout(ip));
3918 var most_aligned_field: u32 = undefined;3918 var most_aligned_field: u32 = 0;
3919 var most_aligned_field_size: u64 = undefined;3919 var most_aligned_field_align: InternPool.Alignment = .@"1";
3920 var biggest_field: u32 = undefined;3920 var most_aligned_field_size: u64 = 0;
3921 var biggest_field: u32 = 0;
3921 var payload_size: u64 = 0;3922 var payload_size: u64 = 0;
3922 var payload_align: InternPool.Alignment = .@"1";3923 var payload_align: InternPool.Alignment = .@"1";
3923 for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| {3924 for (loaded_union.field_types.get(ip), 0..) |field_ty_ip_index, field_index| {
3924 if (Type.fromInterned(field_ty).isNoReturn(zcu)) continue;3925 const field_ty: Type = .fromInterned(field_ty_ip_index);
3926 if (field_ty.isNoReturn(zcu)) continue;
39253927
3926 const explicit_align = loaded_union.fieldAlign(ip, field_index);3928 const explicit_align = loaded_union.fieldAlign(ip, field_index);
3927 const field_align = if (explicit_align != .none)3929 const field_align = if (explicit_align != .none)
3928 explicit_align3930 explicit_align
3929 else3931 else
3930 Type.fromInterned(field_ty).abiAlignment(zcu);3932 field_ty.abiAlignment(zcu);
3931 if (Type.fromInterned(field_ty).hasRuntimeBits(zcu)) {3933 const field_size = field_ty.abiSize(zcu);
3932 const field_size = Type.fromInterned(field_ty).abiSize(zcu);3934 if (field_size > payload_size) {
3933 if (field_size > payload_size) {3935 payload_size = field_size;
3934 payload_size = field_size;3936 biggest_field = @intCast(field_index);
3935 biggest_field = @intCast(field_index);3937 }
3936 }3938 if (field_size > 0 and field_align.compare(.gte, most_aligned_field_align)) {
3937 if (field_align.compare(.gte, payload_align)) {3939 most_aligned_field = @intCast(field_index);
3938 most_aligned_field = @intCast(field_index);3940 most_aligned_field_align = field_align;
3939 most_aligned_field_size = field_size;3941 most_aligned_field_size = field_size;
3940 }
3941 }3942 }
3942 payload_align = payload_align.max(field_align);3943 payload_align = payload_align.max(field_align);
3943 }3944 }