Skip to content

feat(lang): one call rule — bare literal list after 'of' is always an argument list (#405)#458

Merged
InauguralPhysicist merged 1 commit into
mainfrom
lang-405-one-call-rule
Jul 6, 2026
Merged

feat(lang): one call rule — bare literal list after 'of' is always an argument list (#405)#458
InauguralPhysicist merged 1 commit into
mainfrom
lang-405-one-call-rule

Conversation

@InauguralPhysicist

Copy link
Copy Markdown
Collaborator

Implements the #405 language change: a bare literal list after of is always an argument list, at every element count — f of [] is zero arguments, f of [x] is one (the element, not the list), f of [a, b] is two. Parentheses remain the one-argument form: f of ([x]) (#355) passes a literal list whole. This kills the arity-dependent silent-wrong where fib of [n - 1] broke the moment a defaulted parameter was added.

What's in the change

  • src/compiler.c: the whole semantic change is one dropped count > 1 guard — the zero-arg and multi-arg branches merge into the universal rule.
  • W017 (new lint, shared with the LSP): flags a bare 1-element literal arg list and names both unambiguous rewrites (f of x / f of ([x])). It doubles as the migration audit — --lint over a repo surfaces every behavior-changed call site.
  • lib/eigen.eigs: the self-hosted interpreter's call binding rewritten to mirror the C VM under the new rule (element-wise binding, 1-param pack-back for 2+ args, Unflagged semantic change: g of [] on a multi-param function now binds a=null (was a=[]) #154 empty-list bind), so meta parity holds by construction; test_meta_parity pins the new behavior and the paren form.
  • tests/test_call_semantics.eigs rewritten as the spec-of-record, including the resurrected fib of [n - 1]-with-default regression; 13 list-intent call sites across 10 test files migrated to the parenthesized form; repo-wide W017 audit clean.
  • Docs: SPEC call conventions + COMPARISON rewritten (executable examples), DIAGNOSTICS W017 row, CLAUDE.md rule, ROADMAP tick, CHANGELOG BREAKING entry.

Gates run locally

  • Release suite: 2568/2568
  • ASan+UBSan, detect_leaks=1: 2568/2568, leak tally 0
  • make http + tests/test_http_server.sh: 32/32
  • make freestanding-check: both stages OK

Ecosystem coordination

Closes #153

🤖 Generated with Claude Code

…an argument list (#405)

BREAKING. The compiler previously spread a bare literal arg list only at
count > 1, so 'f of [x]' bound the whole 1-element list to the first
parameter — an arity-dependent silent-wrong that broke 'fib of [n - 1]'
the moment a defaulted param was added (#153). Now brackets after 'of'
are an argument list at EVERY count ('f of []' zero args, 'f of [x]'
one arg, 'f of [a, b]' two) and parentheses are one argument —
'f of ([x])' (#355) still passes a literal list whole. One rule,
teachable in one sentence.

- compiler.c: merge the zero-arg / count>1 branches into the universal
  arg-list rule (the whole change is the dropped count>1 guard).
- lint: new W017 — bare 1-element literal arg list; names both
  unambiguous rewrites ('f of x' / 'f of ([x])'). Doubles as the
  migration audit: --lint over a repo surfaces every behavior-changed
  site. Flows to the LSP via the shared pass.
- lib/eigen.eigs: the self-hosted interpreter's call binding now mirrors
  the C VM under the new rule (element-wise binding, 1-param pack-back
  for 2+ args, the #154 1-param empty-list bind), so meta parity holds
  by construction, not by accident. test_meta_parity pins both the new
  1-element behavior and the #355 paren form.
- tests: call-semantics rewritten as the spec-of-record (incl. the
  resurrected 'fib of [n - 1]' + defaulted-param regression); 13
  list-intent sites across 10 test files parenthesized; W017 audit over
  lib/ + tests/ + examples/ is clean.
- docs: SPEC call-conventions + COMPARISON rewritten (executable
  examples updated), DIAGNOSTICS W017 row, CLAUDE.md hard-won rule
  replaced, ROADMAP ticked.

Gates: release suite 2568/2568; ASan+UBSan with detect_leaks=1
2568/2568, leak tally 0; make http + test_http_server.sh 32/32;
freestanding 2-stage symbol gate OK.

The ouroboros frontend.eigs parity leg deliberately rides the next
release + pin bump (its differential runs against the pinned VM), with
the chr raise and #411 tape headers.

Closes #153

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread src/lint.c
"list", name, name, name);
}

static void w017_scan(ASTNode *n, LintContext *ctx) {
@InauguralPhysicist InauguralPhysicist merged commit 9810a99 into main Jul 6, 2026
17 checks passed
@InauguralPhysicist InauguralPhysicist deleted the lang-405-one-call-rule branch July 6, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Argument-spreading contract vs implementation: single-element literal lists don't spread, and default params amplify the surprise

2 participants