authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-31 21:52:47-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-08-31 21:52:47-04:00
logd522f925b7f2f7f9d4782bb42eed95d5da4f3e0f
tree8a2d563320852a4fc6540424da3d2c3f8dcc9ce0
parent9d4a6d67aa3b85c64e0b2367d8ab30ae72f64834
parent7cdb30ee95d81798beb3b13c7d2b577ab5434e84
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9603 from g-w1/arrcat

stage2: add array concatenation + multiplication ( ++ and **) at comptime

5 files changed, 142 insertions(+), 13 deletions(-)

src/Sema.zig+122-4
...@@ -5512,16 +5512,134 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr...@@ -5512,16 +5512,134 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
5512 const tracy = trace(@src());5512 const tracy = trace(@src());
5513 defer tracy.end();5513 defer tracy.end();
55145514
5515 _ = inst;5515 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5516 return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayCat", .{});5516 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
5517 const lhs = sema.resolveInst(extra.lhs);
5518 const rhs = sema.resolveInst(extra.rhs);
5519 const lhs_ty = sema.typeOf(lhs);
5520 const rhs_ty = sema.typeOf(rhs);
5521 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
5522 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
5523
5524 const lhs_info = getArrayCatInfo(lhs_ty) orelse
5525 return sema.mod.fail(&block.base, lhs_src, "expected array, found '{}'", .{lhs_ty});
5526 const rhs_info = getArrayCatInfo(rhs_ty) orelse
5527 return sema.mod.fail(&block.base, rhs_src, "expected array, found '{}'", .{rhs_ty});
5528 if (!lhs_info.elem_type.eql(rhs_info.elem_type)) {
5529 return sema.mod.fail(&block.base, rhs_src, "expected array of type '{}', found '{}'", .{ lhs_info.elem_type, rhs_ty });
5530 }
5531
5532 // When there is a sentinel mismatch, no sentinel on the result. The type system
5533 // will catch this if it is a problem.
5534 var res_sent: ?Value = null;
5535 if (rhs_info.sentinel != null and lhs_info.sentinel != null) {
5536 if (rhs_info.sentinel.?.eql(lhs_info.sentinel.?, lhs_info.elem_type)) {
5537 res_sent = lhs_info.sentinel.?;
5538 }
5539 }
5540
5541 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
5542 if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {
5543 const final_len = lhs_info.len + rhs_info.len;
5544 if (lhs_ty.zigTypeTag() == .Pointer) {
5545 var anon_decl = try block.startAnonDecl();
5546 defer anon_decl.deinit();
5547
5548 const lhs_sub_val = (try lhs_val.pointerDeref(anon_decl.arena())).?;
5549 const rhs_sub_val = (try rhs_val.pointerDeref(anon_decl.arena())).?;
5550 const buf = try anon_decl.arena().alloc(Value, final_len);
5551 {
5552 var i: u64 = 0;
5553 while (i < lhs_info.len) : (i += 1) {
5554 const val = try lhs_sub_val.elemValue(sema.arena, i);
5555 buf[i] = try val.copy(anon_decl.arena());
5556 }
5557 }
5558 {
5559 var i: u64 = 0;
5560 while (i < rhs_info.len) : (i += 1) {
5561 const val = try rhs_sub_val.elemValue(sema.arena, i);
5562 buf[lhs_info.len + i] = try val.copy(anon_decl.arena());
5563 }
5564 }
5565 const ty = if (res_sent) |rs|
5566 try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type, .sentinel = rs })
5567 else
5568 try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type });
5569 const val = try Value.Tag.array.create(anon_decl.arena(), buf);
5570 return sema.analyzeDeclRef(try anon_decl.finish(
5571 ty,
5572 val,
5573 ));
5574 }
5575 return sema.mod.fail(&block.base, lhs_src, "TODO array_cat more types of Values", .{});
5576 } else {
5577 return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_cat", .{});
5578 }
5579 } else {
5580 return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_cat", .{});
5581 }
5582}
5583
5584fn getArrayCatInfo(t: Type) ?Type.ArrayInfo {
5585 return switch (t.zigTypeTag()) {
5586 .Array => t.arrayInfo(),
5587 .Pointer => blk: {
5588 const ptrinfo = t.ptrInfo().data;
5589 if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null;
5590 if (ptrinfo.size != .One) return null;
5591 break :blk ptrinfo.pointee_type.arrayInfo();
5592 },
5593 else => null,
5594 };
5517}5595}
55185596
5519fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5597fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
5520 const tracy = trace(@src());5598 const tracy = trace(@src());
5521 defer tracy.end();5599 defer tracy.end();
55225600
5523 _ = inst;5601 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5524 return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayMul", .{});5602 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
5603 const lhs = sema.resolveInst(extra.lhs);
5604 const lhs_ty = sema.typeOf(lhs);
5605 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
5606 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
5607
5608 // In `**` rhs has to be comptime-known, but lhs can be runtime-known
5609 const tomulby = try sema.resolveInt(block, rhs_src, extra.rhs, Type.initTag(.usize));
5610 const mulinfo = getArrayCatInfo(lhs_ty) orelse
5611 return sema.mod.fail(&block.base, lhs_src, "expected array, found '{}'", .{lhs_ty});
5612
5613 const final_len = std.math.mul(u64, mulinfo.len, tomulby) catch return sema.mod.fail(&block.base, rhs_src, "operation results in overflow", .{});
5614 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
5615 if (lhs_ty.zigTypeTag() == .Pointer) {
5616 var anon_decl = try block.startAnonDecl();
5617 defer anon_decl.deinit();
5618 const lhs_sub_val = (try lhs_val.pointerDeref(anon_decl.arena())).?;
5619
5620 const final_ty = if (mulinfo.sentinel) |sent|
5621 try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type, .sentinel = sent })
5622 else
5623 try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type });
5624
5625 const buf = try anon_decl.arena().alloc(Value, final_len);
5626 var i: u64 = 0;
5627 while (i < tomulby) : (i += 1) {
5628 var j: u64 = 0;
5629 while (j < mulinfo.len) : (j += 1) {
5630 const val = try lhs_sub_val.elemValue(sema.arena, j);
5631 buf[mulinfo.len * i + j] = try val.copy(anon_decl.arena());
5632 }
5633 }
5634 const val = try Value.Tag.array.create(anon_decl.arena(), buf);
5635 return sema.analyzeDeclRef(try anon_decl.finish(
5636 final_ty,
5637 val,
5638 ));
5639 }
5640 return sema.mod.fail(&block.base, lhs_src, "TODO array_mul more types of Values", .{});
5641 }
5642 return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_mul", .{});
5525}5643}
55265644
5527fn zirNegate(5645fn zirNegate(
src/codegen/llvm.zig+3-1
...@@ -788,11 +788,13 @@ pub const DeclGen = struct {...@@ -788,11 +788,13 @@ pub const DeclGen = struct {
788 const gpa = self.gpa;788 const gpa = self.gpa;
789 const elem_ty = tv.ty.elemType();789 const elem_ty = tv.ty.elemType();
790 const elem_vals = payload.data;790 const elem_vals = payload.data;
791 const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len);791 const sento = tv.ty.sentinel();
792 const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len + @boolToInt(sento != null));
792 defer gpa.free(llvm_elems);793 defer gpa.free(llvm_elems);
793 for (elem_vals) |elem_val, i| {794 for (elem_vals) |elem_val, i| {
794 llvm_elems[i] = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_val });795 llvm_elems[i] = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_val });
795 }796 }
797 if (sento) |sent| llvm_elems[elem_vals.len] = try self.genTypedValue(.{ .ty = elem_ty, .val = sent });
796 const llvm_elem_ty = try self.llvmType(elem_ty);798 const llvm_elem_ty = try self.llvmType(elem_ty);
797 return llvm_elem_ty.constArray(799 return llvm_elem_ty.constArray(
798 llvm_elems.ptr,800 llvm_elems.ptr,
src/type.zig+9
...@@ -273,6 +273,15 @@ pub const Type = extern union {...@@ -273,6 +273,15 @@ pub const Type = extern union {
273 };273 };
274 }274 }
275275
276 pub const ArrayInfo = struct { elem_type: Type, sentinel: ?Value = null, len: u64 };
277 pub fn arrayInfo(self: Type) ArrayInfo {
278 return .{
279 .len = self.arrayLen(),
280 .sentinel = self.sentinel(),
281 .elem_type = self.elemType(),
282 };
283 }
284
276 pub fn ptrInfo(self: Type) Payload.Pointer {285 pub fn ptrInfo(self: Type) Payload.Pointer {
277 switch (self.tag()) {286 switch (self.tag()) {
278 .single_const_pointer_to_comptime_int => return .{ .data = .{287 .single_const_pointer_to_comptime_int => return .{ .data = .{
test/behavior/basic.zig+8
...@@ -162,3 +162,11 @@ fn fA() []const u8 {...@@ -162,3 +162,11 @@ fn fA() []const u8 {
162fn fB() []const u8 {162fn fB() []const u8 {
163 return "b";163 return "b";
164}164}
165
166test "string concatenation" {
167 try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
168}
169
170test "array mult operator" {
171 try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
172}
test/behavior/misc.zig-8
...@@ -40,14 +40,6 @@ test "constant equal function pointers" {...@@ -40,14 +40,6 @@ test "constant equal function pointers" {
4040
41fn emptyFn() void {}41fn emptyFn() void {}
4242
43test "string concatenation" {
44 try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
45}
46
47test "array mult operator" {
48 try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
49}
50
51test "string escapes" {43test "string escapes" {
52 try expectEqualStrings("\"", "\x22");44 try expectEqualStrings("\"", "\x22");
53 try expectEqualStrings("\'", "\x27");45 try expectEqualStrings("\'", "\x27");