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).
105 lines
4.9 KiB
Markdown
105 lines
4.9 KiB
Markdown
# 0123 — wrong arg counts to fixed-arity fns reach LLVM emission
|
|
|
|
> **RESOLVED** (2026-06-12). Root cause: no dispatch path in `lowerCall`
|
|
> ever compared the supplied arg count against the callee's declared
|
|
> params (`coerceCallArgs` iterates `@min(args.len, params.len)`, so a
|
|
> mismatch sailed through to the LLVM verifier). Fix: a shared
|
|
> `checkCallArity` (src/ir/lower/call.zig) computes min (params without
|
|
> trailing defaults) / max (`params.len`, unbounded past a variadic)
|
|
> from the AST decl and rejects with a source-located diagnostic at the
|
|
> five plain dispatch sites — bare selected-author + lazy, namespace
|
|
> alias-gate + qualified, struct-method, ufcs. Pack / comptime / generic
|
|
> / `#compiler` / `#builtin` callees are exempt (own dispatch). The
|
|
> method/ufcs sites also gained the `appendDefaultArgs` fill the
|
|
> generic-instance leg already had — trailing defaults on dot-calls
|
|
> previously emitted under-arity calls (same verifier failure). Flushed
|
|
> out en route: `lowerStmt`'s `.fn_decl => |fd| ... (&fd)` registered a
|
|
> STACK address in `fn_ast_map`, so every local fn's map entry aliased
|
|
> the most recently lowered one — pointer capture (`|*fd|`) fixes it.
|
|
> Regressions: `examples/1167-diagnostics-call-arity-mismatch.sx`
|
|
> (too many / too few, bare + stdlib + method + ufcs) and
|
|
> `examples/0054-basic-dot-call-default-args.sx` (dot-call defaults,
|
|
> variadic, `#caller_location`). Gates: zig build test 426/426, suite
|
|
> 590/590 (fix in isolation), distribution repo 14/14.
|
|
|
|
## Symptom
|
|
|
|
Calling a fixed-arity function with the wrong number of arguments is
|
|
not rejected by the frontend — the mismatched argument list flows all
|
|
the way to LLVM emission, which fails verification instead of a
|
|
source-located diagnostic.
|
|
|
|
- **Observed**: `LLVM verification failed: Incorrect number of
|
|
arguments passed to called function!` plus the raw IR call line —
|
|
no file/line/snippet, no callee name in user terms.
|
|
- **Expected**: a compile error at the call site naming the callee
|
|
and its declared arity (matching the style of existing
|
|
diagnostics, e.g. the "unresolved ..." errors with source
|
|
snippets).
|
|
|
|
Both directions are broken, on every plain dispatch path probed:
|
|
|
|
- too MANY args, bare call: `concat("a", "b", "c")` (std's `concat`
|
|
takes 2 strings) → LLVM verifier failure.
|
|
- too FEW args, bare call: `add2(1)` with `add2 :: (a: i64, b: i64)`
|
|
→ same.
|
|
- methods / ufcs dot-calls: same shape, receiver included. Worse:
|
|
a trailing-default param on a plain struct method or a ufcs fn is
|
|
never filled on the dot-call path (`p.scaled()` with
|
|
`scaled :: (self: Point, k: i64 = 2)` emits a 1-arg call to a
|
|
2-param fn — bare calls fill defaults via `expandCallDefaults`,
|
|
the method/ufcs sites never run `appendDefaultArgs`).
|
|
|
|
Legitimate flexible shapes must keep working: slice variadics
|
|
(`..xs: []T` — no upper bound), comptime/protocol packs (`..$args` /
|
|
`..xs: P` — own dispatch), default-valued params (incl.
|
|
`loc: Source_Location = #caller_location`), generic `$T` fns
|
|
(explicit vs inferred type args make the count flexible), `extern`
|
|
C variadics, `#compiler` / `#builtin` bodies.
|
|
|
|
## Reproduction
|
|
|
|
```sx
|
|
#import "modules/std.sx";
|
|
|
|
main :: () -> i32 {
|
|
s := concat("a", "b", "c"); // concat takes (a: string, b: string)
|
|
out(s);
|
|
return 0;
|
|
}
|
|
```
|
|
|
|
Observed at master b3b78e2: compiles past resolution, dies at LLVM
|
|
verification with the verifier message above.
|
|
|
|
## Investigation prompt
|
|
|
|
Call argument binding never compares the supplied arg count against
|
|
the callee's declared parameter list. Suspected area: the plain
|
|
direct-dispatch sites in `src/ir/lower/call.zig` (`lowerCall`) — the
|
|
bare-identifier selected-author and lazy-lower legs, the
|
|
namespace-qualified legs, the qualified struct-method leg, and the
|
|
ufcs leg. All of them run `packVariadicCallArgs` /
|
|
`coerceCallArgs` and emit `builder.call` without an arity check;
|
|
`coerceCallArgs` iterates `@min(args.len, params.len)` so a mismatch
|
|
sails through to the emitter.
|
|
|
|
The fix likely needs a shared `checkCallArity(fd, name, supplied,
|
|
has_receiver, span)` helper consulted at each plain dispatch site,
|
|
computing min (params without trailing defaults) / max
|
|
(`fd.params.len`, unbounded when a variadic param exists) from the
|
|
AST decl and emitting via `self.diagnostics.addFmt(.err, span, ...)`
|
|
on violation. Pack (`isPackFn`), comptime (`hasComptimeParams`),
|
|
generic (`type_params.len > 0`), `#compiler`, and `#builtin` callees
|
|
bind args through their own dispatch and must be exempt. The
|
|
method/ufcs sites also need `appendDefaultArgs` (the generic
|
|
instance-method leg already runs it) so trailing defaults fill
|
|
before the count is meaningful.
|
|
|
|
Verification: the repro errors with a source-located diagnostic
|
|
naming `concat` and its 2-arg signature; too-few errors likewise;
|
|
variadic / pack / default / ufcs / generic calls keep compiling
|
|
(`print`, `format`, `List.append`, `#caller_location` defaults).
|
|
`zig build && zig build test`, `bash tests/run_examples.sh` all
|
|
green; pin the repro as a diagnostics example per CLAUDE.md.
|