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).
5.2 KiB
0121 — aliasing a comptime-pack fn (..$args): "unresolved ''"
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 globalfn_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 viaaliasedFnDecl(nominal.zig; shares 0120's hop walk, now extracted asfollowAliasChain) and, when the chain terminates at a fn decl, registers the ALIAS name infn_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, andmy_print :: s.print;/my_format :: s.format;over std's real pack fns. Regression:examples/0546-packs-fn-alias.sx(+-rich.sxcompanion). 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'sprint) →unresolved 'my_print' - namespace RHS:
my_print :: s.print;withs :: #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
#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:
- The repro prints
7, exit 0. - 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. - Plain-fn and generic-fn aliases unchanged (examples 0211 family).
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).