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).
113 lines
5.2 KiB
Markdown
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`).
|