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