diff --git a/src/Sema.zig b/src/Sema.zig index 80184cb287..648174dcd1 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9255,6 +9255,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs = try sema.resolveInst(extra.rhs); const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); + const src = inst_data.src(); if (lhs_ty.isTuple() and rhs_ty.isTuple()) { return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs); @@ -9267,73 +9268,138 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(sema.mod)}); const rhs_info = (try sema.getArrayCatInfo(block, rhs_src, rhs)) orelse return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty.fmt(sema.mod)}); - if (!lhs_info.elem_type.eql(rhs_info.elem_type, sema.mod)) { - return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{ - lhs_info.elem_type.fmt(sema.mod), rhs_ty.fmt(sema.mod), + + const resolved_elem_ty = t: { + var trash_block = block.makeSubBlock(); + trash_block.is_comptime = false; + defer trash_block.instructions.deinit(sema.gpa); + + const instructions = [_]Air.Inst.Ref{ + try trash_block.addBitCast(lhs_info.elem_type, .void_value), + try trash_block.addBitCast(rhs_info.elem_type, .void_value), + }; + break :t try sema.resolvePeerTypes(block, src, &instructions, .{ + .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, }); - } + }; - // When there is a sentinel mismatch, no sentinel on the result. The type system - // will catch this if it is a problem. - var res_sent: ?Value = null; - if (rhs_info.sentinel != null and lhs_info.sentinel != null) { - if (rhs_info.sentinel.?.eql(lhs_info.sentinel.?, lhs_info.elem_type, sema.mod)) { - res_sent = lhs_info.sentinel.?; - } - } - - if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { - if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { - const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); - const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len); - const final_len = lhs_len + rhs_len; - const final_len_including_sent = final_len + @boolToInt(res_sent != null); - const lhs_single_ptr = lhs_ty.isSinglePointer(); - const rhs_single_ptr = rhs_ty.isSinglePointer(); - const lhs_sub_val = if (lhs_single_ptr) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; - const rhs_sub_val = if (rhs_single_ptr) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val; - var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded); - defer anon_decl.deinit(); - - const buf = try anon_decl.arena().alloc(Value, final_len_including_sent); - { - var i: usize = 0; - while (i < lhs_len) : (i += 1) { - const val = try lhs_sub_val.elemValue(sema.mod, sema.arena, i); - buf[i] = try val.copy(anon_decl.arena()); + // When there is a sentinel mismatch, no sentinel on the result. + // Otherwise, use the sentinel value provided by either operand, + // coercing it to the peer-resolved element type. + const res_sent_val: ?Value = s: { + if (lhs_info.sentinel) |lhs_sent_val| { + const lhs_sent = try sema.addConstant(lhs_info.elem_type, lhs_sent_val); + if (rhs_info.sentinel) |rhs_sent_val| { + const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val); + const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); + const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); + const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted); + const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted); + if (try sema.valuesEqual(block, src, lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) { + break :s lhs_sent_casted_val; + } else { + break :s null; } - } - { - var i: usize = 0; - while (i < rhs_len) : (i += 1) { - const val = try rhs_sub_val.elemValue(sema.mod, sema.arena, i); - buf[lhs_len + i] = try val.copy(anon_decl.arena()); - } - } - const ty = if (res_sent) |rs| ty: { - buf[final_len] = try rs.copy(anon_decl.arena()); - break :ty try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ - .len = final_len, - .elem_type = try lhs_info.elem_type.copy(anon_decl.arena()), - .sentinel = try rs.copy(anon_decl.arena()), - }); - } else try Type.Tag.array.create(anon_decl.arena(), .{ - .len = final_len, - .elem_type = try lhs_info.elem_type.copy(anon_decl.arena()), - }); - const val = try Value.Tag.aggregate.create(anon_decl.arena(), buf); - const decl = try anon_decl.finish(ty, val, 0); - if (lhs_ty.zigTypeTag() == .Pointer or rhs_ty.zigTypeTag() == .Pointer) { - return sema.analyzeDeclRef(decl); } else { - return sema.analyzeDeclVal(block, .unneeded, decl); + const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); + const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted); + break :s lhs_sent_casted_val; } } else { - return sema.fail(block, lhs_src, "TODO runtime array_cat", .{}); + if (rhs_info.sentinel) |rhs_sent_val| { + const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val); + const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); + const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted); + break :s rhs_sent_casted_val; + } else { + break :s null; + } } - } else { - return sema.fail(block, lhs_src, "TODO runtime array_cat", .{}); + }; + + const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); + const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len); + const result_len = lhs_len + rhs_len; + const result_ty = try Type.array(sema.arena, result_len, res_sent_val, resolved_elem_ty, sema.mod); + const is_ref = lhs_ty.zigTypeTag() == .Pointer or rhs_ty.zigTypeTag() == .Pointer; + + const runtime_src = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| rs: { + if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { + const lhs_sub_val = if (lhs_ty.isSinglePointer()) + (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? + else + lhs_val; + + const rhs_sub_val = if (rhs_ty.isSinglePointer()) + (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? + else + rhs_val; + + const final_len_including_sent = result_len + @boolToInt(res_sent_val != null); + const element_vals = try sema.arena.alloc(Value, final_len_including_sent); + var elem_i: usize = 0; + while (elem_i < lhs_len) : (elem_i += 1) { + element_vals[elem_i] = try lhs_sub_val.elemValue(sema.mod, sema.arena, elem_i); + } + while (elem_i < result_len) : (elem_i += 1) { + element_vals[elem_i] = try rhs_sub_val.elemValue(sema.mod, sema.arena, elem_i - lhs_len); + } + if (res_sent_val) |sent_val| { + element_vals[result_len] = sent_val; + } + const val = try Value.Tag.aggregate.create(sema.arena, element_vals); + return sema.addConstantMaybeRef(block, src, result_ty, val, is_ref); + } else break :rs rhs_src; + } else lhs_src; + + try sema.requireRuntimeBlock(block, runtime_src); + + if (is_ref) { + const target = sema.mod.getTarget(); + const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = result_ty, + .@"addrspace" = target_util.defaultAddressSpace(target, .local), + }); + const alloc = try block.addTy(.alloc, alloc_ty); + const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ + .pointee_type = resolved_elem_ty, + .@"addrspace" = target_util.defaultAddressSpace(target, .local), + }); + + var elem_i: usize = 0; + while (elem_i < lhs_len) : (elem_i += 1) { + const elem_index = try sema.addIntUnsigned(Type.usize, elem_i); + const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); + const init = try sema.elemVal(block, lhs_src, lhs, elem_index, src); + try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); + } + while (elem_i < result_len) : (elem_i += 1) { + const elem_index = try sema.addIntUnsigned(Type.usize, elem_i); + const rhs_index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len); + const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty); + const init = try sema.elemVal(block, rhs_src, rhs, rhs_index, src); + try sema.storePtr2(block, src, elem_ptr, src, init, rhs_src, .store); + } + + return alloc; } + + const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); + { + var elem_i: usize = 0; + while (elem_i < lhs_len) : (elem_i += 1) { + const index = try sema.addIntUnsigned(Type.usize, elem_i); + const init = try sema.elemVal(block, lhs_src, lhs, index, src); + element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, lhs_src); + } + while (elem_i < result_len) : (elem_i += 1) { + const index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len); + const init = try sema.elemVal(block, rhs_src, rhs, index, src); + element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, rhs_src); + } + } + + return block.addAggregateInit(result_ty, element_refs); } fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref) !?Type.ArrayInfo { @@ -24947,7 +25013,7 @@ fn valuesEqual( } /// Asserts the values are comparable vectors of type `ty`. -pub fn compareVector( +fn compareVector( sema: *Sema, block: *Block, src: LazySrcLoc, diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 80d39e00d7..cb0de8ed59 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -733,17 +733,49 @@ test "*align(1) u16 is the same as *align(1:0:2) u16" { } } -test "array concatenation forces comptime" { - if (builtin.zig_backend != .stage1) { - // note: our plan is to change the language to support runtime array - // concatenation instead of making this test pass. - return error.SkipZigTest; // TODO - } +test "array concatenation of function calls" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var a = oneItem(3) ++ oneItem(4); try expect(std.mem.eql(i32, &a, &[_]i32{ 3, 4 })); } +test "array concatenation peer resolves element types - value" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + var a = [2]u3{ 1, 7 }; + var b = [3]u8{ 200, 225, 255 }; + var c = a ++ b; + try expect(@TypeOf(c) == [5]u8); + try expect(c[0] == 1); + try expect(c[1] == 7); + try expect(c[2] == 200); + try expect(c[3] == 225); + try expect(c[4] == 255); +} + +test "array concatenation peer resolves element types - pointer" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + + var a = [2]u3{ 1, 7 }; + var b = [3]u8{ 200, 225, 255 }; + var c = &a ++ &b; + try expect(@TypeOf(c) == *[5]u8); + try expect(c[0] == 1); + try expect(c[1] == 7); + try expect(c[2] == 200); + try expect(c[3] == 225); + try expect(c[4] == 255); +} + test "array multiplication forces comptime" { if (builtin.zig_backend != .stage1) { // note: our plan is to change the language to support runtime array