Commit 746137034e

Jacob Young <jacobly0@users.noreply.github.com>
2025-06-09 08:34:30
Sema: fix union layout logic to match struct layout logic
1 parent d312dfc
Changed files (2)
src/Sema.zig
@@ -35054,7 +35054,7 @@ pub fn resolveUnionAlignment(
     union_type.setAlignment(ip, max_align);
 }
 
-/// This logic must be kept in sync with `Zcu.getUnionLayout`.
+/// This logic must be kept in sync with `Type.getUnionLayout`.
 pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
     const pt = sema.pt;
     const ip = &pt.zcu.intern_pool;
@@ -35090,6 +35090,14 @@ pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
         const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]);
         if (field_ty.isNoReturn(pt.zcu)) continue;
 
+        // We need to call `hasRuntimeBits` before calling `abiSize` to prevent reachable `unreachable`s,
+        // but `hasRuntimeBits` only resolves field types and so may infinite recurse on a layout wip type,
+        // so we must resolve the layout manually first, instead of waiting for `abiSize` to do it for us.
+        // This is arguably just hacking around bugs in both `abiSize` for not allowing arbitrary types to
+        // be queried, enabling failures to be handled with the emission of a compile error, and also in
+        // `hasRuntimeBits` for ever being able to infinite recurse in the first place.
+        try field_ty.resolveLayout(pt);
+
         if (try field_ty.hasRuntimeBitsSema(pt)) {
             max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) {
                 error.AnalysisFail => {
src/Type.zig
@@ -3915,29 +3915,30 @@ fn resolveUnionInner(
 pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {
     const ip = &zcu.intern_pool;
     assert(loaded_union.haveLayout(ip));
-    var most_aligned_field: u32 = undefined;
-    var most_aligned_field_size: u64 = undefined;
-    var biggest_field: u32 = undefined;
+    var most_aligned_field: u32 = 0;
+    var most_aligned_field_align: InternPool.Alignment = .@"1";
+    var most_aligned_field_size: u64 = 0;
+    var biggest_field: u32 = 0;
     var payload_size: u64 = 0;
     var payload_align: InternPool.Alignment = .@"1";
-    for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| {
-        if (Type.fromInterned(field_ty).isNoReturn(zcu)) continue;
+    for (loaded_union.field_types.get(ip), 0..) |field_ty_ip_index, field_index| {
+        const field_ty: Type = .fromInterned(field_ty_ip_index);
+        if (field_ty.isNoReturn(zcu)) continue;
 
         const explicit_align = loaded_union.fieldAlign(ip, field_index);
         const field_align = if (explicit_align != .none)
             explicit_align
         else
-            Type.fromInterned(field_ty).abiAlignment(zcu);
-        if (Type.fromInterned(field_ty).hasRuntimeBits(zcu)) {
-            const field_size = Type.fromInterned(field_ty).abiSize(zcu);
-            if (field_size > payload_size) {
-                payload_size = field_size;
-                biggest_field = @intCast(field_index);
-            }
-            if (field_align.compare(.gte, payload_align)) {
-                most_aligned_field = @intCast(field_index);
-                most_aligned_field_size = field_size;
-            }
+            field_ty.abiAlignment(zcu);
+        const field_size = field_ty.abiSize(zcu);
+        if (field_size > payload_size) {
+            payload_size = field_size;
+            biggest_field = @intCast(field_index);
+        }
+        if (field_size > 0 and field_align.compare(.gte, most_aligned_field_align)) {
+            most_aligned_field = @intCast(field_index);
+            most_aligned_field_align = field_align;
+            most_aligned_field_size = field_size;
         }
         payload_align = payload_align.max(field_align);
     }