diff options
| author | Veikka Tuominen <git@vexu.eu> | 2022-07-29 12:30:27 +0300 |
|---|---|---|
| committer | Veikka Tuominen <git@vexu.eu> | 2022-07-30 00:18:08 +0300 |
| commit | 4758752e5d64a3e36086483de569188f62519bac (patch) | |
| tree | 02f7d749c89629a54847186f2be6e8ead61886ce | |
| parent | 17622b9db14cb1d8dd600b21f60c8a1041e5b0e1 (diff) | |
| download | zig-4758752e5d64a3e36086483de569188f62519bac.tar.gz zig-4758752e5d64a3e36086483de569188f62519bac.zip | |
Sema: implement coercion from tuples to tuples
Closes #12242
| -rw-r--r-- | src/Sema.zig | 107 | ||||
| -rw-r--r-- | test/behavior/tuple.zig | 15 | ||||
| -rw-r--r-- | test/cases/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig | 11 | ||||
| -rw-r--r-- | test/cases/compile_errors/type_mismatch_with_tuple_concatenation.zig | 10 |
4 files changed, 130 insertions, 13 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index 705fba9b92..31a0e41d95 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -24528,8 +24528,7 @@ fn coerceTupleToStruct( const struct_ty = try sema.resolveTypeFields(block, dest_ty_src, dest_ty); if (struct_ty.isTupleOrAnonStruct()) { - // NOTE remember to handle comptime fields - return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to tuples", .{}); + return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src); } const fields = struct_ty.structFields(); @@ -24612,6 +24611,110 @@ fn coerceTupleToStruct( ); } +fn coerceTupleToTuple( + sema: *Sema, + block: *Block, + tuple_ty: Type, + inst: Air.Inst.Ref, + inst_src: LazySrcLoc, +) !Air.Inst.Ref { + const field_count = tuple_ty.structFieldCount(); + const field_vals = try sema.arena.alloc(Value, field_count); + const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); + mem.set(Air.Inst.Ref, field_refs, .none); + + const inst_ty = sema.typeOf(inst); + const tuple = inst_ty.tupleFields(); + var runtime_src: ?LazySrcLoc = null; + for (tuple.types) |_, i_usize| { + const i = @intCast(u32, i_usize); + const field_src = inst_src; // TODO better source location + const field_name = if (inst_ty.castTag(.anon_struct)) |payload| + payload.data.names[i] + else + try std.fmt.allocPrint(sema.arena, "{d}", .{i}); + + if (mem.eql(u8, field_name, "len")) { + return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{}); + } + + const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src); + + const field_ty = tuple_ty.structFieldType(i); + const default_val = tuple_ty.structFieldDefaultValue(i); + const elem_ref = try tupleField(sema, block, inst_src, inst, field_src, i); + const coerced = try sema.coerce(block, field_ty, elem_ref, field_src); + field_refs[field_index] = coerced; + if (default_val.tag() != .unreachable_value) { + const init_val = (try sema.resolveMaybeUndefVal(block, field_src, coerced)) orelse { + return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime known"); + }; + + if (!init_val.eql(default_val, field_ty, sema.mod)) { + return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, i); + } + } + if (runtime_src == null) { + if (try sema.resolveMaybeUndefVal(block, field_src, coerced)) |field_val| { + field_vals[field_index] = field_val; + } else { + runtime_src = field_src; + } + } + } + + // Populate default field values and report errors for missing fields. + var root_msg: ?*Module.ErrorMsg = null; + + for (field_refs) |*field_ref, i| { + if (field_ref.* != .none) continue; + + const default_val = tuple_ty.structFieldDefaultValue(i); + const field_ty = tuple_ty.structFieldType(i); + + const field_src = inst_src; // TODO better source location + if (default_val.tag() == .unreachable_value) { + if (tuple_ty.isTuple()) { + const template = "missing tuple field: {d}"; + if (root_msg) |msg| { + try sema.errNote(block, field_src, msg, template, .{i}); + } else { + root_msg = try sema.errMsg(block, field_src, template, .{i}); + } + continue; + } + const template = "missing struct field: {s}"; + const args = .{tuple_ty.structFieldName(i)}; + if (root_msg) |msg| { + try sema.errNote(block, field_src, msg, template, args); + } else { + root_msg = try sema.errMsg(block, field_src, template, args); + } + continue; + } + if (runtime_src == null) { + field_vals[i] = default_val; + } else { + field_ref.* = try sema.addConstant(field_ty, default_val); + } + } + + if (root_msg) |msg| { + try sema.addDeclaredHereNote(msg, tuple_ty); + return sema.failWithOwnedErrorMsg(block, msg); + } + + if (runtime_src) |rs| { + try sema.requireRuntimeBlock(block, inst_src, rs); + return block.addAggregateInit(tuple_ty, field_refs); + } + + return sema.addConstant( + tuple_ty, + try Value.Tag.aggregate.create(sema.arena, field_vals), + ); +} + fn analyzeDeclVal( sema: *Sema, block: *Block, diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 4c43ef6be6..14297bd61c 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -275,3 +275,18 @@ test "tuple in tuple passed to generic function" { const x = comptime S.pair(1.5, 2.5); try S.foo(.{x}); } + +test "coerce tuple to tuple" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + const T = std.meta.Tuple(&.{u8}); + const S = struct { + fn foo(x: T) !void { + try expect(x[0] == 123); + } + }; + try S.foo(.{123}); +} diff --git a/test/cases/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig b/test/cases/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig deleted file mode 100644 index 5983224676..0000000000 --- a/test/cases/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig +++ /dev/null @@ -1,11 +0,0 @@ -export fn entry() void { - var x = .{}; - x = x ++ .{ 1, 2, 3 }; -} - -// error -// backend=stage1 -// target=native -// is_test=1 -// -// tmp.zig:3:11: error: expected type 'struct:2:14', found 'struct:3:11' diff --git a/test/cases/compile_errors/type_mismatch_with_tuple_concatenation.zig b/test/cases/compile_errors/type_mismatch_with_tuple_concatenation.zig new file mode 100644 index 0000000000..4e9bdfa2e5 --- /dev/null +++ b/test/cases/compile_errors/type_mismatch_with_tuple_concatenation.zig @@ -0,0 +1,10 @@ +export fn entry() void { + var x = .{}; + x = x ++ .{ 1, 2, 3 }; +} + +// error +// backend=stage2 +// target=native +// +// :3:11: error: index '0' out of bounds of tuple '@TypeOf(.{})' |
