authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-24 13:55:42-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-12-24 13:55:42-05:00
loga193ec432d922ace7c420228a345a703f737824c
tree2519bbdca841ba7d6981ab9acf0da15a8c1d0b51
parent5bde627f9dbbbb0783bd47fdcafc8cf23e2a5b2d
parent6f288051c1b816adefa1c962740280d94b5ef4f2
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #14058 from jacobly0/concat


3 files changed, 83 insertions(+), 16 deletions(-)

src/Sema.zig+43-15
......@@ -12178,17 +12178,21 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1217812178 const rhs_ty = sema.typeOf(rhs);
1217912179 const src = inst_data.src();
1218012180
12181 if (lhs_ty.isTuple() and rhs_ty.isTuple()) {
12181 const lhs_is_tuple = lhs_ty.isTuple();
12182 const rhs_is_tuple = rhs_ty.isTuple();
12183 if (lhs_is_tuple and rhs_is_tuple) {
1218212184 return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs);
1218312185 }
1218412186
1218512187 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
1218612188 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
1218712189
12188 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs) orelse {
12190 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: {
12191 if (lhs_is_tuple) break :lhs_info @as(Type.ArrayInfo, undefined);
1218912192 return sema.fail(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(sema.mod)});
1219012193 };
12191 const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs) orelse {
12194 const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse {
12195 assert(!rhs_is_tuple);
1219212196 return sema.fail(block, rhs_src, "expected indexable; found '{}'", .{rhs_ty.fmt(sema.mod)});
1219312197 };
1219412198
......@@ -12258,8 +12262,16 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1225812262 break :p null;
1225912263 };
1226012264
12261 const runtime_src = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| rs: {
12262 if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {
12265 const runtime_src = if (switch (lhs_ty.zigTypeTag()) {
12266 .Array, .Struct => try sema.resolveMaybeUndefVal(lhs),
12267 .Pointer => try sema.resolveDefinedValue(block, lhs_src, lhs),
12268 else => unreachable,
12269 }) |lhs_val| rs: {
12270 if (switch (rhs_ty.zigTypeTag()) {
12271 .Array, .Struct => try sema.resolveMaybeUndefVal(rhs),
12272 .Pointer => try sema.resolveDefinedValue(block, rhs_src, rhs),
12273 else => unreachable,
12274 }) |rhs_val| {
1226312275 const lhs_sub_val = if (lhs_ty.isSinglePointer())
1226412276 (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).?
1226512277 else
......@@ -12274,18 +12286,24 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1227412286 const element_vals = try sema.arena.alloc(Value, final_len_including_sent);
1227512287 var elem_i: usize = 0;
1227612288 while (elem_i < lhs_len) : (elem_i += 1) {
12277 const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, elem_i);
12278 const elem_val_inst = try sema.addConstant(lhs_info.elem_type, elem_val);
12289 const lhs_elem_i = elem_i;
12290 const elem_ty = if (lhs_is_tuple) lhs_ty.structFieldType(lhs_elem_i) else lhs_info.elem_type;
12291 const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i) else Value.initTag(.unreachable_value);
12292 const elem_val = if (elem_default_val.tag() == .unreachable_value) try lhs_sub_val.elemValue(sema.mod, sema.arena, lhs_elem_i) else elem_default_val;
12293 const elem_val_inst = try sema.addConstant(elem_ty, elem_val);
1227912294 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded);
12280 const coereced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, "");
12281 element_vals[elem_i] = coereced_elem_val;
12295 const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, "");
12296 element_vals[elem_i] = coerced_elem_val;
1228212297 }
1228312298 while (elem_i < result_len) : (elem_i += 1) {
12284 const elem_val = try rhs_sub_val.elemValue(sema.mod, sema.arena, elem_i - lhs_len);
12285 const elem_val_inst = try sema.addConstant(lhs_info.elem_type, elem_val);
12299 const rhs_elem_i = elem_i - lhs_len;
12300 const elem_ty = if (rhs_is_tuple) rhs_ty.structFieldType(rhs_elem_i) else rhs_info.elem_type;
12301 const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i) else Value.initTag(.unreachable_value);
12302 const elem_val = if (elem_default_val.tag() == .unreachable_value) try rhs_sub_val.elemValue(sema.mod, sema.arena, rhs_elem_i) else elem_default_val;
12303 const elem_val_inst = try sema.addConstant(elem_ty, elem_val);
1228612304 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded);
12287 const coereced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, "");
12288 element_vals[elem_i] = coereced_elem_val;
12305 const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, "");
12306 element_vals[elem_i] = coerced_elem_val;
1228912307 }
1229012308 if (res_sent_val) |sent_val| {
1229112309 element_vals[result_len] = sent_val;
......@@ -12350,7 +12368,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1235012368 return block.addAggregateInit(result_ty, element_refs);
1235112369}
1235212370
12353fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) !?Type.ArrayInfo {
12371fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo {
1235412372 const operand_ty = sema.typeOf(operand);
1235512373 switch (operand_ty.zigTypeTag()) {
1235612374 .Array => return operand_ty.arrayInfo(),
......@@ -12376,6 +12394,16 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins
1237612394 .C => {},
1237712395 }
1237812396 },
12397 .Struct => {
12398 if (operand_ty.isTuple() and peer_ty.isIndexable()) {
12399 assert(!peer_ty.isTuple());
12400 return .{
12401 .elem_type = peer_ty.elemType2(),
12402 .sentinel = null,
12403 .len = operand_ty.arrayLen(),
12404 };
12405 }
12406 },
1237912407 else => {},
1238012408 }
1238112409 return null;
......@@ -12470,7 +12498,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
1247012498 }
1247112499
1247212500 // Analyze the lhs first, to catch the case that someone tried to do exponentiation
12473 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs) orelse {
12501 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse {
1247412502 const msg = msg: {
1247512503 const msg = try sema.errMsg(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(sema.mod)});
1247612504 errdefer msg.destroy(sema.gpa);
src/codegen/c.zig+4-1
......@@ -1066,7 +1066,10 @@ pub const DeclGen = struct {
10661066 var index: usize = 0;
10671067 while (index < ai.len) : (index += 1) {
10681068 const elem_val = try val.elemValue(dg.module, arena_allocator, index);
1069 const elem_val_u8 = @intCast(u8, elem_val.toUnsignedInt(target));
1069 const elem_val_u8 = if (elem_val.isUndef())
1070 undefPattern(u8)
1071 else
1072 @intCast(u8, elem_val.toUnsignedInt(target));
10701073 try writeStringLiteralChar(writer, elem_val_u8);
10711074 }
10721075 if (ai.sentinel) |s| {
test/behavior/array.zig+36
......@@ -45,6 +45,42 @@ fn getArrayLen(a: []const u32) usize {
4545 return a.len;
4646}
4747
48test "array concat with undefined" {
49 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
50
51 {
52 var array = "hello".* ++ @as([5]u8, undefined);
53 array[5..10].* = "world".*;
54 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
55 }
56 {
57 var array = @as([5]u8, undefined) ++ "world".*;
58 array[0..5].* = "hello".*;
59 try std.testing.expect(std.mem.eql(u8, &array, "helloworld"));
60 }
61}
62
63test "array concat with tuple" {
64 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
65 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
66
67 const array: [2]u8 = .{ 1, 2 };
68 {
69 const seq = array ++ .{ 3, 4 };
70 try std.testing.expectEqualSlices(u8, &.{ 1, 2, 3, 4 }, &seq);
71 }
72 {
73 const seq = .{ 3, 4 } ++ array;
74 try std.testing.expectEqualSlices(u8, &.{ 3, 4, 1, 2 }, &seq);
75 }
76}
77
78test "array init with concat" {
79 const a = 'a';
80 var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' };
81 try expect(std.mem.eql(u8, &i, "abcd"));
82}
83
4884test "array init with mult" {
4985 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
5086 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO