go: panic + recover → eval 92/92, total 509/509, Phase 6 closed [shapes-scheduler]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 36s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 36s
Panic/recover builtins + per-frame __go-panic-cell of shape (STATE V). Body panic flips cell :none→:raised BEFORE defers drain so recover() can find it. recover() walks env chain past shadowing cells to the outermost :raised one — flips it :recovered, returns V. Frame exit checks cell: :recovered → return clean; :raised → propagate (:go-panic V). 6 tests: uncaught-from-program, panic-from-fn, defer-recover-swallow, recover-captures-via-channel, propagation-through-no-defer-chain, middle-frame-catches-deeper-panic. Shape: panic cell is a frame-attached out-of-band channel that survives function boundaries via env-chain walk. Same primitive slots into the scheduler kit's termination-record + cleanup-with- error-context hook. Maps cleanly to Erlang try/catch/after. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -231,6 +231,40 @@ real result.
|
||||
|
||||
_Newest first. Append one dated entry per milestone landed._
|
||||
|
||||
- 2026-05-27 — **Phase 6: panic/recover shape lands.** The panic
|
||||
cell is the missing piece. It's a per-frame mutable record of
|
||||
shape `(STATE VALUE)` carrying one of `:none` / `:raised` /
|
||||
`:recovered`. Three properties matter for the scheduler kit:
|
||||
|
||||
1. **It survives the function boundary** via env-chain lookup —
|
||||
when a deferred call's own frame creates a shadowing cell,
|
||||
`recover()` walks past it to find the OUTER frame's cell (the
|
||||
one that's `:raised`). This is the same mechanism the
|
||||
scheduler will need when a panic-unwinding goroutine has
|
||||
multiple frames each carrying their own state, and the
|
||||
"current panic" must be locatable from any depth.
|
||||
|
||||
2. **It flips state in place** (`set-nth!`) so that the change
|
||||
made by `recover()` deep in a defer chain is visible to the
|
||||
enclosing frame's exit check. The scheduler kit needs the
|
||||
same pattern: a goroutine's "termination reason" must be
|
||||
writable by any frame in its stack.
|
||||
|
||||
3. **It's distinct from the return-value channel.** A frame can
|
||||
carry both `(:go-panic V)` from its body AND a recovery
|
||||
commitment in its panic cell; they're checked in sequence.
|
||||
For the scheduler this maps to: a goroutine carries both its
|
||||
running-state (channel-blocked, ready, sleeping) AND its
|
||||
termination-record (panic V / clean exit / killed) — two
|
||||
orthogonal slots, not one tag.
|
||||
|
||||
Concrete kit hint: every frame record should expose
|
||||
`frame-panic-cell` alongside `frame-defer-queue`. The scheduler's
|
||||
exit-path becomes: drain defers (cell may flip :raised→:recovered)
|
||||
→ consult cell → either propagate or return clean. Erlang's
|
||||
`try/catch/after` decomposes identically: `after` is the defer
|
||||
queue, `catch` is the recover-via-cell mechanism.
|
||||
|
||||
- 2026-05-27 — **Phase 6 first slice: defer + LIFO observation.**
|
||||
Go's defer is a *frame-local cleanup queue* — a list of (callee,
|
||||
pre-evaluated-args) records appended on `defer`, drained LIFO at
|
||||
|
||||
Reference in New Issue
Block a user