Sema: fix requiresComptime infinite recursion

When asking a struct or union whether the type requires comptime, it may
need to ask itself recursively, for example because of a field which is
a pointer to itself. This commit adds a field to each to keep track of
when computing the "requires comptime" value and returns `false` if the
check is already ongoing.
This commit is contained in:
Andrew Kelley 2022-01-20 15:36:40 -07:00
parent e86ff712a6
commit 4fccc95b01
2 changed files with 37 additions and 11 deletions

View File

@ -821,6 +821,8 @@ pub const ErrorSet = struct {
}
};
pub const RequiresComptime = enum { no, yes, unknown, wip };
/// Represents the data that a struct declaration provides.
pub const Struct = struct {
/// The Decl that corresponds to the struct itself.
@ -849,6 +851,7 @@ pub const Struct = struct {
/// If true, definitely nonzero size at runtime. If false, resolving the fields
/// is necessary to determine whether it has bits at runtime.
known_has_bits: bool,
requires_comptime: RequiresComptime = .unknown,
pub const Fields = std.StringArrayHashMapUnmanaged(Field);
@ -1038,6 +1041,7 @@ pub const Union = struct {
// which `have_layout` does not ensure.
fully_resolved,
},
requires_comptime: RequiresComptime = .unknown,
pub const Field = struct {
/// undefined until `status` is `have_field_types` or `have_layout`.

View File

@ -1544,22 +1544,40 @@ pub const Type = extern union {
.@"struct" => {
const struct_obj = ty.castTag(.@"struct").?.data;
for (struct_obj.fields.values()) |field| {
if (requiresComptime(field.ty)) {
return true;
}
switch (struct_obj.requires_comptime) {
.no, .wip => return false,
.yes => return true,
.unknown => {
struct_obj.requires_comptime = .wip;
for (struct_obj.fields.values()) |field| {
if (requiresComptime(field.ty)) {
struct_obj.requires_comptime = .yes;
return true;
}
}
struct_obj.requires_comptime = .no;
return false;
},
}
return false;
},
.@"union", .union_tagged => {
const union_obj = ty.cast(Payload.Union).?.data;
for (union_obj.fields.values()) |field| {
if (requiresComptime(field.ty)) {
return true;
}
switch (union_obj.requires_comptime) {
.no, .wip => return false,
.yes => return true,
.unknown => {
union_obj.requires_comptime = .wip;
for (union_obj.fields.values()) |field| {
if (requiresComptime(field.ty)) {
union_obj.requires_comptime = .yes;
return true;
}
}
union_obj.requires_comptime = .no;
return false;
},
}
return false;
},
.error_union => return requiresComptime(errorUnionPayload(ty)),
@ -3661,7 +3679,7 @@ pub const Type = extern union {
.Slice, .Many, .C => true,
.One => ty.elemType().zigTypeTag() == .Array,
},
.Struct => ty.tag() == .tuple,
.Struct => ty.isTuple(),
else => false,
};
}
@ -4501,6 +4519,10 @@ pub const Type = extern union {
}
};
pub fn isTuple(ty: Type) bool {
return ty.tag() == .tuple;
}
/// The sub-types are named after what fields they contain.
pub const Payload = struct {
tag: Tag,