authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-29 12:30:27+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-30 00:18:08+03:00
log4758752e5d64a3e36086483de569188f62519bac
tree02f7d749c89629a54847186f2be6e8ead61886ce
parent17622b9db14cb1d8dd600b21f60c8a1041e5b0e1

Sema: implement coercion from tuples to tuples

Closes #12242

4 files changed, 130 insertions(+), 13 deletions(-)

src/Sema.zig+105-2
......@@ -24528,8 +24528,7 @@ fn coerceTupleToStruct(
2452824528 const struct_ty = try sema.resolveTypeFields(block, dest_ty_src, dest_ty);
2452924529
2453024530 if (struct_ty.isTupleOrAnonStruct()) {
24531 // NOTE remember to handle comptime fields
24532 return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to tuples", .{});
24531 return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src);
2453324532 }
2453424533
2453524534 const fields = struct_ty.structFields();
......@@ -24612,6 +24611,110 @@ fn coerceTupleToStruct(
2461224611 );
2461324612}
2461424613
24614fn coerceTupleToTuple(
24615 sema: *Sema,
24616 block: *Block,
24617 tuple_ty: Type,
24618 inst: Air.Inst.Ref,
24619 inst_src: LazySrcLoc,
24620) !Air.Inst.Ref {
24621 const field_count = tuple_ty.structFieldCount();
24622 const field_vals = try sema.arena.alloc(Value, field_count);
24623 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
24624 mem.set(Air.Inst.Ref, field_refs, .none);
24625
24626 const inst_ty = sema.typeOf(inst);
24627 const tuple = inst_ty.tupleFields();
24628 var runtime_src: ?LazySrcLoc = null;
24629 for (tuple.types) |_, i_usize| {
24630 const i = @intCast(u32, i_usize);
24631 const field_src = inst_src; // TODO better source location
24632 const field_name = if (inst_ty.castTag(.anon_struct)) |payload|
24633 payload.data.names[i]
24634 else
24635 try std.fmt.allocPrint(sema.arena, "{d}", .{i});
24636
24637 if (mem.eql(u8, field_name, "len")) {
24638 return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{});
24639 }
24640
24641 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src);
24642
24643 const field_ty = tuple_ty.structFieldType(i);
24644 const default_val = tuple_ty.structFieldDefaultValue(i);
24645 const elem_ref = try tupleField(sema, block, inst_src, inst, field_src, i);
24646 const coerced = try sema.coerce(block, field_ty, elem_ref, field_src);
24647 field_refs[field_index] = coerced;
24648 if (default_val.tag() != .unreachable_value) {
24649 const init_val = (try sema.resolveMaybeUndefVal(block, field_src, coerced)) orelse {
24650 return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime known");
24651 };
24652
24653 if (!init_val.eql(default_val, field_ty, sema.mod)) {
24654 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, i);
24655 }
24656 }
24657 if (runtime_src == null) {
24658 if (try sema.resolveMaybeUndefVal(block, field_src, coerced)) |field_val| {
24659 field_vals[field_index] = field_val;
24660 } else {
24661 runtime_src = field_src;
24662 }
24663 }
24664 }
24665
24666 // Populate default field values and report errors for missing fields.
24667 var root_msg: ?*Module.ErrorMsg = null;
24668
24669 for (field_refs) |*field_ref, i| {
24670 if (field_ref.* != .none) continue;
24671
24672 const default_val = tuple_ty.structFieldDefaultValue(i);
24673 const field_ty = tuple_ty.structFieldType(i);
24674
24675 const field_src = inst_src; // TODO better source location
24676 if (default_val.tag() == .unreachable_value) {
24677 if (tuple_ty.isTuple()) {
24678 const template = "missing tuple field: {d}";
24679 if (root_msg) |msg| {
24680 try sema.errNote(block, field_src, msg, template, .{i});
24681 } else {
24682 root_msg = try sema.errMsg(block, field_src, template, .{i});
24683 }
24684 continue;
24685 }
24686 const template = "missing struct field: {s}";
24687 const args = .{tuple_ty.structFieldName(i)};
24688 if (root_msg) |msg| {
24689 try sema.errNote(block, field_src, msg, template, args);
24690 } else {
24691 root_msg = try sema.errMsg(block, field_src, template, args);
24692 }
24693 continue;
24694 }
24695 if (runtime_src == null) {
24696 field_vals[i] = default_val;
24697 } else {
24698 field_ref.* = try sema.addConstant(field_ty, default_val);
24699 }
24700 }
24701
24702 if (root_msg) |msg| {
24703 try sema.addDeclaredHereNote(msg, tuple_ty);
24704 return sema.failWithOwnedErrorMsg(block, msg);
24705 }
24706
24707 if (runtime_src) |rs| {
24708 try sema.requireRuntimeBlock(block, inst_src, rs);
24709 return block.addAggregateInit(tuple_ty, field_refs);
24710 }
24711
24712 return sema.addConstant(
24713 tuple_ty,
24714 try Value.Tag.aggregate.create(sema.arena, field_vals),
24715 );
24716}
24717
2461524718fn analyzeDeclVal(
2461624719 sema: *Sema,
2461724720 block: *Block,
test/behavior/tuple.zig+15
......@@ -275,3 +275,18 @@ test "tuple in tuple passed to generic function" {
275275 const x = comptime S.pair(1.5, 2.5);
276276 try S.foo(.{x});
277277}
278
279test "coerce tuple to tuple" {
280 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
281 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
282 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
283 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
284
285 const T = std.meta.Tuple(&.{u8});
286 const S = struct {
287 fn foo(x: T) !void {
288 try expect(x[0] == 123);
289 }
290 };
291 try S.foo(.{123});
292}
test/cases/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig deleted-11
......@@ -1,11 +0,0 @@
1export fn entry() void {
2 var x = .{};
3 x = x ++ .{ 1, 2, 3 };
4}
5
6// error
7// backend=stage1
8// target=native
9// is_test=1
10//
11// tmp.zig:3:11: error: expected type 'struct:2:14', found 'struct:3:11'
test/cases/compile_errors/type_mismatch_with_tuple_concatenation.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 var x = .{};
3 x = x ++ .{ 1, 2, 3 };
4}
5
6// error
7// backend=stage2
8// target=native
9//
10// :3:11: error: index '0' out of bounds of tuple '@TypeOf(.{})'