Commit 98a5998d83

Veikka Tuominen <git@vexu.eu>
2022-03-11 10:24:01
Sema: improve detection of generic parameters
1 parent 078037a
Changed files (3)
lib
src
test
behavior
lib/std/rand.zig
@@ -7,6 +7,7 @@
 //! TODO(tiehuis): Benchmark these against other reference implementations.
 
 const std = @import("std.zig");
+const builtin = @import("builtin");
 const assert = std.debug.assert;
 const expect = std.testing.expect;
 const expectEqual = std.testing.expectEqual;
@@ -30,7 +31,10 @@ pub const Sfc64 = @import("rand/Sfc64.zig");
 
 pub const Random = struct {
     ptr: *anyopaque,
-    fillFn: fn (ptr: *anyopaque, buf: []u8) void,
+    fillFn: if (builtin.zig_backend == .stage1)
+        fn (ptr: *anyopaque, buf: []u8) void
+    else
+        *const fn (ptr: *anyopaque, buf: []u8) void,
 
     pub fn init(pointer: anytype, comptime fillFn: fn (ptr: @TypeOf(pointer), buf: []u8) void) Random {
         const Ptr = @TypeOf(pointer);
src/Sema.zig
@@ -5923,6 +5923,10 @@ fn funcCommon(
                     break :ret_ty ret_ty;
                 } else |err| break :err err;
             } else |err| break :err err;
+            // Check for generic params.
+            for (block.params.items) |param| {
+                if (param.ty.tag() == .generic_poison) is_generic = true;
+            }
         };
         switch (err) {
             error.GenericPoison => {
@@ -6111,6 +6115,13 @@ fn zirParam(
 
             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.
+                        break :err error.GenericPoison;
+                    }
                     break :param_ty param_ty;
                 } else |err| break :err err;
             } else |err| break :err err;
@@ -10965,6 +10976,7 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
 
     const operand = try sema.resolveBody(&child_block, body, inst);
     const operand_ty = sema.typeOf(operand);
+    if (operand_ty.tag() == .generic_poison) return error.GenericPoison;
     return sema.addType(operand_ty);
 }
 
@@ -11044,6 +11056,7 @@ fn zirTypeofPeer(
 
     for (args) |arg_ref, i| {
         inst_list[i] = sema.resolveInst(arg_ref);
+        if (sema.typeOf(inst_list[i]).tag() == .generic_poison) return error.GenericPoison;
     }
 
     const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node });
test/behavior/generics.zig
@@ -230,3 +230,16 @@ fn GenNode(comptime T: type) type {
         }
     };
 }
+
+test "function parameter is generic" {
+    const S = struct {
+        pub fn init(pointer: anytype, comptime fillFn: fn (ptr: *@TypeOf(pointer)) void) void {
+            _ = fillFn;
+        }
+        pub fn fill(self: *u32) void {
+            _ = self;
+        }
+    };
+    var rng: u32 = 2;
+    S.init(rng, S.fill);
+}