issues: file 0123 — wrong arg counts to fixed-arity fns reach LLVM emission
This commit is contained in:
82
issues/0123-call-arity-unchecked.md
Normal file
82
issues/0123-call-arity-unchecked.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# 0123 — wrong arg counts to fixed-arity fns reach LLVM emission
|
||||||
|
|
||||||
|
## 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: s64, b: s64)`
|
||||||
|
→ 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: s64 = 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), `#foreign`
|
||||||
|
C variadics, `#compiler` / `#builtin` bodies.
|
||||||
|
|
||||||
|
## Reproduction
|
||||||
|
|
||||||
|
```sx
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
main :: () -> s32 {
|
||||||
|
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.
|
||||||
Reference in New Issue
Block a user