Skip to content

Discharge and structure the exception channel (EH-09..EH-11) - #152

Merged
biuld merged 1 commit into
masterfrom
slice/eh-structuring
Oct 18, 2026
Merged

biuld merged 1 commit into
masterfrom
slice/eh-structuring

Conversation

@biuld

@biuld biuld commented Oct 18, 2026

Copy link
Copy Markdown
Owner

What lands

Slice 3 of the exception-handling adoption (rows EH-09..EH-11), per the design from PR #149:

  • EH-09 — bindings/runtime/exception.rs: the checked psrs:runtime-exception#checkThrows contract ((Unit -> a) -> Boolean, payload-erased closure operand, no host ABI — plan_import plans no import); mir/lower/exception.rs: the P9 discharge into the inline try region (Try { protected, catch_all } shape; guarded callable called in the protected block; handler value returned through the join); disabled-profile rejection at the binding's span.
  • EH-10 — the structurer's UnitPlan::Try composite unit: synthesized unwind Op::Block enclosing the try_table with result = the caught payload, payload copied into handler parameters like jump arguments, anonymous label entries (unwind block + the try_table's internal label); nested tries, try inside loop (re-armed per iteration), loop inside protected region all have fixtures.
  • EH-11 — an irreducible CFG containing a try region is rejected at the try region's span; try-free irreducible input still uses the dispatcher.

Design-conformance notes recorded in the checklist's slice-3 record: the normal join after a try_table emits unreachable (protected blocks always leave the region, so the try_table never completes normally); the try-region partition's dominance is now computed over the CFG that includes unwind edges (per the design's own invariant, fixing a join-sharing gap); a clause-uniformity rule (one unwind block ⇒ all clauses agree on payload/handler) is checked at P10.

Evidence (coordinator verified fmt + clippy; full suite run by the slice)

PSRS_STDLIB_ROOT=… PSRS_REQUIRE_WASMTIME=1 cargo test --workspace — 2179 passed, 0 failed, wasmtime CLI 48.0.1: discharge normal path (exit 0), catch path (exit 1), nested tries (exit 10, inner handler catches), named catch payload (exit 7), try re-armed across loop iterations (exit 99), loop inside protected region (exit 42), try-free irreducible dispatcher input (exit 1). Checklist rows EH-09..11 → Verified with evidence; D-04 untouched; profile default unchanged. 500-line rule: cfg/mod.rs, region.rs, and one test split into module directories with content preserved.

The P9 discharge turns a call to a psrs:runtime-exception binding into the
design's inline try region, and the structurer emits it as a try_table unit;
the two boundaries the design names are enforced.

- bindings/runtime/exception.rs: the checked source contract for the
  psrs:runtime-exception provider — one guarded callable operand taking the
  erased Unit and a Boolean result, rechecked at the physical boundary; no
  host ABI, so plan_import plans no import (mir/runtime/context.rs collects
  the provider for live direct calls only).
- mir/lower/exception.rs: the discharge builds Try { protected, catch_all }
  inline — the protected block fabricates the Unit operand, applies the
  callable through ClosureCall, and jumps to the shared join with false; the
  handler jumps with true; the join's parameter is the assignment's
  destination. A disabled profile rejects the call at the binding's own span.
- wasm/lower/structure/cfg/try_context.rs: the structurer's checked view —
  all clauses of one region must agree on the payload and handler they
  deliver to (one synthesized unwind block), claims close transitively over
  nested tries, and try/loop overlap must nest (try inside loop, loop inside
  protected region) or stay disjoint.
- wasm/lower/structure/cfg/build.rs: UnitPlan::Try occupies one slot in the
  enclosing region's topological order; the protected sub-region is the raw
  dominated set (inner tries and loops reappear as nested units) and claim
  bubbling keeps every inline-emitted block claimed exactly once.
- wasm/lower/structure/region/try_unit.rs: emit_try_unit — the try block's
  instructions, one synthesized unwind Op::Block whose result is the caught
  payload enclosing the try_table, every clause targeting that block at
  depth 0, the payload copied into the handler's parameter locals like jump
  arguments, then the handler's instructions and terminator. The design's
  sketch writes br to the normal join after the try_table; the emitted body
  ends in unreachable instead — every protected block's terminator leaves
  the region, the dead path carries none of the join's per-path arguments,
  and the unreachable discharges the payload-typed result for the validator.
  The watermark save/restore points (EH-14) are marked, nothing budget-
  related exists yet. Label gains the two anonymous entries (Unwind,
  TryTable) so branch depths stay correct.
- wasm/lower/mod.rs: each tag's function type is appended and its TagDef
  recorded; the tag section is filled from the MIR tag table. Throw emits
  its payload and throw; a Try terminator outside a try unit is rejected.
- The try-region partition's dominance input is now the CFG including
  unwind edges (mir/cfg.rs unwind_successors, dominators_with_unwind_edges),
  per the design's invariant section: the handler edge keeps a join shared
  by the protected and catch paths out of the region. The region plan's
  reachability walk follows unwind edges too, so catch-path blocks are
  planned.
- cfg/mod.rs (773 lines), region.rs (504), and the structuring test file
  (533) are split into module directories with content preserved;
  mir/mod.rs sits at 498 lines.

Checklist rows EH-09..EH-11 verified: direct-MIR discharge and structuring
fixtures executed under wasmtime 48.0.1 (nested tries, named catch payload,
try re-armed across loop iterations, loop inside the protected region,
try-free irreducible dispatcher input), the disabled-profile rejection and
the irreducible-with-try rejection asserted locally.
@biuld
biuld merged commit 02c7dd8 into master Oct 18, 2026
1 check passed
@biuld
biuld deleted the slice/eh-structuring branch October 18, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant