Commit 4405188cf7

Veikka Tuominen <git@vexu.eu>
2022-08-25 10:52:58
Sema: ignore comptime params in partial func type check
This fixes a bug exposed by cd1833044ab7505bc101c85f59889bd3ea3fac80 where a function type would be converted to generic_poison even after being instantiated due to containing comptime only types. This could also be fixed by just checking `is_generic_instantiation` but this way also provides better type names. Closes #12625
1 parent 3a7ea0b
src/Sema.zig
@@ -7720,7 +7720,6 @@ fn funcCommon(
     noalias_bits: u32,
     is_noinline: bool,
 ) CompileError!Air.Inst.Ref {
-    const fn_src = LazySrcLoc.nodeOffset(src_node_offset);
     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
     const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset };
 
@@ -7791,13 +7790,11 @@ fn funcCommon(
             param_types[i] = param.ty;
             sema.analyzeParameter(
                 block,
-                fn_src,
                 .unneeded,
                 param,
                 comptime_params,
                 i,
                 &is_generic,
-                is_extern,
                 cc_workaround,
                 has_body,
             ) catch |err| switch (err) {
@@ -7805,13 +7802,11 @@ fn funcCommon(
                     const decl = sema.mod.declPtr(block.src_decl);
                     try sema.analyzeParameter(
                         block,
-                        fn_src,
                         Module.paramSrc(src_node_offset, sema.gpa, decl, i),
                         param,
                         comptime_params,
                         i,
                         &is_generic,
-                        is_extern,
                         cc_workaround,
                         has_body,
                     );
@@ -7821,9 +7816,10 @@ fn funcCommon(
             };
         }
 
+        var is_comptime_ret = false;
         const ret_poison = if (!is_generic) rp: {
             if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| {
-                is_generic = ret_comptime;
+                is_comptime_ret = ret_comptime;
                 break :rp bare_return_type.tag() == .generic_poison;
             } else |err| switch (err) {
                 error.GenericPoison => {
@@ -7920,6 +7916,8 @@ fn funcCommon(
             return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'Inline'", .{});
         }
         if (is_generic and sema.no_partial_func_ty) return error.GenericPoison;
+        for (comptime_params) |ct| is_generic = is_generic or ct;
+        is_generic = is_generic or is_comptime_ret;
 
         break :fn_ty try Type.Tag.function.create(sema.arena, .{
             .param_types = param_types,
@@ -8010,31 +8008,20 @@ fn funcCommon(
 fn analyzeParameter(
     sema: *Sema,
     block: *Block,
-    func_src: LazySrcLoc,
     param_src: LazySrcLoc,
     param: Block.Param,
     comptime_params: []bool,
     i: usize,
     is_generic: *bool,
-    is_extern: bool,
     cc: std.builtin.CallingConvention,
     has_body: bool,
 ) !void {
     const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty);
     comptime_params[i] = param.is_comptime or requires_comptime;
-    const this_generic = comptime_params[i] or param.ty.tag() == .generic_poison;
+    const this_generic = param.ty.tag() == .generic_poison;
     is_generic.* = is_generic.* or this_generic;
-    if (is_extern and this_generic) {
-        // TODO this check should exist somewhere for notes.
-        if (param_src == .unneeded) return error.NeededSourceLocation;
-        const msg = msg: {
-            const msg = try sema.errMsg(block, func_src, "extern function cannot be generic", .{});
-            errdefer msg.destroy(sema.gpa);
-
-            try sema.errNote(block, param_src, msg, "function is generic because of this parameter", .{});
-            break :msg msg;
-        };
-        return sema.failWithOwnedErrorMsg(msg);
+    if (param.is_comptime and !Type.fnCallingConventionAllowsZigTypes(cc)) {
+        return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
     }
     if (this_generic and !Type.fnCallingConventionAllowsZigTypes(cc)) {
         return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
test/behavior/typename.zig
@@ -235,3 +235,14 @@ test "local variable" {
     try expectEqualStrings("behavior.typename.test.local variable.Qux", @typeName(Qux));
     try expectEqualStrings("behavior.typename.test.local variable.Quux", @typeName(Quux));
 }
+
+test "comptime parameters not converted to anytype in function type" {
+    if (builtin.zig_backend == .stage1) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+    const T = fn (fn (type) void, void) void;
+    try expectEqualStrings("fn(fn(type) void, void) void", @typeName(T));
+}
test/cases/compile_errors/export_function_with_comptime_parameter.zig
@@ -6,4 +6,4 @@ export fn foo(comptime x: anytype, y: i32) i32{
 // backend=stage2
 // target=native
 //
-// :1:15: error: generic parameters not allowed in function with calling convention 'C'
+// :1:15: error: comptime parameters not allowed in function with calling convention 'C'
test/cases/compile_errors/extern_function_with_comptime_parameter.zig
@@ -12,9 +12,6 @@ comptime { _ = entry2; }
 // backend=stage2
 // target=native
 //
-// :5:12: error: extern function cannot be generic
-// :5:30: note: function is generic because of this parameter
-// :6:12: error: extern function cannot be generic
-// :6:30: note: function is generic because of this parameter
-// :1:8: error: extern function cannot be generic
-// :1:15: note: function is generic because of this parameter
+// :1:15: error: comptime parameters not allowed in function with calling convention 'C'
+// :5:30: error: comptime parameters not allowed in function with calling convention 'C'
+// :6:30: error: generic parameters not allowed in function with calling convention 'C'