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

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:
2026-05-27 23:20:46 +00:00
parent 219e2fcfe7
commit f52ad1fac6
6 changed files with 196 additions and 22 deletions

View File

@@ -359,8 +359,12 @@ Progress-log line → push `origin/loops/go`.
- [x] Defer stack per function frame; runs LIFO on normal return.
Args eager at defer-time; frame-local (inner defers don't run
outer ones); defer-in-loop pushes each iteration. 6 tests.
- [ ] `panic(v)` unwinds frames running deferreds; `recover()` inside a
deferred fn captures the panic value and stops unwinding.
- [x] `panic(v)` unwinds frames running deferreds; `recover()` inside a
deferred fn captures the panic value and stops unwinding. Panic
sentinel `(:go-panic V)` propagates through go-eval-block /
go-eval-stmt / go-eval-program-loop. Per-frame panic cell
`(STATE V)` flips :none → :raised → :recovered; recover walks
env chain finding the outermost :raised cell. 6 tests on eval/.
- Goroutine panic propagation: a panicking goroutine that doesn't recover
crashes the whole program (honour Go spec, or document divergence).
- Tests: defer order (LIFO), defer + named-return mutation, panic/recover,
@@ -611,6 +615,24 @@ Minimal repro: see `lib/go/lex.sx#gl-oct-digit?` and `#gl-match-op`.
_Newest first. Append one dated entry per commit._
- 2026-05-27 — **Phase 6: panic + recover.** `panic` and `recover`
builtins. Panic sentinel `(:go-panic V)` propagates like
`:return-value` through stmt/block/program-loop. Each call frame
gets its own `__go-panic-cell` of shape `(STATE V)`; on body panic
the cell flips to `:raised V` BEFORE defers drain so `recover()`
can find it. `recover` walks the env chain looking for the
outermost `:raised` cell (so deferred calls invoked from a
panicking frame still see that frame's cell despite their own
nested cell shadowing it). Flips to `:recovered` and returns V;
the panicking frame then returns normally instead of propagating.
6 tests: uncaught surfaces from program, panic from fn surfaces,
defer-recover swallows, defer-recover captures value via channel,
propagation through 3-deep no-defer chain, middle frame catches
panic from deeper. 509/509 total. **Shape:** the panic cell is
a *frame-attached out-of-band channel* that survives across the
function boundary because env-chain lookup can walk past
shadowed bindings to find it. Same primitive will serve as the
scheduler kit's "cleanup-with-error-context" hook. [shapes-scheduler]
- 2026-05-27 — **Phase 6 first slice: defer + LIFO.** Added
`go-eval-defer-stmt`, `go-run-defers!`, `go-run-defers-prefix!`,
plus new `:quoted-value` AST node so deferred calls can be