From 86d9c3de2b6b43329c16678f1bd7eaddd3d218d7 Mon Sep 17 00:00:00 2001 From: Anton Lilja <12533691+antlilja@users.noreply.github.com> Date: Sat, 6 Aug 2022 13:17:09 +0200 Subject: [PATCH] Sema: fix infinite recursion in `explainWhyTypeIsComptime` Co-authored-by: Veikka Tuominen --- src/Sema.zig | 36 +++++++++++++++---- ..._known_struct_is_resolved_before_error.zig | 19 ++++++++++ ...f_referential_struct_requires_comptime.zig | 18 ++++++++++ ...lf_referential_union_requires_comptime.zig | 17 +++++++++ 4 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 test/cases/compile_errors/AstGen_comptime_known_struct_is_resolved_before_error.zig create mode 100644 test/cases/compile_errors/self_referential_struct_requires_comptime.zig create mode 100644 test/cases/compile_errors/self_referential_union_requires_comptime.zig diff --git a/src/Sema.zig b/src/Sema.zig index 76c077c103..8c2125026d 100644 --- a/src/Sema.zig +++ b/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); } } } diff --git a/test/cases/compile_errors/AstGen_comptime_known_struct_is_resolved_before_error.zig b/test/cases/compile_errors/AstGen_comptime_known_struct_is_resolved_before_error.zig new file mode 100644 index 0000000000..8e9358c6f4 --- /dev/null +++ b/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 diff --git a/test/cases/compile_errors/self_referential_struct_requires_comptime.zig b/test/cases/compile_errors/self_referential_struct_requires_comptime.zig new file mode 100644 index 0000000000..3ce7571026 --- /dev/null +++ b/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 diff --git a/test/cases/compile_errors/self_referential_union_requires_comptime.zig b/test/cases/compile_errors/self_referential_union_requires_comptime.zig new file mode 100644 index 0000000000..a2433adde9 --- /dev/null +++ b/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