Files
sx/issues/0121-pack-fn-alias-unresolved.md
agra b9cfe2554f refactor(ffi-linkage): Phase 9.3/9.4 — purge 'foreign' from issues/*.md; GATE PASS
Rewrote 20 issue writeups to the extern/runtime-class vocabulary (#foreign→extern,
foreign_class_map→runtime_class_map, parseForeignClassDecl→parseRuntimeClassDecl,
findForeignMethodInChain→findRuntimeMethodInChain, dedupeForeignSymbol→
dedupeExternSymbol, is_foreign_c_api→is_extern_c_api, stale filename refs to the
renamed examples, foreign-class→runtime-class, bare foreign→extern). Renamed
issues/0043-…-foreign-class-…→…-runtime-class-….

PHASE 9 COMPLETE — 9.4 GATE PASSES: zero 'foreign' across src/library/examples/
issues/docs/editors/specs/readme/CLAUDE, excluding only the SQLite API constant
SQLITE_CONSTRAINT_FOREIGNKEY + vendored sqlite3.c/.h (upstream third-party).
Suite green (644 corpus / 443 unit, 0 failed).
2026-06-15 11:18:35 +03:00

113 lines
5.2 KiB
Markdown

# 0121 — aliasing a comptime-pack fn (`..$args`): "unresolved '<alias>'"
> **RESOLVED** (2026-06-11, same session — Agra-directed). The symptom
> was broader than filed: RENAMED fn aliases failed for EVERY fn kind
> (plain `helper2 :: r.helper;` too) — the "plain fns verified working"
> claim below was a same-name confound (same-name re-exports resolve
> through the name-keyed global `fn_ast_map`, no alias mechanism
> involved; `my_pack :: r.my_pack;` already worked for packs too).
> Fix: fn aliases register at SCAN time — `scanDecls`' const-decl arm
> follows ident-/`ns.X`-RHS alias chains via `aliasedFnDecl`
> (nominal.zig; shares 0120's hop walk, now extracted as
> `followAliasChain`) and, when the chain terminates at a fn decl,
> registers the ALIAS name in `fn_ast_map` (absent-only — a real
> same-name fn keeps its slot). Every dispatch path reads that map
> (early pack/comptime/generic, plain lazy-lower, plan-side typing),
> so the alias dispatches exactly like the target with no per-path
> changes. Verified matrix: same-file pack alias (the repro), renamed
> plain / generic / pack through a facade, and `my_print :: s.print;`
> / `my_format :: s.format;` over std's real pack fns. Regression:
> `examples/0546-packs-fn-alias.sx` (+ `-rich.sx` companion). Gates:
> zig build test 0, suite 588/588.
## Symptom
A const alias of a function whose signature carries a comptime pack
(`..$args`) is not callable — every call through the alias fails with
`unresolved '<alias>'`. All three alias shapes fail identically:
- same-file bare: `sum_alias :: pack_sum;``unresolved 'sum_alias'`
- bare RHS over a flat import: `my_print :: print;` (std's `print`) →
`unresolved 'my_print'`
- namespace RHS: `my_print :: s.print;` with `s :: #import
"modules/std.sx";` → `unresolved 'my_print'`
Contrast (all verified working): plain concrete fns (`helper ::
r.helper;`), runtime-generic fns (`first_of :: r.first_of;` with
`(xs: []$T) -> T`), and — since 0120 — generic struct heads
(`List :: list.List;`). Only the comptime-pack shape misses.
Expected: the alias dispatches exactly like the target —
`my_print("x {}\n", 1)` behaves as `print("x {}\n", 1)`. (If fn
aliasing of pack fns is NOT meant to be promised, the hypothesis is
wrong and the decl or the call should get a clean tailored
diagnostic instead — but the std.sx-as-pure-re-exports restructure
wants `print :: fmt.print;` to work, so support is the desirable
outcome. Confirm with Agra only if support turns out prohibitively
deep.)
## Reproduction
```sx
#import "modules/std.sx";
pack_sum :: (..$args) -> i64 {
args[0] + args[1]
}
sum_alias :: pack_sum;
main :: () {
print("{}\n", sum_alias(3, 4)); // error: unresolved 'sum_alias'
}
```
Direct `pack_sum(3, 4)` works; only the aliased spelling fails.
## Investigation prompt
Comptime-pack calls (`..$args` — NOT slice-variadics `..xs: []T`)
dispatch EARLY in call lowering, keyed on the callee NAME against the
pack-fn template registry (the `fn_ast_map` entry whose FnDecl has a
comptime pack param; the early-dispatch gate lives in the call path —
`src/ir/lower/call.zig` / `src/ir/packs.zig`, grep for the pack-param
detection on the callee). An alias name has no `fn_ast_map` entry, so
the early pack dispatch misses; no later stage handles pack fns
(they cannot lower as ordinary declared functions — each call site
expands with its own bound pack), so the call falls through to the
generic `unresolved '<name>'`.
The fix likely: where the early pack dispatch resolves the callee
name, on a miss follow const-ALIAS decls to their target FnDecl and
dispatch with the TARGET's fd under the alias's call node. Issue
0120's fix added exactly this follow for generic STRUCT heads —
`aliasedStructTemplate` in `src/ir/lower/nominal.zig`
(`singleVisibleAuthor` + hop-by-hop `followToTemplate`, each hop
resolved from the ALIAS AUTHOR's source, `namespaceAliasVerdictFrom`
for `ns.X` RHS, depth-capped). Mirror that shape for fn targets — a
`followToFnDecl` sibling reusing `singleVisibleAuthor` (consider
extracting the shared walk) — and route the early pack dispatch
through it. Mind: own-wins / single-flat collision semantics must
match 0120's (≥2 flat alias authors → loud, no silent pick), and the
ufcs-alias map (`name :: ufcs target;`) is a DIFFERENT mechanism
(`ufcs_alias_map`) — don't conflate.
Verification:
1. The repro prints `7`, exit 0.
2. Matrix: same-file bare alias, bare RHS over a flat import
(`my_print :: print;`), namespace RHS (`my_print :: s.print;` /
`my_format :: s.format;` — formats AND returns a value), and a
consumer one flat hop from the aliasing facade.
3. Plain-fn and generic-fn aliases unchanged (examples 0211 family).
4. `bash tests/run_examples.sh` — 587/587 baseline must hold; pin the
repro as a regression example per CLAUDE.md.
Context: BLOCKS the std.sx-as-pure-re-exports restructure — `print` /
`format` are the prelude's most-used names and are exactly this shape
(`($fmt: string, ..$args)`). Generic struct heads (`List`) were
unblocked by 0120; this is the remaining known gap. Still unprobed
for the restructure (next session, after this fix): protocol aliases
(`Allocator`, parameterized `Into`), `#builtin` decl aliases
(`size_of`, `out`, `string :: []u8`), `extern` decl aliases
(`memcpy`).