ir: type value-pack mono params from lowered args, not pre-lowering inference

lowerPackFnCall computed pack arg types via inferExprType *before* lowering
the args, then lowered them anyway. For a value-pack (..xs: P) the lowered
value has an authoritative concrete type, so take the pack type from
getRefType of the lowered Ref instead of a speculative inferExprType guess --
this removes the dependency that made a monomorphised pack param able to end
up wrong/.unresolved from incomplete static inference. Comptime ..$args packs
keep inferExprType (their args may be type-position). Also drops the dead
runtime_arg_types list (collected, never read). 236/236 green.
This commit is contained in:
agra
2026-05-29 23:19:02 +03:00
parent 8681b72b47
commit a9c116ebb1

View File

@@ -8933,33 +8933,56 @@ pub const Lowering = struct {
// value into the mangle (NOT a runtime IR param).
// - pack param (always last) → consume the remaining call args
// as the pack expansion.
var runtime_arg_types = std.ArrayList(TypeId).empty;
defer runtime_arg_types.deinit(self.alloc);
var pack_arg_types = std.ArrayList(TypeId).empty;
defer pack_arg_types.deinit(self.alloc);
var pack_start: usize = call_node.args.len;
// Constraint protocol of the pack param (`..xs: P`), if any. The
// comptime type-pack `..$args` has no constraint to check.
var pack_protocol: ?[]const u8 = null;
var fi: usize = 0;
for (fd.params) |p| {
if (isPackParam(p)) {
pack_start = fi;
if (p.is_pack and p.type_expr.data == .type_expr) {
pack_protocol = p.type_expr.data.type_expr.name;
var pack_is_comptime = false;
{
var fi: usize = 0;
for (fd.params) |p| {
if (isPackParam(p)) {
pack_start = fi;
pack_is_comptime = p.is_comptime;
if (p.is_pack and p.type_expr.data == .type_expr) {
pack_protocol = p.type_expr.data.type_expr.name;
}
break;
}
break;
if (fi >= call_node.args.len) break;
fi += 1;
}
if (fi >= call_node.args.len) break;
if (!p.is_comptime) {
runtime_arg_types.append(self.alloc, self.inferExprType(call_node.args[fi])) catch return self.builder.constInt(0, .void);
}
// Comptime non-pack: consumed but not added to runtime types.
fi += 1;
}
if (pack_start <= call_node.args.len) {
for (call_node.args[pack_start..]) |a| {
// Lower the runtime prefix args and the pack args up front, taking each
// pack type from the lowered value (`getRefType`) rather than a
// pre-lowering `inferExprType` guess: a lowered value always has a
// concrete type, so a monomorphised pack param can never end up
// `.unresolved` from incomplete static inference. (A comptime `..$args`
// pack still uses `inferExprType` — its args may be type-position.)
var args = std.ArrayList(Ref).empty;
defer args.deinit(self.alloc);
{
var ri: usize = 0;
for (fd.params) |p| {
if (isPackParam(p)) break;
if (ri >= call_node.args.len) break;
if (!p.is_comptime) {
args.append(self.alloc, self.lowerExpr(call_node.args[ri])) catch return self.builder.constInt(0, .void);
}
ri += 1;
}
}
for (call_node.args[pack_start..]) |a| {
if (pack_is_comptime) {
args.append(self.alloc, self.lowerExpr(a)) catch return self.builder.constInt(0, .void);
pack_arg_types.append(self.alloc, self.inferExprType(a)) catch return self.builder.constInt(0, .void);
} else {
const r = self.lowerExpr(a);
args.append(self.alloc, r) catch return self.builder.constInt(0, .void);
pack_arg_types.append(self.alloc, self.builder.getRefType(r)) catch return self.builder.constInt(0, .void);
}
}
@@ -9007,23 +9030,6 @@ pub const Lowering = struct {
self.monomorphizePackFn(fd, mangled, pack_arg_types.items, call_node);
}
// Lower ONLY runtime args (skip comptime non-pack args; their
// values are folded into the mangle, not passed at runtime).
var args = std.ArrayList(Ref).empty;
defer args.deinit(self.alloc);
var ri: usize = 0;
for (fd.params) |p| {
if (isPackParam(p)) break;
if (ri >= call_node.args.len) break;
if (!p.is_comptime) {
args.append(self.alloc, self.lowerExpr(call_node.args[ri])) catch return self.builder.constInt(0, .void);
}
ri += 1;
}
for (call_node.args[pack_start..]) |a| {
args.append(self.alloc, self.lowerExpr(a)) catch return self.builder.constInt(0, .void);
}
const fid = self.resolveFuncByName(mangled) orelse return self.builder.constInt(0, .void);
const func = &self.module.functions.items[@intFromEnum(fid)];
const ret_ty = func.ret;