Files
Dario Gabriel LipicarandClaude Opus 5 6a24b64448 fix(cdylib): bound the argument count above, not just below
An EXTRA argument was dropped and the call succeeded. The generated dispatch
guarded `args.size() < minArgs` and nothing bounded the other direction, on
every method at every arity -- measured on both providers of two different
contracts, through three consumer surfaces, as conformance case family
`failure/B/arity/too-many`.

Arity is the one part of a contract a caller cannot verify for itself. A
method that gains or loses a parameter upstream answered a stale caller with a
plausible value instead of a refusal, and the caller had no way to tell which
contract it had just talked to.

Two arms, not one, which is why the conformance table registered them as two
defects:

  * The ordinary arm gets `args.size() > maxArgs`, where maxArgs is the
    DECLARED parameter count rather than the required one -- bounding at
    minArgs would reject a caller who legitimately supplies a trailing
    optional. When the two coincide the message keeps the exact-count wording;
    when they differ it says `at most`, because claiming a count the method
    does not require would be wrong in the other direction.
  * A ZERO-parameter method had no gate at all -- not the same guard with
    minArgs = 0, a different code path, since `args.size() < 0` is unsigned and
    was skipped as dead. The upper bound is emitted unconditionally, so it lands
    here too.

The guard sits ABOVE the `md.derived` branch, so the generated identity
dispatch inherits it: `version("junk")` used to answer "1.0.0" with status ok,
which is worse than answering nothing -- a correct-looking reply to a call that
should have been refused.

Two existing tests asserted the old behaviour and are updated, not deleted:
`WrongArgumentCountReportsInvalidArgs` forbade any `args.size() >` in the
output, and `ZeroArgumentMethodEmitsNoArityGate` asserted a zero-arg method
emitted no `invalid_args` at all. That second assertion WAS the defect, written
down as a guarantee. Both now pin the bound, and two cases are added for the
arms they did not reach: a trailing optional widening the upper bound, and the
derived identity dispatch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 22:46:44 -03:00
..