From 0559cdb5542a2acb50ce49363c1973f3ca70365e Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 24 Dec 2022 02:32:13 -0500 Subject: [PATCH 1/4] Sema: support concat of tuple and array Closes #14041 --- src/Sema.zig | 46 +++++++++++++++++++++++++++++------------ test/behavior/array.zig | 18 ++++++++++++++++ 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 9de6945fc5..216200d9d3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12178,17 +12178,21 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const rhs_ty = sema.typeOf(rhs); const src = inst_data.src(); - if (lhs_ty.isTuple() and rhs_ty.isTuple()) { + const lhs_is_tuple = lhs_ty.isTuple(); + const rhs_is_tuple = rhs_ty.isTuple(); + if (lhs_is_tuple and rhs_is_tuple) { return sema.analyzeTupleCat(block, inst_data.src_node, lhs, 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 = try sema.getArrayCatInfo(block, lhs_src, lhs) orelse { + const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: { + if (lhs_is_tuple) break :lhs_info @as(Type.ArrayInfo, undefined); return sema.fail(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(sema.mod)}); }; - const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs) orelse { + const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse { + assert(!rhs_is_tuple); return sema.fail(block, rhs_src, "expected indexable; found '{}'", .{rhs_ty.fmt(sema.mod)}); }; @@ -12274,18 +12278,24 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const element_vals = try sema.arena.alloc(Value, final_len_including_sent); var elem_i: usize = 0; while (elem_i < lhs_len) : (elem_i += 1) { - const elem_val = try lhs_sub_val.elemValue(sema.mod, sema.arena, elem_i); - const elem_val_inst = try sema.addConstant(lhs_info.elem_type, elem_val); + const lhs_elem_i = elem_i; + const elem_ty = if (lhs_is_tuple) lhs_ty.structFieldType(lhs_elem_i) else lhs_info.elem_type; + const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i) else Value.initTag(.unreachable_value); + 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; + const elem_val_inst = try sema.addConstant(elem_ty, elem_val); const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded); - const coereced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); - element_vals[elem_i] = coereced_elem_val; + const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); + element_vals[elem_i] = coerced_elem_val; } while (elem_i < result_len) : (elem_i += 1) { - const elem_val = try rhs_sub_val.elemValue(sema.mod, sema.arena, elem_i - lhs_len); - const elem_val_inst = try sema.addConstant(lhs_info.elem_type, elem_val); + const rhs_elem_i = elem_i - lhs_len; + const elem_ty = if (rhs_is_tuple) rhs_ty.structFieldType(rhs_elem_i) else rhs_info.elem_type; + const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i) else Value.initTag(.unreachable_value); + 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; + const elem_val_inst = try sema.addConstant(elem_ty, elem_val); const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded); - const coereced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); - element_vals[elem_i] = coereced_elem_val; + const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); + element_vals[elem_i] = coerced_elem_val; } if (res_sent_val) |sent_val| { element_vals[result_len] = sent_val; @@ -12350,7 +12360,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai return block.addAggregateInit(result_ty, element_refs); } -fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) !?Type.ArrayInfo { +fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo { const operand_ty = sema.typeOf(operand); switch (operand_ty.zigTypeTag()) { .Array => return operand_ty.arrayInfo(), @@ -12376,6 +12386,16 @@ fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Ins .C => {}, } }, + .Struct => { + if (operand_ty.isTuple() and peer_ty.isIndexable()) { + assert(!peer_ty.isTuple()); + return .{ + .elem_type = peer_ty.elemType2(), + .sentinel = null, + .len = operand_ty.arrayLen(), + }; + } + }, else => {}, } return null; @@ -12470,7 +12490,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai } // Analyze the lhs first, to catch the case that someone tried to do exponentiation - const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs) orelse { + const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse { const msg = msg: { const msg = try sema.errMsg(block, lhs_src, "expected indexable; found '{}'", .{lhs_ty.fmt(sema.mod)}); errdefer msg.destroy(sema.gpa); diff --git a/test/behavior/array.zig b/test/behavior/array.zig index b886869be1..155ac294cf 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -45,6 +45,24 @@ fn getArrayLen(a: []const u32) usize { return a.len; } +test "array concat with tuple" { + const array: [2]u8 = .{ 1, 2 }; + { + const seq = array ++ .{ 3, 4 }; + try std.testing.expectEqualSlices(u8, &.{ 1, 2, 3, 4 }, &seq); + } + { + const seq = .{ 3, 4 } ++ array; + try std.testing.expectEqualSlices(u8, &.{ 3, 4, 1, 2 }, &seq); + } +} + +test "array init with concat" { + const a = 'a'; + var i: [4]u8 = [2]u8{ a, 'b' } ++ [2]u8{ 'c', 'd' }; + try expect(std.mem.eql(u8, &i, "abcd")); +} + test "array init with mult" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO From 6cd80042133c28d37bb30eba49d022a4fb23c058 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 24 Dec 2022 02:33:06 -0500 Subject: [PATCH 2/4] Sema: relax undefined checks for concat Closes #14037 --- src/Sema.zig | 12 ++++++++++-- test/behavior/array.zig | 13 +++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 216200d9d3..81da501d3c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12262,8 +12262,16 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :p null; }; - const runtime_src = if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| rs: { - if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { + const runtime_src = if (switch (lhs_ty.zigTypeTag()) { + .Array, .Struct => try sema.resolveMaybeUndefVal(lhs), + .Pointer => try sema.resolveDefinedValue(block, lhs_src, lhs), + else => unreachable, + }) |lhs_val| rs: { + if (switch (rhs_ty.zigTypeTag()) { + .Array, .Struct => try sema.resolveMaybeUndefVal(rhs), + .Pointer => try sema.resolveDefinedValue(block, rhs_src, rhs), + else => unreachable, + }) |rhs_val| { const lhs_sub_val = if (lhs_ty.isSinglePointer()) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 155ac294cf..0faa58a7d4 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -45,6 +45,19 @@ fn getArrayLen(a: []const u32) usize { return a.len; } +test "array concat with undefined" { + { + var array = "hello".* ++ @as([5]u8, undefined); + array[5..10].* = "world".*; + try std.testing.expect(std.mem.eql(u8, &array, "helloworld")); + } + { + var array = @as([5]u8, undefined) ++ "world".*; + array[0..5].* = "hello".*; + try std.testing.expect(std.mem.eql(u8, &array, "helloworld")); + } +} + test "array concat with tuple" { const array: [2]u8 = .{ 1, 2 }; { From bc913295b52ae0368c1a0fb33678ec6ac3408c9b Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 24 Dec 2022 02:39:56 -0500 Subject: [PATCH 3/4] CBE: fix emitting a partially undefined string literal --- src/codegen/c.zig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 51b2f30cae..fe6e245716 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1066,7 +1066,10 @@ pub const DeclGen = struct { var index: usize = 0; while (index < ai.len) : (index += 1) { const elem_val = try val.elemValue(dg.module, arena_allocator, index); - const elem_val_u8 = @intCast(u8, elem_val.toUnsignedInt(target)); + const elem_val_u8 = if (elem_val.isUndef()) + undefPattern(u8) + else + @intCast(u8, elem_val.toUnsignedInt(target)); try writeStringLiteralChar(writer, elem_val_u8); } if (ai.sentinel) |s| { From 6f288051c1b816adefa1c962740280d94b5ef4f2 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 24 Dec 2022 02:54:06 -0500 Subject: [PATCH 4/4] behavior: disable tests on failing backends --- test/behavior/array.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 0faa58a7d4..2f426bf5bf 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -46,6 +46,8 @@ fn getArrayLen(a: []const u32) usize { } test "array concat with undefined" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + { var array = "hello".* ++ @as([5]u8, undefined); array[5..10].* = "world".*; @@ -59,6 +61,9 @@ test "array concat with undefined" { } test "array concat with tuple" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + const array: [2]u8 = .{ 1, 2 }; { const seq = array ++ .{ 3, 4 };