Commit cd1833044a

Veikka Tuominen <git@vexu.eu>
2022-08-24 18:52:52
Sema: do not construct nested partial function types
Closes #12616
1 parent d515d37
Changed files (3)
src/Sema.zig
@@ -78,6 +78,9 @@ post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{},
 err: ?*Module.ErrorMsg = null,
 /// True when analyzing a generic instantiation. Used to suppress some errors.
 is_generic_instantiation: bool = false,
+/// Set to true when analyzing a func type instruction so that nested generic
+/// function types will emit generic poison instead of a partial type.
+no_partial_func_ty: bool = false,
 
 const std = @import("std");
 const math = std.math;
@@ -7917,6 +7920,7 @@ fn funcCommon(
         if (cc_workaround == .Inline and is_noinline) {
             return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'Inline'", .{});
         }
+        if (is_generic and sema.no_partial_func_ty) return error.GenericPoison;
 
         break :fn_ty try Type.Tag.function.create(sema.arena, .{
             .param_types = param_types,
@@ -8097,25 +8101,19 @@ fn zirParam(
             // Make sure any nested param instructions don't clobber our work.
             const prev_params = block.params;
             const prev_preallocated_new_func = sema.preallocated_new_func;
+            const prev_no_partial_func_type = sema.no_partial_func_ty;
             block.params = .{};
             sema.preallocated_new_func = null;
+            sema.no_partial_func_ty = true;
             defer {
                 block.params.deinit(sema.gpa);
                 block.params = prev_params;
                 sema.preallocated_new_func = prev_preallocated_new_func;
+                sema.no_partial_func_ty = prev_no_partial_func_type;
             }
 
             if (sema.resolveBody(block, body, inst)) |param_ty_inst| {
                 if (sema.analyzeAsType(block, src, param_ty_inst)) |param_ty| {
-                    if (param_ty.zigTypeTag() == .Fn and param_ty.fnInfo().is_generic) {
-                        // zirFunc will not emit error.GenericPoison to build a
-                        // partial type for generic functions but we still need to
-                        // detect if a function parameter is a generic function
-                        // to force the parent function to also be generic.
-                        if (!sema.inst_map.contains(inst)) {
-                            break :err error.GenericPoison;
-                        }
-                    }
                     break :param_ty param_ty;
                 } else |err| break :err err;
             } else |err| break :err err;
test/behavior/generics.zig
@@ -342,3 +342,18 @@ test "generic instantiation of tagged union with only one field" {
     try expect(S.foo(.{ .s = "a" }) == 1);
     try expect(S.foo(.{ .s = "ab" }) == 2);
 }
+
+test "nested generic function" {
+    const S = struct {
+        fn foo(comptime T: type, callback: *const fn (user_data: T) anyerror!void, data: T) anyerror!void {
+            try callback(data);
+        }
+        fn bar(a: u32) anyerror!void {
+            try expect(a == 123);
+        }
+
+        fn g(_: *const fn (anytype) void) void {}
+    };
+    try expect(@typeInfo(@TypeOf(S.g)).Fn.is_generic);
+    try S.foo(u32, S.bar, 123);
+}
test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig
@@ -1,5 +1,6 @@
 fn f(_: anytype) void {}
-fn g(h: *const fn (anytype) void) void {
+const T = *const fn (anytype) void;
+fn g(h: T) void {
     h({});
 }
 pub export fn entry() void {
@@ -19,5 +20,5 @@ pub export fn entry1() void {
 // backend=stage2
 // target=native
 //
-// :2:6: error: parameter of type '*const fn(anytype) void' must be declared comptime
-// :9:34: error: parameter of type 'comptime_int' must be declared comptime
+// :3:6: error: parameter of type '*const fn(anytype) void' must be declared comptime
+// :10:34: error: parameter of type 'comptime_int' must be declared comptime