Commit bb0e28a54f

Andrew Kelley <andrew@ziglang.org>
2022-03-25 03:36:36
Sema: fix generic function with void parameters
This also fixes a bug that I didn't see causing any problems yet in generic function instantiation where it would read from a GetOrPutResult too late. Also it delays full resolution of generic function type parameters until after the function body is finished being analyzed. closes #11291
1 parent 7cfa97a
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -5149,7 +5149,7 @@ fn instantiateGenericCall(
         .target = target,
     };
     const gop = try mod.monomorphed_funcs.getOrPutContextAdapted(gpa, {}, adapter, .{ .target = target });
-    if (!gop.found_existing) {
+    const callee = if (!gop.found_existing) callee: {
         const new_module_func = try gpa.create(Module.Fn);
         gop.key_ptr.* = new_module_func;
         errdefer gpa.destroy(new_module_func);
@@ -5357,9 +5357,9 @@ fn instantiateGenericCall(
         try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
 
         try new_decl.finalizeNewArena(&new_decl_arena);
-    }
+        break :callee new_func;
+    } else gop.key_ptr.*;
 
-    const callee = gop.key_ptr.*;
     const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl);
 
     // Make a runtime call to the new function, making sure to omit the comptime args.
@@ -5397,8 +5397,8 @@ fn instantiateGenericCall(
                 const param_ty = new_fn_info.param_types[runtime_i];
                 const arg_src = call_src; // TODO: better source location
                 const uncasted_arg = uncasted_args[total_i];
-                try sema.resolveTypeFully(block, arg_src, param_ty);
                 const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
+                try sema.queueFullTypeResolution(param_ty);
                 runtime_args[runtime_i] = casted_arg;
                 runtime_i += 1;
             }
@@ -6474,10 +6474,13 @@ fn zirParam(
         const err = err: {
             // 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;
             block.params = .{};
+            sema.preallocated_new_func = null;
             defer {
                 block.params.deinit(sema.gpa);
                 block.params = prev_params;
+                sema.preallocated_new_func = prev_preallocated_new_func;
             }
 
             if (sema.resolveBody(block, body, inst)) |param_ty_inst| {
@@ -6524,6 +6527,17 @@ fn zirParam(
         assert(sema.inst_map.remove(inst));
     }
 
+    if (sema.preallocated_new_func != null) {
+        if (try sema.typeHasOnePossibleValue(block, src, param_ty)) |opv| {
+            // In this case we are instantiating a generic function call with a non-comptime
+            // non-anytype parameter that ended up being a one-possible-type.
+            // We don't want the parameter to be part of the instantiated function type.
+            const result = try sema.addConstant(param_ty, opv);
+            try sema.inst_map.put(sema.gpa, inst, result);
+            return;
+        }
+    }
+
     try block.params.append(sema.gpa, .{
         .ty = param_ty,
         .is_comptime = is_comptime,
test/behavior/generics.zig
@@ -277,3 +277,16 @@ test "generic function instantiation turns into comptime call" {
     };
     try S.doTheTest();
 }
+
+test "generic function with void and comptime parameter" {
+    const S = struct { x: i32 };
+    const namespace = struct {
+        fn foo(v: void, s: *S, comptime T: type) !void {
+            _ = @as(void, v);
+            try expect(s.x == 1234);
+            try expect(T == u8);
+        }
+    };
+    var s: S = .{ .x = 1234 };
+    try namespace.foo({}, &s, u8);
+}