refactor(mir): phase260 p0.1 strangler hardening + smoke fixtures

This commit is contained in:
2025-12-21 05:47:37 +09:00
parent 4dfe3349bf
commit 1fe5be347d
28 changed files with 442 additions and 504 deletions

View File

@ -0,0 +1,115 @@
# Catch / Cleanup / Async — Join-Explicit CFG extensions
Status: Draftdesign 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-argsblock 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 に落としてから CFGMIRにする箱。
## catch最小語彙例外 edge
### 目標
- 例外経路を “暗黙” にせず、CFG の edge として明示する。
- 例外値は catch block の paramsedge-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, ...])` に正規化する
### verifyFail-Fast
- `Invoke` は terminatorblock の最後)であること
- 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-argsblock 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 用に予約)
### verifyFail-Fast
- S の内部ブロックから “S の外” への edge が存在したら落とす(例外: cleanup_dispatch のみ)
- `Invoke.err` など “例外 edge” も漏れなく cleanup に寄せられていること
- `ExitTag` の分岐が未処理になっていないことUnknown は即死)
## async/awaitstate machine lowering
### 目標
- `await` を CFG 語彙に混ぜず、AsyncLowerBox が責務として消す(残ったら verify で即死)。
- cancel/drop が必要なら `ExitTag::Cancel` と cleanup を接続して後始末を一貫化する。
### 最小インターフェース(案)
- `await` は “前段IRAsyncPrep” にのみ存在してよい
- AsyncLowerBox で state machine 化した後、MIR は `Jump/Branch/Return/Invoke/Call` の語彙だけにする
### verifyFail-Fast
- AsyncLowerBox 後に `await` が 1 つでも残っていたら落とす
- state dispatch が全 state をカバーしていること(未到達 state は削除可)