# Phase 269: Pattern8 への Frag 適用(test-only + 最小 fixture) Status: 🚧 進行中(P0) Date: 2025-12-21 ## 目的 **EdgeCFG Fragment を Pattern8 に適用し、NormalizedShadow への適用パターンを確立** - **P0**: Pattern8 の Frag 版 lowerer を test-only で作成(既存実装と並走) - **P1 以降**: 既存 Pattern8 を段階的に置換(将来フェーズ) ## 実装範囲(重要:スコープ境界) ### ✅ 触る(P0 スコープ) - `pattern8_scan_bool_predicate.rs` に Frag 版 lowerer を追加(test-only) - EdgeCFG Fragment API(compose::loop_(), emit_frag())を使用 - 最小 fixture(`phase269_p0_pattern8_frag_min.hako`)を作成 - 最小 smoke test(VM のみ)を作成 ### ❌ 触らない(P0 スコープ外) - 既存 Pattern8 本体(`cf_loop_pattern8_bool_predicate_impl()`)は維持 - Pattern6/7 は触らない - JoinIR merge 層は触らない - LLVM backend は P1 以降 ## 実装戦略(Phase 268 パターン踏襲) ### アーキテクチャ図 ``` pattern8_scan_bool_predicate.rs (Pattern8 層) ↓ 新規追加(test-only) lower_pattern8_frag() (Frag 版 lowerer) ↓ 内部で使用 Frag 構築 + compose::loop_() + emit_frag() (EdgeCFG Fragment API) ↓ 最終的に呼び出し set_branch_with_edge_args() / set_jump_with_edge_args() (Phase 260 SSOT) ``` ### P0 実装順序 1. **Phase 269 README 作成**(このファイル) - 設計境界と適用順を SSOT 化 - "どの層を触る/触らない" を明記 2. **Pattern8 Frag 版 lowerer 作成**(test-only) - `#[cfg(test)] pub(crate) fn lower_pattern8_frag()` として実装 - JoinModule から MIR terminator を生成 - unit test で MIR terminator 生成を確認 3. **最小 fixture + smoke test**(VM のみ) - `apps/tests/phase269_p0_pattern8_frag_min.hako` - `tools/smokes/v2/profiles/integration/apps/phase269_p0_pattern8_frag_vm.sh` - PASS を確認(既存 quick 45/46 を悪化させない) 4. **docs 更新** - `10-Now.md` に Phase 269 P0 追記 - `30-Backlog.md` に Phase 269 項目追加 ## Pattern8 の構造理解 ### 検出基準(Phase 259 P0 固定形式) 1. Loop condition: `i < s.length()` 2. Loop body has if statement: - Condition: `not this.method(...)` (UnaryOp::Not + MethodCall) - Then branch: `return false` (early exit) 3. Loop body has step: `i = i + 1` 4. Post-loop: `return true` ### 既存実装の処理フロー ```rust // 1. Extract pattern parts let parts = extract_bool_predicate_scan_parts(condition, body)?; // 2. Get host ValueIds let s_host = variable_map[haystack]; let i_host = variable_map[loop_var]; let me_host = build_me_expression()?; // 3. Create JoinModule let join_module = lower_scan_bool_predicate_minimal(...); // 4. Build boundary let boundary = JoinInlineBoundaryBuilder::new() .with_inputs(join_inputs, host_inputs) .with_loop_invariants(loop_invariants) .with_exit_bindings(exit_bindings) .with_carrier_info(carrier_info) .with_loop_var_name(Some(loop_var)) .with_expr_result(Some(join_exit_value)) .build(); // 5. Execute JoinIRConversionPipeline let result = JoinIRConversionPipeline::execute(self, join_module, Some(&boundary), ...)?; ``` ## P0 Frag 版 lowerer 設計 ### 目的 - JoinModule から MIR terminator を生成する部分を Frag 化 - 既存の JoinModule 生成・boundary 構築は維持 - emit_frag() による terminator 生成を導入 ### 実装方針 ```rust #[cfg(test)] pub(crate) fn lower_pattern8_frag( builder: &mut MirBuilder, join_module: JoinModule, boundary: &JoinInlineBoundary, debug: bool, ) -> Result, String> { // 1. JoinModule から必要な情報を取得 // - loop_step function (loop body) // - k_exit function (return true) // 2. Loop body Frag 構築 // - loop_step の処理を Frag で表現 // - early return (return false) は Return exit として扱う // 3. compose::loop_() で合成 // - loop_id, header, after, body_frag // 4. emit_frag() で MIR terminator に変換 // 5. result 返却(expr_result) Ok(boundary.expr_result) } ``` ### Unit Test 設計 ```rust #[cfg(test)] mod tests { use super::*; #[test] fn test_pattern8_frag_lowering() { // 1. Create minimal JoinModule // 2. Create boundary // 3. Call lower_pattern8_frag() // 4. Verify MIR terminator generation // - Branch terminator exists // - Jump terminator exists // - Return terminator exists (early exit) } } ``` ## 最小 fixture 設計 ### `phase269_p0_pattern8_frag_min.hako` Phase 259 の `is_integer_min.hako` の縮小版: ```nyash static box StringUtils { is_digit(ch) { return ch == "0" or ch == "1" } is_integer(s) { if s.length() == 0 { return false } local i = 0 loop(i < s.length()) { if not this.is_digit(s.substring(i, i + 1)) { return false } i = i + 1 } return true } } static box Main { main() { return StringUtils.is_integer("01") ? 7 : 1 } } ``` ### Smoke Test 設計 `tools/smokes/v2/profiles/integration/apps/phase269_p0_pattern8_frag_vm.sh`: ```bash #!/bin/bash set -e cd "$(dirname "$0")/../../../../../.." HAKORUNE_BIN="${HAKORUNE_BIN:-./target/release/hakorune}" set +e $HAKORUNE_BIN apps/tests/phase269_p0_pattern8_frag_min.hako > /tmp/phase269_out.txt 2>&1 EXIT_CODE=$? set -e if [ $EXIT_CODE -eq 7 ]; then echo "[PASS] phase269_p0_pattern8_frag_vm" exit 0 else echo "[FAIL] phase269_p0_pattern8_frag_vm: expected exit 7, got $EXIT_CODE" cat /tmp/phase269_out.txt exit 1 fi ``` ## 重要な設計判断 ### なぜ test-only か 1. **非破壊的**: 既存 Pattern8 実装を壊さない 2. **段階的**: Frag 版の動作を先に確認してから統合 3. **デバッグ容易**: 問題切り分けが簡単(Frag 版 vs 既存実装) 4. **拡張性**: Pattern6/7 にも同じパターンを適用可能 ### なぜ最小 fixture か 1. **高速検証**: 小さいコードで問題を早期発見 2. **デバッグ容易**: MIR dump が読みやすい 3. **回帰テスト**: quick smoke に含めやすい ### P0 での制約 - VM のみ(LLVM は P1 以降) - Pattern8 のみ(Pattern6/7 は P1 以降) - test-only(既存実装置換は P1 以降) ## 次フェーズへの橋渡し **Phase 269 P1+**: 既存 Pattern8 を Frag 版に置換 - `cf_loop_pattern8_bool_predicate_impl()` から `lower_pattern8_frag()` を呼び出す - JoinIRConversionPipeline を廃止(Frag 版に一本化) - Pattern6/7 にも適用 **Phase 270+**: Pattern 分岐削減 - Pattern 番号による分岐を削減 - compose API による統一的なループ処理 ## 関連ドキュメント - **Phase 268**: `docs/development/current/main/phases/phase-268/README.md` - **設計図**: `docs/development/current/main/design/edgecfg-fragments.md` - **JoinIR アーキテクチャ**: `docs/development/current/main/joinir-architecture-overview.md` - **現在のタスク**: `docs/development/current/main/10-Now.md` ## 受け入れ基準(P0) - ✅ `cargo build --release` 成功 - ✅ `cargo test --lib --release` で全テスト PASS - ✅ Pattern8 Frag 版 lowerer の unit test PASS - ✅ `apps/tests/phase269_p0_pattern8_frag_min.hako` 作成 - ✅ `tools/smokes/v2/profiles/integration/apps/phase269_p0_pattern8_frag_vm.sh` 作成 - ✅ smoke test PASS(exit code 7) - ✅ `tools/smokes/v2/run.sh --profile quick` で 45/46 PASS 維持 - ✅ MIR dump で Branch/Jump/Return terminator 正常生成確認 - ✅ ドキュメント更新完了: - ✅ `phases/phase-269/README.md` 新規作成(このファイル) - ✅ `10-Now.md` 追記 - ✅ `30-Backlog.md` 更新 ## まとめ **Phase 269 P0 の核心**: - ✅ Pattern8 を Frag 化(test-only、既存と並走) - ✅ compose::loop_() + emit_frag() を使用 - ✅ 最小 fixture + smoke test で動作確認 - ✅ quick smoke 45/46 を悪化させない **次のステップ**: P1 で既存 Pattern8 を Frag 版に置換 → Pattern6/7 にも適用