From 6673b476855931137119ae92e91fdcb2822e9c4f Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 7 Sep 2025 17:28:01 -0700
Subject: [PATCH 1/6] frontend: vectors and arrays no longer support in-memory
coercion
closes #25172
---
doc/langref.html.in | 8 +++++++
doc/langref/test_vector.zig | 2 +-
src/Sema.zig | 44 -------------------------------------
3 files changed, 9 insertions(+), 45 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index cf1c48f9e0..43d350ca2b 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -1969,6 +1969,14 @@ or
{#see_also|@splat|@shuffle|@select|@reduce#}
+ {#header_open|Relationship with Arrays#}
+ Vectors and {#link|Arrays#} each have a well-defined bit layout
+ and therefore support {#link|@bitCast#} between each other. {#link|Type Coercion#} implicitly peforms
+ {#syntax#}@bitCast{#endsyntax#}.
+ Arrays have well-defined byte layout, but vectors do not, making {#link|@ptrCast#} between
+ them {#link|Illegal Behavior#}.
+ {#header_close#}
+
{#header_open|Destructuring Vectors#}
Vectors can be destructured:
diff --git a/doc/langref/test_vector.zig b/doc/langref/test_vector.zig
index cd1e860886..933a282910 100644
--- a/doc/langref/test_vector.zig
+++ b/doc/langref/test_vector.zig
@@ -17,7 +17,7 @@ test "Basic vector usage" {
}
test "Conversion between vectors, arrays, and slices" {
- // Vectors and fixed-length arrays can be automatically assigned back and forth
+ // Vectors can be coerced to arrays, and vice versa.
const arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 };
const vec: @Vector(4, f32) = arr1;
const arr2: [4]f32 = vec;
diff --git a/src/Sema.zig b/src/Sema.zig
index 587c6b2d70..51b93fb308 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -29649,50 +29649,6 @@ pub fn coerceInMemoryAllowed(
return .ok;
}
- // Arrays <-> Vectors
- if ((dest_tag == .vector and src_tag == .array) or
- (dest_tag == .array and src_tag == .vector))
- {
- const dest_len = dest_ty.arrayLen(zcu);
- const src_len = src_ty.arrayLen(zcu);
- if (dest_len != src_len) {
- return .{ .array_len = .{
- .actual = src_len,
- .wanted = dest_len,
- } };
- }
-
- const dest_elem_ty = dest_ty.childType(zcu);
- const src_elem_ty = src_ty.childType(zcu);
- const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null);
- if (child != .ok) {
- return .{ .array_elem = .{
- .child = try child.dupe(sema.arena),
- .actual = src_elem_ty,
- .wanted = dest_elem_ty,
- } };
- }
-
- if (dest_tag == .array) {
- const dest_info = dest_ty.arrayInfo(zcu);
- if (dest_info.sentinel != null) {
- return .{ .array_sentinel = .{
- .actual = Value.@"unreachable",
- .wanted = dest_info.sentinel.?,
- .ty = dest_info.elem_type,
- } };
- }
- }
-
- // The memory layout of @Vector(N, iM) is the same as the integer type i(N*M),
- // that is to say, the padding bits are not in the same place as the array [N]iM.
- // If there's no padding, the bitcast is possible.
- const elem_bit_size = dest_elem_ty.bitSize(zcu);
- const elem_abi_byte_size = dest_elem_ty.abiSize(zcu);
- if (elem_abi_byte_size * 8 == elem_bit_size)
- return .ok;
- }
-
// Optionals
if (dest_tag == .optional and src_tag == .optional) {
if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
From 5701617b27666f87fbf9898a94555c6c419778ce Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 7 Sep 2025 20:11:37 -0700
Subject: [PATCH 2/6] wasm backend: disable failing behavior tests
---
test/behavior/cast.zig | 1 +
test/behavior/vector.zig | 1 +
2 files changed, 2 insertions(+)
diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig
index 7eee20e3e0..700bfb0991 100644
--- a/test/behavior/cast.zig
+++ b/test/behavior/cast.zig
@@ -1737,6 +1737,7 @@ test "peer type resolution: array and vector with same child type" {
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
var arr: [2]u32 = .{ 0, 1 };
var vec: @Vector(2, u32) = .{ 2, 3 };
diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig
index 3012830079..4de7c42d49 100644
--- a/test/behavior/vector.zig
+++ b/test/behavior/vector.zig
@@ -196,6 +196,7 @@ test "array to vector" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
const S = struct {
fn doTheTest() !void {
From 04bd30f021d4f05cfcf0f72d9be5568a6b6e7407 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 7 Sep 2025 20:22:38 -0700
Subject: [PATCH 3/6] std.debug.assertAligned: support const pointers
---
lib/std/debug.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
index 18b1568b19..bd849a32d3 100644
--- a/lib/std/debug.zig
+++ b/lib/std/debug.zig
@@ -569,7 +569,7 @@ pub fn assertReadable(slice: []const volatile u8) void {
/// Invokes detectable illegal behavior when the provided array is not aligned
/// to the provided amount.
pub fn assertAligned(ptr: anytype, comptime alignment: std.mem.Alignment) void {
- const aligned_ptr: *align(alignment.toByteUnits()) anyopaque = @ptrCast(@alignCast(ptr));
+ const aligned_ptr: *align(alignment.toByteUnits()) const anyopaque = @ptrCast(@alignCast(ptr));
_ = aligned_ptr;
}
From 2d9df0bb1a79267b4bc6b91125d4d2eebc31bc26 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 7 Sep 2025 20:22:56 -0700
Subject: [PATCH 4/6] behavior tests: remove one dependency on std lib
---
test/behavior/union.zig | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/test/behavior/union.zig b/test/behavior/union.zig
index 1807cf14cf..d581d25a1a 100644
--- a/test/behavior/union.zig
+++ b/test/behavior/union.zig
@@ -103,7 +103,11 @@ test "basic extern unions" {
var foo = FooExtern{ .int = 1 };
try expect(foo.int == 1);
foo.str.slice = "Well";
- try expect(std.mem.eql(u8, std.mem.sliceTo(foo.str.slice, 0), "Well"));
+ try expect(foo.str.slice[0] == 'W');
+ try expect(foo.str.slice[1] == 'e');
+ try expect(foo.str.slice[2] == 'l');
+ try expect(foo.str.slice[3] == 'l');
+ try expect(foo.str.slice[4] == 0);
}
const ExternPtrOrInt = extern union {
From dca4c302dd323f093c53b354ad64d504ed7a2dea Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 7 Sep 2025 20:23:19 -0700
Subject: [PATCH 5/6] std.mem.indexOfSentinel: eliminate unnecessary `@ptrCast`
it was always unnecessary but now it's illegal
---
lib/std/mem.zig | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lib/std/mem.zig b/lib/std/mem.zig
index e31e93aae1..8ca34aa51c 100644
--- a/lib/std/mem.zig
+++ b/lib/std/mem.zig
@@ -1156,10 +1156,10 @@ pub fn indexOfSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]co
}
}
- assert(std.mem.isAligned(@intFromPtr(&p[i]), block_size));
+ std.debug.assertAligned(&p[i], .fromByteUnits(block_size));
while (true) {
- const block: *const Block = @ptrCast(@alignCast(p[i..][0..block_len]));
- const matches = block.* == mask;
+ const block: Block = p[i..][0..block_len].*;
+ const matches = block == mask;
if (@reduce(.Or, matches)) {
return i + std.simd.firstTrue(matches).?;
}
From 7666d5fc2653023670f6e61b6d19b0ba15750d67 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 7 Sep 2025 23:03:06 -0700
Subject: [PATCH 6/6] add compile error test case
---
.../in_memory_coerce_vector_to_array.zig | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
create mode 100644 test/cases/compile_errors/in_memory_coerce_vector_to_array.zig
diff --git a/test/cases/compile_errors/in_memory_coerce_vector_to_array.zig b/test/cases/compile_errors/in_memory_coerce_vector_to_array.zig
new file mode 100644
index 0000000000..59ecf149dc
--- /dev/null
+++ b/test/cases/compile_errors/in_memory_coerce_vector_to_array.zig
@@ -0,0 +1,16 @@
+export fn entry() void {
+ _ = foo() catch {};
+}
+fn foo() anyerror![4]u32 {
+ return bar();
+}
+fn bar() anyerror!@Vector(4, u32) {
+ return .{ 1, 2, 3, 4 };
+}
+// error
+// backend=stage2
+// target=native
+//
+// :5:15: error: expected type 'anyerror![4]u32', found 'anyerror!@Vector(4, u32)'
+// :5:15: note: error union payload '@Vector(4, u32)' cannot cast into error union payload '[4]u32'
+// :4:18: note: function return type declared here