authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-08 03:59:38-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-09-08 03:59:38-07:00
log05cff8a558fda0e974ea7d00f26f8f4ff6e1195f
treec99a4d2366532e5937ca7f944eb0008601a4edce
parent426d65d700012b27ffa652258554032f171ad77d
parent7666d5fc2653023670f6e61b6d19b0ba15750d67
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #25186 from ziglang/vector-memory-coercion

frontend: vectors and arrays no longer support in-memory coercion

9 files changed, 36 insertions(+), 50 deletions(-)

doc/langref.html.in+8
...@@ -1969,6 +1969,14 @@ or...@@ -1969,6 +1969,14 @@ or
1969 </p>1969 </p>
1970 {#see_also|@splat|@shuffle|@select|@reduce#}1970 {#see_also|@splat|@shuffle|@select|@reduce#}
19711971
1972 {#header_open|Relationship with Arrays#}
1973 <p>Vectors and {#link|Arrays#} each have a well-defined <strong>bit layout</strong>
1974 and therefore support {#link|@bitCast#} between each other. {#link|Type Coercion#} implicitly peforms
1975 {#syntax#}@bitCast{#endsyntax#}.</p>
1976 <p>Arrays have well-defined byte layout, but vectors do not, making {#link|@ptrCast#} between
1977 them {#link|Illegal Behavior#}.</p>
1978 {#header_close#}
1979
1972 {#header_open|Destructuring Vectors#}1980 {#header_open|Destructuring Vectors#}
1973 <p>1981 <p>
1974 Vectors can be destructured:1982 Vectors can be destructured:
doc/langref/test_vector.zig+1-1
...@@ -17,7 +17,7 @@ test "Basic vector usage" {...@@ -17,7 +17,7 @@ test "Basic vector usage" {
17}17}
1818
19test "Conversion between vectors, arrays, and slices" {19test "Conversion between vectors, arrays, and slices" {
20 // Vectors and fixed-length arrays can be automatically assigned back and forth20 // Vectors can be coerced to arrays, and vice versa.
21 const arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 };21 const arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 };
22 const vec: @Vector(4, f32) = arr1;22 const vec: @Vector(4, f32) = arr1;
23 const arr2: [4]f32 = vec;23 const arr2: [4]f32 = vec;
lib/std/debug.zig+1-1
...@@ -569,7 +569,7 @@ pub fn assertReadable(slice: []const volatile u8) void {...@@ -569,7 +569,7 @@ pub fn assertReadable(slice: []const volatile u8) void {
569/// Invokes detectable illegal behavior when the provided array is not aligned569/// Invokes detectable illegal behavior when the provided array is not aligned
570/// to the provided amount.570/// to the provided amount.
571pub fn assertAligned(ptr: anytype, comptime alignment: std.mem.Alignment) void {571pub fn assertAligned(ptr: anytype, comptime alignment: std.mem.Alignment) void {
572 const aligned_ptr: *align(alignment.toByteUnits()) anyopaque = @ptrCast(@alignCast(ptr));572 const aligned_ptr: *align(alignment.toByteUnits()) const anyopaque = @ptrCast(@alignCast(ptr));
573 _ = aligned_ptr;573 _ = aligned_ptr;
574}574}
575575
lib/std/mem.zig+3-3
...@@ -1156,10 +1156,10 @@ pub fn indexOfSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]co...@@ -1156,10 +1156,10 @@ pub fn indexOfSentinel(comptime T: type, comptime sentinel: T, p: [*:sentinel]co
1156 }1156 }
1157 }1157 }
11581158
1159 assert(std.mem.isAligned(@intFromPtr(&p[i]), block_size));1159 std.debug.assertAligned(&p[i], .fromByteUnits(block_size));
1160 while (true) {1160 while (true) {
1161 const block: *const Block = @ptrCast(@alignCast(p[i..][0..block_len]));1161 const block: Block = p[i..][0..block_len].*;
1162 const matches = block.* == mask;1162 const matches = block == mask;
1163 if (@reduce(.Or, matches)) {1163 if (@reduce(.Or, matches)) {
1164 return i + std.simd.firstTrue(matches).?;1164 return i + std.simd.firstTrue(matches).?;
1165 }1165 }
src/Sema.zig-44
...@@ -29649,50 +29649,6 @@ pub fn coerceInMemoryAllowed(...@@ -29649,50 +29649,6 @@ pub fn coerceInMemoryAllowed(
29649 return .ok;29649 return .ok;
29650 }29650 }
2965129651
29652 // Arrays <-> Vectors
29653 if ((dest_tag == .vector and src_tag == .array) or
29654 (dest_tag == .array and src_tag == .vector))
29655 {
29656 const dest_len = dest_ty.arrayLen(zcu);
29657 const src_len = src_ty.arrayLen(zcu);
29658 if (dest_len != src_len) {
29659 return .{ .array_len = .{
29660 .actual = src_len,
29661 .wanted = dest_len,
29662 } };
29663 }
29664
29665 const dest_elem_ty = dest_ty.childType(zcu);
29666 const src_elem_ty = src_ty.childType(zcu);
29667 const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null);
29668 if (child != .ok) {
29669 return .{ .array_elem = .{
29670 .child = try child.dupe(sema.arena),
29671 .actual = src_elem_ty,
29672 .wanted = dest_elem_ty,
29673 } };
29674 }
29675
29676 if (dest_tag == .array) {
29677 const dest_info = dest_ty.arrayInfo(zcu);
29678 if (dest_info.sentinel != null) {
29679 return .{ .array_sentinel = .{
29680 .actual = Value.@"unreachable",
29681 .wanted = dest_info.sentinel.?,
29682 .ty = dest_info.elem_type,
29683 } };
29684 }
29685 }
29686
29687 // The memory layout of @Vector(N, iM) is the same as the integer type i(N*M),
29688 // that is to say, the padding bits are not in the same place as the array [N]iM.
29689 // If there's no padding, the bitcast is possible.
29690 const elem_bit_size = dest_elem_ty.bitSize(zcu);
29691 const elem_abi_byte_size = dest_elem_ty.abiSize(zcu);
29692 if (elem_abi_byte_size * 8 == elem_bit_size)
29693 return .ok;
29694 }
29695
29696 // Optionals29652 // Optionals
29697 if (dest_tag == .optional and src_tag == .optional) {29653 if (dest_tag == .optional and src_tag == .optional) {
29698 if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {29654 if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
test/behavior/cast.zig+1
...@@ -1737,6 +1737,7 @@ test "peer type resolution: array and vector with same child type" {...@@ -1737,6 +1737,7 @@ test "peer type resolution: array and vector with same child type" {
1737 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1737 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1738 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;1738 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1739 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO1739 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
1740 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
17401741
1741 var arr: [2]u32 = .{ 0, 1 };1742 var arr: [2]u32 = .{ 0, 1 };
1742 var vec: @Vector(2, u32) = .{ 2, 3 };1743 var vec: @Vector(2, u32) = .{ 2, 3 };
test/behavior/union.zig+5-1
...@@ -103,7 +103,11 @@ test "basic extern unions" {...@@ -103,7 +103,11 @@ test "basic extern unions" {
103 var foo = FooExtern{ .int = 1 };103 var foo = FooExtern{ .int = 1 };
104 try expect(foo.int == 1);104 try expect(foo.int == 1);
105 foo.str.slice = "Well";105 foo.str.slice = "Well";
106 try expect(std.mem.eql(u8, std.mem.sliceTo(foo.str.slice, 0), "Well"));106 try expect(foo.str.slice[0] == 'W');
107 try expect(foo.str.slice[1] == 'e');
108 try expect(foo.str.slice[2] == 'l');
109 try expect(foo.str.slice[3] == 'l');
110 try expect(foo.str.slice[4] == 0);
107}111}
108112
109const ExternPtrOrInt = extern union {113const ExternPtrOrInt = extern union {
test/behavior/vector.zig+1
...@@ -196,6 +196,7 @@ test "array to vector" {...@@ -196,6 +196,7 @@ test "array to vector" {
196 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO196 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
197 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO197 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
198 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;198 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
199 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
199200
200 const S = struct {201 const S = struct {
201 fn doTheTest() !void {202 fn doTheTest() !void {
test/cases/compile_errors/in_memory_coerce_vector_to_array.zig created+16
...@@ -0,0 +1,16 @@
1export fn entry() void {
2 _ = foo() catch {};
3}
4fn foo() anyerror![4]u32 {
5 return bar();
6}
7fn bar() anyerror!@Vector(4, u32) {
8 return .{ 1, 2, 3, 4 };
9}
10// error
11// backend=stage2
12// target=native
13//
14// :5:15: error: expected type 'anyerror![4]u32', found 'anyerror!@Vector(4, u32)'
15// :5:15: note: error union payload '@Vector(4, u32)' cannot cast into error union payload '[4]u32'
16// :4:18: note: function return type declared here