Commit f83fe2714b

mlugg <mlugg@mlugg.co.uk>
2025-04-28 01:28:55
compiler: fix comptime memory store bugs
* When storing a zero-bit type, we should short-circuit almost immediately. Zero-bit stores do not need to do any work. * The bit size computation for arrays is incorrect; the `abiSize` will already be appropriately aligned, but the logic to do so here incorrectly assumes that zero-bit types have an alignment of 0. They don't; their alignment is 1. Resolves: #21202 Resolves: #21508 Resolves: #23307
1 parent ae1b444
Changed files (3)
src/Sema/comptime_ptr_access.zig
@@ -65,6 +65,15 @@ pub fn storeComptimePtr(
     const zcu = pt.zcu;
     const ptr_info = ptr.typeOf(zcu).ptrInfo(zcu);
     assert(store_val.typeOf(zcu).toIntern() == ptr_info.child);
+
+    {
+        const store_ty: Type = .fromInterned(ptr_info.child);
+        if (!try store_ty.comptimeOnlySema(pt) and !try store_ty.hasRuntimeBitsIgnoreComptimeSema(pt)) {
+            // zero-bit store; nothing to do
+            return .success;
+        }
+    }
+
     // TODO: host size for vectors is terrible
     const host_bits = switch (ptr_info.flags.vector_index) {
         .none => ptr_info.packed_offset.host_size * 8,
src/Type.zig
@@ -1637,10 +1637,7 @@ pub fn bitSizeInner(
             const len = array_type.lenIncludingSentinel();
             if (len == 0) return 0;
             const elem_ty = Type.fromInterned(array_type.child);
-            const elem_size = @max(
-                (try elem_ty.abiAlignmentInner(strat_lazy, zcu, tid)).scalar.toByteUnits() orelse 0,
-                (try elem_ty.abiSizeInner(strat_lazy, zcu, tid)).scalar,
-            );
+            const elem_size = (try elem_ty.abiSizeInner(strat_lazy, zcu, tid)).scalar;
             if (elem_size == 0) return 0;
             const elem_bit_size = try elem_ty.bitSizeInner(strat, zcu, tid);
             return (len - 1) * 8 * elem_size + elem_bit_size;
test/behavior/comptime_memory.zig
@@ -515,3 +515,66 @@ fn fieldPtrTest() u32 {
 test "pointer in aggregate field can mutate comptime state" {
     try comptime std.testing.expect(fieldPtrTest() == 2);
 }
+
+test "comptime store of extern struct with void field" {
+    comptime {
+        var x: extern struct { a: u8, b: void } = undefined;
+        x = .{ .a = 123, .b = {} };
+        std.debug.assert(x.a == 123);
+    }
+}
+
+test "comptime store of extern struct with void field into array" {
+    comptime {
+        var x: [3]extern struct { a: u8, b: void } = undefined;
+        x[1] = .{ .a = 123, .b = {} };
+        std.debug.assert(x[1].a == 123);
+    }
+}
+
+test "comptime store of packed struct with void field" {
+    comptime {
+        var x: packed struct { a: u8, b: void } = undefined;
+        x = .{ .a = 123, .b = {} };
+        std.debug.assert(x.a == 123);
+    }
+}
+
+test "comptime store of packed struct with void field into array" {
+    comptime {
+        var x: [3]packed struct { a: u8, b: void } = undefined;
+        x[1] = .{ .a = 123, .b = {} };
+        std.debug.assert(x[1].a == 123);
+    }
+}
+
+test "comptime store of reinterpreted zero-bit type" {
+    const S = struct {
+        fn doTheTest(comptime T: type) void {
+            comptime var buf: T = undefined;
+            const ptr: *void = @ptrCast(&buf);
+            ptr.* = {};
+        }
+    };
+    S.doTheTest(void);
+    S.doTheTest(u0);
+    S.doTheTest([0]u8);
+    S.doTheTest([1]u0);
+    S.doTheTest([5]u0);
+    S.doTheTest([5]void);
+    S.doTheTest(packed struct(u0) {});
+}
+
+test "comptime store to extern struct reinterpreted as byte array" {
+    const T = extern struct {
+        x: u32,
+        y: f32,
+        z: [2]void,
+    };
+    comptime var val: T = undefined;
+
+    const bytes: *[@sizeOf(T)]u8 = @ptrCast(&val);
+    @memset(bytes, 0);
+
+    comptime std.debug.assert(val.x == 0);
+}