5.2 KiB
5.2 KiB
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 つだけ:
- 制御フローは edge を明示し、値は edge-args(block params)で運ぶ
- “意味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 として扱う(メタ追いが不要)
順序(迷子が減る順):
catch/cleanup(例外):Invoke(ok_edge, err_edge)を追加(例外 edge を明示)cleanup/defer(後始末): “脱出 edge 正規化” を追加(Return/Throw/Break/Continue を cleanup に寄せる)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(例):
ReturnThrowBreakContinueCancel(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 は削除可)