php: witnesses, trait values, objects, counts and Close order under PHP (slice 3 of #64) #761
Labels
No labels
loop
queue/failed
queue/queued
ready
status/parked
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
torbscript/language#761
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Step 4 of #64 (backend: PHP), slice 3 of
docs/design/PHP.mdsection 6: the rest of the language, part 2. Step 3 (#735, PR #738, merged asf10e88a6) is onmain:Variant,Tag,Switch,FixedArray,Closure,CallClosure,Cell,Projection,Read/Writethrough everyPathStep,tools/php-output.sh, and the programs oftests/conformance/php.allow. It is the sibling of the JavaScript slice 3 (witnesses,TraitValue,Object,SharedObject, counts), whose choices it copies where the IR, not the host, decides.Source
docs/design/PHP.mdsection 6 row 3, section 2.6 (witnesses as arrays of function names,CallWitness,Object,SharedObject,TraitValue), section 4 (Close, copy on write, identity), section 7 (the dispatch statement), and the paragraph "What slice 2 left out".docs/design/JAVASCRIPT-BACKEND.mdsection 6 row 3 and "What slice 3 left out", sections 2.6 and 4.docs/design/DESTRUCTORS.mdsection 2a (when exactly a release happens).What
php: witnesses, trait values, objects, counts and
Closeorder under PHP. Extend the PHP emitter, compiler only. Whatcompiler/src/backend/php/body.trbreports as unsupported today and this issue makes work:parametersOf("a function with witness tables") andemitCall("a call through witness tables") stop reporting. A witness table is a PHP array of function names, astaticproperty of the generated class, members in the order of the trait;CallWitnessis$m = $w[k]; $r = $m($self, ...)with the holder handed over as the JavaScript thunks do (payload for a plain member, the holder itself for avar selfmember, which is an object with->v).dropandcopyentries work on the payload. Only the tables a function names are written. Newcompiler/src/backend/php/witness.trb, modelled onbackend/javascript/witness.trb.TraitValue,Object,SharedObject: a trait-typed value is an object{v, w0, w1, ...}(payload and one table per bound), a call through boundireads$o->w<i>, narrowing to a supertrait reads thenestedentry. Ashared typeobject is a generated class;isSameis===on the objects (PHP.md section 4, "Identity").Closeorder: a type for whichClosing.holdsanswers true carriesrc(1 at construction);Retainis$x->rc++,Releaseis a call of a generatedtorb_release_<type>($x)that decrements and at zero runsclose()and then releases the fields in reverse declaration order, a list or array element by element from the last to the first. No__destruct; a type that cannot hold aCloseobject gets no count andRetain/Releaseof it write nothing, as in the C back end. A program without aCloseobject has no such code in its output. Newcompiler/src/backend/php/count.trb, modelled onbackend/javascript/count.trb.Unreachable/Stopbehaviour and every other report stay as onmain;BoxNew,LazyForce, aPlacecapture and aContentsstep stay named findings.Decided by the loop
MakeUniqueand aWrite-drivenrcare deferred to slice 4, as in the JavaScript record:Retainof an aggregate copies it in place since slice 2 (an eager copy), so two values never share storage and there is nothing to make unique. PHP.md row 3 and section 4 nameMakeUnique; the implementer reads howbackend/javascript/body.trbtreats it, does the same, and amends row 3 and the section 4 text in this PR ("What slice 3 left out").backend/shared/is limited to code that is the same text in both back ends and does not mention a host (for example the traversal that decides which types need a release function). The implementer moves exactly that and nothing else, the JavaScript output stays byte for byte as it was (the--jsleg andcompiler/testsprove it), and where nothing is the same text the PHP files simply sit beside the JavaScript ones. Refactor size is no argument (LOOP rule); churn in the JavaScript back end that changes its output is.m3) is measured in the forge job and its figure goes into PHP.md section 7; the form changes only if the string dispatch is more than twice as slow, which the record already says. No owner question: all of it is a technical choice with no outward effect (recommendation test).Shown values stay with slice 4 (ListStorage): programs that need them stay offphp.allow.Files
compiler/src/backend/php/{body,type,emit,writer}.trb, newwitness.trbandcount.trb;natives.trbonly if a runtime helper is named;runtime/php/core.phpfor the release and witness support, included only for a program that reaches it (a program without them produces the PHP it did before,php_first.trbunchanged);compiler/src/backend/shared/only as decided above.tests/conformance/php.allow; a newtests/conformance/php-witnesses-close.trbwith.expected,.stderr,.exit, modelled onjs-witnesses-close.trb.tools/php-dispatch.sh(or a.phpscript it runs): the dispatch micro benchmark, run by theconformance-phpjob on 8.4 and printing its figures;.forgejo/workflows/gates.ymlfor the call if needed.docs/design/PHP.md(row 3 marked done, a "What slice 3 left out" paragraph, the dispatch figure in section 7); docs regenerated astorb docsrequires.Tests
php.allowonce they pass on 5.6.40, 7.4.33 and 8.4 with the expected files of the C leg: start from the JavaScript list of slice 3 (js-witnesses-close,traits,trait-values,generics,implementation-trait-arguments,destructor-scope-end,destructor-conditional-temporaries,destructor-using). A program that also needs a list, a map,Show, a float or a task stays off, as on the JavaScript leg, and the PR names each one left off and why.php-witnesses-close.trbexercises each construct of "What" (a generic function with a witness, a trait value with two bounds and a supertrait narrowing, ashared typewithisSame,Closeorder of fields and of scope end, avar selftrait member) and prints theclose()calls; the leak-style programs that countclose()calls print the same lines as on the C leg.tools/php-output.sh) that the emitted file of a program with noCloseobject contains norcand notorb_release_, and that no output of this slice contains__destruct,&$or= &.Acceptance criteria
php.allowgrows by the slice-3 programs listed in the PR; none is removed; they pass on 5.6.40, 7.4.33 and 8.4 with the same.expected,.stderrand.exitfiles as the C, VM and JavaScript legsphp-witnesses-close.trb) passing on all four legs; theclose()order matchesdocs/design/DESTRUCTORS.mdsection 2aCloseobject has no count or release code,php_first.trboutput is unchanged, and no output contains__destruct--jspasses with the samejs.allowand the emitted JavaScript of the conformance programs is byte for byte whatmainemitsconformance-phpand its figure is indocs/design/PHP.mdsection 7; the witness form stays or the record says why it changedBoxNew,LazyForce, aPlacecapture and aContentsstep still failtorb build --target linux-phpwith a named finding and no output filedocs/design/PHP.mdmarked done with the "What slice 3 left out" paragraph;torb format --checkandtorb docs checkgreentools/check-commits.shclean;sh tools/gates.sh aand theconformance-phpjob 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
Architecturecase, so the two-commit rule does not apply. One PR; one Conventional Commit per logical change (feat(php): ..., and arefactor:commit for any move tobackend/shared/), confirmed bytools/check-commits.sh. The seed refresh stays Torben's.Size
One PR. If a review cannot hold it, cut the counts and
Closeorder into a second commit of the same PR (witnesses and trait values first); nothing is carved out into a new issue. Model: sonnet, thinking high (compiler work). Out of scope: slices 4 to 8 (kernel,UInt64, floats, storage, tasks, limits, subset andtorb test),MakeUnique, a minifier.Dependencies
f10e88a6onmain).Part of #64 (epic; do not close it).
[loop:torbscript] claim implement by session:loop-torbscript-20261113 until 2026-11-14T01:10:00Z
[loop:torbscript] implement: PR #766, head
6319ee75, CI pending after ~30 min (commit messages, touches and pdisc green;gatesandconformance-phpstill queued, runner queue congested), next: waiting (CI; nothing was built or run locally, so a red job is likely to need a fix pass: new count.trb and witness.trb are uncompiled).Worker token estimate: about 1.5M (several context windows).
For the reviewer:
isSameis a compile error on every back end today, sophp-witnesses-close.trbpins identity with a sharedBoxand a write through two names.FixedArrayrelease runs first to last, as in JS (the expected output requires it); the issue text says last to first.php-witnesses-close.allocationsisunbudgeteduntil the native count is measured in CI.php.allowgrew by 9 programs (nothing removed); the final membership is decided by CI, any program that fails will be named in the PR with its reason.tools/php-dispatch.shruns on 8.4 inconformance-php; PHP.md section 7 names it, the number goes in after the first green run (witness form stays an array of function names unless the ratio is above 2).skills/regeneration (torb docs skill) was not possible locally; CI decides whether it changes.MakeUniqueand Write-drivenrc(slice 4), lists/maps/sets/Show(slice 4), recursion through witness parameters not depth-counted.[loop:torbscript] claim review-fix by session:loop-torbscript-20261113 until 2026-11-14T00:00:00Z
[loop:torbscript] review-fix: PR #766 was red only on
format --check; fixed on the branch, CI re-runningd1e54275):ci / tier A (linux-x64)failed after 44 min with exactly one red gate,format --check:compiler/src/backend/php/body.trbandwitness.trb"would change". All other tier A gates were green (compiler suite, std/example packages native and VM, docs, REPL/LSP/debug, packages, lock).ci / gates"after 0s" is not a runner problem: it is the reusable-workflow wrapper job ofgates.yml(no steps of its own), red becausetier-awas red.torb formatlays them out (one closure-capture append inbody.trb, one command call inwitness.trb); layout only. The first attempt (552a2167, subjectstyle(php): ...) went red oncommit messagesbecausestyleis not an allowed Conventional Commit type, so the commit was reworded tochore(php): torb format body.trb and witness.trb(b3687c5f, same tree) and pushed with--force-with-leasetoloop/761-php-slice3.torbof CI'scompiler-linux-x64artifact (run 2786) withformat --checkover the 10.trbfiles this PR adds or changes: 0 of 10 would change.sh tools/check-commits.sh origin/main HEADpasses. No local C compiler, so no local build or gates beyond these.b3687c5fqueued/pending (the old runs on552a2167were superseded). Tier A takes ~45 min. argus is requested as reviewer again.[loop:torbscript] release review-fix: done
std/tensoron the CPU #82[loop:torbscript] claim review-fix by session:loop-torbscript-20261114 until 2026-11-14T03:06:00Z
[loop:torbscript] review-fix: the red check on #766 is a flaky playground smoke test, not this PR; no code change, CI rerun queued, next: waiting (runner backlog, then merge gate)
ci / the playground (torb as WebAssembly)onb3687c5f(run index 2804, job 32223). Everything before it passed: emcc build, 26.1 MB wasm, hello world, language server init, diagnostics, completion, hover, signature help, rename. The only failing step wasa request Monaco cancels answers nullinplayground/smoke-test.mjs: it sends a hover, yields onesetTimeout(0), then cancels and expectsnull. The hover answer had already come back (const names: List<String>), so the cancel landed too late. The test is a timing race by construction; the runner was slow (rename step 33 s, vs 31-32 s on #765/#771 where the same step passed and the cancel test took 0-2 ms).compiler/src/backend/{php,javascript,shared}, PHP tests/docs/tools andgates.yml; nothing in the language server, hover or the playground. In the same run tier A, tier B,conformance (PHP 5.6, 7.4 and 8.4),conformance (JavaScript under Node.js),every host emits the same C, bootstrap, VS Code extension and release-sync were all green;gatesis red only as the aggregate of the playground job.ci-rerun.sh(close + reopen, no new SHA, argus is still the requested reviewer). After ~27 min no job of the rerun had started: the runner queue is backed up (#773, #769, #778, #777 also sit inpending), which is a backlog, not a failure.playground/smoke-test.mjs, not part of this PR.next: waiting (runner backlog), then argus's review.