php: Variant, Switch, arrays, closures and references under PHP (slice 2 of #64) #735

Closed
opened 2026-11-13 10:01:59 +00:00 by orion · 8 comments
Collaborator

Context

Step 3 of #64 (backend: PHP), slice 2 of docs/design/PHP.md section 6: the rest of the language, part 1. Step 2 (#727, PR #730, merged as 187618a9) is on main: target linux-php, compiler/src/backend/php/{emit,body,type,natives,writer}.trb, runtime/php/{core,int64}.php, tools/conformance.sh --php, tools/php-guards.sh, tests/conformance/php.allow, the action .forgejo/actions/php and the job conformance-php. It is the sibling of #684 (slice 2 of the JavaScript back end, PR #694), whose choices it copies where the IR, not the host, decides.

Source

  • docs/design/PHP.md section 6 row 2, section 2.1 (Switch, no PHP reference), 2.3 (values, Read/Write), 2.4 (closures, captures, projections), section 7 (statements pinned by this slice).
  • docs/design/JAVASCRIPT-BACKEND.md sections 2.3 to 2.4 and "What slice 2 left out".

What

php: Variant, Switch, arrays, closures and references under PHP. Extend the slice-1 emitter, compiler only. What body.trb reports as unsupported today and this issue makes work:

  • Variant and Tag: a payload-free enumeration is the int of the case; otherwise one class per case with t first, then f0, ... (type.trb: isPlainLayout and the "variant or boxed layout" reports in Construct, placeOf). Switch on an integer or a tag is a PHP switch whose cases are integer literals, each holding a goto; the unsupported report for Switch on a non-integer stays.
  • FixedArray and Buffer: an object that owns its storage ({a} with a PHP array, a string for 8-bit elements), with a bounds check wherever the IR has one; the Element step of placeOf and Read/Write through every PathStep (field, tuple field, variant payload, element).
  • Closure with the three captures, CallClosure ($f = $c->f; $r = $f($c->e, ...), never function () use), and the var parameters and var arguments that slice 1 reports (parametersOf, emitCall): a Cell is an object with ->v, a Projection an object with get() and set($x). No PHP reference (&) in the output.
  • Lazy as the IR builds it, as far as the lowering produces it; BoxNew, LazyForce, a Place capture and a Contents step are not produced by the lowering and the C and JavaScript back ends name them as unsupported, so this one names them too (PHP.md section 1.1; amend the record if the code shows otherwise).

Decided by the loop

  • Where the IR builds a Buffer as new List (ListStorage of slice 4), that stays unsupported here, as in #684; FixedArray is a PHP array wrapper until then. Programs that print lists, maps or a Shown value stay off php.allow (slice 4).
  • Reasoning: all of it is a technical choice inside the record with no outward effect (recommendation test); no owner question is open.

Files

  • compiler/src/backend/php/{body,type,emit,writer}.trb (and natives.trb only if a runtime helper is named); runtime/php/core.php for the array and cell helpers.
  • tests/conformance/php.allow; a new tests/conformance/php-variants-arrays.trb with .expected, .stderr, .exit, modelled on js-variants-arrays.trb, plus a check that greps the emitted file for &$ and = &.
  • docs/design/PHP.md (row 2 marked done, a "What slice 2 left out" paragraph, section 7 figures); docs regenerated as torb docs requires.

Tests

  1. The existing programs on enums, matching, closures and arrays that use only slice-2 constructs (start from the JavaScript list: matching, match-order, closures, closure-counts, closure-frame, records, places, place-counted, rest-patterns, array-index-bounds) join php.allow once they pass on 5.6.40, 7.4.33 and 8.4 with the C leg's expected files.
  2. php-variants-arrays.trb exercises each construct above; a program that aliases through a reference proves no PHP reference is emitted.

Acceptance criteria

  • php.allow grows by the programs on enums, matching, closures and arrays; none is removed; they pass on 5.6.40, 7.4.33 and 8.4 with the same .expected, .stderr and .exit files as the C, VM and JavaScript legs
  • Each construct of "What" has a conformance program (existing or php-variants-arrays.trb) passing on all four legs
  • A test greps the emitted PHP of a reference-using program for &$ and = & and finds none
  • BoxNew, LazyForce, a Place capture and a Contents step still fail torb build --target linux-php with a named finding and no output file
  • Row 2 of docs/design/PHP.md marked done; torb format --check and torb docs check green
  • tools/check-commits.sh clean; sh tools/gates.sh a and the conformance-php job green in forge CI (CI only, no local bootstrap or gates)

Seed

Compiler only: no syntax change, no new native, no new std name looked up by string, no new Architecture case, so the two-commit rule of #727 does not apply. One PR; one Conventional Commit per logical change (feat(php): ...), confirmed by tools/check-commits.sh. The seed refresh stays Torben's.

Size

One PR. If a review cannot hold it, cut closures and references into a second commit of the same PR; nothing is carved out into a new issue. Model: sonnet, thinking high (compiler work). Out of scope: slices 3 to 8 (witnesses, trait values, objects, counts and Close order; kernel; tasks; limits; subset and torb test), a minifier.

Dependencies

Part of #64 (epic; do not close it).

## Context Step 3 of #64 (backend: PHP), slice 2 of `docs/design/PHP.md` section 6: the rest of the language, part 1. Step 2 (#727, PR #730, merged as 187618a9) is on `main`: target `linux-php`, `compiler/src/backend/php/{emit,body,type,natives,writer}.trb`, `runtime/php/{core,int64}.php`, `tools/conformance.sh --php`, `tools/php-guards.sh`, `tests/conformance/php.allow`, the action `.forgejo/actions/php` and the job `conformance-php`. It is the sibling of #684 (slice 2 of the JavaScript back end, PR #694), whose choices it copies where the IR, not the host, decides. ## Source - `docs/design/PHP.md` section 6 row 2, section 2.1 (`Switch`, no PHP reference), 2.3 (values, `Read`/`Write`), 2.4 (closures, captures, projections), section 7 (statements pinned by this slice). - `docs/design/JAVASCRIPT-BACKEND.md` sections 2.3 to 2.4 and "What slice 2 left out". ## What *php: Variant, Switch, arrays, closures and references under PHP.* Extend the slice-1 emitter, compiler only. What `body.trb` reports as unsupported today and this issue makes work: - `Variant` and `Tag`: a payload-free enumeration is the `int` of the case; otherwise one class per case with `t` first, then `f0`, ... (`type.trb`: `isPlainLayout` and the "variant or boxed layout" reports in `Construct`, `placeOf`). `Switch` on an integer or a tag is a PHP `switch` whose cases are integer literals, each holding a `goto`; the unsupported report for `Switch` on a non-integer stays. - `FixedArray` and `Buffer`: an object that owns its storage (`{a}` with a PHP array, a string for 8-bit elements), with a bounds check wherever the IR has one; the `Element` step of `placeOf` and `Read`/`Write` through every `PathStep` (field, tuple field, variant payload, element). - `Closure` with the three captures, `CallClosure` (`$f = $c->f; $r = $f($c->e, ...)`, never `function () use`), and the `var` parameters and `var` arguments that slice 1 reports (`parametersOf`, `emitCall`): a `Cell` is an object with `->v`, a `Projection` an object with `get()` and `set($x)`. **No PHP reference (`&`) in the output.** - `Lazy` as the IR builds it, as far as the lowering produces it; `BoxNew`, `LazyForce`, a `Place` capture and a `Contents` step are not produced by the lowering and the C and JavaScript back ends name them as unsupported, so this one names them too (PHP.md section 1.1; amend the record if the code shows otherwise). ## Decided by the loop - Where the IR builds a `Buffer` as `new List` (`ListStorage` of slice 4), that stays unsupported here, as in #684; `FixedArray` is a PHP array wrapper until then. Programs that print lists, maps or a `Show`n value stay off `php.allow` (slice 4). - Reasoning: all of it is a technical choice inside the record with no outward effect (recommendation test); no owner question is open. ## Files - `compiler/src/backend/php/{body,type,emit,writer}.trb` (and `natives.trb` only if a runtime helper is named); `runtime/php/core.php` for the array and cell helpers. - `tests/conformance/php.allow`; a new `tests/conformance/php-variants-arrays.trb` with `.expected`, `.stderr`, `.exit`, modelled on `js-variants-arrays.trb`, plus a check that greps the emitted file for `&$` and `= &`. - `docs/design/PHP.md` (row 2 marked done, a "What slice 2 left out" paragraph, section 7 figures); docs regenerated as `torb docs` requires. ## Tests 1. The existing programs on enums, matching, closures and arrays that use only slice-2 constructs (start from the JavaScript list: `matching`, `match-order`, `closures`, `closure-counts`, `closure-frame`, `records`, `places`, `place-counted`, `rest-patterns`, `array-index-bounds`) join `php.allow` once they pass on 5.6.40, 7.4.33 and 8.4 with the C leg's expected files. 2. `php-variants-arrays.trb` exercises each construct above; a program that aliases through a reference proves no PHP reference is emitted. ## Acceptance criteria - [ ] `php.allow` grows by the programs on enums, matching, closures and arrays; none is removed; they pass on 5.6.40, 7.4.33 and 8.4 with the same `.expected`, `.stderr` and `.exit` files as the C, VM and JavaScript legs - [ ] Each construct of "What" has a conformance program (existing or `php-variants-arrays.trb`) passing on all four legs - [ ] A test greps the emitted PHP of a reference-using program for `&$` and `= &` and finds none - [ ] `BoxNew`, `LazyForce`, a `Place` capture and a `Contents` step still fail `torb build --target linux-php` with a named finding and no output file - [ ] Row 2 of `docs/design/PHP.md` marked done; `torb format --check` and `torb docs check` green - [ ] `tools/check-commits.sh` clean; `sh tools/gates.sh a` and the `conformance-php` job green in forge CI (CI only, no local bootstrap or gates) ## Seed Compiler only: no syntax change, no new native, no new std name looked up by string, no new `Architecture` case, so the two-commit rule of #727 does not apply. One PR; one Conventional Commit per logical change (`feat(php): ...`), confirmed by `tools/check-commits.sh`. The seed refresh stays Torben's. ## Size One PR. If a review cannot hold it, cut closures and references into a second commit of the same PR; nothing is carved out into a new issue. Model: sonnet, thinking high (compiler work). Out of scope: slices 3 to 8 (witnesses, trait values, objects, counts and `Close` order; kernel; tasks; limits; subset and `torb test`), a minifier. ## Dependencies - #727 (closed, PR #730 on `main`). Part of #64 (epic; do not close it).
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261113 until 2026-11-13T12:30:00Z

[loop:torbscript] claim implement by session:loop-torbscript-20261113 until 2026-11-13T12:30:00Z
Author
Collaborator

[loop:torbscript] release implement: the worker died before its first reply (provider 403 at login), no branch and no PR; respawned on a fresh claim

[loop:torbscript] release implement: the worker died before its first reply (provider 403 at login), no branch and no PR; respawned on a fresh claim
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261113 until 2026-11-13T12:50:00Z

[loop:torbscript] claim implement by session:loop-torbscript-20261113 until 2026-11-13T12:50:00Z
Author
Collaborator

[loop:torbscript] implement: the worker hit its time limit after opening PR #738 (head bbfd637); CI is running, no red job yet, Argus is requested. Claim released.
next: review

[loop:torbscript] implement: the worker hit its time limit after opening PR #738 (head bbfd637); CI is running, no red job yet, Argus is requested. Claim released. next: review
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261113 until 2026-11-13T18:32:00Z

[loop:torbscript] claim implement by session:loop-torbscript-20261113 until 2026-11-13T18:32:00Z
Author
Collaborator

[loop:torbscript] implement checkpoint: done: root causes found. (1) "conformance (PHP ...)": the 10 programs added to php.allow end through Process.exit, and the PHP back end did not list torb_process_exit. (2) "tier B": php-variants-arrays.trb had no allocation budget (15 blocks measured). Fix pushed as ffecc839 (torb_process_exit mapped to torb_exit in natives.trb and core.php, plus php-variants-arrays.allocations = 15). In flight: forge CI on ffecc839. Next: read CI, fix any remaining PHP-leg findings.

[loop:torbscript] implement checkpoint: done: root causes found. (1) "conformance (PHP ...)": the 10 programs added to php.allow end through `Process.exit`, and the PHP back end did not list `torb_process_exit`. (2) "tier B": `php-variants-arrays.trb` had no allocation budget (15 blocks measured). Fix pushed as ffecc839 (`torb_process_exit` mapped to `torb_exit` in natives.trb and core.php, plus `php-variants-arrays.allocations` = 15). In flight: forge CI on ffecc839. Next: read CI, fix any remaining PHP-leg findings.
Author
Collaborator

[loop:torbscript] implement checkpoint: done: torb_process_exit for the PHP back end, php-variants-arrays allocation budget (15), php-output.sh findings replaced by ones the back end can reach (float and UInt64 arithmetic; BoxNew/LazyForce/Place capture/Contents are not reachable from source). In flight: forge CI on ba641fd8 (branch loop/735-php-variants-arrays, worktree code/wt/torbscript-language-735-php2). Next: read conformance (PHP) and tier B results, then final comment.

[loop:torbscript] implement checkpoint: done: torb_process_exit for the PHP back end, php-variants-arrays allocation budget (15), php-output.sh findings replaced by ones the back end can reach (float and UInt64 arithmetic; BoxNew/LazyForce/Place capture/Contents are not reachable from source). In flight: forge CI on ba641fd8 (branch loop/735-php-variants-arrays, worktree code/wt/torbscript-language-735-php2). Next: read conformance (PHP) and tier B results, then final comment.
Author
Collaborator

[loop:torbscript] release implement: worker run timed out after checkpoints on PR #738; branch keeps the work, next: implement (respawn when a slot is free)

[loop:torbscript] release implement: worker run timed out after checkpoints on PR #738; branch keeps the work, next: implement (respawn when a slot is free)
argus closed this issue 2026-11-13 14:22:31 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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#735
No description provided.