Repository navigation
Bridge Wasm exception transport into Control delimiter bookkeeping #156
Description
The native exception transport and stack budget landed in #149–#155. The stage-1 Control bridge is completed in #157 (2026-10-20). CB-01..CB-15 are Verified in the committed design and execution checklist; the closing comment records the full validation and serial runtime remeasurement. The scope and downstream exclusions below remain unchanged.
References
docs/design/backend/fp/control-exception-bridge.md— authoritative bridge design.docs/implementation/backend/control-exception-bridge.md— CB-01..CB-15 acceptance matrix, all Verified in Bridge native exception cleanup into Control runners and owned resumptions #157.docs/design/backend/fp/control-machine.mdandcontrol-elimination-and-handler-scopes.md.docs/design/backend/fp/exception-handling-and-stack-exhaustion.md.- DEC-19, DEC-20, DEC-21 and DEC-23.
- Phase 5 epic Phase 5: prove the backend on real corpus programs #78; BE-21 in
docs/design/D-04-suite-roadmap.md.
Correction to the handoff accounting proposal
The machine captures persistent heap queues, not native stacks. Inspection of the native budget emission and actual Resume/cancel plans shows no native charge attached to a saved queue: return/tail epilogues already settle native activations. Do not subtract queue weights at cancel, and do not implement the proposed saved − ΣW_f(live remaining chain) restoration. The design derives the existing native depth = saved rule. Execution evidence is still required.
The actual lifecycle gaps are normal-only root closure and normal-only completion of acquired one-shot owners. A channel throw can bypass both.
Stage 1 — bridge scope
- Add a target-neutral opaque exception-reference shape and generic CC protected regions with explicit Yield/Propagate exits. Verify binder scope and disjointness from erased source payloads.
- Add MIR Exn, ordered catch clauses, CatchAllRef, and ThrowRef. CatchAllRef carries one exception reference; named payload-plus-reference catches remain unsupported until multi-result labels are designed.
- Extend the single shared try-region/unwind relation. Inner handler bodies must remain protected by enclosing native regions. Unknown-tag ThrowRef must retain all possible enclosing catches/caller propagation without a private single-handler guess.
- Protect the flat executor from the run helper. Exceptional exit closes that run's root and propagates the original exception. Existing ancestor checks expire abandoned child checkpoints; no extent registry or GC finalizer is needed.
- Protect acquired-owner callback application locally. Pending → Running clears the callback slot before guest code. Normal and exceptional callback exit both end in Consumed. Illegal acquisition retains its existing rejection trap.
- Restore the native watermark before cleanup. Cleanup uses direct private-cell writes, without allocation, callbacks, or instrumented helper calls that could exhaust again.
- Keep native catch scope dynamic. A saved queue carries neither a native catch nor a budget charge. Ordinary Control
handleis not an exception handler. The run guard covers all installs and interpreted resumes in its flat executor.
Abort produces no answer pair and guarantees lifecycle consistency; it does not roll back state, IO, or source mutations. Engine traps and foreign-resource recovery remain separate contracts.
Downstream consumers
- Stage 2: a separately typed library exception-as-effect operation and recovery/transaction policy.
- Stage 3: official
Effect.Exceptionusing direct native dynamic throw/catch plus bridge cleanup when a throw exits a runner.
Both consume stage 1; stage 3 does not strictly depend on stage 2. Neither public API is implemented by completing this bridge.
Validation
Follow the compiler iteration SOP before compiler changes. Implement and verify every CB-01..CB-15 row, including malformed CC/MIR, construction-plan mutations, original tag/payload identity through propagation, exceptional root expiry, acquired-owner completion, cancellation/abandonment accounting, nested runs/replay ancestry, dynamic lexical catches, budget exhaustion in cleanup paths, disabled profiles and optimized source execution. Runtime evidence must actually execute.
Run cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, and PSRS_STDLIB_ROOT=/home/biu/Projects/psrs-stdlib PSRS_REQUIRE_WASMTIME=1 cargo test --workspace for Rust changes. Budget evidence pins -W max-wasm-stack=524288 and records the actual engine version. Follow the serial per-root runtime board protocol and remeasure before changing D-04; current boards do not establish Control × exception behavior.
Completion requires every checklist row Verified. Bridge completion does not close BE-21 or claim either downstream API complete.
Activity
biuld commentedon Oct 20, 2026
Completed in #157, merged with a merge commit. The acceptance record verifies CB-01..CB-15 and maps each requirement to its owning code and evidence:
https://github.com/biuld/purescript-rs/blob/master/docs/implementation/backend/control-exception-bridge.md
Validation on the final candidate:
cargo fmt --all --check, required-Wasmtimecargo test --workspace, andcargo clippy --workspace --all-targets -- -D warningspassed. Backend: 602 unit tests; driver: 859; workspace integration tests and doctests passed.-W max-wasm-stack=524288. Standard-library source and pin remain unchanged.This closes the stage-1 bridge only. BE-21, official Effect.Exception, exception-as-effect APIs, engine-trap recovery and foreign-resource cleanup retain their separate obligations. No corpus-blocker count moved, so the Corpus cases field is unchanged. Project Status is Done.