Commit dca17d3603

Andrew Kelley <andrew@ziglang.org>
2023-09-21 04:40:21
Sema: fix struct alignment regressions
1 parent d06bf70
Changed files (2)
src/InternPool.zig
@@ -536,6 +536,19 @@ pub const Key = union(enum) {
             s.flagsPtr(ip).layout_wip = false;
         }
 
+        pub fn setAlignmentWip(s: @This(), ip: *InternPool) bool {
+            if (s.layout == .Packed) return false;
+            const flags_ptr = s.flagsPtr(ip);
+            if (flags_ptr.alignment_wip) return true;
+            flags_ptr.alignment_wip = true;
+            return false;
+        }
+
+        pub fn clearAlignmentWip(s: @This(), ip: *InternPool) void {
+            if (s.layout == .Packed) return;
+            s.flagsPtr(ip).alignment_wip = false;
+        }
+
         pub fn setFullyResolved(s: @This(), ip: *InternPool) bool {
             if (s.layout == .Packed) return true;
             const flags_ptr = s.flagsPtr(ip);
@@ -2971,6 +2984,8 @@ pub const Tag = enum(u8) {
             any_aligned_fields: bool,
             /// `undefined` until the layout_resolved
             alignment: Alignment,
+            /// Dependency loop detection when resolving struct alignment.
+            alignment_wip: bool,
             /// Dependency loop detection when resolving field types.
             field_types_wip: bool,
             /// Dependency loop detection when resolving struct layout.
@@ -2982,7 +2997,7 @@ pub const Tag = enum(u8) {
             // which `layout_resolved` does not ensure.
             fully_resolved: bool,
 
-            _: u12 = 0,
+            _: u11 = 0,
         };
     };
 };
@@ -5300,6 +5315,7 @@ pub fn getStructType(
                 .any_default_inits = ini.any_default_inits,
                 .any_aligned_fields = ini.any_aligned_fields,
                 .alignment = .none,
+                .alignment_wip = false,
                 .field_types_wip = false,
                 .layout_wip = false,
                 .layout_resolved = false,
src/Sema.zig
@@ -26698,21 +26698,25 @@ fn structFieldPtrByIndex(
             }
         }
     } else if (struct_type.layout == .Extern) {
-        // For extern structs, field aligment might be bigger than type's natural alignment. Eg, in
-        // `extern struct { x: u32, y: u16 }` the second field is aligned as u32.
+        // For extern structs, field alignment might be bigger than type's
+        // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the
+        // second field is aligned as u32.
         const field_offset = struct_ty.structFieldOffset(field_index, mod);
         ptr_ty_data.flags.alignment = if (parent_align == .none)
             .none
         else
             @enumFromInt(@min(@intFromEnum(parent_align), @ctz(field_offset)));
     } else {
-        // Our alignment is capped at the field alignment
+        // Our alignment is capped at the field alignment.
         const field_align = try sema.structFieldAlignment(
             struct_type.fieldAlign(ip, field_index),
             field_ty.toType(),
             struct_type.layout,
         );
-        ptr_ty_data.flags.alignment = field_align.min(parent_align);
+        ptr_ty_data.flags.alignment = if (struct_ptr_ty_info.flags.alignment == .none)
+            field_align
+        else
+            field_align.min(parent_align);
     }
 
     const ptr_field_ty = try sema.ptrType(ptr_ty_data);
@@ -28635,8 +28639,8 @@ const InMemoryCoercionResult = union(enum) {
                 break;
             },
             .ptr_alignment => |pair| {
-                try sema.errNote(block, src, msg, "pointer alignment '{}' cannot cast into pointer alignment '{}'", .{
-                    pair.actual, pair.wanted,
+                try sema.errNote(block, src, msg, "pointer alignment '{d}' cannot cast into pointer alignment '{d}'", .{
+                    pair.actual.toByteUnits(0), pair.wanted.toByteUnits(0),
                 });
                 break;
             },
@@ -34307,20 +34311,29 @@ pub fn resolveStructAlignment(
 
     try sema.resolveTypeFieldsStruct(ty, struct_type);
 
+    if (struct_type.setAlignmentWip(ip)) {
+        // We'll guess "pointer-aligned", if the struct has an
+        // underaligned pointer field then some allocations
+        // might require explicit alignment.
+        //TODO write this bit and emit an error later if incorrect
+        //struct_type.flagsPtr(ip).assumed_pointer_aligned = true;
+        const result = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
+        struct_type.flagsPtr(ip).alignment = result;
+        return result;
+    }
+
     var result: Alignment = .@"1";
 
     for (0..struct_type.field_types.len) |i| {
-        if (struct_type.fieldIsComptime(ip, i)) continue;
         const field_ty = struct_type.field_types.get(ip)[i].toType();
-        if (try sema.typeRequiresComptime(field_ty)) continue;
-        if (try sema.typeHasRuntimeBits(field_ty)) {
-            const field_align = try sema.structFieldAlignment(
-                struct_type.fieldAlign(ip, i),
-                field_ty,
-                struct_type.layout,
-            );
-            result = result.max(field_align);
-        }
+        if (struct_type.fieldIsComptime(ip, i) or try sema.typeRequiresComptime(field_ty))
+            continue;
+        const field_align = try sema.structFieldAlignment(
+            struct_type.fieldAlign(ip, i),
+            field_ty,
+            struct_type.layout,
+        );
+        result = result.max(field_align);
     }
 
     struct_type.flagsPtr(ip).alignment = result;
@@ -34358,7 +34371,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
 
     for (aligns, sizes, 0..) |*field_align, *field_size, i| {
         const field_ty = struct_type.field_types.get(ip)[i].toType();
-        if (struct_type.fieldIsComptime(ip, i) or !(try sema.typeHasRuntimeBits(field_ty))) {
+        if (struct_type.fieldIsComptime(ip, i) or try sema.typeRequiresComptime(field_ty)) {
             struct_type.offsets.get(ip)[i] = 0;
             field_size.* = 0;
             field_align.* = .none;
@@ -34439,10 +34452,8 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
     var offset: u64 = 0;
     var big_align: Alignment = .@"1";
     while (it.next()) |i| {
-        const field_ty = struct_type.field_types.get(ip)[i].toType();
-        // Type query definitely valid as we performed it earlier
-        if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
-        big_align = big_align.max(aligns[i]);
+        if (aligns[i] == .none) continue;
+        big_align = big_align.maxStrict(aligns[i]);
         offsets[i] = @intCast(aligns[i].forward(offset));
         offset = offsets[i] + sizes[i];
     }
@@ -36870,7 +36881,7 @@ fn structFieldAlignment(
     // extern
     const ty_abi_align = try sema.typeAbiAlignment(field_ty);
     if (field_ty.isAbiInt(mod) and field_ty.intInfo(mod).bits >= 128) {
-        return ty_abi_align.max(.@"16");
+        return ty_abi_align.maxStrict(.@"16");
     }
     return ty_abi_align;
 }