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:
23
examples/0726-modules-flat-same-name-variadic.sx
Normal file
23
examples/0726-modules-flat-same-name-variadic.sx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// fix-0102c F1 (issue 0102): two flat FILE imports each author same-name free
|
||||||
|
// functions whose VARIADIC SHAPE differs. `combine` is fixed-arity in a.sx
|
||||||
|
// (the first-wins winner) but variadic in b.sx (the shadow); `pick` is the
|
||||||
|
// reverse. Each module's bare call must pack arguments against ITS OWN
|
||||||
|
// author's signature — not the first-wins author's. Pre-fix the call path
|
||||||
|
// re-fetched the first-wins AST by name to drive variadic packing, so b.sx's
|
||||||
|
// variadic `combine` was packed as if fixed (and its fixed `pick` as if
|
||||||
|
// variadic) → wrong lowering. Regression for the F1 review finding.
|
||||||
|
#import "modules/std.sx";
|
||||||
|
#import "0726-modules-flat-same-name-variadic/a.sx";
|
||||||
|
#import "0726-modules-flat-same-name-variadic/b.sx";
|
||||||
|
|
||||||
|
report :: (label: string, ok: bool) {
|
||||||
|
if ok { print("{}: ok\n", label); } else { print("{}: FAIL\n", label); }
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: () -> s32 {
|
||||||
|
report("from_a combine fixed", from_a_combine() == 30);
|
||||||
|
report("from_b combine variadic", from_b_combine() == 10);
|
||||||
|
report("from_a pick variadic", from_a_pick() == 6);
|
||||||
|
report("from_b pick fixed", from_b_pick() == 5);
|
||||||
|
0
|
||||||
|
}
|
||||||
11
examples/0726-modules-flat-same-name-variadic/a.sx
Normal file
11
examples/0726-modules-flat-same-name-variadic/a.sx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// a.sx is the first-wins winner for both names. `combine` is FIXED arity;
|
||||||
|
// `pick` is VARIADIC. `from_a_*` call them bare — a authors the winner, so
|
||||||
|
// they resolve through the existing path and pack against a's own shapes.
|
||||||
|
combine :: (x: s64, y: s64) -> s64 { return x + y; }
|
||||||
|
pick :: (..xs: []s64) -> s64 {
|
||||||
|
result := 0;
|
||||||
|
for xs: (it) { result = result + it; }
|
||||||
|
result
|
||||||
|
}
|
||||||
|
from_a_combine :: () -> s64 { return combine(10, 20); }
|
||||||
|
from_a_pick :: () -> s64 { return pick(1, 2, 3); }
|
||||||
12
examples/0726-modules-flat-same-name-variadic/b.sx
Normal file
12
examples/0726-modules-flat-same-name-variadic/b.sx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// b.sx is the SHADOW author for both names, with the OPPOSITE shapes:
|
||||||
|
// `combine` is VARIADIC, `pick` is FIXED. Each `from_b_*` bare call must pack
|
||||||
|
// against b's OWN author's signature (the F1 fix) — combine sums its variadic
|
||||||
|
// pack, pick subtracts its two fixed args.
|
||||||
|
combine :: (..xs: []s64) -> s64 {
|
||||||
|
result := 0;
|
||||||
|
for xs: (it) { result = result + it; }
|
||||||
|
result
|
||||||
|
}
|
||||||
|
pick :: (a: s64, b: s64) -> s64 { return b - a; }
|
||||||
|
from_b_combine :: () -> s64 { return combine(1, 2, 3, 4); }
|
||||||
|
from_b_pick :: () -> s64 { return pick(2, 7); }
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from_a combine fixed: ok
|
||||||
|
from_b combine variadic: ok
|
||||||
|
from_a pick variadic: ok
|
||||||
|
from_b pick fixed: ok
|
||||||
@@ -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", main_path) == .ambiguous);
|
||||||
try std.testing.expect(lowering.resolveBareCallee("greet", a_path) == .none);
|
try std.testing.expect(lowering.resolveBareCallee("greet", a_path) == .none);
|
||||||
switch (lowering.resolveBareCallee("greet", b_path)) {
|
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,
|
else => return error.TestUnexpectedResult,
|
||||||
}
|
}
|
||||||
// A name no module authors (and no flat import provides) never routes.
|
// A name no module authors (and no flat import provides) never routes.
|
||||||
|
|||||||
@@ -1521,9 +1521,12 @@ pub const Lowering = struct {
|
|||||||
|
|
||||||
/// Result of bare-call disambiguation (fix-0102c).
|
/// Result of bare-call disambiguation (fix-0102c).
|
||||||
pub const BareCallee = union(enum) {
|
pub const BareCallee = union(enum) {
|
||||||
/// Bind the call to this specific author's FuncId — the identity-
|
/// Bind the call to this specific author — its identity-addressable
|
||||||
/// addressable body lowered by `bareAuthorFuncId` (fix-0102b).
|
/// FuncId (fix-0102b's `bareAuthorFuncId`) AND its `*FnDecl`. The decl
|
||||||
func: FuncId,
|
/// 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
|
/// ≥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.
|
/// the caller's own — the bare call can't pick one; require a qualifier.
|
||||||
ambiguous,
|
ambiguous,
|
||||||
@@ -1532,6 +1535,11 @@ pub const Lowering = struct {
|
|||||||
none,
|
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
|
/// THE bare-name call resolver (fix-0102c). One canonical traversal over
|
||||||
/// fix-0102a's `module_fns` + `flat_import_graph` that routes a bare
|
/// fix-0102a's `module_fns` + `flat_import_graph` that routes a bare
|
||||||
/// identifier call `name` from `caller_file` to the right same-name author
|
/// 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 (own_fns.get(name)) |own| {
|
||||||
if (winner != null and winner.? == own) return .none;
|
if (winner != null and winner.? == own) return .none;
|
||||||
if (!isPlainFreeFn(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.*;
|
const the_path = entry.value_ptr.*;
|
||||||
if (winner != null and winner.? == the_one) return .none;
|
if (winner != null and winner.? == the_one) return .none;
|
||||||
if (!isPlainFreeFn(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.
|
/// 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});
|
d.addFmt(.err, c.callee.span, "'{s}' is ambiguous; declared by multiple imported modules — qualify the call", .{func_name});
|
||||||
return Ref.none;
|
return Ref.none;
|
||||||
},
|
},
|
||||||
.func => |fid| {
|
.func => |resolved| {
|
||||||
|
const fid = resolved.fid;
|
||||||
const func = &self.module.functions.items[@intFromEnum(fid)];
|
const func = &self.module.functions.items[@intFromEnum(fid)];
|
||||||
const ret_ty = func.ret;
|
const ret_ty = func.ret;
|
||||||
const params = func.params;
|
const params = func.params;
|
||||||
if (self.program_index.fn_ast_map.get(func_name)) |fd| {
|
// The RESOLVED author's decl drives variadic
|
||||||
self.packVariadicCallArgs(fd, c, &args);
|
// 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);
|
const final_args = self.prependCtxIfNeeded(func, args.items);
|
||||||
self.coerceCallArgs(final_args, params);
|
self.coerceCallArgs(final_args, params);
|
||||||
if (func.is_variadic) self.promoteCVariadicArgs(final_args, params.len);
|
if (func.is_variadic) self.promoteCVariadicArgs(final_args, params.len);
|
||||||
|
|||||||
Reference in New Issue
Block a user