# Catch / Cleanup / Async — Join-Explicit CFG extensions

Status: Draft（design SSOT candidate）  
Last updated: 2025-12-20

Related:
- North star: `docs/development/current/main/design/join-explicit-cfg-construction.md`
- Phase 260 roadmap: `docs/development/current/main/phases/phase-260/README.md`

## 目的

Nyash/Hakorune の表面文法（主に postfix `catch/cleanup`）に合わせて、例外/後始末/中断（async）を追加するときに JoinIR→MIR の暗黙ABI（推測/メタ/例外的分岐）を再増殖させないための設計メモ。

注: `try { ... }` は言語資料上は legacy/非推奨として扱われることがあるが、この設計は **`try` の存在を前提にしない**（catch/cleanup を正規化の入口にする）。

ポイントは 2 つだけ:

1. **制御フローは edge を明示し、値は edge-args（block params）で運ぶ**
2. **“意味SSOT” と “配線SSOT” を分離し、Fail-Fast の verify を常設する**

## 実装タイミング（推奨）

前提（Phase 260 で固める）:

- MIR で edge-args が terminator operand にあり、`BasicBlock.jump_args` に依存しない（併存→移行→削除の P2 到達が理想）
- “読む側” の参照点が `out_edges()`/`edge_args_to(target)` に一本化されている（Branch 含む）
- “書く側” の terminator 設定が API で一元化されている（successors キャッシュ同期漏れを構造で潰している）
- DCE/verify/printer が terminator operand を SSOT として扱う（メタ追いが不要）

順序（迷子が減る順）:

1. `catch/cleanup`（例外）: `Invoke(ok_edge, err_edge)` を追加（例外 edge を明示）
2. `cleanup/defer`（後始末）: “脱出 edge 正規化” を追加（Return/Throw/Break/Continue を cleanup に寄せる）
3. `async/await`: CFG 語彙に混ぜず **state-machine lowering**（AsyncLowerBox）で分離

## 用語（この文書の範囲）

- **edge-args**: branch/jump の edge に紐づく引数。ターゲット block の params と 1:1 で対応する。
- **Invoke**: 正常継続（ok）と例外継続（err）を持つ呼び出し terminator。
- **cleanup normalizer**: cleanup/defer を実現するために「スコープ外へ出る edge」を cleanup ブロックに集約する正規化箱。
- **async lowering**: `await` を state machine に落としてから CFG（MIR）にする箱。

## catch（最小語彙：例外 edge）

### 目標

- 例外経路を “暗黙” にせず、CFG の edge として明示する。
- 例外値は catch block の params（edge-args）で受ける。

### 最小追加語彙（案）

- `MirTerminator::Invoke { callee, args, ok: (bb_ok, ok_args), err: (bb_err, err_args) }`

設計ノート:

- ok/err 両方が必須（片側欠落を許さない）
- ok/err の args は “役割付きABI” で解釈する（将来 `JoinAbi`/`ContSigId` へ）

### throw の扱い（最小）

MIR で `Throw` を増やさずに済む形:

- 正規化（Normalizer）が “現在の例外継続” を知っており、`throw e` を `Jump(unwind_bb, [e, ...])` に正規化する

### verify（Fail-Fast）

- `Invoke` は terminator（block の最後）であること
- ok/err のターゲット block params と args の数が一致すること
- err 側の先頭 param は例外値（role=Exception）であること（最低限の役割固定）
- “may_throw な呼び出し” を `Call` で表していないこと（暫定: 当面は全部 Invoke に倒しても良い）

## cleanup（脱出 edge 正規化）

### 目標（finally の後継としての cleanup）

- return/break/continue/throw 等の “脱出” を cleanup 経由に統一して、後始末漏れを構造で潰す。
- 例外/return の payload は edge-args（block params）で運ぶ（PHI/メタに逃げない）。

### 最小形（案）

スコープ S ごとに次の 2 ブロック（または 1 ブロック + dispatch）を作る:

- `cleanup_entry_S(tag, payload..., carriers...)`
- `cleanup_dispatch_S(tag, payload..., carriers...)`

`ExitTag`（例）:

- `Return`
- `Throw`
- `Break`
- `Continue`
- `Cancel`（async の drop/cancel 用に予約）

### verify（Fail-Fast）

- S の内部ブロックから “S の外” への edge が存在したら落とす（例外: cleanup_dispatch のみ）
- `Invoke.err` など “例外 edge” も漏れなく cleanup に寄せられていること
- `ExitTag` の分岐が未処理になっていないこと（Unknown は即死）

## async/await（state machine lowering）

### 目標

- `await` を CFG 語彙に混ぜず、AsyncLowerBox が責務として消す（残ったら verify で即死）。
- cancel/drop が必要なら `ExitTag::Cancel` と cleanup を接続して後始末を一貫化する。

### 最小インターフェース（案）

- `await` は “前段IR（AsyncPrep）” にのみ存在してよい
- AsyncLowerBox で state machine 化した後、MIR は `Jump/Branch/Return/Invoke/Call` の語彙だけにする

### verify（Fail-Fast）

- AsyncLowerBox 後に `await` が 1 つでも残っていたら落とす
- state dispatch が全 state をカバーしていること（未到達 state は削除可）
