From eedfce92b0363af1b6783ab626690be0aebf4e94 Mon Sep 17 00:00:00 2001 From: mlugg Date: Sat, 22 Mar 2025 01:58:13 +0000 Subject: [PATCH] Sema: fix in-memory coercion of functions introducing new generic parameters While it is not allowed for a function coercion to change whether a function is generic, it *is* okay to make existing concrete parameters of a generic function also generic, or vice versa. Either of these cases implies that the result is a generic function, so comptime type checks will happen when the function is ultimately called. Resolves: #21099 --- src/Sema.zig | 25 +++++++++++-------------- test/behavior/fn.zig | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index cdc80f3cea..38b7102f60 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -31031,20 +31031,17 @@ fn coerceInMemoryAllowedFns( } }; } - switch (src_param_ty.toIntern()) { - .generic_poison_type => {}, - else => { - // Note: Cast direction is reversed here. - const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, dest_is_mut, target, dest_src, src_src, null); - if (param != .ok) { - return .{ .fn_param = .{ - .child = try param.dupe(sema.arena), - .actual = src_param_ty, - .wanted = dest_param_ty, - .index = param_i, - } }; - } - }, + if (!src_param_ty.isGenericPoison() and !dest_param_ty.isGenericPoison()) { + // Note: Cast direction is reversed here. + const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, dest_is_mut, target, dest_src, src_src, null); + if (param != .ok) { + return .{ .fn_param = .{ + .child = try param.dupe(sema.arena), + .actual = src_param_ty, + .wanted = dest_param_ty, + .index = param_i, + } }; + } } } diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 27be4f9586..cff4cf567e 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -732,3 +732,27 @@ test "inline function return type is evaluated at comptime" { comptime assert(@TypeOf(result) == u16); try expect(result == 123); } + +test "coerce generic function making concrete parameter generic" { + const S = struct { + fn foo(_: anytype, x: u32) u32 { + comptime assert(@TypeOf(x) == u32); + return x; + } + }; + const coerced: fn (anytype, anytype) u32 = S.foo; + const result = coerced({}, 123); + try expect(result == 123); +} + +test "coerce generic function making generic parameter concrete" { + const S = struct { + fn foo(_: anytype, x: anytype) u32 { + comptime assert(@TypeOf(x) == u32); + return x; + } + }; + const coerced: fn (anytype, u32) u32 = S.foo; + const result = coerced({}, 123); + try expect(result == 123); +}