Commit 4fccc95b01

Andrew Kelley <andrew@ziglang.org>
2022-01-20 23:36:40
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.
1 parent e86ff71
Changed files (2)
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`.
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,