aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2023-03-03 21:13:31 +0000
committerVeikka Tuominen <git@vexu.eu>2023-03-14 13:06:23 +0200
commit1e6d7f77639d52b61b2852cf0de19e2b5a50f31f (patch)
treea921304e7e045c77279504dc53fe11a8925b589a
parent5a12d00708df019fa510076f8af40d6efcb7c608 (diff)
downloadzig-1e6d7f77639d52b61b2852cf0de19e2b5a50f31f.tar.gz
zig-1e6d7f77639d52b61b2852cf0de19e2b5a50f31f.zip
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.
-rw-r--r--src/Sema.zig17
-rw-r--r--test/behavior/array.zig27
-rw-r--r--test/behavior/comptime_memory.zig8
3 files changed, 42 insertions, 10 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index e6652a5d66..8b6c269246 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -26648,6 +26648,23 @@ fn beginComptimePtrMutation(
});
}
const elem_ty = parent.ty.childType();
+
+ // We might have a pointer to multiple elements of the array (e.g. a pointer
+ // to a sub-array). In this case, we just have to reinterpret the relevant
+ // bytes of the whole array rather than any single element.
+ const elem_abi_size_u64 = try sema.typeAbiSize(elem_ptr.elem_ty);
+ if (elem_abi_size_u64 < try sema.typeAbiSize(ptr_elem_ty)) {
+ const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
+ return .{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .pointee = .{ .reinterpret = .{
+ .val_ptr = val_ptr,
+ .byte_offset = elem_abi_size * elem_ptr.index,
+ } },
+ .ty = parent.ty,
+ };
+ }
+
switch (val_ptr.tag()) {
.undef => {
// An array has been initialized to undefined at comptime and now we
diff --git a/test/behavior/array.zig b/test/behavior/array.zig
index a5ecd6f115..3d711357f3 100644
--- a/test/behavior/array.zig
+++ b/test/behavior/array.zig
@@ -48,16 +48,23 @@ fn getArrayLen(a: []const u32) usize {
test "array concat with undefined" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
- {
- var array = "hello".* ++ @as([5]u8, undefined);
- array[5..10].* = "world".*;
- try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
- }
- {
- var array = @as([5]u8, undefined) ++ "world".*;
- array[0..5].* = "hello".*;
- try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
- }
+ const S = struct {
+ fn doTheTest() !void {
+ {
+ var array = "hello".* ++ @as([5]u8, undefined);
+ array[5..10].* = "world".*;
+ try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
+ }
+ {
+ var array = @as([5]u8, undefined) ++ "world".*;
+ array[0..5].* = "hello".*;
+ try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
+ }
+ }
+ };
+
+ try S.doTheTest();
+ comptime try S.doTheTest();
}
test "array concat with tuple" {
diff --git a/test/behavior/comptime_memory.zig b/test/behavior/comptime_memory.zig
index a4f9f2f7a9..71d177395b 100644
--- a/test/behavior/comptime_memory.zig
+++ b/test/behavior/comptime_memory.zig
@@ -412,3 +412,11 @@ test "bitcast packed union to integer" {
try testing.expectEqual(@as(u2, 2), cast_b);
}
}
+
+test "mutate entire slice at comptime" {
+ comptime {
+ var buf: [3]u8 = undefined;
+ const x: [2]u8 = .{ 1, 2 }; // Avoid RLS
+ buf[1..3].* = x;
+ }
+}