From 4fccc95b0152aefeb40912768ec045b57a2fdc2b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 20 Jan 2022 15:36:40 -0700 Subject: 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. --- src/Module.zig | 4 ++++ src/type.zig | 44 +++++++++++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/Module.zig b/src/Module.zig index c66509f33a..1cb890b886 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -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`. diff --git a/src/type.zig b/src/type.zig index f7d8795162..0801d7e01f 100644 --- a/src/type.zig +++ b/src/type.zig @@ -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, -- cgit v1.2.3