fix(lower): resolved author drives variadic packing in bare call [0102c F1]

Attempt-2 fix for the F1 review finding. After `resolveBareCallee` picks a
shadowed same-name author's FuncId at a normal call site, the call path still
re-fetched the FIRST-WINS function AST by name to drive variadic argument
packing. When the resolved (shadow) author's variadic shape differs from the
first-wins author's, arguments were packed against the WRONG signature — a
fixed-arity shadow packed as if variadic, or a variadic shadow not packed at
all — producing IR with the wrong argument count (LLVM verification failure).

The `.func` arm now carries the resolved `*FnDecl` alongside its FuncId
(`BareCallee.func: ResolvedAuthor`), so `packVariadicCallArgs` reads THE
resolved author's signature. The rest of the arm already used the resolved
FuncId's IR function (ret/params/ctx/coercion), so the callee now has one
source of truth in the whole call lowering — no re-fetch by name after
resolution. Default-arg / closure / UFCS / comptime *sites* remain first-wins
(fix-0102d); `expandCallDefaults` runs before resolution and is a default site.

Regression: examples/0726-modules-flat-same-name-variadic — two flat file
imports each author `combine` and `pick` with OPPOSITE variadic shapes (a.sx
fixed `combine` / variadic `pick`; b.sx variadic `combine` / fixed `pick`).
Each module's bare call must pack against ITS OWN author. Fails on the pre-fix
re-lookup (LLVM "Incorrect number of arguments passed to called function" for
both `combine.1` and `pick.2`); passes after.

Gate: zig build, zig build test (400/400), bash tests/run_examples.sh
(463 passed) all green. Matrix 0722-0725/0727 unchanged; single-author / local
resolution byte-for-byte unchanged (the `.func` arm never runs for them).
This commit is contained in:
agra
2026-06-06 14:28:00 +03:00
parent ea35a05b26
commit 8f9c00dcdb
8 changed files with 72 additions and 10 deletions

View File

@@ -1438,7 +1438,7 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
try std.testing.expect(lowering.resolveBareCallee("greet", main_path) == .ambiguous);
try std.testing.expect(lowering.resolveBareCallee("greet", a_path) == .none);
switch (lowering.resolveBareCallee("greet", b_path)) {
.func => |fid| try std.testing.expectEqual(shadow_fid.?, fid),
.func => |resolved| try std.testing.expectEqual(shadow_fid.?, resolved.fid),
else => return error.TestUnexpectedResult,
}
// A name no module authors (and no flat import provides) never routes.

View File

@@ -1521,9 +1521,12 @@ pub const Lowering = struct {
/// Result of bare-call disambiguation (fix-0102c).
pub const BareCallee = union(enum) {
/// Bind the call to this specific author's FuncId — the identity-
/// addressable body lowered by `bareAuthorFuncId` (fix-0102b).
func: FuncId,
/// Bind the call to this specific author — its identity-addressable
/// FuncId (fix-0102b's `bareAuthorFuncId`) AND its `*FnDecl`. The decl
/// travels with the FuncId so every callee-signature decision in the
/// call path (variadic packing, …) reads the RESOLVED author, never a
/// first-wins re-lookup by name (fix-0102c F1).
func: ResolvedAuthor,
/// ≥2 distinct flat authors are reachable from the caller and none is
/// the caller's own — the bare call can't pick one; require a qualifier.
ambiguous,
@@ -1532,6 +1535,11 @@ pub const Lowering = struct {
none,
};
/// A resolved bare-call author: its FuncId and the `*FnDecl` that defined
/// it, kept together so the call path has ONE source of truth for the
/// callee (no re-fetch by name after resolution).
pub const ResolvedAuthor = struct { fid: FuncId, decl: *const ast.FnDecl };
/// THE bare-name call resolver (fix-0102c). One canonical traversal over
/// fix-0102a's `module_fns` + `flat_import_graph` that routes a bare
/// identifier call `name` from `caller_file` to the right same-name author
@@ -1561,7 +1569,7 @@ pub const Lowering = struct {
if (own_fns.get(name)) |own| {
if (winner != null and winner.? == own) return .none;
if (!isPlainFreeFn(own)) return .none;
return .{ .func = self.bareAuthorFuncId(own, name, caller_file) };
return .{ .func = .{ .fid = self.bareAuthorFuncId(own, name, caller_file), .decl = own } };
}
}
@@ -1584,7 +1592,7 @@ pub const Lowering = struct {
const the_path = entry.value_ptr.*;
if (winner != null and winner.? == the_one) return .none;
if (!isPlainFreeFn(the_one)) return .none;
return .{ .func = self.bareAuthorFuncId(the_one, name, the_path) };
return .{ .func = .{ .fid = self.bareAuthorFuncId(the_one, name, the_path), .decl = the_one } };
}
/// The FuncId for a resolved bare-call author, ensuring its body is lowered.
@@ -7560,13 +7568,15 @@ pub const Lowering = struct {
d.addFmt(.err, c.callee.span, "'{s}' is ambiguous; declared by multiple imported modules — qualify the call", .{func_name});
return Ref.none;
},
.func => |fid| {
.func => |resolved| {
const fid = resolved.fid;
const func = &self.module.functions.items[@intFromEnum(fid)];
const ret_ty = func.ret;
const params = func.params;
if (self.program_index.fn_ast_map.get(func_name)) |fd| {
self.packVariadicCallArgs(fd, c, &args);
}
// The RESOLVED author's decl drives variadic
// packing — not a first-wins re-lookup by name,
// whose variadic shape may differ (fix-0102c F1).
self.packVariadicCallArgs(resolved.decl, c, &args);
const final_args = self.prependCtxIfNeeded(func, args.items);
self.coerceCallArgs(final_args, params);
if (func.is_variadic) self.promoteCVariadicArgs(final_args, params.len);