docs(edgecfg): Phase 280 - Frag Composition SSOT Positioning (A→B→C)
## Purpose Stop pattern number enumeration proliferation by establishing Frag composition API as the Single Source of Truth (SSOT) for structured control flow → CFG lowering. Pattern numbers (1-9+) are **symptom labels** for regression tests, NOT architectural concepts. The architectural SSOT is **Frag composition rules** (seq/if/loop/cleanup). ## Changes Summary **Phase A (Docs-only, no code)**: SSOT Positioning - edgecfg-fragments.md: Status Draft → Active SSOT (+243 lines) - Added 5 sections: Composition SSOT, Rules, Laws, Fail-Fast, Ownership - Documented 3-tier ownership model (Normalizer/Composition/Lowerer) - Established composition as pattern absorption destination - joinir-architecture-overview.md: Pattern absorption documentation (+90 lines) - Added Section 0.2: Pattern Number Absorption Destination - JoinIR vs Plan comparison (different extraction, same SSOT) - Pattern absorption status table (Pattern6/7 as Phase 280 targets) - phase-280/README.md: Full roadmap (new) **Phase B (API solidification)**: Contract Verification - compose.rs: Module-level + function-level Phase 280 docs (+149 lines) - Documented composition SSOT, ownership model, usage example - Added constraint/composition law sections to seq/if/loop/cleanup - Contract verification: All seq/if/loop contracts verified (no gaps) - Test gap analysis: No missing tests (wires/exits separation explicitly tested) **Phase C (Pattern preparation)**: Documentation-only - normalizer.rs: Pattern6/7 TODO comments (+10 lines) - Pattern6: Early exit doesn't fit compose::if_() → cleanup() target - Pattern7: 挙動不変保証難 → compose::if_() migration deferred to Phase 281 ## Impact - **Net +460 lines** (docs-heavy, minimal code) - **4 files modified**, 1 directory created - **SSOT established**: Frag composition is now THE absorption destination - **導線固定**: Clear migration path for Pattern6/7 (Phase 281+) - **No behavior change**: Documentation-only for Phase C (tests not run) ## Phase 280 Goal Achieved ✅ SSOT positioning + 導線固定 (NOT full migration - that's Phase 281) ✅ Phase A complete: Docs updated to "Active SSOT" ✅ Phase B complete: API contract verified and documented ✅ Phase C complete: Pattern6/7 hand-rolled locations documented ## Next Phase (Phase 281+) - Phase 281: Full Pattern6/7 absorption (replace hand-rolled with compose_*) - Phase 282: Router shrinkage (pattern numbers → test labels) - Phase 283+: Pattern8 and beyond 🤖 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,12 +1,67 @@
|
||||
/*!
|
||||
* Frag 合成関数群(Phase 264: シグネチャのみ + TODO実装)
|
||||
* # Frag Composition API - Single Source of Truth (Phase 280)
|
||||
*
|
||||
* 設計原則:
|
||||
* - 各関数は「入口の形」だけを固定
|
||||
* - 中身は TODO!() で次フェーズに委譲
|
||||
* - シグネチャ変更は破壊的変更として扱う
|
||||
* - pub(crate) にして外部から誤って触れないようにする
|
||||
* (実装フェーズ Phase 265+ で pub に昇格)
|
||||
* This module is the **Single Source of Truth** for Frag composition.
|
||||
*
|
||||
* ## Purpose (Phase 280)
|
||||
*
|
||||
* Pattern numbers (1-9+) are **symptom labels** for regression tests, NOT architectural concepts.
|
||||
* The architectural SSOT is **Frag composition rules** (`seq`/`if`/`loop`/`cleanup`).
|
||||
*
|
||||
* **Upstream (Extractor/Normalizer)**: Finish "shape recognition" and extract pattern-specific knowledge
|
||||
* **Downstream (Composition)**: Use Frag composition rules to build CFG converging to SSOT
|
||||
* **Terminator Generation**: `emit_frag()` as sole SSOT (Phase 267 P0)
|
||||
*
|
||||
* ## Entry Points (Composition Operations)
|
||||
*
|
||||
* - `seq(a, b)`: Sequential composition (Normal wiring)
|
||||
* - `if_(header, cond, t, e, join_frag)`: Conditional composition (Branch wiring)
|
||||
* - `loop_(loop_id, header, after, body)`: Loop composition (Break/Continue wiring)
|
||||
* - `cleanup(body, cleanup)`: Cleanup composition (TODO: Phase 280+)
|
||||
*
|
||||
* ## Composition Contract (Invariants)
|
||||
*
|
||||
* - **Input**: `Frag` (entry + exits + wires + branches)
|
||||
* - **Output**: `Frag` (new entry + merged exits + merged wires + merged branches)
|
||||
* - **Guarantee**: Composition preserves invariants (`verify_frag_invariants_strict`)
|
||||
* - **No Allocation**: Caller (Normalizer) allocates `BasicBlockId`/`ValueId`
|
||||
* - **Pure CFG Transform**: Composition rearranges `exits`/`wires`/`branches` only
|
||||
*
|
||||
* ## Ownership Model (3-tier)
|
||||
*
|
||||
* 1. **Normalizer** (Tier 1): Allocates blocks/values, pattern-specific knowledge
|
||||
* 2. **Composition** (Tier 2): Rearranges exits/wires/branches, pattern-agnostic
|
||||
* 3. **Lowerer** (Tier 3): Emits MIR terminators via `emit_frag()`
|
||||
*
|
||||
* ## Usage Example
|
||||
*
|
||||
* ```rust
|
||||
* // Tier 1: Normalizer allocates blocks
|
||||
* let header_bb = builder.next_block_id();
|
||||
* let body_bb = builder.next_block_id();
|
||||
* let after_bb = builder.next_block_id();
|
||||
*
|
||||
* // Build Frags for body
|
||||
* let body_frag = Frag { /* body CFG */ };
|
||||
*
|
||||
* // Tier 2: Composition wires exits (no allocation)
|
||||
* let loop_frag = compose::loop_(loop_id, header_bb, after_bb, body_frag);
|
||||
*
|
||||
* // Tier 3: Lowerer emits terminators
|
||||
* emit_frag(func, &loop_frag)?;
|
||||
* ```
|
||||
*
|
||||
* ## References
|
||||
*
|
||||
* - **SSOT Documentation**: `docs/development/current/main/design/edgecfg-fragments.md` (Active SSOT)
|
||||
* - **Pattern Absorption**: `docs/development/current/main/joinir-architecture-overview.md` (Section 0.2)
|
||||
* - **Phase 280 Roadmap**: `docs/development/current/main/phases/phase-280/README.md`
|
||||
*
|
||||
* ## History
|
||||
*
|
||||
* - Phase 264: Entry API creation (signatures only)
|
||||
* - Phase 265-268: Implementation (seq/if/loop wiring, emit_frag SSOT)
|
||||
* - Phase 280: SSOT positioning (composition as pattern absorption destination)
|
||||
*/
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
@ -20,6 +75,25 @@ use super::branch_stub::BranchStub; // Phase 267 P0: Branch 生成に必要
|
||||
|
||||
/// 順次合成: `a; b`
|
||||
///
|
||||
/// # Phase 280: Composition SSOT
|
||||
///
|
||||
/// ## Constraint (Caller Allocates)
|
||||
///
|
||||
/// - **Caller allocates**: `b.entry` (`BasicBlockId`)
|
||||
/// - **Composition wires**: `a.Normal` → `b.entry`
|
||||
///
|
||||
/// ## Composition Law (Input → Output)
|
||||
///
|
||||
/// - `a.Normal` exits → `wires` (target = `Some(b.entry)`)
|
||||
/// - Non-Normal exits (Return/Break/Continue/Unwind) → propagate upward (`exits`)
|
||||
/// - Result: `seq.entry = a.entry`, `seq.exits = a.non-Normal + b.all`
|
||||
///
|
||||
/// ## Invariants Preserved
|
||||
///
|
||||
/// - Wires/Exits separation: wires have `target = Some`, exits have `target = None`
|
||||
/// - Terminator uniqueness: 1 block = 1 terminator (from-grouping in emit_frag)
|
||||
/// - Entry consistency: `seq.entry` is valid `BasicBlockId`
|
||||
///
|
||||
/// # Phase 265 P2: wires/exits 分離実装完了
|
||||
/// - a.Normal → b.entry を wires に追加(内部配線)
|
||||
/// - seq の exits[Normal] は b の Normal のみ(外へ出る exit)
|
||||
@ -84,6 +158,27 @@ pub(crate) fn seq(a: Frag, b: Frag) -> Frag {
|
||||
|
||||
/// 条件分岐合成: `if (cond) { t } else { e }`
|
||||
///
|
||||
/// # Phase 280: Composition SSOT
|
||||
///
|
||||
/// ## Constraint (Caller Allocates)
|
||||
///
|
||||
/// - **Caller allocates**: `header`, `t.entry`, `e.entry`, `join_frag.entry` (`BasicBlockId`), `cond` (`ValueId`)
|
||||
/// - **Caller provides**: `then_entry_args`, `else_entry_args` (`EdgeArgs`) - Phase 268 P1 SSOT
|
||||
/// - **Composition wires**: `header` → `t.entry`/`e.entry` (BranchStub), `t/e.Normal` → `join_frag.entry`
|
||||
///
|
||||
/// ## Composition Law (Input → Output)
|
||||
///
|
||||
/// - `header` → `t.entry`/`e.entry` (`BranchStub` → `branches`)
|
||||
/// - `t/e.Normal` → `join_frag.entry` (`EdgeStub` → `wires`)
|
||||
/// - Non-Normal exits → propagate upward (`exits`)
|
||||
/// - Result: `if.entry = header`, `if.exits = t/e.non-Normal + join_frag.all`
|
||||
///
|
||||
/// ## Invariants Preserved
|
||||
///
|
||||
/// - Wires/Exits separation: BranchStub in `branches`, Normal wiring in `wires`, exits `target = None`
|
||||
/// - Terminator uniqueness: 1 block = 1 terminator (header gets Branch, t/e/join get Jump/Return)
|
||||
/// - Entry consistency: `if.entry` is valid `BasicBlockId`
|
||||
///
|
||||
/// # Phase 267 P0: Branch 生成実装完了
|
||||
/// - header → then/else の BranchStub を branches に追加
|
||||
/// - t/e.Normal → join_frag.entry を wires に追加(内部配線)
|
||||
@ -194,6 +289,26 @@ pub(crate) fn if_(
|
||||
|
||||
/// ループ合成: `loop (cond) { body }`
|
||||
///
|
||||
/// # Phase 280: Composition SSOT
|
||||
///
|
||||
/// ## Constraint (Caller Allocates)
|
||||
///
|
||||
/// - **Caller allocates**: `loop_id` (`LoopId`), `header`, `after` (`BasicBlockId`)
|
||||
/// - **Composition wires**: `Continue(loop_id)` → `header`, `Break(loop_id)` → `after`
|
||||
///
|
||||
/// ## Composition Law (Input → Output)
|
||||
///
|
||||
/// - `Continue(loop_id)` → `header` (`EdgeStub` → `wires`)
|
||||
/// - `Break(loop_id)` → `after` (`EdgeStub` → `wires`)
|
||||
/// - Normal/Return/Unwind → propagate upward (`exits`)
|
||||
/// - Result: `loop.entry = header`, `loop.exits = Normal/Return/Unwind only` (no Break/Continue)
|
||||
///
|
||||
/// ## Invariants Preserved
|
||||
///
|
||||
/// - Wires/Exits separation: Continue/Break have `target = Some`, other exits `target = None`
|
||||
/// - Terminator uniqueness: 1 block = 1 terminator (from-grouping in emit_frag)
|
||||
/// - Entry consistency: `loop.entry` is valid `BasicBlockId`
|
||||
///
|
||||
/// # Phase 265 P2: wires/exits 分離実装完了
|
||||
/// - Continue(loop_id) → header へ配線(wires へ)
|
||||
/// - Break(loop_id) → after へ配線(wires へ)
|
||||
@ -268,6 +383,20 @@ pub(crate) fn loop_(
|
||||
|
||||
/// cleanup 合成: finally の後継(すべての exit を正規化)
|
||||
///
|
||||
/// # Phase 280: Composition SSOT
|
||||
///
|
||||
/// ## Planned Composition Law (Future: Phase 280+)
|
||||
///
|
||||
/// - All exits (Normal/Break/Continue/Return/Unwind) → `cleanup` (`EdgeStub` → `wires`)
|
||||
/// - Cleanup re-dispatches original exit (`ExitTag` + payload via block params)
|
||||
/// - `Invoke.err` also routed through cleanup
|
||||
///
|
||||
/// ## Status
|
||||
///
|
||||
/// - **Signature fixed**: Phase 264
|
||||
/// - **Implementation**: TODO (Phase 280+)
|
||||
/// - **Usage**: Not yet used (planned for exception/async cleanup)
|
||||
///
|
||||
/// # 配線ルール(TODO実装)
|
||||
/// - body の全 exit を cleanup 経由へリライト
|
||||
/// - cleanup 後に元の exit を再発射(ExitTag + payload を block params で運ぶ)
|
||||
@ -276,15 +405,11 @@ pub(crate) fn loop_(
|
||||
/// # 引数
|
||||
/// - `body`: 本体の断片
|
||||
/// - `cleanup_block`: cleanup 処理を行うブロック
|
||||
///
|
||||
/// # Phase 264
|
||||
/// - シグネチャのみ固定、中身は TODO
|
||||
/// - pub(crate) で外部から触れないようにする
|
||||
pub(crate) fn cleanup(
|
||||
_body: Frag,
|
||||
_cleanup_block: BasicBlockId,
|
||||
) -> Frag {
|
||||
todo!("Phase 264: cleanup 合成は次フェーズで実装")
|
||||
todo!("Phase 280+: cleanup 合成は将来実装予定(exception/async cleanup 用)")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -295,6 +295,10 @@ impl PlanNormalizer {
|
||||
values: vec![i_current],
|
||||
};
|
||||
|
||||
// Phase 280 TODO: Hand-rolled Frag construction for early exit pattern
|
||||
// Reason: `found_bb` is early Return, doesn't fit compose::if_() model
|
||||
// Future: Consider compose::cleanup() for early exit normalization (Phase 281+)
|
||||
// Current structure: 2 BranchStub (header→body/after, body→found/step) + 2 EdgeStub (step→header, found→Return)
|
||||
let branches = vec![
|
||||
BranchStub {
|
||||
from: header_bb,
|
||||
@ -688,6 +692,12 @@ impl PlanNormalizer {
|
||||
];
|
||||
|
||||
// Step 12: Build Frag (2 branches + 3 wires)
|
||||
//
|
||||
// Phase 280 TODO: Hand-rolled Frag construction for split scan pattern
|
||||
// Target (Phase 281): compose::if_(body_bb, cond_match, then_frag, else_frag, step_frag)
|
||||
// Reason deferred: 挙動不変保証が難しい、Phase 280 は SSOT positioning 優先
|
||||
// Migration: Phase 281+ で compose::if_() への移行を検討
|
||||
// Current structure: 2 BranchStub (header→body/after, body→then/else) + 3 EdgeStub (then→step, else→step, step→header)
|
||||
let empty_args = EdgeArgs {
|
||||
layout: JumpArgsLayout::CarriersOnly,
|
||||
values: vec![],
|
||||
|
||||
Reference in New Issue
Block a user