authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-08 22:19:25-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-08 22:19:25-07:00
logbb73775d40e04796b0dd60341303d567db0a8020
treecef4e263687d31c5255d6a2c22fb0ed006d18972
parent6f560c99094eec2210f2d76a2449b5f60191e11b

Sema: implement coercion of tuples to structs


3 files changed, 124 insertions(+), 9 deletions(-)

src/Sema.zig+106-1
...@@ -15724,7 +15724,7 @@ fn tupleField(...@@ -15724,7 +15724,7 @@ fn tupleField(
15724 field_index_src: LazySrcLoc,15724 field_index_src: LazySrcLoc,
15725) CompileError!Air.Inst.Ref {15725) CompileError!Air.Inst.Ref {
15726 const tuple_ty = sema.typeOf(tuple);15726 const tuple_ty = sema.typeOf(tuple);
15727 const tuple_info = tuple_ty.castTag(.tuple).?.data;15727 const tuple_info = tuple_ty.tupleFields();
1572815728
15729 if (field_index > tuple_info.types.len) {15729 if (field_index > tuple_info.types.len) {
15730 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{15730 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
...@@ -16195,6 +16195,9 @@ fn coerce(...@@ -16195,6 +16195,9 @@ fn coerce(
16195 if (inst == .empty_struct) {16195 if (inst == .empty_struct) {
16196 return structInitEmpty(sema, block, dest_ty, dest_ty_src, inst_src);16196 return structInitEmpty(sema, block, dest_ty, dest_ty_src, inst_src);
16197 }16197 }
16198 if (inst_ty.isTupleOrAnonStruct()) {
16199 return sema.coerceTupleToStruct(block, dest_ty, dest_ty_src, inst, inst_src);
16200 }
16198 },16201 },
16199 else => {},16202 else => {},
16200 }16203 }
...@@ -17442,6 +17445,94 @@ fn coerceTupleToArray(...@@ -17442,6 +17445,94 @@ fn coerceTupleToArray(
17442 );17445 );
17443}17446}
1744417447
17448/// Handles both tuples and anon struct literals. Coerces field-wise. Reports
17449/// errors for both extra fields and missing fields.
17450fn coerceTupleToStruct(
17451 sema: *Sema,
17452 block: *Block,
17453 dest_ty: Type,
17454 dest_ty_src: LazySrcLoc,
17455 inst: Air.Inst.Ref,
17456 inst_src: LazySrcLoc,
17457) !Air.Inst.Ref {
17458 if (dest_ty.isTupleOrAnonStruct()) {
17459 return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to tuples", .{});
17460 }
17461
17462 const fields = dest_ty.structFields();
17463 const field_vals = try sema.arena.alloc(Value, fields.count());
17464 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
17465 mem.set(Air.Inst.Ref, field_refs, .none);
17466
17467 const inst_ty = sema.typeOf(inst);
17468 const tuple = inst_ty.tupleFields();
17469 var runtime_src: ?LazySrcLoc = null;
17470 for (tuple.types) |_, i_usize| {
17471 const i = @intCast(u32, i_usize);
17472 const field_src = inst_src; // TODO better source location
17473 const field_name = if (inst_ty.castTag(.anon_struct)) |payload|
17474 payload.data.names[i]
17475 else
17476 try std.fmt.allocPrint(sema.arena, "{d}", .{i});
17477 const field_index = try sema.structFieldIndex(block, dest_ty, field_name, field_src);
17478 const field = fields.values()[field_index];
17479 if (field.is_comptime) {
17480 return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{});
17481 }
17482 const elem_ref = try tupleField(sema, block, inst, i, inst_src, field_src);
17483 const coerced = try sema.coerce(block, field.ty, elem_ref, field_src);
17484 field_refs[field_index] = coerced;
17485 if (runtime_src == null) {
17486 if (try sema.resolveMaybeUndefVal(block, field_src, coerced)) |field_val| {
17487 field_vals[field_index] = field_val;
17488 } else {
17489 runtime_src = field_src;
17490 }
17491 }
17492 }
17493
17494 // Populate default field values and report errors for missing fields.
17495 var root_msg: ?*Module.ErrorMsg = null;
17496
17497 for (field_refs) |*field_ref, i| {
17498 if (field_ref.* != .none) continue;
17499
17500 const field_name = fields.keys()[i];
17501 const field = fields.values()[i];
17502 const field_src = inst_src; // TODO better source location
17503 if (field.default_val.tag() == .unreachable_value) {
17504 const template = "missing struct field: {s}";
17505 const args = .{field_name};
17506 if (root_msg) |msg| {
17507 try sema.errNote(block, field_src, msg, template, args);
17508 } else {
17509 root_msg = try sema.errMsg(block, field_src, template, args);
17510 }
17511 continue;
17512 }
17513 if (runtime_src == null) {
17514 field_vals[i] = field.default_val;
17515 } else {
17516 field_ref.* = try sema.addConstant(field.ty, field.default_val);
17517 }
17518 }
17519
17520 if (root_msg) |msg| {
17521 try sema.addDeclaredHereNote(msg, dest_ty);
17522 return sema.failWithOwnedErrorMsg(block, msg);
17523 }
17524
17525 if (runtime_src) |rs| {
17526 try sema.requireRuntimeBlock(block, rs);
17527 return block.addAggregateInit(dest_ty, field_refs);
17528 }
17529
17530 return sema.addConstant(
17531 dest_ty,
17532 try Value.Tag.@"struct".create(sema.arena, field_vals),
17533 );
17534}
17535
17445fn analyzeDeclVal(17536fn analyzeDeclVal(
17446 sema: *Sema,17537 sema: *Sema,
17447 block: *Block,17538 block: *Block,
...@@ -20365,3 +20456,17 @@ fn unionFieldIndex(...@@ -20365,3 +20456,17 @@ fn unionFieldIndex(
20365 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);20456 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
20366 return @intCast(u32, field_index_usize);20457 return @intCast(u32, field_index_usize);
20367}20458}
20459
20460fn structFieldIndex(
20461 sema: *Sema,
20462 block: *Block,
20463 unresolved_struct_ty: Type,
20464 field_name: []const u8,
20465 field_src: LazySrcLoc,
20466) !u32 {
20467 const struct_ty = try sema.resolveTypeFields(block, field_src, unresolved_struct_ty);
20468 const struct_obj = struct_ty.castTag(.@"struct").?.data;
20469 const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
20470 return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
20471 return @intCast(u32, field_index_usize);
20472}
src/type.zig+7-5
...@@ -4519,21 +4519,23 @@ pub const Type = extern union {...@@ -4519,21 +4519,23 @@ pub const Type = extern union {
4519 if (field.is_comptime) {4519 if (field.is_comptime) {
4520 return field.default_val;4520 return field.default_val;
4521 } else {4521 } else {
4522 return null;4522 return field.ty.onePossibleValue();
4523 }4523 }
4524 },4524 },
4525 .tuple => {4525 .tuple => {
4526 const val = ty.castTag(.tuple).?.data.values[index];4526 const tuple = ty.castTag(.tuple).?.data;
4527 const val = tuple.values[index];
4527 if (val.tag() == .unreachable_value) {4528 if (val.tag() == .unreachable_value) {
4528 return null;4529 return tuple.types[index].onePossibleValue();
4529 } else {4530 } else {
4530 return val;4531 return val;
4531 }4532 }
4532 },4533 },
4533 .anon_struct => {4534 .anon_struct => {
4534 const val = ty.castTag(.anon_struct).?.data.values[index];4535 const anon_struct = ty.castTag(.anon_struct).?.data;
4536 const val = anon_struct.values[index];
4535 if (val.tag() == .unreachable_value) {4537 if (val.tag() == .unreachable_value) {
4536 return null;4538 return anon_struct.types[index].onePossibleValue();
4537 } else {4539 } else {
4538 return val;4540 return val;
4539 }4541 }
test/behavior/struct.zig+11-3
...@@ -995,7 +995,11 @@ test "comptime struct field" {...@@ -995,7 +995,11 @@ test "comptime struct field" {
995}995}
996996
997test "tuple element initialized with fn call" {997test "tuple element initialized with fn call" {
998 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO998 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
999 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1000 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1001 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1002 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
9991003
1000 const S = struct {1004 const S = struct {
1001 fn doTheTest() !void {1005 fn doTheTest() !void {
...@@ -1011,7 +1015,7 @@ test "tuple element initialized with fn call" {...@@ -1011,7 +1015,7 @@ test "tuple element initialized with fn call" {
1011}1015}
10121016
1013test "struct with union field" {1017test "struct with union field" {
1014 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO1018 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10151019
1016 const Value = struct {1020 const Value = struct {
1017 ref: u32 = 2,1021 ref: u32 = 2,
...@@ -1029,7 +1033,11 @@ test "struct with union field" {...@@ -1029,7 +1033,11 @@ test "struct with union field" {
1029}1033}
10301034
1031test "type coercion of anon struct literal to struct" {1035test "type coercion of anon struct literal to struct" {
1032 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO1036 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1037 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1038 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1039 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1040 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10331041
1034 const S = struct {1042 const S = struct {
1035 const S2 = struct {1043 const S2 = struct {