authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-04-28 00:28:55+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-05-03 20:10:26+01:00
logf83fe2714bd4441610156e1a6017d07409ad6093
treeace9b73ebcc5dec94488312d8078f62822849a08
parentae1b444d6a651c6a6c6f09c15565d7b37759e488

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

3 files changed, 73 insertions(+), 4 deletions(-)

src/Sema/comptime_ptr_access.zig+9
...@@ -65,6 +65,15 @@ pub fn storeComptimePtr(...@@ -65,6 +65,15 @@ pub fn storeComptimePtr(
65 const zcu = pt.zcu;65 const zcu = pt.zcu;
66 const ptr_info = ptr.typeOf(zcu).ptrInfo(zcu);66 const ptr_info = ptr.typeOf(zcu).ptrInfo(zcu);
67 assert(store_val.typeOf(zcu).toIntern() == ptr_info.child);67 assert(store_val.typeOf(zcu).toIntern() == ptr_info.child);
68
69 {
70 const store_ty: Type = .fromInterned(ptr_info.child);
71 if (!try store_ty.comptimeOnlySema(pt) and !try store_ty.hasRuntimeBitsIgnoreComptimeSema(pt)) {
72 // zero-bit store; nothing to do
73 return .success;
74 }
75 }
76
68 // TODO: host size for vectors is terrible77 // TODO: host size for vectors is terrible
69 const host_bits = switch (ptr_info.flags.vector_index) {78 const host_bits = switch (ptr_info.flags.vector_index) {
70 .none => ptr_info.packed_offset.host_size * 8,79 .none => ptr_info.packed_offset.host_size * 8,
src/Type.zig+1-4
...@@ -1637,10 +1637,7 @@ pub fn bitSizeInner(...@@ -1637,10 +1637,7 @@ pub fn bitSizeInner(
1637 const len = array_type.lenIncludingSentinel();1637 const len = array_type.lenIncludingSentinel();
1638 if (len == 0) return 0;1638 if (len == 0) return 0;
1639 const elem_ty = Type.fromInterned(array_type.child);1639 const elem_ty = Type.fromInterned(array_type.child);
1640 const elem_size = @max(1640 const elem_size = (try elem_ty.abiSizeInner(strat_lazy, zcu, tid)).scalar;
1641 (try elem_ty.abiAlignmentInner(strat_lazy, zcu, tid)).scalar.toByteUnits() orelse 0,
1642 (try elem_ty.abiSizeInner(strat_lazy, zcu, tid)).scalar,
1643 );
1644 if (elem_size == 0) return 0;1641 if (elem_size == 0) return 0;
1645 const elem_bit_size = try elem_ty.bitSizeInner(strat, zcu, tid);1642 const elem_bit_size = try elem_ty.bitSizeInner(strat, zcu, tid);
1646 return (len - 1) * 8 * elem_size + elem_bit_size;1643 return (len - 1) * 8 * elem_size + elem_bit_size;
test/behavior/comptime_memory.zig+63
...@@ -515,3 +515,66 @@ fn fieldPtrTest() u32 {...@@ -515,3 +515,66 @@ fn fieldPtrTest() u32 {
515test "pointer in aggregate field can mutate comptime state" {515test "pointer in aggregate field can mutate comptime state" {
516 try comptime std.testing.expect(fieldPtrTest() == 2);516 try comptime std.testing.expect(fieldPtrTest() == 2);
517}517}
518
519test "comptime store of extern struct with void field" {
520 comptime {
521 var x: extern struct { a: u8, b: void } = undefined;
522 x = .{ .a = 123, .b = {} };
523 std.debug.assert(x.a == 123);
524 }
525}
526
527test "comptime store of extern struct with void field into array" {
528 comptime {
529 var x: [3]extern struct { a: u8, b: void } = undefined;
530 x[1] = .{ .a = 123, .b = {} };
531 std.debug.assert(x[1].a == 123);
532 }
533}
534
535test "comptime store of packed struct with void field" {
536 comptime {
537 var x: packed struct { a: u8, b: void } = undefined;
538 x = .{ .a = 123, .b = {} };
539 std.debug.assert(x.a == 123);
540 }
541}
542
543test "comptime store of packed struct with void field into array" {
544 comptime {
545 var x: [3]packed struct { a: u8, b: void } = undefined;
546 x[1] = .{ .a = 123, .b = {} };
547 std.debug.assert(x[1].a == 123);
548 }
549}
550
551test "comptime store of reinterpreted zero-bit type" {
552 const S = struct {
553 fn doTheTest(comptime T: type) void {
554 comptime var buf: T = undefined;
555 const ptr: *void = @ptrCast(&buf);
556 ptr.* = {};
557 }
558 };
559 S.doTheTest(void);
560 S.doTheTest(u0);
561 S.doTheTest([0]u8);
562 S.doTheTest([1]u0);
563 S.doTheTest([5]u0);
564 S.doTheTest([5]void);
565 S.doTheTest(packed struct(u0) {});
566}
567
568test "comptime store to extern struct reinterpreted as byte array" {
569 const T = extern struct {
570 x: u32,
571 y: f32,
572 z: [2]void,
573 };
574 comptime var val: T = undefined;
575
576 const bytes: *[@sizeOf(T)]u8 = @ptrCast(&val);
577 @memset(bytes, 0);
578
579 comptime std.debug.assert(val.x == 0);
580}