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(
1572415724 field_index_src: LazySrcLoc,
1572515725) CompileError!Air.Inst.Ref {
1572615726 const tuple_ty = sema.typeOf(tuple);
15727 const tuple_info = tuple_ty.castTag(.tuple).?.data;
15727 const tuple_info = tuple_ty.tupleFields();
1572815728
1572915729 if (field_index > tuple_info.types.len) {
1573015730 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
......@@ -16195,6 +16195,9 @@ fn coerce(
1619516195 if (inst == .empty_struct) {
1619616196 return structInitEmpty(sema, block, dest_ty, dest_ty_src, inst_src);
1619716197 }
16198 if (inst_ty.isTupleOrAnonStruct()) {
16199 return sema.coerceTupleToStruct(block, dest_ty, dest_ty_src, inst, inst_src);
16200 }
1619816201 },
1619916202 else => {},
1620016203 }
......@@ -17442,6 +17445,94 @@ fn coerceTupleToArray(
1744217445 );
1744317446}
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
1744517536fn analyzeDeclVal(
1744617537 sema: *Sema,
1744717538 block: *Block,
......@@ -20365,3 +20456,17 @@ fn unionFieldIndex(
2036520456 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
2036620457 return @intCast(u32, field_index_usize);
2036720458}
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 {
45194519 if (field.is_comptime) {
45204520 return field.default_val;
45214521 } else {
4522 return null;
4522 return field.ty.onePossibleValue();
45234523 }
45244524 },
45254525 .tuple => {
4526 const val = ty.castTag(.tuple).?.data.values[index];
4526 const tuple = ty.castTag(.tuple).?.data;
4527 const val = tuple.values[index];
45274528 if (val.tag() == .unreachable_value) {
4528 return null;
4529 return tuple.types[index].onePossibleValue();
45294530 } else {
45304531 return val;
45314532 }
45324533 },
45334534 .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];
45354537 if (val.tag() == .unreachable_value) {
4536 return null;
4538 return anon_struct.types[index].onePossibleValue();
45374539 } else {
45384540 return val;
45394541 }
test/behavior/struct.zig+11-3
......@@ -995,7 +995,11 @@ test "comptime struct field" {
995995}
996996
997997test "tuple element initialized with fn call" {
998 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
998 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
10001004 const S = struct {
10011005 fn doTheTest() !void {
......@@ -1011,7 +1015,7 @@ test "tuple element initialized with fn call" {
10111015}
10121016
10131017test "struct with union field" {
1014 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
1018 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10151019
10161020 const Value = struct {
10171021 ref: u32 = 2,
......@@ -1029,7 +1033,11 @@ test "struct with union field" {
10291033}
10301034
10311035test "type coercion of anon struct literal to struct" {
1032 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
1036 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
10341042 const S = struct {
10351043 const S2 = struct {