aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Sema.zig126
-rw-r--r--src/codegen/llvm.zig4
-rw-r--r--src/type.zig9
-rw-r--r--test/behavior/basic.zig8
-rw-r--r--test/behavior/misc.zig8
5 files changed, 142 insertions, 13 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index 90505c6806..eccf22d02d 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -5512,16 +5512,134 @@ fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
const tracy = trace(@src());
defer tracy.end();
- _ = inst;
- return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayCat", .{});
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const lhs = sema.resolveInst(extra.lhs);
+ const rhs = sema.resolveInst(extra.rhs);
+ const lhs_ty = sema.typeOf(lhs);
+ const rhs_ty = sema.typeOf(rhs);
+ const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
+ const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
+
+ const lhs_info = getArrayCatInfo(lhs_ty) orelse
+ return sema.mod.fail(&block.base, lhs_src, "expected array, found '{}'", .{lhs_ty});
+ const rhs_info = getArrayCatInfo(rhs_ty) orelse
+ return sema.mod.fail(&block.base, rhs_src, "expected array, found '{}'", .{rhs_ty});
+ if (!lhs_info.elem_type.eql(rhs_info.elem_type)) {
+ return sema.mod.fail(&block.base, rhs_src, "expected array of type '{}', found '{}'", .{ lhs_info.elem_type, rhs_ty });
+ }
+
+ // 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)) {
+ 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 final_len = lhs_info.len + rhs_info.len;
+ if (lhs_ty.zigTypeTag() == .Pointer) {
+ var anon_decl = try block.startAnonDecl();
+ defer anon_decl.deinit();
+
+ const lhs_sub_val = (try lhs_val.pointerDeref(anon_decl.arena())).?;
+ const rhs_sub_val = (try rhs_val.pointerDeref(anon_decl.arena())).?;
+ const buf = try anon_decl.arena().alloc(Value, final_len);
+ {
+ var i: u64 = 0;
+ while (i < lhs_info.len) : (i += 1) {
+ const val = try lhs_sub_val.elemValue(sema.arena, i);
+ buf[i] = try val.copy(anon_decl.arena());
+ }
+ }
+ {
+ var i: u64 = 0;
+ while (i < rhs_info.len) : (i += 1) {
+ const val = try rhs_sub_val.elemValue(sema.arena, i);
+ buf[lhs_info.len + i] = try val.copy(anon_decl.arena());
+ }
+ }
+ const ty = if (res_sent) |rs|
+ try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type, .sentinel = rs })
+ else
+ try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = lhs_info.elem_type });
+ const val = try Value.Tag.array.create(anon_decl.arena(), buf);
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ ty,
+ val,
+ ));
+ }
+ return sema.mod.fail(&block.base, lhs_src, "TODO array_cat more types of Values", .{});
+ } else {
+ return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_cat", .{});
+ }
+ } else {
+ return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_cat", .{});
+ }
+}
+
+fn getArrayCatInfo(t: Type) ?Type.ArrayInfo {
+ return switch (t.zigTypeTag()) {
+ .Array => t.arrayInfo(),
+ .Pointer => blk: {
+ const ptrinfo = t.ptrInfo().data;
+ if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null;
+ if (ptrinfo.size != .One) return null;
+ break :blk ptrinfo.pointee_type.arrayInfo();
+ },
+ else => null,
+ };
}
fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
- _ = inst;
- return sema.mod.fail(&block.base, sema.src, "TODO implement zirArrayMul", .{});
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const lhs = sema.resolveInst(extra.lhs);
+ const lhs_ty = sema.typeOf(lhs);
+ const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
+ const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
+
+ // In `**` rhs has to be comptime-known, but lhs can be runtime-known
+ const tomulby = try sema.resolveInt(block, rhs_src, extra.rhs, Type.initTag(.usize));
+ const mulinfo = getArrayCatInfo(lhs_ty) orelse
+ return sema.mod.fail(&block.base, lhs_src, "expected array, found '{}'", .{lhs_ty});
+
+ const final_len = std.math.mul(u64, mulinfo.len, tomulby) catch return sema.mod.fail(&block.base, rhs_src, "operation results in overflow", .{});
+ if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
+ if (lhs_ty.zigTypeTag() == .Pointer) {
+ var anon_decl = try block.startAnonDecl();
+ defer anon_decl.deinit();
+ const lhs_sub_val = (try lhs_val.pointerDeref(anon_decl.arena())).?;
+
+ const final_ty = if (mulinfo.sentinel) |sent|
+ try Type.Tag.array_sentinel.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type, .sentinel = sent })
+ else
+ try Type.Tag.array.create(anon_decl.arena(), .{ .len = final_len, .elem_type = mulinfo.elem_type });
+
+ const buf = try anon_decl.arena().alloc(Value, final_len);
+ var i: u64 = 0;
+ while (i < tomulby) : (i += 1) {
+ var j: u64 = 0;
+ while (j < mulinfo.len) : (j += 1) {
+ const val = try lhs_sub_val.elemValue(sema.arena, j);
+ buf[mulinfo.len * i + j] = try val.copy(anon_decl.arena());
+ }
+ }
+ const val = try Value.Tag.array.create(anon_decl.arena(), buf);
+ return sema.analyzeDeclRef(try anon_decl.finish(
+ final_ty,
+ val,
+ ));
+ }
+ return sema.mod.fail(&block.base, lhs_src, "TODO array_mul more types of Values", .{});
+ }
+ return sema.mod.fail(&block.base, lhs_src, "TODO runtime array_mul", .{});
}
fn zirNegate(
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index f176c74ec6..8e6c064e5c 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -788,11 +788,13 @@ pub const DeclGen = struct {
const gpa = self.gpa;
const elem_ty = tv.ty.elemType();
const elem_vals = payload.data;
- const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len);
+ const sento = tv.ty.sentinel();
+ const llvm_elems = try gpa.alloc(*const llvm.Value, elem_vals.len + @boolToInt(sento != null));
defer gpa.free(llvm_elems);
for (elem_vals) |elem_val, i| {
llvm_elems[i] = try self.genTypedValue(.{ .ty = elem_ty, .val = elem_val });
}
+ if (sento) |sent| llvm_elems[elem_vals.len] = try self.genTypedValue(.{ .ty = elem_ty, .val = sent });
const llvm_elem_ty = try self.llvmType(elem_ty);
return llvm_elem_ty.constArray(
llvm_elems.ptr,
diff --git a/src/type.zig b/src/type.zig
index 467e9c931b..aa4517ab05 100644
--- a/src/type.zig
+++ b/src/type.zig
@@ -273,6 +273,15 @@ pub const Type = extern union {
};
}
+ pub const ArrayInfo = struct { elem_type: Type, sentinel: ?Value = null, len: u64 };
+ pub fn arrayInfo(self: Type) ArrayInfo {
+ return .{
+ .len = self.arrayLen(),
+ .sentinel = self.sentinel(),
+ .elem_type = self.elemType(),
+ };
+ }
+
pub fn ptrInfo(self: Type) Payload.Pointer {
switch (self.tag()) {
.single_const_pointer_to_comptime_int => return .{ .data = .{
diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig
index 1372dfaeeb..517162c8d4 100644
--- a/test/behavior/basic.zig
+++ b/test/behavior/basic.zig
@@ -162,3 +162,11 @@ fn fA() []const u8 {
fn fB() []const u8 {
return "b";
}
+
+test "string concatenation" {
+ try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
+}
+
+test "array mult operator" {
+ try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
+}
diff --git a/test/behavior/misc.zig b/test/behavior/misc.zig
index 1330b757ee..5394e6fd14 100644
--- a/test/behavior/misc.zig
+++ b/test/behavior/misc.zig
@@ -40,14 +40,6 @@ test "constant equal function pointers" {
fn emptyFn() void {}
-test "string concatenation" {
- try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
-}
-
-test "array mult operator" {
- try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
-}
-
test "string escapes" {
try expectEqualStrings("\"", "\x22");
try expectEqualStrings("\'", "\x27");