diff options
| -rw-r--r-- | src/Sema.zig | 126 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 4 | ||||
| -rw-r--r-- | src/type.zig | 9 | ||||
| -rw-r--r-- | test/behavior/basic.zig | 8 | ||||
| -rw-r--r-- | test/behavior/misc.zig | 8 |
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"); |
