diff --git a/src/Sema.zig b/src/Sema.zig index ac9886fa62..89955e5ef4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -28115,6 +28115,50 @@ 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(mod); + const src_len = src_ty.arrayLen(mod); + if (dest_len != src_len) { + return InMemoryCoercionResult{ .array_len = .{ + .actual = src_len, + .wanted = dest_len, + } }; + } + + const dest_elem_ty = dest_ty.childType(mod); + const src_elem_ty = src_ty.childType(mod); + const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src); + if (child != .ok) { + return InMemoryCoercionResult{ .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(mod); + if (dest_info.sentinel != null) { + return InMemoryCoercionResult{ .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(mod); + const elem_abi_byte_size = dest_elem_ty.abiSize(mod); + 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)) { @@ -30005,10 +30049,22 @@ fn coerceArrayLike( ) !Air.Inst.Ref { const mod = sema.mod; const inst_ty = sema.typeOf(inst); - const inst_len = inst_ty.arrayLen(mod); - const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod)); const target = mod.getTarget(); + // try coercion of the whole array + const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src); + if (in_memory_result == .ok) { + if (try sema.resolveMaybeUndefVal(inst)) |inst_val| { + // These types share the same comptime value representation. + return sema.coerceInMemory(inst_val, dest_ty); + } + try sema.requireRuntimeBlock(block, inst_src, null); + return block.addBitCast(dest_ty, inst); + } + + // otherwise, try element by element + const inst_len = inst_ty.arrayLen(mod); + const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod)); if (dest_len != inst_len) { const msg = msg: { const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{ @@ -30023,17 +30079,6 @@ fn coerceArrayLike( } const dest_elem_ty = dest_ty.childType(mod); - const inst_elem_ty = inst_ty.childType(mod); - const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src); - if (in_memory_result == .ok) { - if (try sema.resolveMaybeUndefVal(inst)) |inst_val| { - // These types share the same comptime value representation. - return sema.coerceInMemory(inst_val, dest_ty); - } - try sema.requireRuntimeBlock(block, inst_src, null); - return block.addBitCast(dest_ty, inst); - } - const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); var runtime_src: ?LazySrcLoc = null; diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index 6819b8d58a..0b95caa0d3 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -176,6 +176,43 @@ test "array to vector" { try comptime S.doTheTest(); } +test "array vector coercion - odd sizes" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + 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_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + + const S = struct { + fn doTheTest() !void { + var foo1: i48 = 124578; + var vec1: @Vector(2, i48) = [2]i48{ foo1, 1 }; + var arr1: [2]i48 = vec1; + try expect(vec1[0] == foo1 and vec1[1] == 1); + try expect(arr1[0] == foo1 and arr1[1] == 1); + + var foo2: u4 = 5; + var vec2: @Vector(2, u4) = [2]u4{ foo2, 1 }; + var arr2: [2]u4 = vec2; + try expect(vec2[0] == foo2 and vec2[1] == 1); + try expect(arr2[0] == foo2 and arr2[1] == 1); + + var foo3: u13 = 13; + var vec3: @Vector(3, u13) = [3]u13{ foo3, 0, 1 }; + var arr3: [3]u13 = vec3; + try expect(vec3[0] == foo3 and vec3[1] == 0 and vec3[2] == 1); + try expect(arr3[0] == foo3 and arr3[1] == 0 and arr3[2] == 1); + + var arr4 = [4:0]u24{ foo3, foo2, 0, 1 }; + var vec4: @Vector(4, u24) = arr4; + try expect(vec4[0] == foo3 and vec4[1] == foo2 and vec4[2] == 0 and vec4[3] == 1); + } + }; + try S.doTheTest(); + try comptime S.doTheTest(); +} + test "array to vector with element type coercion" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64 and