Sema: allow coercion of generic function pointer

to runtime function pointer when comptime-known.
This commit is contained in:
Andrew Kelley 2025-09-10 01:47:51 -07:00
parent 0b75a2a1b1
commit 504c85a626

View file

@ -29545,18 +29545,18 @@ pub fn coerceInMemoryAllowed(
const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty); const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty);
if (maybe_dest_ptr_ty) |dest_ptr_ty| { if (maybe_dest_ptr_ty) |dest_ptr_ty| {
if (maybe_src_ptr_ty) |src_ptr_ty| { if (maybe_src_ptr_ty) |src_ptr_ty| {
return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src); return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src, src_val);
} }
} }
// Slices // Slices
if (dest_ty.isSlice(zcu) and src_ty.isSlice(zcu)) { if (dest_ty.isSlice(zcu) and src_ty.isSlice(zcu)) {
return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src, src_val);
} }
// Functions // Functions
if (dest_tag == .@"fn" and src_tag == .@"fn") { if (dest_tag == .@"fn" and src_tag == .@"fn") {
return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src, src_val);
} }
// Error Unions // Error Unions
@ -29801,6 +29801,7 @@ fn coerceInMemoryAllowedFns(
target: *const std.Target, target: *const std.Target,
dest_src: LazySrcLoc, dest_src: LazySrcLoc,
src_src: LazySrcLoc, src_src: LazySrcLoc,
src_val: ?Value,
) !InMemoryCoercionResult { ) !InMemoryCoercionResult {
const pt = sema.pt; const pt = sema.pt;
const zcu = pt.zcu; const zcu = pt.zcu;
@ -29809,17 +29810,22 @@ fn coerceInMemoryAllowedFns(
const dest_info = zcu.typeToFunc(dest_ty).?; const dest_info = zcu.typeToFunc(dest_ty).?;
const src_info = zcu.typeToFunc(src_ty).?; const src_info = zcu.typeToFunc(src_ty).?;
const comptime_known_src = src_val != null;
const inline_exempt = src_info.cc == .@"inline" and comptime_known_src;
{ {
if (dest_info.is_var_args != src_info.is_var_args) { if (dest_info.is_var_args != src_info.is_var_args) {
return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args }; return .{ .fn_var_args = dest_info.is_var_args };
} }
if (dest_info.is_generic != src_info.is_generic) { if (dest_info.is_generic != src_info.is_generic) {
return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic }; // Coercion of pointer to generic function to regular function pointer is allowed as long
// as the pointer to generic function is comptime known.
if (!comptime_known_src) return .{ .fn_generic = dest_info.is_generic };
} }
const callconv_ok = callconvCoerceAllowed(target, src_info.cc, dest_info.cc) and const callconv_ok = inline_exempt or (callconvCoerceAllowed(target, src_info.cc, dest_info.cc) and
(!dest_is_mut or callconvCoerceAllowed(target, dest_info.cc, src_info.cc)); (!dest_is_mut or callconvCoerceAllowed(target, dest_info.cc, src_info.cc)));
if (!callconv_ok) { if (!callconv_ok) {
return .{ .fn_cc = .{ return .{ .fn_cc = .{
@ -29874,6 +29880,7 @@ fn coerceInMemoryAllowedFns(
const src_param_ty: Type = .fromInterned(src_info.param_types.get(ip)[param_i]); const src_param_ty: Type = .fromInterned(src_info.param_types.get(ip)[param_i]);
comptime_param: { comptime_param: {
if (inline_exempt) break :comptime_param;
const src_is_comptime = src_info.paramIsComptime(@intCast(param_i)); const src_is_comptime = src_info.paramIsComptime(@intCast(param_i));
const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i)); const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i));
if (src_is_comptime == dest_is_comptime) break :comptime_param; if (src_is_comptime == dest_is_comptime) break :comptime_param;
@ -29956,6 +29963,7 @@ fn coerceInMemoryAllowedPtrs(
target: *const std.Target, target: *const std.Target,
dest_src: LazySrcLoc, dest_src: LazySrcLoc,
src_src: LazySrcLoc, src_src: LazySrcLoc,
src_val: ?Value,
) !InMemoryCoercionResult { ) !InMemoryCoercionResult {
const pt = sema.pt; const pt = sema.pt;
const zcu = pt.zcu; const zcu = pt.zcu;
@ -30004,6 +30012,11 @@ fn coerceInMemoryAllowedPtrs(
} }; } };
} }
const child_src_val: ?Value = if (src_val) |v| switch (try pointerDerefExtra(sema, block, src_src, v)) {
.val => |val| val,
.runtime_load, .needed_well_defined, .out_of_bounds => null,
} else null;
const dest_child: Type = .fromInterned(dest_info.child); const dest_child: Type = .fromInterned(dest_info.child);
const src_child: Type = .fromInterned(src_info.child); const src_child: Type = .fromInterned(src_info.child);
const child = try sema.coerceInMemoryAllowed( const child = try sema.coerceInMemoryAllowed(
@ -30025,7 +30038,7 @@ fn coerceInMemoryAllowedPtrs(
target, target,
dest_src, dest_src,
src_src, src_src,
null, child_src_val,
); );
if (child != .ok and !dest_is_mut) allow: { if (child != .ok and !dest_is_mut) allow: {
// As a special case, we also allow coercing `*[n:s]T` to `*[n]T`, akin to dropping the sentinel from a slice. // As a special case, we also allow coercing `*[n:s]T` to `*[n]T`, akin to dropping the sentinel from a slice.
@ -33750,11 +33763,11 @@ fn resolvePeerTypesInner(
.peer_idx_b = i, .peer_idx_b = i,
} }; } };
// ty -> cur_ty // ty -> cur_ty
if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, false, target, src, src)) { if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, false, target, src, src, null)) {
continue; continue;
} }
// cur_ty -> ty // cur_ty -> ty
if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, false, target, src, src)) { if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, false, target, src, src, null)) {
opt_cur_ty = ty; opt_cur_ty = ty;
continue; continue;
} }