lang: fn aliases dispatch like their target (fix 0121) — scan-time registration through the shared alias-chain walk
Renamed fn aliases failed for EVERY kind (the filed pack-only scope was a same-name confound: same-name re-exports already resolved through the name-keyed fn_ast_map). scanDecls now follows ident-/ns.X-RHS const alias chains (aliasedFnDecl; 0120's hop walk extracted as followAliasChain) and registers the alias name in fn_ast_map (absent-only), so every dispatch path — early pack/comptime/generic, plain lazy-lower, plan-side typing — sees the target decl unchanged. my_print :: s.print; / my_format :: s.format; now work (the std.sx re-export shape). Regression: examples/0546 (+rich). Gates: zig build test 0, suite 588/588.
This commit is contained in:
112
issues/0121-pack-fn-alias-unresolved.md
Normal file
112
issues/0121-pack-fn-alias-unresolved.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# 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) -> s64 {
|
||||
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`), `#foreign` decl aliases
|
||||
(`memcpy`).
|
||||
Reference in New Issue
Block a user