authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-03-03 21:13:31+00:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-03-14 13:06:23+02:00
log1e6d7f77639d52b61b2852cf0de19e2b5a50f31f
treea921304e7e045c77279504dc53fe11a8925b589a
parent5a12d00708df019fa510076f8af40d6efcb7c608

Sema: allow comptime mutation of multiple array elements

Previously, if you had a pointer to multiple array elements and tried to write to it at comptime, it was incorrectly treated as a pointer to one specific array value, leading to an assertion down the line. If we try to mutate a value at an elem_ptr larger than the element type, we need to perform a modification to multiple array elements. This solution isn't ideal, since it will result in storePtrVal serializing the whole array, modifying the relevant parts, and storing it back. Ideally, it would only take the required elements. However, this change would have been more complex, and this is a fairly rare operation (nobody ever ran into the bug before after all), so it doesn't matter all that much.

3 files changed, 42 insertions(+), 10 deletions(-)

src/Sema.zig+17
...@@ -26648,6 +26648,23 @@ fn beginComptimePtrMutation(...@@ -26648,6 +26648,23 @@ fn beginComptimePtrMutation(
26648 });26648 });
26649 }26649 }
26650 const elem_ty = parent.ty.childType();26650 const elem_ty = parent.ty.childType();
26651
26652 // We might have a pointer to multiple elements of the array (e.g. a pointer
26653 // to a sub-array). In this case, we just have to reinterpret the relevant
26654 // bytes of the whole array rather than any single element.
26655 const elem_abi_size_u64 = try sema.typeAbiSize(elem_ptr.elem_ty);
26656 if (elem_abi_size_u64 < try sema.typeAbiSize(ptr_elem_ty)) {
26657 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
26658 return .{
26659 .decl_ref_mut = parent.decl_ref_mut,
26660 .pointee = .{ .reinterpret = .{
26661 .val_ptr = val_ptr,
26662 .byte_offset = elem_abi_size * elem_ptr.index,
26663 } },
26664 .ty = parent.ty,
26665 };
26666 }
26667
26651 switch (val_ptr.tag()) {26668 switch (val_ptr.tag()) {
26652 .undef => {26669 .undef => {
26653 // An array has been initialized to undefined at comptime and now we26670 // An array has been initialized to undefined at comptime and now we
test/behavior/array.zig+17-10
...@@ -48,16 +48,23 @@ fn getArrayLen(a: []const u32) usize {...@@ -48,16 +48,23 @@ fn getArrayLen(a: []const u32) usize {
48test "array concat with undefined" {48test "array concat with undefined" {
49 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO49 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5050
51 {51 const S = struct {
52 var array = "hello".* ++ @as([5]u8, undefined);52 fn doTheTest() !void {
53 array[5..10].* = "world".*;53 {
54 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));54 var array = "hello".* ++ @as([5]u8, undefined);
55 }55 array[5..10].* = "world".*;
56 {56 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
57 var array = @as([5]u8, undefined) ++ "world".*;57 }
58 array[0..5].* = "hello".*;58 {
59 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));59 var array = @as([5]u8, undefined) ++ "world".*;
60 }60 array[0..5].* = "hello".*;
61 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
62 }
63 }
64 };
65
66 try S.doTheTest();
67 comptime try S.doTheTest();
61}68}
6269
63test "array concat with tuple" {70test "array concat with tuple" {
test/behavior/comptime_memory.zig+8
...@@ -412,3 +412,11 @@ test "bitcast packed union to integer" {...@@ -412,3 +412,11 @@ test "bitcast packed union to integer" {
412 try testing.expectEqual(@as(u2, 2), cast_b);412 try testing.expectEqual(@as(u2, 2), cast_b);
413 }413 }
414}414}
415
416test "mutate entire slice at comptime" {
417 comptime {
418 var buf: [3]u8 = undefined;
419 const x: [2]u8 = .{ 1, 2 }; // Avoid RLS
420 buf[1..3].* = x;
421 }
422}