mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
Sema: improve circular dependency errors
This commit is contained in:
parent
797ded47f0
commit
aa78ebaf95
109
src/Sema.zig
109
src/Sema.zig
@ -26629,21 +26629,41 @@ fn resolveStructLayout(
|
||||
switch (struct_obj.status) {
|
||||
.none, .have_field_types => {},
|
||||
.field_types_wip, .layout_wip => {
|
||||
return sema.fail(block, src, "struct '{}' depends on itself", .{ty.fmt(sema.mod)});
|
||||
const msg = try Module.ErrorMsg.create(
|
||||
sema.gpa,
|
||||
struct_obj.srcLoc(sema.mod),
|
||||
"struct '{}' depends on itself",
|
||||
.{ty.fmt(sema.mod)},
|
||||
);
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
},
|
||||
.have_layout, .fully_resolved_wip, .fully_resolved => return,
|
||||
}
|
||||
struct_obj.status = .layout_wip;
|
||||
for (struct_obj.fields.values()) |field| {
|
||||
try sema.resolveTypeLayout(block, src, field.ty);
|
||||
for (struct_obj.fields.values()) |field, i| {
|
||||
sema.resolveTypeLayout(block, src, field.ty) catch |err| switch (err) {
|
||||
error.AnalysisFail => {
|
||||
const msg = sema.err orelse return err;
|
||||
try sema.addFieldErrNote(block, ty, i, msg, "while checking this field", .{});
|
||||
return err;
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
}
|
||||
struct_obj.status = .have_layout;
|
||||
|
||||
// In case of querying the ABI alignment of this struct, we will ask
|
||||
// for hasRuntimeBits() of each field, so we need "requires comptime"
|
||||
// to be known already before this function returns.
|
||||
for (struct_obj.fields.values()) |field| {
|
||||
_ = try sema.typeRequiresComptime(block, src, field.ty);
|
||||
for (struct_obj.fields.values()) |field, i| {
|
||||
_ = sema.typeRequiresComptime(block, src, field.ty) catch |err| switch (err) {
|
||||
error.AnalysisFail => {
|
||||
const msg = sema.err orelse return err;
|
||||
try sema.addFieldErrNote(block, ty, i, msg, "while checking this field", .{});
|
||||
return err;
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
}
|
||||
}
|
||||
// otherwise it's a tuple; no need to resolve anything
|
||||
@ -26660,13 +26680,26 @@ fn resolveUnionLayout(
|
||||
switch (union_obj.status) {
|
||||
.none, .have_field_types => {},
|
||||
.field_types_wip, .layout_wip => {
|
||||
return sema.fail(block, src, "union '{}' depends on itself", .{ty.fmt(sema.mod)});
|
||||
const msg = try Module.ErrorMsg.create(
|
||||
sema.gpa,
|
||||
union_obj.srcLoc(sema.mod),
|
||||
"union '{}' depends on itself",
|
||||
.{ty.fmt(sema.mod)},
|
||||
);
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
},
|
||||
.have_layout, .fully_resolved_wip, .fully_resolved => return,
|
||||
}
|
||||
union_obj.status = .layout_wip;
|
||||
for (union_obj.fields.values()) |field| {
|
||||
try sema.resolveTypeLayout(block, src, field.ty);
|
||||
for (union_obj.fields.values()) |field, i| {
|
||||
sema.resolveTypeLayout(block, src, field.ty) catch |err| switch (err) {
|
||||
error.AnalysisFail => {
|
||||
const msg = sema.err orelse return err;
|
||||
try sema.addFieldErrNote(block, ty, i, msg, "while checking this field", .{});
|
||||
return err;
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
}
|
||||
union_obj.status = .have_layout;
|
||||
}
|
||||
@ -26794,12 +26827,12 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
|
||||
switch (ty.tag()) {
|
||||
.@"struct" => {
|
||||
const struct_obj = ty.castTag(.@"struct").?.data;
|
||||
try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);
|
||||
try sema.resolveTypeFieldsStruct(ty, struct_obj);
|
||||
return ty;
|
||||
},
|
||||
.@"union", .union_safety_tagged, .union_tagged => {
|
||||
const union_obj = ty.cast(Type.Payload.Union).?.data;
|
||||
try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
|
||||
try sema.resolveTypeFieldsUnion(ty, union_obj);
|
||||
return ty;
|
||||
},
|
||||
.type_info => return sema.resolveBuiltinTypeFields(block, src, "Type"),
|
||||
@ -26820,15 +26853,19 @@ pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type)
|
||||
|
||||
fn resolveTypeFieldsStruct(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
ty: Type,
|
||||
struct_obj: *Module.Struct,
|
||||
) CompileError!void {
|
||||
switch (struct_obj.status) {
|
||||
.none => {},
|
||||
.field_types_wip => {
|
||||
return sema.fail(block, src, "struct '{}' depends on itself", .{ty.fmt(sema.mod)});
|
||||
const msg = try Module.ErrorMsg.create(
|
||||
sema.gpa,
|
||||
struct_obj.srcLoc(sema.mod),
|
||||
"struct '{}' depends on itself",
|
||||
.{ty.fmt(sema.mod)},
|
||||
);
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
},
|
||||
.have_field_types,
|
||||
.have_layout,
|
||||
@ -26842,17 +26879,17 @@ fn resolveTypeFieldsStruct(
|
||||
try semaStructFields(sema.mod, struct_obj);
|
||||
}
|
||||
|
||||
fn resolveTypeFieldsUnion(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
ty: Type,
|
||||
union_obj: *Module.Union,
|
||||
) CompileError!void {
|
||||
fn resolveTypeFieldsUnion(sema: *Sema, ty: Type, union_obj: *Module.Union) CompileError!void {
|
||||
switch (union_obj.status) {
|
||||
.none => {},
|
||||
.field_types_wip => {
|
||||
return sema.fail(block, src, "union '{}' depends on itself", .{ty.fmt(sema.mod)});
|
||||
const msg = try Module.ErrorMsg.create(
|
||||
sema.gpa,
|
||||
union_obj.srcLoc(sema.mod),
|
||||
"union '{}' depends on itself",
|
||||
.{ty.fmt(sema.mod)},
|
||||
);
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
},
|
||||
.have_field_types,
|
||||
.have_layout,
|
||||
@ -27786,9 +27823,19 @@ pub fn typeHasOnePossibleValue(
|
||||
.@"struct" => {
|
||||
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
|
||||
const s = resolved_ty.castTag(.@"struct").?.data;
|
||||
for (s.fields.values()) |value| {
|
||||
if (value.is_comptime) continue;
|
||||
if ((try sema.typeHasOnePossibleValue(block, src, value.ty)) == null) {
|
||||
for (s.fields.values()) |field, i| {
|
||||
if (field.is_comptime) continue;
|
||||
if (field.ty.eql(resolved_ty, sema.mod)) {
|
||||
const msg = try Module.ErrorMsg.create(
|
||||
sema.gpa,
|
||||
s.srcLoc(sema.mod),
|
||||
"struct '{}' depends on itself",
|
||||
.{ty.fmt(sema.mod)},
|
||||
);
|
||||
try sema.addFieldErrNote(block, resolved_ty, i, msg, "while checking this field", .{});
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
}
|
||||
if ((try sema.typeHasOnePossibleValue(block, src, field.ty)) == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -27854,6 +27901,16 @@ pub fn typeHasOnePossibleValue(
|
||||
const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse
|
||||
return null;
|
||||
const only_field = union_obj.fields.values()[0];
|
||||
if (only_field.ty.eql(resolved_ty, sema.mod)) {
|
||||
const msg = try Module.ErrorMsg.create(
|
||||
sema.gpa,
|
||||
union_obj.srcLoc(sema.mod),
|
||||
"union '{}' depends on itself",
|
||||
.{ty.fmt(sema.mod)},
|
||||
);
|
||||
try sema.addFieldErrNote(block, resolved_ty, 0, msg, "while checking this field", .{});
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
}
|
||||
const val_val = (try sema.typeHasOnePossibleValue(block, src, only_field.ty)) orelse
|
||||
return null;
|
||||
// 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
|
||||
if (struct_obj.status == .field_types_wip)
|
||||
return false;
|
||||
|
||||
try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);
|
||||
try sema.resolveTypeFieldsStruct(ty, struct_obj);
|
||||
|
||||
struct_obj.requires_comptime = .wip;
|
||||
for (struct_obj.fields.values()) |field| {
|
||||
@ -28518,7 +28575,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
|
||||
if (union_obj.status == .field_types_wip)
|
||||
return false;
|
||||
|
||||
try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
|
||||
try sema.resolveTypeFieldsUnion(ty, union_obj);
|
||||
|
||||
union_obj.requires_comptime = .wip;
|
||||
for (union_obj.fields.values()) |field| {
|
||||
|
||||
9
test/cases/compile_errors/direct_struct_loop.zig
Normal file
9
test/cases/compile_errors/direct_struct_loop.zig
Normal file
@ -0,0 +1,9 @@
|
||||
const A = struct { a : A, };
|
||||
export fn entry() usize { return @sizeOf(A); }
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :1:11: error: struct 'tmp.A' depends on itself
|
||||
// :1:20: note: while checking this field
|
||||
13
test/cases/compile_errors/indirect_struct_loop.zig
Normal file
13
test/cases/compile_errors/indirect_struct_loop.zig
Normal file
@ -0,0 +1,13 @@
|
||||
const A = struct { b : B, };
|
||||
const B = struct { c : C, };
|
||||
const C = struct { a : A, };
|
||||
export fn entry() usize { return @sizeOf(A); }
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :1:11: error: struct 'tmp.A' depends on itself
|
||||
// :3:20: note: while checking this field
|
||||
// :2:20: note: while checking this field
|
||||
// :1:20: note: while checking this field
|
||||
@ -9,7 +9,8 @@ export fn entry() usize {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:13: error: struct 'Foo' depends on itself
|
||||
// :1:13: error: struct 'tmp.Foo' depends on itself
|
||||
// :2:5: note: while checking this field
|
||||
@ -0,0 +1,16 @@
|
||||
const Foo = union {
|
||||
x: Foo,
|
||||
};
|
||||
|
||||
var foo: Foo = undefined;
|
||||
|
||||
export fn entry() usize {
|
||||
return @sizeOf(@TypeOf(foo.x));
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :1:13: error: union 'tmp.Foo' depends on itself
|
||||
// :2:5: note: while checking this field
|
||||
@ -1,8 +0,0 @@
|
||||
const A = struct { a : A, };
|
||||
export fn entry() usize { return @sizeOf(A); }
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:11: error: struct 'A' depends on itself
|
||||
@ -1,10 +0,0 @@
|
||||
const A = struct { b : B, };
|
||||
const B = struct { c : C, };
|
||||
const C = struct { a : A, };
|
||||
export fn entry() usize { return @sizeOf(A); }
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:11: error: struct 'A' depends on itself
|
||||
@ -11,9 +11,9 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:17: error: struct 'LhsExpr' depends on itself
|
||||
// tmp.zig:5:5: note: while checking this field
|
||||
// tmp.zig:2:5: note: while checking this field
|
||||
// :1:17: error: struct 'tmp.LhsExpr' depends on itself
|
||||
// :5:5: note: while checking this field
|
||||
// :2:5: note: while checking this field
|
||||
Loading…
x
Reference in New Issue
Block a user