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(...@@ -26629,21 +26629,41 @@ fn resolveStructLayout(
26629 switch (struct_obj.status) {26629 switch (struct_obj.status) {
26630 .none, .have_field_types => {},26630 .none, .have_field_types => {},
26631 .field_types_wip, .layout_wip => {26631 .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);
26633 },26639 },
26634 .have_layout, .fully_resolved_wip, .fully_resolved => return,26640 .have_layout, .fully_resolved_wip, .fully_resolved => return,
26635 }26641 }
26636 struct_obj.status = .layout_wip;26642 struct_obj.status = .layout_wip;
26637 for (struct_obj.fields.values()) |field| {26643 for (struct_obj.fields.values()) |field, i| {
26638 try sema.resolveTypeLayout(block, src, field.ty);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 };
26639 }26652 }
26640 struct_obj.status = .have_layout;26653 struct_obj.status = .have_layout;
2664126654
26642 // In case of querying the ABI alignment of this struct, we will ask26655 // In case of querying the ABI alignment of this struct, we will ask
26643 // for hasRuntimeBits() of each field, so we need "requires comptime"26656 // for hasRuntimeBits() of each field, so we need "requires comptime"
26644 // to be known already before this function returns.26657 // to be known already before this function returns.
26645 for (struct_obj.fields.values()) |field| {26658 for (struct_obj.fields.values()) |field, i| {
26646 _ = try sema.typeRequiresComptime(block, src, field.ty);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 };
26647 }26667 }
26648 }26668 }
26649 // otherwise it's a tuple; no need to resolve anything26669 // otherwise it's a tuple; no need to resolve anything
...@@ -26660,13 +26680,26 @@ fn resolveUnionLayout(...@@ -26660,13 +26680,26 @@ fn resolveUnionLayout(
26660 switch (union_obj.status) {26680 switch (union_obj.status) {
26661 .none, .have_field_types => {},26681 .none, .have_field_types => {},
26662 .field_types_wip, .layout_wip => {26682 .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);
26664 },26690 },
26665 .have_layout, .fully_resolved_wip, .fully_resolved => return,26691 .have_layout, .fully_resolved_wip, .fully_resolved => return,
26666 }26692 }
26667 union_obj.status = .layout_wip;26693 union_obj.status = .layout_wip;
26668 for (union_obj.fields.values()) |field| {26694 for (union_obj.fields.values()) |field, i| {
26669 try sema.resolveTypeLayout(block, src, field.ty);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 };
26670 }26703 }
26671 union_obj.status = .have_layout;26704 union_obj.status = .have_layout;
26672}26705}
...@@ -26794,12 +26827,12 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)...@@ -26794,12 +26827,12 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
26794 switch (ty.tag()) {26827 switch (ty.tag()) {
26795 .@"struct" => {26828 .@"struct" => {
26796 const struct_obj = ty.castTag(.@"struct").?.data;26829 const struct_obj = ty.castTag(.@"struct").?.data;
26797 try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);26830 try sema.resolveTypeFieldsStruct(ty, struct_obj);
26798 return ty;26831 return ty;
26799 },26832 },
26800 .@"union", .union_safety_tagged, .union_tagged => {26833 .@"union", .union_safety_tagged, .union_tagged => {
26801 const union_obj = ty.cast(Type.Payload.Union).?.data;26834 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);
26803 return ty;26836 return ty;
26804 },26837 },
26805 .type_info => return sema.resolveBuiltinTypeFields(block, src, "Type"),26838 .type_info => return sema.resolveBuiltinTypeFields(block, src, "Type"),
...@@ -26820,15 +26853,19 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)...@@ -26820,15 +26853,19 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
2682026853
26821fn resolveTypeFieldsStruct(26854fn resolveTypeFieldsStruct(
26822 sema: *Sema,26855 sema: *Sema,
26823 block: *Block,
26824 src: LazySrcLoc,
26825 ty: Type,26856 ty: Type,
26826 struct_obj: *Module.Struct,26857 struct_obj: *Module.Struct,
26827) CompileError!void {26858) CompileError!void {
26828 switch (struct_obj.status) {26859 switch (struct_obj.status) {
26829 .none => {},26860 .none => {},
26830 .field_types_wip => {26861 .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);
26832 },26869 },
26833 .have_field_types,26870 .have_field_types,
26834 .have_layout,26871 .have_layout,
...@@ -26842,17 +26879,17 @@ fn resolveTypeFieldsStruct(...@@ -26842,17 +26879,17 @@ fn resolveTypeFieldsStruct(
26842 try semaStructFields(sema.mod, struct_obj);26879 try semaStructFields(sema.mod, struct_obj);
26843}26880}
2684426881
26845fn resolveTypeFieldsUnion(26882fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_obj: *Module.Union) CompileError!void {
26846 sema: *Sema,
26847 block: *Block,
26848 src: LazySrcLoc,
26849 ty: Type,
26850 union_obj: *Module.Union,
26851) CompileError!void {
26852 switch (union_obj.status) {26883 switch (union_obj.status) {
26853 .none => {},26884 .none => {},
26854 .field_types_wip => {26885 .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);
26856 },26893 },
26857 .have_field_types,26894 .have_field_types,
26858 .have_layout,26895 .have_layout,
...@@ -27786,9 +27823,19 @@ pub fn typeHasOnePossibleValue(...@@ -27786,9 +27823,19 @@ pub fn typeHasOnePossibleValue(
27786 .@"struct" => {27823 .@"struct" => {
27787 const resolved_ty = try sema.resolveTypeFields(block, src, ty);27824 const resolved_ty = try sema.resolveTypeFields(block, src, ty);
27788 const s = resolved_ty.castTag(.@"struct").?.data;27825 const s = resolved_ty.castTag(.@"struct").?.data;
27789 for (s.fields.values()) |value| {27826 for (s.fields.values()) |field, i| {
27790 if (value.is_comptime) continue;27827 if (field.is_comptime) continue;
27791 if ((try sema.typeHasOnePossibleValue(block, src, value.ty)) == null) {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) {
27792 return null;27839 return null;
27793 }27840 }
27794 }27841 }
...@@ -27854,6 +27901,16 @@ pub fn typeHasOnePossibleValue(...@@ -27854,6 +27901,16 @@ pub fn typeHasOnePossibleValue(
27854 const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse27901 const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse
27855 return null;27902 return null;
27856 const only_field = union_obj.fields.values()[0];27903 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 }
27857 const val_val = (try sema.typeHasOnePossibleValue(block, src, only_field.ty)) orelse27914 const val_val = (try sema.typeHasOnePossibleValue(block, src, only_field.ty)) orelse
27858 return null;27915 return null;
27859 // TODO make this not allocate. The function in `Type.onePossibleValue`27916 // 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...@@ -28493,7 +28550,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
28493 if (struct_obj.status == .field_types_wip)28550 if (struct_obj.status == .field_types_wip)
28494 return false;28551 return false;
2849528552
28496 try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);28553 try sema.resolveTypeFieldsStruct(ty, struct_obj);
2849728554
28498 struct_obj.requires_comptime = .wip;28555 struct_obj.requires_comptime = .wip;
28499 for (struct_obj.fields.values()) |field| {28556 for (struct_obj.fields.values()) |field| {
...@@ -28518,7 +28575,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ...@@ -28518,7 +28575,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
28518 if (union_obj.status == .field_types_wip)28575 if (union_obj.status == .field_types_wip)
28519 return false;28576 return false;
2852028577
28521 try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);28578 try sema.resolveTypeFieldsUnion(ty, union_obj);
2852228579
28523 union_obj.requires_comptime = .wip;28580 union_obj.requires_comptime = .wip;
28524 for (union_obj.fields.values()) |field| {28581 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