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
This commit is contained in:
mlugg 2025-04-28 00:28:55 +01:00 committed by Alex Rønne Petersen
parent 87983e800a
commit db936b9094
No known key found for this signature in database
3 changed files with 73 additions and 4 deletions

View file

@ -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,

View file

@ -1740,10 +1740,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;

View file

@ -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);
}