refactor(edgecfg): add Frag/ExitKind API entrypoint (Phase 264 design-first)
Phase 264 P0: EdgeCFG Fragment 入口API作成(実装置換なし) - 入口フォルダ作成: src/mir/builder/control_flow/edgecfg/api/ - コア型定義: ExitKind, EdgeStub, Frag - 合成関数シグネチャ: seq, if_, loop_, cleanup(中身TODO、pub(crate)) - 最小テスト: 3個のユニットテスト追加(frag.rs) - ドキュメント連動: edgecfg-fragments.md に実装入口追記 制約遵守: - 既存 pattern6/7/8 未改変 - merge/EdgeCFG 未改変 - 既存LoopId使用(control_form.rs に PartialOrd, Ord 追加) - MIR側EdgeArgs使用(JoinIRと混線回避) - BTreeMap採用(決定的順序保証、Phase 69-3 教訓) 次フェーズ: Phase 265 で Pattern8 適用時に compose::loop_ を実装 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -1,24 +1,46 @@
|
||||
# Self Current Task — Now (main)
|
||||
|
||||
## 2025-12-21:Phase 263 P0(Pattern2 LoopBodyLocal fallback 修正)✅
|
||||
## 2025-12-21:Phase 263 P0.2(Pattern2 promotion API SSOT)✅
|
||||
|
||||
- **Goal**: Pattern2 で処理できない LoopBodyLocal を検出したら Pattern2 全体を早期終了(部分的な処理続行は禁止)
|
||||
- **修正内容**:
|
||||
- `promote_step_box.rs`: 戻り値を `Result<Option<_>, String>` に変更、Reject を二分化(対象外 → Ok(None)、対象だが未対応 → Err)
|
||||
- `pattern2_lowering_orchestrator.rs`: Ok(None) 検出で早期 return
|
||||
- Fail-Fast 原則: 対象外は Ok(None) で後続経路へ、対象だが未対応は Err で即座に失敗
|
||||
- **Goal**: Pattern2 の “Reject/continue/fallback の揺れ” を **型 + 入口SSOT**で封じ、部分続行(後段で落ちる)を構造で不可能にする
|
||||
- **実装**:
|
||||
- `PromoteDecision::{Promoted, NotApplicable, Freeze}`(Option 多重を撤去)
|
||||
- `pattern2/api/` を入口SSOTとして新設し、`try_promote(...)` を **単一参照点**に固定
|
||||
- **効果**:
|
||||
- `NotApplicable` は **必ず** `Ok(None)` で Pattern2 全体を抜ける(後続経路へ)
|
||||
- `Freeze` は **必ず** Fail-Fast(close-but-unsupported のみ即死)
|
||||
- **検証結果**:
|
||||
- cargo test --lib: **1368/1368 PASS** ✅ (1367→1368 に改善)
|
||||
- quick smoke: **45/46 PASS** ✅ (大幅改善!)
|
||||
- エラーメッセージ変化: `[cf_loop/pattern2] Variable not found: seg` → `[joinir/freeze] Loop lowering failed` (Pattern2 が正しく abort)
|
||||
- **Commit**: `93022e7e1` - fix(pattern2): abort entire Pattern2 on unpromoted LoopBodyLocal instead of partial execution
|
||||
- cargo test --lib: **1368/1368 PASS** ✅
|
||||
- quick smoke: **45/46 PASS** ✅(既知 1 件は別論点)
|
||||
- **Commits**:
|
||||
- `abdb860e7`(P0.1): PromoteDecision 導入(Option 揺れの撤去)
|
||||
- `e17902a44`(P0.2): `pattern2/api/` で入口SSOT物理固定
|
||||
- **詳細**: `docs/development/current/main/phases/phase-263/README.md`
|
||||
|
||||
## 2025-12-21:Phase 264 P0(EdgeCFG Fragment 入口作成)✅
|
||||
|
||||
**目的**: Frag/ExitKind を一次概念にする入口APIを用意(実装置換は次フェーズ)
|
||||
|
||||
**完了内容**:
|
||||
- 入口フォルダ作成: `src/mir/builder/control_flow/edgecfg/api/`
|
||||
- コア型定義: `ExitKind`, `EdgeStub`, `Frag`
|
||||
- 合成関数シグネチャ: `seq`, `if_`, `loop_`, `cleanup`(中身TODO)
|
||||
- 最小テスト: 3個のユニットテスト追加
|
||||
- ドキュメント連動: `edgecfg-fragments.md` に入口情報追記
|
||||
|
||||
**重要**: 既存実装(pattern6/7/8, merge/EdgeCFG)は未改変。
|
||||
入口だけ固定し、適用は quick 復旧後の次フェーズで実施。
|
||||
|
||||
**次のステップ**:
|
||||
- Phase 265: Pattern8 を Frag 合成に移行(最小適用)
|
||||
- Phase 266: Pattern6/7 への展開(再利用確認)
|
||||
|
||||
**詳細**: `docs/development/current/main/phases/phase-264/README.md` + `docs/development/current/main/design/edgecfg-fragments.md`
|
||||
|
||||
## Next (planned)
|
||||
|
||||
- Phase 259: `StringUtils.is_integer/1`(nested-if + loop)を JoinIR で受理して `--profile quick` を進める
|
||||
- Phase 260: block-parameterized CFG へ向けた "edge-args terminator 併存導入"(大工事 / Strangler)
|
||||
- Phase 259.x: Me receiver SSOT(`variable_map["me"]`)を API 化して `"this"`/`"me"` 混同を構造で潰す
|
||||
- Phase 265(planned): Pattern8 を Frag 合成に移行し、ExitKind+Frag の実装適用を開始(`compose::loop_` 実装)
|
||||
- Phase 266(planned): catch/cleanup / cleanup/defer / async を "exit-edge 正規化" で追加できる形へ(設計: `docs/development/current/main/design/exception-cleanup-async.md`)
|
||||
- Phase 141 P2+: Call/MethodCall 対応(effects + typing を分離して段階投入、ANF を前提に順序固定)
|
||||
- Phase 143-loopvocab P3+: 条件スコープ拡張(impure conditions 対応)
|
||||
- 詳細: `docs/development/current/main/30-Backlog.md`
|
||||
|
||||
@ -64,14 +64,25 @@ Related:
|
||||
- 最小再現 fixture + smoke で固定(先に失敗を SSOT 化)
|
||||
- Pattern2 が不成立のときは “部分続行” せず `Ok(None)` で fallback(既定挙動不変)
|
||||
|
||||
- **Phase 263+(planned / refactor): Pattern2 PromoteDecision API hardening**
|
||||
- ねらい: “Reject でも続行して後段で落ちる” を構造で不可能にする(迷子防止)
|
||||
- 形(最小):
|
||||
- `PromoteStepBox::try_promote(...) -> Result<PromoteDecision, String>`
|
||||
- `PromoteDecision::{Promoted, NotApplicable, Freeze}`
|
||||
- 受け入れ条件:
|
||||
- orchestrator が `NotApplicable` を受け取ったら Pattern2 を `Ok(None)` で抜けて fallback(SSOT)
|
||||
- “Reject=continue” のような曖昧挙動がコードから消える
|
||||
- **(DONE)Phase 263 P0.2: Pattern2 PromoteDecision API hardening**
|
||||
- 入口SSOT: `src/mir/builder/control_flow/joinir/patterns/pattern2/api/`
|
||||
- `PromoteDecision::{Promoted, NotApplicable, Freeze}` と `try_promote(...)` に参照点を収束(Option揺れを撤去)
|
||||
|
||||
- **Phase 264(✅ 入口作成完了): EdgeCFG Fragment 入口作成(design-first)**
|
||||
- **ステータス**: ✅ 入口作成完了(適用は次フェーズ)
|
||||
- **実装内容**:
|
||||
- `edgecfg/api/` フォルダに SSOT 入口作成
|
||||
- `ExitKind`, `EdgeStub`, `Frag` の型定義
|
||||
- `seq`, `if_`, `loop_`, `cleanup` のシグネチャ固定(pub(crate))
|
||||
- 最小ユニットテスト 3個
|
||||
- ドキュメント連動(edgecfg-fragments.md)
|
||||
- **制約遵守**:
|
||||
- 既存 pattern6/7/8 未改変
|
||||
- merge/EdgeCFG 未改変
|
||||
- cargo test -p nyash-rust --lib --no-run 成功確認
|
||||
- **次フェーズへの橋渡し**:
|
||||
- Phase 265 で Pattern8 適用時に `compose::loop_` を実装
|
||||
- 再利用確認後、pattern番号分岐を段階的に削減
|
||||
|
||||
- **real-app loop regression の横展開(VM + LLVM EXE)**
|
||||
- ねらい: 実コード由来ループを 1 本ずつ最小抽出して fixture/smoke で固定する(段階投入)。
|
||||
|
||||
142
docs/development/current/main/design/edgecfg-fragments.md
Normal file
142
docs/development/current/main/design/edgecfg-fragments.md
Normal file
@ -0,0 +1,142 @@
|
||||
# EdgeCFG Flow Fragments(Frag / ExitKind)— Structured→CFG lowering SSOT
|
||||
|
||||
Status: Draft(design SSOT candidate)
|
||||
Last updated: 2025-12-21
|
||||
|
||||
Related:
|
||||
- North star(CFG/ABI): `docs/development/current/main/design/join-explicit-cfg-construction.md`
|
||||
- Catch/Cleanup/Async: `docs/development/current/main/design/exception-cleanup-async.md`
|
||||
|
||||
## 目的(なぜ必要?)
|
||||
|
||||
EdgeCFG(block-parameterized CFG / edge-args SSOT)が固まると、次に残る “泥沼” はここだけになる:
|
||||
|
||||
- **構造化制御(if/loop + catch/cleanup)→ CFG** の lowering で起きる **exit 配線問題**
|
||||
- 「pattern番号で推測分岐」が増殖しやすい領域(長期的には消したい)
|
||||
|
||||
この文書は「pattern番号の列挙」を設計の中心にしないために、Structured→CFG の lowering を
|
||||
**合成代数(fragment composition)**として SSOT 化する。
|
||||
|
||||
結論(本書の北極星):
|
||||
|
||||
- “分岐の中心” は pattern番号ではなく **ExitKind** と **Frag(fragment)** に置く
|
||||
- 値の合流は EdgeCFG の **block params + edge-args** で表し、PHI/推測/メタに逃げない
|
||||
- pattern は「Extractor(形の認識)/ Plan(最小要件の抽出)」までに縮退し、merge/配線層へ逆流させない
|
||||
|
||||
## “フロー” は 2 層ある(混ぜると崩れる)
|
||||
|
||||
1. **CFG層(EdgeCFG / plumbing)**
|
||||
- terminator 語彙: `Jump/Branch/Return/Invoke`
|
||||
- edge-args: terminator operand が SSOT
|
||||
- out_edges の参照点が SSOT(複数 edge 前提)
|
||||
|
||||
2. **Structured→CFG lowering 層(flow composition)**
|
||||
- `if/loop/catch/cleanup/seq` を “Frag の合成” として書く
|
||||
- 難しさの本体は **exit(脱出)の種類** と **ネスト** と **合流**
|
||||
|
||||
## コア概念(最小の強い箱)
|
||||
|
||||
### ExitKind(脱出の種類を一次概念にする)
|
||||
|
||||
最低限の ExitKind:
|
||||
|
||||
- `Normal`(fallthrough)
|
||||
- `Break(loop_id)` / `Continue(loop_id)`
|
||||
- `Return`
|
||||
- `Unwind`(Invoke.err / catch へ)
|
||||
- `Cancel`(async の drop/cancel 用。今は予約)
|
||||
|
||||
### EdgeStub(未配線の脱出エッジ)
|
||||
|
||||
“どこへ飛ぶべきか未確定” な edge を表す。最終的に EdgeCFG の terminator edge に落ちる。
|
||||
|
||||
例(概念):
|
||||
|
||||
- `from: BlockId`
|
||||
- `kind: ExitKind`
|
||||
- `args: EdgeArgs`(ターゲット params に対応する値。target が未確定でも “役割” はここで決める)
|
||||
|
||||
### Frag(fragment)
|
||||
|
||||
```text
|
||||
Frag = { entry_block, exits: Map<ExitKind, Vec<EdgeStub>> }
|
||||
```
|
||||
|
||||
- `entry_block`: 断片の入口
|
||||
- `exits`: 断片から外へ出る未配線 edge の集合
|
||||
|
||||
## 合成則(pattern列挙を写像へ落とす)
|
||||
|
||||
### seq(a, b)
|
||||
|
||||
- `a.exits[Normal]` を `b.entry` へ接続する(edge-args を必要に応じて写像)
|
||||
- それ以外の exit は上位へ伝搬する
|
||||
|
||||
### if(cond, t, e)
|
||||
|
||||
- header に `Branch(cond, t.entry, e.entry)` を置く
|
||||
- `t.Normal` と `e.Normal` は join へ集める(必要なら join block params を作る)
|
||||
- `Break/Continue/Return/Unwind` は上位へ伝搬
|
||||
|
||||
### loop(body)
|
||||
|
||||
- header / latch / after を組み、`Continue` を header に戻す
|
||||
- `Break` を after へ出す
|
||||
- `Return/Unwind` は上位へ伝搬
|
||||
|
||||
### cleanup(body, cleanup_block)(finally の後継)
|
||||
|
||||
狙い: “脱出 edge 正規化”
|
||||
|
||||
- body の全 exit(Normal/Break/Continue/Return/Unwind/Cancel)を cleanup 経由へリライトする
|
||||
- cleanup 後に “元の exit” を再発射する(ExitTag + payload を block params で運ぶ)
|
||||
|
||||
重要: 例外 edge(Invoke.err)も漏れなく cleanup に寄せる。
|
||||
|
||||
## pattern は最終的に消える?(設計としての答え)
|
||||
|
||||
消える(実装の中心概念から降格する)。
|
||||
|
||||
- pattern番号は **回帰テスト名/症状ラベル**としては残して良い
|
||||
- 実装の中心は `Frag/ExitKind/join(block params)` の合成則になる
|
||||
- 各 pattern 実装は “Extractor(形の認識)→ Frag 合成呼び出し” の薄い層へ縮退する
|
||||
|
||||
## 実装の入口(SSOT API を先に作る)
|
||||
|
||||
目的: “どこを触ればいいか” を 1 箇所に固定し、推測・部分続行・場当たり分岐を減らす。
|
||||
|
||||
推奨の入口:
|
||||
|
||||
- `EdgeCFG` の plumbing API(既存): `BasicBlock::out_edges()` 等
|
||||
- Structured→CFG の入口 API(新規): `Frag` / `ExitKind` / `compose::{seq, if_, loop_ , cleanup}` 等
|
||||
|
||||
物理配置(案):
|
||||
|
||||
- `src/mir/builder/control_flow/edgecfg/api/`(または `.../joinir/api/` に併設してもよい)
|
||||
- `frag.rs` / `exit_kind.rs` / `compose.rs` / `patch.rs`
|
||||
|
||||
## verify(Fail-Fast の置き場所)
|
||||
|
||||
- **NormalizeBox 直後**: terminator 語彙固定・edge-args 長さ一致・cond付きJump禁止など “意味SSOT” を確定
|
||||
- **merge直前**: boundary/ABI/edge-args の矛盾を即死させ “配線SSOT” を確定
|
||||
- **--verify**: PHI predecessor / CFG cache 整合 / edge-args の長さ一致を常設
|
||||
|
||||
## 直近の導入ステップ(最小で始める)
|
||||
|
||||
1. `Frag/ExitKind/EdgeStub` の型を追加(docs+code 入口 SSOT)
|
||||
2. `seq/if/loop` の合成だけ実装(cleanup/Invoke は後段)
|
||||
3. 既存 pattern のうち 1 本だけ `Frag` 合成に寄せる(Pattern8 推奨)
|
||||
4. 2 本目で再利用が見えたら "pattern番号での枝刈り" を削って合成側へ寄せる
|
||||
|
||||
## 実装入口(コード SSOT)
|
||||
|
||||
**Phase 264 で入口API を作成完了**
|
||||
|
||||
- 物理配置: `src/mir/builder/control_flow/edgecfg/api/`
|
||||
- コア型: `ExitKind`, `EdgeStub`, `Frag`
|
||||
- 合成関数: `seq`, `if_`, `loop_`, `cleanup`(シグネチャのみ、中身TODO)
|
||||
- 検証: `verify_frag_invariants`(空実装)
|
||||
|
||||
次フェーズ(Phase 265+)で既存 pattern への適用を開始。
|
||||
現時点では既存 pattern6/7/8 や merge/EdgeCFG は未改変(入口だけ用意)。
|
||||
|
||||
102
src/mir/builder/control_flow/edgecfg/api/compose.rs
Normal file
102
src/mir/builder/control_flow/edgecfg/api/compose.rs
Normal file
@ -0,0 +1,102 @@
|
||||
/*!
|
||||
* Frag 合成関数群(Phase 264: シグネチャのみ + TODO実装)
|
||||
*
|
||||
* 設計原則:
|
||||
* - 各関数は「入口の形」だけを固定
|
||||
* - 中身は TODO!() で次フェーズに委譲
|
||||
* - シグネチャ変更は破壊的変更として扱う
|
||||
* - pub(crate) にして外部から誤って触れないようにする
|
||||
* (実装フェーズ Phase 265+ で pub に昇格)
|
||||
*/
|
||||
|
||||
use crate::mir::basic_block::BasicBlockId;
|
||||
use crate::mir::control_form::LoopId;
|
||||
use crate::mir::value_id::ValueId;
|
||||
use super::frag::Frag;
|
||||
|
||||
/// 順次合成: `a; b`
|
||||
///
|
||||
/// # 配線ルール(TODO実装)
|
||||
/// - `a.exits[Normal]` を `b.entry` へ接続
|
||||
/// - それ以外の exit は上位へ伝搬
|
||||
///
|
||||
/// # 引数
|
||||
/// - `a`: 前段の断片
|
||||
/// - `b`: 後段の断片
|
||||
///
|
||||
/// # Phase 264
|
||||
/// - シグネチャのみ固定、中身は TODO
|
||||
/// - pub(crate) で外部から触れないようにする
|
||||
pub(crate) fn seq(_a: Frag, _b: Frag) -> Frag {
|
||||
todo!("Phase 264: seq 合成は次フェーズで実装")
|
||||
}
|
||||
|
||||
/// 条件分岐合成: `if (cond) { t } else { e }`
|
||||
///
|
||||
/// # 配線ルール(TODO実装)
|
||||
/// - header に Branch(cond, t.entry, e.entry) を配置
|
||||
/// - `t.Normal` と `e.Normal` を join へ集約
|
||||
/// - Break/Continue/Return/Unwind は上位へ伝搬
|
||||
///
|
||||
/// # 引数
|
||||
/// - `header`: 条件判定を行うブロック
|
||||
/// - `cond`: 条件値
|
||||
/// - `t`: then 分岐の断片
|
||||
/// - `e`: else 分岐の断片
|
||||
///
|
||||
/// # Phase 264
|
||||
/// - シグネチャのみ固定、中身は TODO
|
||||
/// - pub(crate) で外部から触れないようにする
|
||||
pub(crate) fn if_(
|
||||
_header: BasicBlockId,
|
||||
_cond: ValueId,
|
||||
_t: Frag,
|
||||
_e: Frag,
|
||||
) -> Frag {
|
||||
todo!("Phase 264: if_ 合成は次フェーズで実装")
|
||||
}
|
||||
|
||||
/// ループ合成: `loop (cond) { body }`
|
||||
///
|
||||
/// # 配線ルール(TODO実装)
|
||||
/// - header / latch / after を組む
|
||||
/// - Continue → header へ戻す
|
||||
/// - Break → after へ出す
|
||||
/// - Return/Unwind は上位へ伝搬
|
||||
///
|
||||
/// # 引数
|
||||
/// - `loop_id`: ループ識別子
|
||||
/// - `header`: ループヘッダー
|
||||
/// - `body`: ループ本体の断片
|
||||
///
|
||||
/// # Phase 264
|
||||
/// - シグネチャのみ固定、中身は TODO
|
||||
/// - pub(crate) で外部から触れないようにする
|
||||
pub(crate) fn loop_(
|
||||
_loop_id: LoopId,
|
||||
_header: BasicBlockId,
|
||||
_body: Frag,
|
||||
) -> Frag {
|
||||
todo!("Phase 264: loop_ 合成は次フェーズで実装")
|
||||
}
|
||||
|
||||
/// cleanup 合成: finally の後継(すべての exit を正規化)
|
||||
///
|
||||
/// # 配線ルール(TODO実装)
|
||||
/// - body の全 exit を cleanup 経由へリライト
|
||||
/// - cleanup 後に元の exit を再発射(ExitTag + payload を block params で運ぶ)
|
||||
/// - Invoke.err も cleanup に寄せる
|
||||
///
|
||||
/// # 引数
|
||||
/// - `body`: 本体の断片
|
||||
/// - `cleanup_block`: cleanup 処理を行うブロック
|
||||
///
|
||||
/// # Phase 264
|
||||
/// - シグネチャのみ固定、中身は TODO
|
||||
/// - pub(crate) で外部から触れないようにする
|
||||
pub(crate) fn cleanup(
|
||||
_body: Frag,
|
||||
_cleanup_block: BasicBlockId,
|
||||
) -> Frag {
|
||||
todo!("Phase 264: cleanup 合成は次フェーズで実装")
|
||||
}
|
||||
58
src/mir/builder/control_flow/edgecfg/api/edge_stub.rs
Normal file
58
src/mir/builder/control_flow/edgecfg/api/edge_stub.rs
Normal file
@ -0,0 +1,58 @@
|
||||
/*!
|
||||
* EdgeStub - 未配線の脱出エッジ(Phase 264: EdgeCFG Fragment)
|
||||
*
|
||||
* Frag 合成時に「どこへ飛ぶべきか未確定」な edge を表現。
|
||||
* 最終的に EdgeCFG の terminator edge に解決される。
|
||||
*/
|
||||
|
||||
use crate::mir::basic_block::{BasicBlockId, EdgeArgs};
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
use super::exit_kind::ExitKind;
|
||||
|
||||
/// 未配線の脱出エッジ
|
||||
///
|
||||
/// # 責務
|
||||
/// - `from`: 脱出元のブロックID
|
||||
/// - `kind`: 脱出の種別(配線先の決定に使う)
|
||||
/// - `args`: edge-args(target が未確定でも「役割」は確定)
|
||||
///
|
||||
/// # 既存型の使用
|
||||
/// - `BasicBlockId`: `crate::mir::basic_block::BasicBlockId` を使用
|
||||
/// - 定義場所: `src/mir/basic_block.rs:16`
|
||||
/// - `EdgeArgs`: `crate::mir::basic_block::EdgeArgs` を使用(**MIR側のEdgeArgs**)
|
||||
/// - 定義場所: `src/mir/basic_block.rs:46-51`(Phase 260 P0)
|
||||
/// - EdgeCFG の terminator operand で使ってる型と同じ(混線回避)
|
||||
/// - 構成: `{ layout: JumpArgsLayout, values: Vec<ValueId> }`
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EdgeStub {
|
||||
/// 脱出元ブロック
|
||||
pub from: BasicBlockId,
|
||||
|
||||
/// 脱出種別(配線ルール決定に使用)
|
||||
pub kind: ExitKind,
|
||||
|
||||
/// エッジ引数(Phase 260 EdgeArgs SSOT)
|
||||
///
|
||||
/// target が未確定でも、このエッジが運ぶ値の「役割」は
|
||||
/// ここで確定する(合成則で args を写像する設計)
|
||||
pub args: EdgeArgs,
|
||||
}
|
||||
|
||||
impl EdgeStub {
|
||||
/// EdgeStub を生成
|
||||
pub fn new(from: BasicBlockId, kind: ExitKind, args: EdgeArgs) -> Self {
|
||||
Self { from, kind, args }
|
||||
}
|
||||
|
||||
/// EdgeArgs を持たない EdgeStub を生成(利便性)
|
||||
pub fn without_args(from: BasicBlockId, kind: ExitKind) -> Self {
|
||||
Self {
|
||||
from,
|
||||
kind,
|
||||
args: EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
66
src/mir/builder/control_flow/edgecfg/api/exit_kind.rs
Normal file
66
src/mir/builder/control_flow/edgecfg/api/exit_kind.rs
Normal file
@ -0,0 +1,66 @@
|
||||
/*!
|
||||
* ExitKind - 制御フローの脱出種別(Phase 264: EdgeCFG Fragment入口)
|
||||
*
|
||||
* 構造化制御(if/loop/catch/cleanup)から CFG への lowering において、
|
||||
* 脱出エッジの配線先を決定するための一次概念。
|
||||
*/
|
||||
|
||||
use crate::mir::control_form::LoopId;
|
||||
|
||||
/// 制御フローの脱出種別
|
||||
///
|
||||
/// # 設計原則
|
||||
/// - ExitKind は pattern番号より優先される一次概念
|
||||
/// - 各 Frag は ExitKind ごとに未配線の EdgeStub を保持
|
||||
/// - 合成則(seq/if_/loop_/cleanup)で配線ルールを統一
|
||||
///
|
||||
/// # 既存型の使用
|
||||
/// - `LoopId`: `crate::mir::control_form::LoopId` を使用(既存の安定な型)
|
||||
/// - 定義場所: `src/mir/control_form.rs:23`
|
||||
/// - 後で差し替える必要なし(既に control_form で SSOT 化済み)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum ExitKind {
|
||||
/// 通常のフォールスルー(次の文・ループ継続など)
|
||||
Normal,
|
||||
|
||||
/// 関数からのreturn
|
||||
Return,
|
||||
|
||||
/// 指定ループからの脱出
|
||||
Break(LoopId),
|
||||
|
||||
/// 指定ループの次回イテレーション
|
||||
Continue(LoopId),
|
||||
|
||||
/// 例外のunwind(Invoke.err → catch)
|
||||
Unwind,
|
||||
|
||||
/// 非同期タスクのキャンセル(予約:将来の async/drop 用)
|
||||
#[allow(dead_code)]
|
||||
Cancel,
|
||||
}
|
||||
|
||||
impl ExitKind {
|
||||
/// ループ関連の脱出か判定
|
||||
pub fn is_loop_exit(&self) -> bool {
|
||||
matches!(self, ExitKind::Break(_) | ExitKind::Continue(_))
|
||||
}
|
||||
|
||||
/// 関数全体からの脱出か判定
|
||||
pub fn is_function_exit(&self) -> bool {
|
||||
matches!(self, ExitKind::Return | ExitKind::Unwind)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ExitKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ExitKind::Normal => write!(f, "Normal"),
|
||||
ExitKind::Return => write!(f, "Return"),
|
||||
ExitKind::Break(id) => write!(f, "Break({})", id.0),
|
||||
ExitKind::Continue(id) => write!(f, "Continue({})", id.0),
|
||||
ExitKind::Unwind => write!(f, "Unwind"),
|
||||
ExitKind::Cancel => write!(f, "Cancel"),
|
||||
}
|
||||
}
|
||||
}
|
||||
129
src/mir/builder/control_flow/edgecfg/api/frag.rs
Normal file
129
src/mir/builder/control_flow/edgecfg/api/frag.rs
Normal file
@ -0,0 +1,129 @@
|
||||
/*!
|
||||
* Frag - CFG Fragment(Phase 264: 構造化制御の合成単位)
|
||||
*
|
||||
* 構造化制御(if/loop/catch/cleanup)から CFG への lowering において、
|
||||
* 未配線の脱出エッジを持つ CFG 断片を表現する。
|
||||
*/
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use crate::mir::basic_block::BasicBlockId;
|
||||
use super::exit_kind::ExitKind;
|
||||
use super::edge_stub::EdgeStub;
|
||||
|
||||
/// CFG Fragment(構造化制御の合成単位)
|
||||
///
|
||||
/// # 責務
|
||||
/// - `entry`: 断片の入口ブロック
|
||||
/// - `exits`: 断片から外へ出る未配線 edge の集合(ExitKind → EdgeStub*)
|
||||
///
|
||||
/// # 設計原則
|
||||
/// - 各 Frag は「入口1つ、出口複数(種別ごと)」を持つ
|
||||
/// - 合成則(seq/if_/loop_/cleanup)で複数 Frag を組み合わせる
|
||||
/// - pattern番号は「形の認識」までに留め、配線層へ逆流させない
|
||||
///
|
||||
/// # 不変条件(verify で検証)
|
||||
/// - entry は有効な BasicBlockId
|
||||
/// - 同一 ExitKind に対する EdgeStub は from が一意(重複禁止)
|
||||
/// - EdgeStub.kind と Map のキーが一致
|
||||
///
|
||||
/// # BTreeMap の使用理由
|
||||
/// - Phase 69-3 の教訓: HashMap は非決定的イテレーションを起こす
|
||||
/// - ExitKind の順序を決定的にすることで、デバッグ出力・テストが安定
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Frag {
|
||||
/// 断片の入口ブロック
|
||||
pub entry: BasicBlockId,
|
||||
|
||||
/// 断片からの未配線脱出エッジ(ExitKind → EdgeStub のリスト)
|
||||
///
|
||||
/// BTreeMap を使用して決定的順序を保証(Phase 69-3 の教訓)
|
||||
pub exits: BTreeMap<ExitKind, Vec<EdgeStub>>,
|
||||
}
|
||||
|
||||
impl Frag {
|
||||
/// 新規 Frag を生成(出口なし)
|
||||
pub fn new(entry: BasicBlockId) -> Self {
|
||||
Self {
|
||||
entry,
|
||||
exits: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 単一出口を持つ Frag を生成
|
||||
pub fn with_single_exit(entry: BasicBlockId, stub: EdgeStub) -> Self {
|
||||
let mut exits = BTreeMap::new();
|
||||
exits.insert(stub.kind, vec![stub]);
|
||||
Self { entry, exits }
|
||||
}
|
||||
|
||||
/// 特定 ExitKind の未配線 edge を追加
|
||||
pub fn add_exit(&mut self, stub: EdgeStub) {
|
||||
self.exits.entry(stub.kind).or_insert_with(Vec::new).push(stub);
|
||||
}
|
||||
|
||||
/// 特定 ExitKind の未配線 edge を取得
|
||||
pub fn get_exits(&self, kind: &ExitKind) -> Option<&Vec<EdgeStub>> {
|
||||
self.exits.get(kind)
|
||||
}
|
||||
|
||||
/// すべての ExitKind を列挙
|
||||
pub fn exit_kinds(&self) -> impl Iterator<Item = &ExitKind> {
|
||||
self.exits.keys()
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// ユニットテスト(Phase 264: 最小3個)
|
||||
// ============================================================================
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mir::basic_block::EdgeArgs;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
||||
|
||||
#[test]
|
||||
fn test_frag_basic_construction() {
|
||||
// 最小のテスト: Frag が ExitKind::Normal を保持できる
|
||||
let entry = BasicBlockId::new(0);
|
||||
let from = BasicBlockId::new(1);
|
||||
|
||||
let stub = EdgeStub::without_args(from, ExitKind::Normal);
|
||||
let frag = Frag::with_single_exit(entry, stub.clone());
|
||||
|
||||
assert_eq!(frag.entry, entry);
|
||||
|
||||
let exits = frag.get_exits(&ExitKind::Normal).unwrap();
|
||||
assert_eq!(exits.len(), 1);
|
||||
assert_eq!(exits[0].from, from);
|
||||
assert_eq!(exits[0].kind, ExitKind::Normal);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_frag_multiple_exits() {
|
||||
// 複数 ExitKind のテスト
|
||||
let entry = BasicBlockId::new(0);
|
||||
let mut frag = Frag::new(entry);
|
||||
|
||||
frag.add_exit(EdgeStub::without_args(BasicBlockId::new(1), ExitKind::Normal));
|
||||
frag.add_exit(EdgeStub::without_args(BasicBlockId::new(2), ExitKind::Return));
|
||||
|
||||
assert_eq!(frag.exits.len(), 2);
|
||||
assert!(frag.get_exits(&ExitKind::Normal).is_some());
|
||||
assert!(frag.get_exits(&ExitKind::Return).is_some());
|
||||
assert!(frag.get_exits(&ExitKind::Unwind).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exit_kind_helpers() {
|
||||
use crate::mir::control_form::LoopId;
|
||||
|
||||
assert!(!ExitKind::Normal.is_loop_exit());
|
||||
assert!(ExitKind::Break(LoopId(0)).is_loop_exit());
|
||||
assert!(ExitKind::Continue(LoopId(0)).is_loop_exit());
|
||||
|
||||
assert!(ExitKind::Return.is_function_exit());
|
||||
assert!(ExitKind::Unwind.is_function_exit());
|
||||
assert!(!ExitKind::Normal.is_function_exit());
|
||||
}
|
||||
}
|
||||
33
src/mir/builder/control_flow/edgecfg/api/mod.rs
Normal file
33
src/mir/builder/control_flow/edgecfg/api/mod.rs
Normal file
@ -0,0 +1,33 @@
|
||||
//! EdgeCFG Fragment API(Phase 264: 入口SSOT)
|
||||
//!
|
||||
//! # コア概念
|
||||
//! - [`ExitKind`]: 脱出種別(一次概念)
|
||||
//! - [`EdgeStub`]: 未配線エッジ
|
||||
//! - [`Frag`]: CFG断片
|
||||
//! - [`compose`]: 合成関数群(Phase 264: TODO実装、pub(crate))
|
||||
//!
|
||||
//! # 設計原則
|
||||
//! - ExitKind を一次概念にし、pattern番号は「形の認識」までに縮退
|
||||
//! - 値の合流は EdgeCFG の block params + edge-args で表す(PHI/推測/メタに逃げない)
|
||||
//! - Fail-Fast: verify で不変条件を早期検証
|
||||
//!
|
||||
//! # 関連文書
|
||||
//! - 北極星設計: `docs/development/current/main/design/edgecfg-fragments.md`
|
||||
//! - EdgeCFG 基盤: `docs/development/current/main/design/join-explicit-cfg-construction.md`
|
||||
|
||||
pub mod exit_kind;
|
||||
pub mod edge_stub;
|
||||
pub mod frag;
|
||||
pub mod compose;
|
||||
pub mod verify;
|
||||
|
||||
// 公開型(安定)
|
||||
pub use exit_kind::ExitKind;
|
||||
pub use edge_stub::EdgeStub;
|
||||
pub use frag::Frag;
|
||||
|
||||
// 合成関数(Phase 264: crate内のみ公開、Phase 265+でpub化)
|
||||
pub(crate) use compose::{seq, if_, loop_, cleanup};
|
||||
|
||||
// 検証関数
|
||||
pub use verify::verify_frag_invariants;
|
||||
28
src/mir/builder/control_flow/edgecfg/api/verify.rs
Normal file
28
src/mir/builder/control_flow/edgecfg/api/verify.rs
Normal file
@ -0,0 +1,28 @@
|
||||
/*!
|
||||
* Frag 検証関数(Phase 264: Fail-Fast の置き場所)
|
||||
*
|
||||
* Phase 264 では空実装。
|
||||
* 実装適用時(Phase 265+)に検証項目を段階的に追加。
|
||||
*/
|
||||
|
||||
use super::frag::Frag;
|
||||
|
||||
/// Frag の不変条件を検証(Phase 264: 空実装)
|
||||
///
|
||||
/// # 検証項目(TODO)
|
||||
/// - entry は有効な BasicBlockId
|
||||
/// - 同一 ExitKind に対する EdgeStub.from が一意
|
||||
/// - EdgeStub.kind と Map のキーが一致
|
||||
/// - EdgeArgs の layout と values の長さが一致
|
||||
///
|
||||
/// # 戻り値
|
||||
/// - Ok(()): 検証成功
|
||||
/// - Err(String): 検証失敗(エラーメッセージ)
|
||||
///
|
||||
/// # Phase 264
|
||||
/// - 空実装(次フェーズで Fail-Fast 検証を追加)
|
||||
/// - 不変条件は edgecfg-fragments.md に文書化済み
|
||||
pub fn verify_frag_invariants(_frag: &Frag) -> Result<(), String> {
|
||||
// Phase 264: 空実装(次フェーズで検証項目追加)
|
||||
Ok(())
|
||||
}
|
||||
12
src/mir/builder/control_flow/edgecfg/mod.rs
Normal file
12
src/mir/builder/control_flow/edgecfg/mod.rs
Normal file
@ -0,0 +1,12 @@
|
||||
//! EdgeCFG 関連モジュール(Phase 264)
|
||||
//!
|
||||
//! # 構成
|
||||
//! - [`api`]: Fragment API 入口(SSOT)
|
||||
|
||||
pub mod api;
|
||||
|
||||
// 公開型(安定)
|
||||
pub use api::{ExitKind, EdgeStub, Frag, verify_frag_invariants};
|
||||
|
||||
// 合成関数(Phase 264: crate内のみ公開)
|
||||
pub(crate) use api::{seq, if_, loop_, cleanup};
|
||||
@ -57,6 +57,9 @@ pub(in crate::mir::builder) mod utils;
|
||||
// Phase 134 P0: Normalization entry point consolidation
|
||||
pub(in crate::mir::builder) mod normalization;
|
||||
|
||||
// Phase 264: EdgeCFG Fragment API (入口SSOT)
|
||||
pub(in crate::mir::builder) mod edgecfg;
|
||||
|
||||
// Phase 140-P4-A: Re-export for loop_canonicalizer SSOT (crate-wide visibility)
|
||||
pub(crate) use joinir::detect_skip_whitespace_pattern;
|
||||
|
||||
|
||||
@ -19,7 +19,9 @@ use std::collections::BTreeSet;
|
||||
// ============================================================================
|
||||
|
||||
/// ループを一意に識別する ID
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
///
|
||||
/// Phase 264: PartialOrd, Ord を追加(ExitKind の BTreeMap キーとして使用)
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
|
||||
pub struct LoopId(pub u32);
|
||||
|
||||
/// 出口辺を一意に識別する ID
|
||||
|
||||
Reference in New Issue
Block a user