# Session 2026-10-23 — ISO exceptions (M1–M5) Follows the same-day range-check/PACKEDSET session (`summary_session-2026-10-23.md`) and the earlier design commit `2a30d8c`. **Branch:** `master`. **Feature state:** `v3-exceptions` + `v3-ptrchain` + `v3-function-exception` + `v3-finally`. **Gates at the end:** suite **283/283**, fixpoint **OK (4,184,457 bytes)**, corpus **512/612 compile-OK, 0 crashes**. --- ## Numbers | gate | at design start | at end | | --- | --- | --- | | suite (`compiler/run_tests.sh`) | 263/263 | **283/283** | | fixpoint (`bootstrap/fixpoint.sh`) | 4,075,853 B | **4,184,457 B** | | corpus (`tools/v3-corpus/corpus.sh`) | 511/612 | **512/612 compile-OK, 0 crashes** | The corpus is compile-only. After the follow-up pointer-chain fix, `lazyunique` compiles **and runs** (`rc=0`, unique output `0, 2, 1`); the corpus count stays 512 because it was already compile-OK. `except2/4/5/7/8/9` still fail the strict declaration-order rule because `ip` is declared after the procedures, like `onebyte*`. --- ## What landed ### M1 — parser/AST (`98da444`) - `EXCEPT` and `RETRY` became reserved words. - New AST nodes: - `NkExcept` — body statements plus handler statements - `NkRetry` - `EXCEPT` accepted on procedure, program-module, implementation-module, local-module and class implementation bodies. - `RETRY` outside a handler is error **237** (`RETRY outside EXCEPT`). - Two-phase defer/reparse carries the handler body like any other statement sequence. - `EXCEPT` initially reported `230` so no handler was silently ignored. ### M2 — runtime and codegen (`6e87def`) - `runtime/syslib/shim.c`: - handler stack with saved/restored exception state - `m2_exc_push`, `m2_exc_leave`, `m2_exc_retry`, `m2_exc_raise_lang`, `m2_exc_raise_user` - `m2_exc_init` - basic `SIGSEGV`/`SIGFPE` to language-exception mapping - `QbeGen`: - `ExcBegin`, `ExcBodyDone`, `ExcHandlerBegin`, `ExcEnd`, `ExcRetry` - handler pop on `RETURN` - `m2_exc_init` emitted at `main` entry - `SYSTEM.THROW` is bound to `m2_exc_throw`. - Tests: `t_except_parse`, `t_except_throw`, `t_except_retry`, `t_except_return`, `t_except_nested`, `t_except_module`, `t_except_signal`; `t_retry_bad` (237). ### M3 — libraries (`5f47b76`) - `runtime/syslib/SysExceptions.def/.mod` — C runtime binding. - `stdlib/exceptions.def/.mod` — ISO `EXCEPTIONS`. - `stdlib/m2exception.def/.mod` — ISO `M2EXCEPTION`. - `t_except_lib` covers: - `AllocateSource` / `RAISE` - `CurrentNumber` and wrong-source `exException` - `GetMessage`, including empty message in normal state - `IsCurrentSource`, `IsExceptionalExecution` - `IsM2Exception`, `M2Exception` - `M2Exception` after `SYSTEM.THROW` ### M4 — catchable language traps (`02b3763`) - `QbeGen.TrapCode`. - `CheckIndex` → `indexException`; `CheckRange` → `rangeException`. - `CASE` no match with no `ELSE` → `caseSelectException`. - Internal descriptor/copy mismatch → `sysException`. - Unhandled exceptions still abort with **rc 134**. - Tests: `t_except_trap`, `t_except_case`, `t_except_unhandled`; existing `t_trap_*` tests still pass. ### M5 — finalization - This summary, handoff, status and feature/tag updates. - Feature tag **`v3-exceptions`** at `02b3763` (M4). - Session tag **`v3-session-2026-10-23-exceptions-end`** at this docs commit. --- ## Commits | commit | tag | work | | --- | --- | --- | | `98da444` | — | M1 parser/AST for `EXCEPT`/`RETRY` | | `6e87def` | — | M2 runtime, `sigsetjmp` codegen, `SYSTEM.THROW` | | `5f47b76` | — | M3 `EXCEPTIONS` / `M2EXCEPTION` libraries | | `02b3763` | `v3-exceptions` | M4 catchable index/range/CASE traps | | `96a3080` | `v3-session-2026-10-23-exceptions-end` | M5 summary/docs | | `e804e57` | `v3-ptrchain` | self-referential record layout / pointer chains | | `4238f67` | `v3-function-exception` | result-function fall-through trap | | `c0c227c` | `v3-finally` | module finalization at normal termination | The design commit is `2a30d8c`. ### Follow-up — pointer-chain codegen The self-referential `TYPE List = POINTER TO RECORD next: List ... END` and `p^.next^.value` gaps were fixed by publishing an unresolved alias for a TYPE before parsing its body, then invalidating record/class layout when `SetTarget` completes the alias. Regression `t_ptrchain`; `lazyunique` now runs with rc 0. This follow-up is tagged `v3-ptrchain` (`e804e57`). ### Follow-up — functionException `QbeGen.EndFunc` now traps with language code 4 when a result function reaches its end without `RETURN`, instead of silently returning 0. `t_except_funcret` catches the exception and `t_trap_funcret` verifies the unhandled rc=134 path. Tagged `v3-function-exception` (`4238f67`). ### Follow-up — module FINALLY Module bodies now accept `FINALLY` (with its own optional `EXCEPT`) for program, implementation, local and class bodies. The finalizer is emitted as `_fini`; main calls the program finalizer first, then imported/local finalizers in reverse initialization order. The program body itself is emitted as `_init`, so a module `RETURN` leaves the init function and finalizers still run. Tests: `t_finally`, `t_finally_return`, `t_finally_except`, `t_finally_local`, `FinallyOrder` (reverse order `P!B!A!`). Tagged `v3-finally` (`c0c227c`). --- ## Remaining work - Module termination edge cases: `HALT`/unhandled aborts do not run finalizers; the ISO `TERMINATION` module is not implemented. - Coroutine (`coException`) and protection (`protException`) exceptions. - Complex-number exceptions (complex arithmetic is still a placeholder). - Multi-dimensional open arrays, separate compilation and the dialect library-folder layout (see `handoff-2026-10-23-next.md` and `plan-separate-compilation.md`).