authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-02 19:54:54+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-03 16:45:33+03:00
logaa78ebaf95af5a3587194d8dbcb101a49c0bb898
tree9bcaccf99a4460368f9e9bafc9032d6167864e92
parent797ded47f05ce033be58d3fb78d777ec3218048b

Sema: improve circular dependency errors


10 files changed, 156 insertions(+), 78 deletions(-)

src/Sema.zig+83-26
......@@ -26629,21 +26629,41 @@ fn resolveStructLayout(
2662926629 switch (struct_obj.status) {
2663026630 .none, .have_field_types => {},
2663126631 .field_types_wip, .layout_wip => {
26632 return sema.fail(block, src, "struct '{}' depends on itself", .{ty.fmt(sema.mod)});
26632 const msg = try Module.ErrorMsg.create(
26633 sema.gpa,
26634 struct_obj.srcLoc(sema.mod),
26635 "struct '{}' depends on itself",
26636 .{ty.fmt(sema.mod)},
26637 );
26638 return sema.failWithOwnedErrorMsg(msg);
2663326639 },
2663426640 .have_layout, .fully_resolved_wip, .fully_resolved => return,
2663526641 }
2663626642 struct_obj.status = .layout_wip;
26637 for (struct_obj.fields.values()) |field| {
26638 try sema.resolveTypeLayout(block, src, field.ty);
26643 for (struct_obj.fields.values()) |field, i| {
26644 sema.resolveTypeLayout(block, src, field.ty) catch |err| switch (err) {
26645 error.AnalysisFail => {
26646 const msg = sema.err orelse return err;
26647 try sema.addFieldErrNote(block, ty, i, msg, "while checking this field", .{});
26648 return err;
26649 },
26650 else => return err,
26651 };
2663926652 }
2664026653 struct_obj.status = .have_layout;
2664126654
2664226655 // In case of querying the ABI alignment of this struct, we will ask
2664326656 // for hasRuntimeBits() of each field, so we need "requires comptime"
2664426657 // to be known already before this function returns.
26645 for (struct_obj.fields.values()) |field| {
26646 _ = try sema.typeRequiresComptime(block, src, field.ty);
26658 for (struct_obj.fields.values()) |field, i| {
26659 _ = sema.typeRequiresComptime(block, src, field.ty) catch |err| switch (err) {
26660 error.AnalysisFail => {
26661 const msg = sema.err orelse return err;
26662 try sema.addFieldErrNote(block, ty, i, msg, "while checking this field", .{});
26663 return err;
26664 },
26665 else => return err,
26666 };
2664726667 }
2664826668 }
2664926669 // otherwise it's a tuple; no need to resolve anything
......@@ -26660,13 +26680,26 @@ fn resolveUnionLayout(
2666026680 switch (union_obj.status) {
2666126681 .none, .have_field_types => {},
2666226682 .field_types_wip, .layout_wip => {
26663 return sema.fail(block, src, "union '{}' depends on itself", .{ty.fmt(sema.mod)});
26683 const msg = try Module.ErrorMsg.create(
26684 sema.gpa,
26685 union_obj.srcLoc(sema.mod),
26686 "union '{}' depends on itself",
26687 .{ty.fmt(sema.mod)},
26688 );
26689 return sema.failWithOwnedErrorMsg(msg);
2666426690 },
2666526691 .have_layout, .fully_resolved_wip, .fully_resolved => return,
2666626692 }
2666726693 union_obj.status = .layout_wip;
26668 for (union_obj.fields.values()) |field| {
26669 try sema.resolveTypeLayout(block, src, field.ty);
26694 for (union_obj.fields.values()) |field, i| {
26695 sema.resolveTypeLayout(block, src, field.ty) catch |err| switch (err) {
26696 error.AnalysisFail => {
26697 const msg = sema.err orelse return err;
26698 try sema.addFieldErrNote(block, ty, i, msg, "while checking this field", .{});
26699 return err;
26700 },
26701 else => return err,
26702 };
2667026703 }
2667126704 union_obj.status = .have_layout;
2667226705}
......@@ -26794,12 +26827,12 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
2679426827 switch (ty.tag()) {
2679526828 .@"struct" => {
2679626829 const struct_obj = ty.castTag(.@"struct").?.data;
26797 try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);
26830 try sema.resolveTypeFieldsStruct(ty, struct_obj);
2679826831 return ty;
2679926832 },
2680026833 .@"union", .union_safety_tagged, .union_tagged => {
2680126834 const union_obj = ty.cast(Type.Payload.Union).?.data;
26802 try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
26835 try sema.resolveTypeFieldsUnion(ty, union_obj);
2680326836 return ty;
2680426837 },
2680526838 .type_info => return sema.resolveBuiltinTypeFields(block, src, "Type"),
......@@ -26820,15 +26853,19 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
2682026853
2682126854fn resolveTypeFieldsStruct(
2682226855 sema: *Sema,
26823 block: *Block,
26824 src: LazySrcLoc,
2682526856 ty: Type,
2682626857 struct_obj: *Module.Struct,
2682726858) CompileError!void {
2682826859 switch (struct_obj.status) {
2682926860 .none => {},
2683026861 .field_types_wip => {
26831 return sema.fail(block, src, "struct '{}' depends on itself", .{ty.fmt(sema.mod)});
26862 const msg = try Module.ErrorMsg.create(
26863 sema.gpa,
26864 struct_obj.srcLoc(sema.mod),
26865 "struct '{}' depends on itself",
26866 .{ty.fmt(sema.mod)},
26867 );
26868 return sema.failWithOwnedErrorMsg(msg);
2683226869 },
2683326870 .have_field_types,
2683426871 .have_layout,
......@@ -26842,17 +26879,17 @@ fn resolveTypeFieldsStruct(
2684226879 try semaStructFields(sema.mod, struct_obj);
2684326880}
2684426881
26845fn resolveTypeFieldsUnion(
26846 sema: *Sema,
26847 block: *Block,
26848 src: LazySrcLoc,
26849 ty: Type,
26850 union_obj: *Module.Union,
26851) CompileError!void {
26882fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_obj: *Module.Union) CompileError!void {
2685226883 switch (union_obj.status) {
2685326884 .none => {},
2685426885 .field_types_wip => {
26855 return sema.fail(block, src, "union '{}' depends on itself", .{ty.fmt(sema.mod)});
26886 const msg = try Module.ErrorMsg.create(
26887 sema.gpa,
26888 union_obj.srcLoc(sema.mod),
26889 "union '{}' depends on itself",
26890 .{ty.fmt(sema.mod)},
26891 );
26892 return sema.failWithOwnedErrorMsg(msg);
2685626893 },
2685726894 .have_field_types,
2685826895 .have_layout,
......@@ -27786,9 +27823,19 @@ pub fn typeHasOnePossibleValue(
2778627823 .@"struct" => {
2778727824 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
2778827825 const s = resolved_ty.castTag(.@"struct").?.data;
27789 for (s.fields.values()) |value| {
27790 if (value.is_comptime) continue;
27791 if ((try sema.typeHasOnePossibleValue(block, src, value.ty)) == null) {
27826 for (s.fields.values()) |field, i| {
27827 if (field.is_comptime) continue;
27828 if (field.ty.eql(resolved_ty, sema.mod)) {
27829 const msg = try Module.ErrorMsg.create(
27830 sema.gpa,
27831 s.srcLoc(sema.mod),
27832 "struct '{}' depends on itself",
27833 .{ty.fmt(sema.mod)},
27834 );
27835 try sema.addFieldErrNote(block, resolved_ty, i, msg, "while checking this field", .{});
27836 return sema.failWithOwnedErrorMsg(msg);
27837 }
27838 if ((try sema.typeHasOnePossibleValue(block, src, field.ty)) == null) {
2779227839 return null;
2779327840 }
2779427841 }
......@@ -27854,6 +27901,16 @@ pub fn typeHasOnePossibleValue(
2785427901 const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse
2785527902 return null;
2785627903 const only_field = union_obj.fields.values()[0];
27904 if (only_field.ty.eql(resolved_ty, sema.mod)) {
27905 const msg = try Module.ErrorMsg.create(
27906 sema.gpa,
27907 union_obj.srcLoc(sema.mod),
27908 "union '{}' depends on itself",
27909 .{ty.fmt(sema.mod)},
27910 );
27911 try sema.addFieldErrNote(block, resolved_ty, 0, msg, "while checking this field", .{});
27912 return sema.failWithOwnedErrorMsg(msg);
27913 }
2785727914 const val_val = (try sema.typeHasOnePossibleValue(block, src, only_field.ty)) orelse
2785827915 return null;
2785927916 // TODO make this not allocate. The function in `Type.onePossibleValue`
......@@ -28493,7 +28550,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
2849328550 if (struct_obj.status == .field_types_wip)
2849428551 return false;
2849528552
28496 try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);
28553 try sema.resolveTypeFieldsStruct(ty, struct_obj);
2849728554
2849828555 struct_obj.requires_comptime = .wip;
2849928556 for (struct_obj.fields.values()) |field| {
......@@ -28518,7 +28575,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
2851828575 if (union_obj.status == .field_types_wip)
2851928576 return false;
2852028577
28521 try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
28578 try sema.resolveTypeFieldsUnion(ty, union_obj);
2852228579
2852328580 union_obj.requires_comptime = .wip;
2852428581 for (union_obj.fields.values()) |field| {
test/cases/compile_errors/direct_struct_loop.zig created+9
......@@ -0,0 +1,9 @@
1const A = struct { a : A, };
2export fn entry() usize { return @sizeOf(A); }
3
4// error
5// backend=stage2
6// target=native
7//
8// :1:11: error: struct 'tmp.A' depends on itself
9// :1:20: note: while checking this field
test/cases/compile_errors/indirect_struct_loop.zig created+13
......@@ -0,0 +1,13 @@
1const A = struct { b : B, };
2const B = struct { c : C, };
3const C = struct { a : A, };
4export fn entry() usize { return @sizeOf(A); }
5
6// error
7// backend=stage2
8// target=native
9//
10// :1:11: error: struct 'tmp.A' depends on itself
11// :3:20: note: while checking this field
12// :2:20: note: while checking this field
13// :1:20: note: while checking this field
test/cases/compile_errors/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig created+16
......@@ -0,0 +1,16 @@
1const Foo = struct {
2 x: Foo,
3};
4
5var foo: Foo = undefined;
6
7export fn entry() usize {
8 return @sizeOf(@TypeOf(foo.x));
9}
10
11// error
12// backend=stage2
13// target=native
14//
15// :1:13: error: struct 'tmp.Foo' depends on itself
16// :2:5: note: while checking this field
test/cases/compile_errors/instantiating_an_undefined_value_for_an_invalid_union_that_contains_itself.zig created+16
......@@ -0,0 +1,16 @@
1const Foo = union {
2 x: Foo,
3};
4
5var foo: Foo = undefined;
6
7export fn entry() usize {
8 return @sizeOf(@TypeOf(foo.x));
9}
10
11// error
12// backend=stage2
13// target=native
14//
15// :1:13: error: union 'tmp.Foo' depends on itself
16// :2:5: note: while checking this field
test/cases/compile_errors/stage1/obj/direct_struct_loop.zig deleted-8
......@@ -1,8 +0,0 @@
1const A = struct { a : A, };
2export fn entry() usize { return @sizeOf(A); }
3
4// error
5// backend=stage1
6// target=native
7//
8// tmp.zig:1:11: error: struct 'A' depends on itself
test/cases/compile_errors/stage1/obj/indirect_struct_loop.zig deleted-10
......@@ -1,10 +0,0 @@
1const A = struct { b : B, };
2const B = struct { c : C, };
3const C = struct { a : A, };
4export fn entry() usize { return @sizeOf(A); }
5
6// error
7// backend=stage1
8// target=native
9//
10// tmp.zig:1:11: error: struct 'A' depends on itself
test/cases/compile_errors/stage1/obj/instantiating_an_undefined_value_for_an_invalid_struct_that_contains_itself.zig deleted-15
......@@ -1,15 +0,0 @@
1const Foo = struct {
2 x: Foo,
3};
4
5var foo: Foo = undefined;
6
7export fn entry() usize {
8 return @sizeOf(@TypeOf(foo.x));
9}
10
11// error
12// backend=stage1
13// target=native
14//
15// tmp.zig:1:13: error: struct 'Foo' depends on itself
test/cases/compile_errors/stage1/obj/struct_depends_on_itself_via_optional_field.zig deleted-19
......@@ -1,19 +0,0 @@
1const LhsExpr = struct {
2 rhsExpr: ?AstObject,
3};
4const AstObject = union {
5 lhsExpr: LhsExpr,
6};
7export fn entry() void {
8 const lhsExpr = LhsExpr{ .rhsExpr = null };
9 const obj = AstObject{ .lhsExpr = lhsExpr };
10 _ = obj;
11}
12
13// error
14// backend=stage1
15// target=native
16//
17// tmp.zig:1:17: error: struct 'LhsExpr' depends on itself
18// tmp.zig:5:5: note: while checking this field
19// tmp.zig:2:5: note: while checking this field
test/cases/compile_errors/struct_depends_on_itself_via_optional_field.zig created+19
......@@ -0,0 +1,19 @@
1const LhsExpr = struct {
2 rhsExpr: ?AstObject,
3};
4const AstObject = union {
5 lhsExpr: LhsExpr,
6};
7export fn entry() void {
8 const lhsExpr = LhsExpr{ .rhsExpr = null };
9 const obj = AstObject{ .lhsExpr = lhsExpr };
10 _ = obj;
11}
12
13// error
14// backend=stage2
15// target=native
16//
17// :1:17: error: struct 'tmp.LhsExpr' depends on itself
18// :5:5: note: while checking this field
19// :2:5: note: while checking this field