Files
hakorune/docs/development/current/main/design/exception-cleanup-async.md

5.2 KiB
Raw Blame History

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 loweringAsyncLowerBoxで分離

用語(この文書の範囲)

  • 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 eJump(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
  • Cancelasync の 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 は削除可)