authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-25 20:29:12-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-25 22:33:48-07:00
logb82081e7092a6198e6f8c65524a5829e2e08527b
tree8d49d1559cbb966fd0cee6c06df7c5a6f20981b7
parentab88165326abfd81c5046e8c064bd6603198ed94

Sema: implement array concatenation with runtime operands


2 files changed, 161 insertions(+), 63 deletions(-)

src/Sema.zig+123-57
......@@ -9255,6 +9255,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
92559255 const rhs = try sema.resolveInst(extra.rhs);
92569256 const lhs_ty = sema.typeOf(lhs);
92579257 const rhs_ty = sema.typeOf(rhs);
9258 const src = inst_data.src();
92589259
92599260 if (lhs_ty.isTuple() and rhs_ty.isTuple()) {
92609261 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
92679268 return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty.fmt(sema.mod)});
92689269 const rhs_info = (try sema.getArrayCatInfo(block, rhs_src, rhs)) orelse
92699270 return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty.fmt(sema.mod)});
9270 if (!lhs_info.elem_type.eql(rhs_info.elem_type, sema.mod)) {
9271 return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{
9272 lhs_info.elem_type.fmt(sema.mod), rhs_ty.fmt(sema.mod),
9271
9272 const resolved_elem_ty = t: {
9273 var trash_block = block.makeSubBlock();
9274 trash_block.is_comptime = false;
9275 defer trash_block.instructions.deinit(sema.gpa);
9276
9277 const instructions = [_]Air.Inst.Ref{
9278 try trash_block.addBitCast(lhs_info.elem_type, .void_value),
9279 try trash_block.addBitCast(rhs_info.elem_type, .void_value),
9280 };
9281 break :t try sema.resolvePeerTypes(block, src, &instructions, .{
9282 .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
92739283 });
9274 }
9284 };
92759285
9276 // When there is a sentinel mismatch, no sentinel on the result. The type system
9277 // will catch this if it is a problem.
9278 var res_sent: ?Value = null;
9279 if (rhs_info.sentinel != null and lhs_info.sentinel != null) {
9280 if (rhs_info.sentinel.?.eql(lhs_info.sentinel.?, lhs_info.elem_type, sema.mod)) {
9281 res_sent = lhs_info.sentinel.?;
9286 // When there is a sentinel mismatch, no sentinel on the result.
9287 // Otherwise, use the sentinel value provided by either operand,
9288 // coercing it to the peer-resolved element type.
9289 const res_sent_val: ?Value = s: {
9290 if (lhs_info.sentinel) |lhs_sent_val| {
9291 const lhs_sent = try sema.addConstant(lhs_info.elem_type, lhs_sent_val);
9292 if (rhs_info.sentinel) |rhs_sent_val| {
9293 const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val);
9294 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
9295 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
9296 const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted);
9297 const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted);
9298 if (try sema.valuesEqual(block, src, lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) {
9299 break :s lhs_sent_casted_val;
9300 } else {
9301 break :s null;
9302 }
9303 } else {
9304 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
9305 const lhs_sent_casted_val = try sema.resolveConstValue(block, lhs_src, lhs_sent_casted);
9306 break :s lhs_sent_casted_val;
9307 }
9308 } else {
9309 if (rhs_info.sentinel) |rhs_sent_val| {
9310 const rhs_sent = try sema.addConstant(rhs_info.elem_type, rhs_sent_val);
9311 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
9312 const rhs_sent_casted_val = try sema.resolveConstValue(block, rhs_src, rhs_sent_casted);
9313 break :s rhs_sent_casted_val;
9314 } else {
9315 break :s null;
9316 }
92829317 }
9283 }
9318 };
92849319
9285 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
9320 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
9321 const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len);
9322 const result_len = lhs_len + rhs_len;
9323 const result_ty = try Type.array(sema.arena, result_len, res_sent_val, resolved_elem_ty, sema.mod);
9324 const is_ref = lhs_ty.zigTypeTag() == .Pointer or rhs_ty.zigTypeTag() == .Pointer;
9325
9326 const runtime_src = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| rs: {
92869327 if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {
9287 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
9288 const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len);
9289 const final_len = lhs_len + rhs_len;
9290 const final_len_including_sent = final_len + @boolToInt(res_sent != null);
9291 const lhs_single_ptr = lhs_ty.isSinglePointer();
9292 const rhs_single_ptr = rhs_ty.isSinglePointer();
9293 const lhs_sub_val = if (lhs_single_ptr) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val;
9294 const rhs_sub_val = if (rhs_single_ptr) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val;
9295 var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded);
9296 defer anon_decl.deinit();
9328 const lhs_sub_val = if (lhs_ty.isSinglePointer())
9329 (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
9330 else
9331 lhs_val;
92979332
9298 const buf = try anon_decl.arena().alloc(Value, final_len_including_sent);
9299 {
9300 var i: usize = 0;
9301 while (i < lhs_len) : (i += 1) {
9302 const val = try lhs_sub_val.elemValue(sema.mod, sema.arena, i);
9303 buf[i] = try val.copy(anon_decl.arena());
9304 }
9333 const rhs_sub_val = if (rhs_ty.isSinglePointer())
9334 (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).?
9335 else
9336 rhs_val;
9337
9338 const final_len_including_sent = result_len + @boolToInt(res_sent_val != null);
9339 const element_vals = try sema.arena.alloc(Value, final_len_including_sent);
9340 var elem_i: usize = 0;
9341 while (elem_i < lhs_len) : (elem_i += 1) {
9342 element_vals[elem_i] = try lhs_sub_val.elemValue(sema.mod, sema.arena, elem_i);
93059343 }
9306 {
9307 var i: usize = 0;
9308 while (i < rhs_len) : (i += 1) {
9309 const val = try rhs_sub_val.elemValue(sema.mod, sema.arena, i);
9310 buf[lhs_len + i] = try val.copy(anon_decl.arena());
9311 }
9344 while (elem_i < result_len) : (elem_i += 1) {
9345 element_vals[elem_i] = try rhs_sub_val.elemValue(sema.mod, sema.arena, elem_i - lhs_len);
93129346 }
9313 const ty = if (res_sent) |rs| ty: {
9314 buf[final_len] = try rs.copy(anon_decl.arena());
9315 break :ty try Type.Tag.array_sentinel.create(anon_decl.arena(), .{
9316 .len = final_len,
9317 .elem_type = try lhs_info.elem_type.copy(anon_decl.arena()),
9318 .sentinel = try rs.copy(anon_decl.arena()),
9319 });
9320 } else try Type.Tag.array.create(anon_decl.arena(), .{
9321 .len = final_len,
9322 .elem_type = try lhs_info.elem_type.copy(anon_decl.arena()),
9323 });
9324 const val = try Value.Tag.aggregate.create(anon_decl.arena(), buf);
9325 const decl = try anon_decl.finish(ty, val, 0);
9326 if (lhs_ty.zigTypeTag() == .Pointer or rhs_ty.zigTypeTag() == .Pointer) {
9327 return sema.analyzeDeclRef(decl);
9328 } else {
9329 return sema.analyzeDeclVal(block, .unneeded, decl);
9347 if (res_sent_val) |sent_val| {
9348 element_vals[result_len] = sent_val;
93309349 }
9331 } else {
9332 return sema.fail(block, lhs_src, "TODO runtime array_cat", .{});
9350 const val = try Value.Tag.aggregate.create(sema.arena, element_vals);
9351 return sema.addConstantMaybeRef(block, src, result_ty, val, is_ref);
9352 } else break :rs rhs_src;
9353 } else lhs_src;
9354
9355 try sema.requireRuntimeBlock(block, runtime_src);
9356
9357 if (is_ref) {
9358 const target = sema.mod.getTarget();
9359 const alloc_ty = try Type.ptr(sema.arena, sema.mod, .{
9360 .pointee_type = result_ty,
9361 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
9362 });
9363 const alloc = try block.addTy(.alloc, alloc_ty);
9364 const elem_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{
9365 .pointee_type = resolved_elem_ty,
9366 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
9367 });
9368
9369 var elem_i: usize = 0;
9370 while (elem_i < lhs_len) : (elem_i += 1) {
9371 const elem_index = try sema.addIntUnsigned(Type.usize, elem_i);
9372 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
9373 const init = try sema.elemVal(block, lhs_src, lhs, elem_index, src);
9374 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
93339375 }
9334 } else {
9335 return sema.fail(block, lhs_src, "TODO runtime array_cat", .{});
9376 while (elem_i < result_len) : (elem_i += 1) {
9377 const elem_index = try sema.addIntUnsigned(Type.usize, elem_i);
9378 const rhs_index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len);
9379 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
9380 const init = try sema.elemVal(block, rhs_src, rhs, rhs_index, src);
9381 try sema.storePtr2(block, src, elem_ptr, src, init, rhs_src, .store);
9382 }
9383
9384 return alloc;
93369385 }
9386
9387 const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
9388 {
9389 var elem_i: usize = 0;
9390 while (elem_i < lhs_len) : (elem_i += 1) {
9391 const index = try sema.addIntUnsigned(Type.usize, elem_i);
9392 const init = try sema.elemVal(block, lhs_src, lhs, index, src);
9393 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, lhs_src);
9394 }
9395 while (elem_i < result_len) : (elem_i += 1) {
9396 const index = try sema.addIntUnsigned(Type.usize, elem_i - lhs_len);
9397 const init = try sema.elemVal(block, rhs_src, rhs, index, src);
9398 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, rhs_src);
9399 }
9400 }
9401
9402 return block.addAggregateInit(result_ty, element_refs);
93379403}
93389404
93399405fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref) !?Type.ArrayInfo {
......@@ -24947,7 +25013,7 @@ fn valuesEqual(
2494725013}
2494825014
2494925015/// Asserts the values are comparable vectors of type `ty`.
24950pub fn compareVector(
25016fn compareVector(
2495125017 sema: *Sema,
2495225018 block: *Block,
2495325019 src: LazySrcLoc,
test/behavior/eval.zig+38-6
......@@ -733,17 +733,49 @@ test "*align(1) u16 is the same as *align(1:0:2) u16" {
733733 }
734734}
735735
736test "array concatenation forces comptime" {
737 if (builtin.zig_backend != .stage1) {
738 // note: our plan is to change the language to support runtime array
739 // concatenation instead of making this test pass.
740 return error.SkipZigTest; // TODO
741 }
736test "array concatenation of function calls" {
737 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
738 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
739 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
742740
743741 var a = oneItem(3) ++ oneItem(4);
744742 try expect(std.mem.eql(i32, &a, &[_]i32{ 3, 4 }));
745743}
746744
745test "array concatenation peer resolves element types - value" {
746 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
747 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
748 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
749 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
750
751 var a = [2]u3{ 1, 7 };
752 var b = [3]u8{ 200, 225, 255 };
753 var c = a ++ b;
754 try expect(@TypeOf(c) == [5]u8);
755 try expect(c[0] == 1);
756 try expect(c[1] == 7);
757 try expect(c[2] == 200);
758 try expect(c[3] == 225);
759 try expect(c[4] == 255);
760}
761
762test "array concatenation peer resolves element types - pointer" {
763 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
764 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
765 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
766 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
767
768 var a = [2]u3{ 1, 7 };
769 var b = [3]u8{ 200, 225, 255 };
770 var c = &a ++ &b;
771 try expect(@TypeOf(c) == *[5]u8);
772 try expect(c[0] == 1);
773 try expect(c[1] == 7);
774 try expect(c[2] == 200);
775 try expect(c[3] == 225);
776 try expect(c[4] == 255);
777}
778
747779test "array multiplication forces comptime" {
748780 if (builtin.zig_backend != .stage1) {
749781 // note: our plan is to change the language to support runtime array