Commit 86d9c3de2b

Anton Lilja <12533691+antlilja@users.noreply.github.com>
2022-08-06 13:17:09
Sema: fix infinite recursion in `explainWhyTypeIsComptime`
Co-authored-by: Veikka Tuominen <git@vexu.eu>
1 parent 5c98266
src/Sema.zig
@@ -19764,6 +19764,8 @@ fn validateRunTimeType(
     };
 }
 
+const TypeSet = std.HashMapUnmanaged(Type, void, Type.HashContext64, std.hash_map.default_max_load_percentage);
+
 fn explainWhyTypeIsComptime(
     sema: *Sema,
     block: *Block,
@@ -19771,6 +19773,22 @@ fn explainWhyTypeIsComptime(
     msg: *Module.ErrorMsg,
     src_loc: Module.SrcLoc,
     ty: Type,
+) CompileError!void {
+    var type_set = TypeSet{};
+    defer type_set.deinit(sema.gpa);
+
+    try sema.resolveTypeFully(block, src, ty);
+    return sema.explainWhyTypeIsComptimeInner(block, src, msg, src_loc, ty, &type_set);
+}
+
+fn explainWhyTypeIsComptimeInner(
+    sema: *Sema,
+    block: *Block,
+    src: LazySrcLoc,
+    msg: *Module.ErrorMsg,
+    src_loc: Module.SrcLoc,
+    ty: Type,
+    type_set: *TypeSet,
 ) CompileError!void {
     const mod = sema.mod;
     switch (ty.zigTypeTag()) {
@@ -19808,7 +19826,7 @@ fn explainWhyTypeIsComptime(
         },
 
         .Array, .Vector => {
-            try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType());
+            try sema.explainWhyTypeIsComptimeInner(block, src, msg, src_loc, ty.elemType(), type_set);
         },
         .Pointer => {
             const elem_ty = ty.elemType2();
@@ -19826,18 +19844,20 @@ fn explainWhyTypeIsComptime(
                 }
                 return;
             }
-            try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType());
+            try sema.explainWhyTypeIsComptimeInner(block, src, msg, src_loc, ty.elemType(), type_set);
         },
 
         .Optional => {
             var buf: Type.Payload.ElemType = undefined;
-            try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.optionalChild(&buf));
+            try sema.explainWhyTypeIsComptimeInner(block, src, msg, src_loc, ty.optionalChild(&buf), type_set);
         },
         .ErrorUnion => {
-            try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.errorUnionPayload());
+            try sema.explainWhyTypeIsComptimeInner(block, src, msg, src_loc, ty.errorUnionPayload(), type_set);
         },
 
         .Struct => {
+            if ((try type_set.getOrPutContext(sema.gpa, ty, .{ .mod = mod })).found_existing) return;
+
             if (ty.castTag(.@"struct")) |payload| {
                 const struct_obj = payload.data;
                 for (struct_obj.fields.values()) |field, i| {
@@ -19845,9 +19865,10 @@ fn explainWhyTypeIsComptime(
                         .index = i,
                         .range = .type,
                     });
+
                     if (try sema.typeRequiresComptime(block, src, field.ty)) {
                         try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{});
-                        try sema.explainWhyTypeIsComptime(block, src, msg, field_src_loc, field.ty);
+                        try sema.explainWhyTypeIsComptimeInner(block, src, msg, field_src_loc, field.ty, type_set);
                     }
                 }
             }
@@ -19855,6 +19876,8 @@ fn explainWhyTypeIsComptime(
         },
 
         .Union => {
+            if ((try type_set.getOrPutContext(sema.gpa, ty, .{ .mod = mod })).found_existing) return;
+
             if (ty.cast(Type.Payload.Union)) |payload| {
                 const union_obj = payload.data;
                 for (union_obj.fields.values()) |field, i| {
@@ -19862,9 +19885,10 @@ fn explainWhyTypeIsComptime(
                         .index = i,
                         .range = .type,
                     });
+
                     if (try sema.typeRequiresComptime(block, src, field.ty)) {
                         try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{});
-                        try sema.explainWhyTypeIsComptime(block, src, msg, field_src_loc, field.ty);
+                        try sema.explainWhyTypeIsComptimeInner(block, src, msg, field_src_loc, field.ty, type_set);
                     }
                 }
             }
test/cases/compile_errors/AstGen_comptime_known_struct_is_resolved_before_error.zig
@@ -0,0 +1,19 @@
+const S1 = struct {
+    a: S2,
+};
+const S2 = struct {
+    b: fn () void,
+};
+pub export fn entry() void {
+    var s: S1 = undefined;
+    _ = s;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :8:12: error: variable of type 'tmp.S1' must be const or comptime
+// :2:8: note: struct requires comptime because of this field
+// :5:8: note: struct requires comptime because of this field
+// :5:8: note: use '*const fn() void' for a function pointer type
test/cases/compile_errors/self_referential_struct_requires_comptime.zig
@@ -0,0 +1,18 @@
+const S = struct {
+    a: fn () void,
+    b: *S,
+};
+pub export fn entry() void {
+    var s: S = undefined;
+    _ = s;
+}
+
+
+// error
+// backend=stage2
+// target=native
+//
+// :6:12: error: variable of type 'tmp.S' must be const or comptime
+// :2:8: note: struct requires comptime because of this field
+// :2:8: note: use '*const fn() void' for a function pointer type
+// :3:8: note: struct requires comptime because of this field
test/cases/compile_errors/self_referential_union_requires_comptime.zig
@@ -0,0 +1,17 @@
+const U = union {
+    a: fn () void,
+    b: *U,
+};
+pub export fn entry() void {
+    var u: U = undefined;
+    _ = u;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :6:12: error: variable of type 'tmp.U' must be const or comptime
+// :2:8: note: union requires comptime because of this field
+// :2:8: note: use '*const fn() void' for a function pointer type
+// :3:8: note: union requires comptime because of this field