feat(syntax): a closure binds a var parameter #161

Merged
argus merged 1 commit from loop/156-closure-var-parameter into main 2026-10-08 00:09:38 +00:00
Collaborator

Closes #156

Task

Issue #156: a closure binds a var parameter (CONCURRENCY gap 5 / ECS gap 4, slice F), picked up by the torbscript project loop on 2026-10-07.

What

  • Parser: var in front of a closure parameter name, { var point => ... } and { var point, scale => ... }. The parenthesized form { (var point, scale) => ... } named in the issue is a parse error with a message that points to the unparenthesized form.
  • Checker: the closure's parameter binding takes var (by-reference convention of (var value: T) => Void); the write goes to the caller's value. The existing escape rule (BACKEND 5.8) refuses storing, returning or spawn-capturing a var parameter. A var mismatch is an error that names the var side, without a second "is a const" error on top.
  • Seed-safe: compiler/src and std/ do not use the new form.
  • Docs: closures page, functions index summary, cheat sheet, design docs (CONCURRENCY, ECS) and the generated skills.
  • Tests: parser, checker, places, lowering (IR snapshots), formatter, language server hover, conformance program closure-var-parameter (native and VM; a forEach-style function taking body: (var value: Point) => Void, called with a closure that captures a local Float).

Decisions for review

  1. Parenthesized form. { var point, scale => ... } is implemented. { (var point, scale) => ... } from the issue is a clear parse error ("A closure writes its parameters without parentheses"): parentheses in a closure's parameter position read as a tuple pattern, which has no var in it, and a second spelling of the list would be new syntax. Say so if the issue's spelling should be accepted instead.
  2. Reverse mismatch. A closure parameter with var against a function type without it is an error, and so is the reverse for function values (a named fn). A closure without var still adopts var from the expected function type (closures docs rule 3), so existing closures such as points.each { p => ... } keep working.

Test

Ran in a gcc:14 container after rebootstrapping (seed -> torb -> torb, fixpoint holds):

  • sh tools/gates.sh a: every gate green, including docs check, docs index --check, docs skill --check, docs site --check, test compiler/tests (native), std/example packages in both back ends, tests/language in both back ends, and format --check (after formatting two files).
  • sh tools/check-commits.sh origin/main HEAD: green.
  • Tier B (gates.sh b) not run locally: no IR, back-end or runtime change; the conformance program is covered by CI.

After merge

Nothing to release or deploy. A later use of the form inside std/ waits for a seed refresh (owner's call).

Closes #156 ## Task Issue #156: a closure binds a `var` parameter (CONCURRENCY gap 5 / ECS gap 4, slice F), picked up by the torbscript project loop on 2026-10-07. ## What - Parser: `var` in front of a closure parameter name, `{ var point => ... }` and `{ var point, scale => ... }`. The parenthesized form `{ (var point, scale) => ... }` named in the issue is a parse error with a message that points to the unparenthesized form. - Checker: the closure's parameter binding takes `var` (by-reference convention of `(var value: T) => Void`); the write goes to the caller's value. The existing escape rule (BACKEND 5.8) refuses storing, returning or `spawn`-capturing a `var` parameter. A `var` mismatch is an error that names the `var` side, without a second "is a const" error on top. - Seed-safe: `compiler/src` and `std/` do not use the new form. - Docs: closures page, functions index summary, cheat sheet, design docs (CONCURRENCY, ECS) and the generated skills. - Tests: parser, checker, places, lowering (IR snapshots), formatter, language server hover, conformance program `closure-var-parameter` (native and VM; a `forEach`-style function taking `body: (var value: Point) => Void`, called with a closure that captures a local `Float`). ## Decisions for review 1. **Parenthesized form.** `{ var point, scale => ... }` is implemented. `{ (var point, scale) => ... }` from the issue is a clear parse error ("A closure writes its parameters without parentheses"): parentheses in a closure's parameter position read as a tuple pattern, which has no `var` in it, and a second spelling of the list would be new syntax. Say so if the issue's spelling should be accepted instead. 2. **Reverse mismatch.** A closure parameter *with* `var` against a function type *without* it is an error, and so is the reverse for function values (a named `fn`). A closure *without* `var` still adopts `var` from the expected function type (closures docs rule 3), so existing closures such as `points.each { p => ... }` keep working. ## Test Ran in a `gcc:14` container after rebootstrapping (seed -> torb -> torb, fixpoint holds): - `sh tools/gates.sh a`: every gate green, including `docs check`, `docs index --check`, `docs skill --check`, `docs site --check`, `test compiler/tests (native)`, std/example packages in both back ends, `tests/language` in both back ends, and `format --check` (after formatting two files). - `sh tools/check-commits.sh origin/main HEAD`: green. - Tier B (`gates.sh b`) not run locally: no IR, back-end or runtime change; the conformance program is covered by CI. ## After merge Nothing to release or deploy. A later use of the form inside `std/` waits for a seed refresh (owner's call).
feat(syntax): a closure binds a var parameter
Some checks failed
ci / commit messages (pull_request) Successful in 8s
ci / what the change touches (pull_request) Successful in 7s
pdisc / pdisc (pull_request) Successful in 1m36s
ci / which runners exist (pull_request) Successful in 2s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / bootstrap (linux-x64) (pull_request) Successful in 8m11s
ci / tier B (linux-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / the VS Code extension (editors/vscode) (pull_request) Successful in 48s
ci / tier A (linux-x64) (pull_request) Failing after 10m40s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 3m19s
ci / every host emits the same C (pull_request) Successful in 4m46s
ci / the playground (torb as WebAssembly) (pull_request) Successful in 2m21s
ci / pack the seed (pull_request) Successful in 14s
ci / archives (pull_request) Has been skipped
ci / gates (pull_request) Failing after 0s
551062315b
Closes #156
orion requested review from argus 2026-10-07 20:37:57 +00:00
Collaborator

CI red on 5510623: ci / tier A (linux-x64) (and the aggregate gates). The tier A log ends with a runner abort (context canceled at 22:42:55, after tests/command-line), not with a failing check, so this looks like a runner/infra cancel: please re-run it.

Also, the PR is not mergeable: main moved to 1d6f50c (merge base is 9e2ab91) and both sides touch docs/design/CONCURRENCY.md. Rebase onto main; the new head will need a green CI run before review.

CI red on 5510623: `ci / tier A (linux-x64)` (and the aggregate `gates`). The tier A log ends with a runner abort (`context canceled` at 22:42:55, after `tests/command-line`), not with a failing check, so this looks like a runner/infra cancel: please re-run it. Also, the PR is not mergeable: `main` moved to 1d6f50c (merge base is 9e2ab91) and both sides touch `docs/design/CONCURRENCY.md`. Rebase onto `main`; the new head will need a green CI run before review.
argus requested changes 2026-10-07 23:55:35 +00:00
Dismissed
argus left a comment

Changes requested on 5510623.

  1. [blocker] Not mergeable: main moved to 1d6f50c (merge base is 9e2ab91) and both sides touch docs/design/CONCURRENCY.md. Rebase onto main and resolve the conflict (rebase merge, linear history).
  2. [blocker] CI is red on this head: ci / tier A (linux-x64) and the aggregate gates. The tier A log ends with a runner abort (context canceled after tests/command-line), not a failing check, so it likely clears on a re-run. The rebased head needs a fully green run before merge.

Not reviewed in depth yet: the rebase changes the head, so the diff review (parser/checker/lowering/formatter/LSP tests, conformance program closure-var-parameter, docs) happens on the new head. Decisions 1 and 2 in the PR body are accepted as the author's design rationale.

Changes requested on 5510623. 1. [blocker] Not mergeable: `main` moved to 1d6f50c (merge base is 9e2ab91) and both sides touch `docs/design/CONCURRENCY.md`. Rebase onto `main` and resolve the conflict (rebase merge, linear history). 2. [blocker] CI is red on this head: `ci / tier A (linux-x64)` and the aggregate `gates`. The tier A log ends with a runner abort (`context canceled` after `tests/command-line`), not a failing check, so it likely clears on a re-run. The rebased head needs a fully green run before merge. Not reviewed in depth yet: the rebase changes the head, so the diff review (parser/checker/lowering/formatter/LSP tests, conformance program `closure-var-parameter`, docs) happens on the new head. Decisions 1 and 2 in the PR body are accepted as the author's design rationale.
orion force-pushed loop/156-closure-var-parameter from 551062315b
Some checks failed
ci / commit messages (pull_request) Successful in 8s
ci / what the change touches (pull_request) Successful in 7s
pdisc / pdisc (pull_request) Successful in 1m36s
ci / which runners exist (pull_request) Successful in 2s
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / bootstrap (linux-x64) (pull_request) Successful in 8m11s
ci / tier B (linux-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / the VS Code extension (editors/vscode) (pull_request) Successful in 48s
ci / tier A (linux-x64) (pull_request) Failing after 10m40s
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 3m19s
ci / every host emits the same C (pull_request) Successful in 4m46s
ci / the playground (torb as WebAssembly) (pull_request) Successful in 2m21s
ci / pack the seed (pull_request) Successful in 14s
ci / archives (pull_request) Has been skipped
ci / gates (pull_request) Failing after 0s
to d55c8d9c10
All checks were successful
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 2m51s
ci / the playground (torb as WebAssembly) (pull_request) Successful in 1m52s
ci / pack the seed (pull_request) Successful in 10s
ci / every host emits the same C (pull_request) Successful in 4m26s
ci / archives (pull_request) Has been skipped
ci / gates (pull_request) Successful in 0s
images / cr.torb.dev/torbscript/registry (push) Has been skipped
images / cr.torb.dev/torbscript/release-sync (push) Has been skipped
images / cr.torb.dev/torbscript/site (push) Has been skipped
ci / what the change touches (push) Successful in 6s
ci / commit messages (push) Successful in 7s
pdisc / pdisc (push) Successful in 2m14s
ci / which runners exist (push) Successful in 1s
ci / bootstrap and conformance (windows-x64) (push) Has been skipped
ci / bootstrap and conformance (linux-arm64) (push) Has been skipped
ci / bootstrap and conformance (macos-arm64) (push) Has been skipped
images / cr.torb.dev/torbscript/site:edge (main) (push) Successful in 4m0s
ci / the VS Code extension (editors/vscode) (push) Successful in 23s
ci / bootstrap (linux-x64) (push) Successful in 4m6s
ci / cross-compiled on linux-x64 (windows-x64) (push) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (push) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (push) Has been skipped
ci / tier A (linux-x64) (push) Successful in 3m2s
ci / tools/release-sync and tools/registry (linux-x64) (push) Successful in 2m52s
ci / tier B (linux-x64) (push) Successful in 9m22s
ci / pack the seed (push) Has been skipped
ci / every host emits the same C (push) Successful in 4m37s
ci / archives (push) Has been skipped
ci / the playground (torb as WebAssembly) (push) Successful in 1m58s
ci / gates (push) Successful in 0s
2026-10-07 23:56:57 +00:00
Compare
orion requested review from argus 2026-10-07 23:56:59 +00:00
argus approved these changes 2026-10-08 00:09:36 +00:00
argus left a comment

Approved on d55c8d9. Both earlier blockers are cleared: the PR is mergeable on current main (1d6f50c) and CI is fully green on this head (tier A, gates, pdisc, bootstrap; tier B is skipped by the repo rule because no ir/backend/runtime/tools change).

  • Tests: ok. Parser, checker, places (escape rule), lowering IR snapshots, formatter, LSP hover and conformance program closure-var-parameter. I checked the .expected by hand against the program (and the trb run doc example, validated by docs check, prints the same record format).
  • Correctness: ok. var is parsed as part of the try, so { var total = 0 ... } stays a closure body; both mismatch directions are covered; a mismatched closure keeps a var binding so the body is not blamed twice.
  • Scope: ok. Exactly issue #156, no drive-bys. Decisions 1 and 2 accepted on the author rationale.
  • Security/data: ok. No secrets, no storage or format change. The var parameter stays under the existing escape rule (stored, returned and spawn-captured cases tested).
  • Docs: ok. closures page (rule 9), index summaries, cheat sheet, CONCURRENCY and ECS design docs, generated skills; pdisc green.
  • Release/deploy: nothing after merge.

Non-blocking: tier B (including the new conformance program) does not run on this PR, so it first runs on the push to main; I verified the expected output by hand.

Approved on d55c8d9. Both earlier blockers are cleared: the PR is mergeable on current `main` (1d6f50c) and CI is fully green on this head (tier A, `gates`, `pdisc`, bootstrap; tier B is skipped by the repo rule because no ir/backend/runtime/tools change). - Tests: ok. Parser, checker, places (escape rule), lowering IR snapshots, formatter, LSP hover and conformance program `closure-var-parameter`. I checked the `.expected` by hand against the program (and the `trb run` doc example, validated by `docs check`, prints the same record format). - Correctness: ok. `var` is parsed as part of the try, so `{ var total = 0 ... }` stays a closure body; both mismatch directions are covered; a mismatched closure keeps a `var` binding so the body is not blamed twice. - Scope: ok. Exactly issue #156, no drive-bys. Decisions 1 and 2 accepted on the author rationale. - Security/data: ok. No secrets, no storage or format change. The `var` parameter stays under the existing escape rule (stored, returned and spawn-captured cases tested). - Docs: ok. closures page (rule 9), index summaries, cheat sheet, CONCURRENCY and ECS design docs, generated skills; pdisc green. - Release/deploy: nothing after merge. Non-blocking: tier B (including the new conformance program) does not run on this PR, so it first runs on the push to `main`; I verified the expected output by hand.
argus merged commit d55c8d9c10 into main 2026-10-08 00:09:38 +00:00
argus deleted branch loop/156-closure-var-parameter 2026-10-08 00:09:38 +00:00
Sign in to join this conversation.
No reviewers
No labels
loop
ready
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language!161
No description provided.