Files
hakorune/docs/development/current/main/phases/phase-256/README.md
tomoaki 575a5d750f refactor(joinir): Phase 255 P2 - loop_invariants + var() unification + Phase 256 prep
## Task A: Loop Invariants Architecture (Option A - Boundary Extension)

Introduced explicit loop_invariants concept for variables that are:
- Used in loop body but never modified (e.g., substring needle in index_of)
- Need header PHI (all iterations use same value)
- Do NOT need exit PHI (not a LoopState)

Implementation:
- Added `loop_invariants: Vec<(String, ValueId)>` field to JoinInlineBoundary
- Added `with_loop_invariants()` method to JoinInlineBoundaryBuilder
- Modified Pattern 6 to use loop_invariants instead of ConditionOnly misuse
  * s (haystack) and ch (needle) now properly classified
  * exit_bindings simplified to only LoopState carriers
- Extended LoopHeaderPhiBuilder to generate invariant PHIs
  * Entry PHI: from host
  * Latch PHI: self-reference (same value every iteration)
- Updated instruction_rewriter to handle invariant latch incoming

Files Modified:
- src/mir/join_ir/lowering/inline_boundary.rs (structure + builder)
- src/mir/join_ir/lowering/inline_boundary_builder.rs (builder method)
- src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs
- src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs
- src/mir/builder/control_flow/joinir/merge/mod.rs
- src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs

## Task B: Code Quality - var() Helper Unification

Created common module to eliminate var() duplication:
- New module: src/mir/builder/control_flow/joinir/patterns/common/
- Centralized var() helper in ast_helpers.rs
- Updated Pattern 6 and Pattern 2 to use common var()
- Test code (5 occurrences) deferred to Phase 256+ per 80/20 rule

Result: Eliminated 2 duplicate var() functions, 5 test occurrences remain

Files Created:
- src/mir/builder/control_flow/joinir/patterns/common/ast_helpers.rs
- src/mir/builder/control_flow/joinir/patterns/common/mod.rs

Files Modified:
- src/mir/builder/control_flow/joinir/patterns/mod.rs
- src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs
- src/mir/builder/control_flow/joinir/patterns/policies/balanced_depth_scan_policy.rs

## Task C: Documentation - PostLoopEarlyReturnPlan Usage Examples

Enhanced post_loop_early_return_plan.rs with:
- Architecture explanation (exit PHI usage prevents DCE)
- Pattern 2 example (Less: balanced_depth_scan)
- Pattern 6 example (NotEqual: index_of)
- Builder defer decision: when 4+ patterns emerge, add builder

Files Modified:
- src/mir/builder/control_flow/joinir/patterns/policies/post_loop_early_return_plan.rs

## Task D: Phase 256 Preparation

Analyzed next failure from smoke tests:
- `StringUtils.split/2` with variable-step loop
- Not constant step (i += len or similar)
- Three implementation options identified

Created Phase 256 README with:
- Minimal reproduction code
- Root cause analysis
- Implementation options with trade-offs
- Clear next steps

Files Created:
- docs/development/current/main/phases/phase-256/README.md

## Verification Results

 All tests passing:
- pattern254_p0_index_of_vm.sh: PASS
- No regression in Pattern 1-5
- Smoke test progresses past json_lint_vm to next failure

 Architecture achievements:
- Semantic clarity: loop_invariants vs exit_bindings properly separated
- ConditionOnly misuse eliminated
- PHI generation correct for all carrier types
- Code quality: var() duplication reduced
- Next phase clearly defined

## Summary

Phase 255 P2 achieves root-cause fix for multi-param loop support:
- Boundary concept expanded (loop_invariants field)
- Pattern 6 architecture corrected (no ConditionOnly misuse)
- Code quality improved (var() centralized)
- Next pattern (variable-step) is now the challenge

 Box-first principles maintained: clean separation, explicit roles, minimal coupling

🧠 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-19 23:48:49 +09:00

4.4 KiB
Raw Blame History

Phase 256: StringUtils.split/2 Pattern Support

Status: Planning Scope: Loop pattern recognition for split/tokenization operations Related:

  • Phase 255 完了loop_invariants 導入、Pattern 6 完成)
  • Phase 254 完了Pattern 6 index_of 実装)

失敗詳細

テスト: json_lint_vm (quick profile) エラー: [joinir/freeze] Loop lowering failed: JoinIR does not support this pattern 関数: StringUtils.split/2

エラーメッセージ全体

[trace:dev] loop_canonicalizer:   Decision: FAIL_FAST
[trace:dev] loop_canonicalizer:   Missing caps: [ConstStep]
[trace:dev] loop_canonicalizer:   Reason: Phase 143-P2: Loop does not match read_digits(loop(true)),
    skip_whitespace, parse_number, continue, parse_string, or parse_array pattern
[ERROR] ❌ MIR compilation error: [joinir/freeze] Loop lowering failed: JoinIR does not support this pattern,
    and LoopBuilder has been removed.
Function: StringUtils.split/2
Hint: This loop pattern is not supported. All loops must use JoinIR lowering.

期待される動作

StringUtils.split(s, separator) が正常にコンパイルされ、文字列分割が動作すること。

実際の動作

Loop canonicalizer が ConstStep を要求しているが、このループはステップが複雑で定数ではない。

最小再現コード

split(s, separator) {
    local result = new ArrayBox()

    // Early return for empty separator
    if separator.length() == 0 {
        result.push(s)
        return result
    }

    local start = 0
    local i = 0

    // Main scan loop
    loop(i <= s.length() - separator.length()) {
        if s.substring(i, i + separator.length()) == separator {
            result.push(s.substring(start, i))
            start = i + separator.length()
            i = start  // Variable step - moves by separator.length()
        } else {
            i = i + 1  // Constant step - moves by 1
        }
    }

    // Push remaining segment
    if start <= s.length() {
        result.push(s.substring(start, s.length()))
    }

    return result
}

分析

ループ構造

  1. 条件: i <= s.length() - separator.length()
  2. ボディ:
    • If branch: マッチング検出時
      • result.push() でセグメント追加
      • start = i + separator.length() で次の開始位置更新
      • i = start で大きくジャンプ(可変ステップ)
    • Else branch: マッチなし
      • i = i + 1 で 1 進む(定数ステップ)
  3. 特徴:
    • 可変ステップ: マッチング時は separator.length() 分ジャンプ
    • 複数キャリア: i, start, result を更新
    • MethodCall: substring(), push(), length() を使用

Canonicalizer の問題

Missing caps: [ConstStep]
  • 既存の Pattern 1-6 は定数ステップを想定
  • このループは条件分岐で異なるステップ幅を使う
  • Pattern 2 (balanced_depth_scan) に近いが、可変ステップがネック

実装計画

Option A: Pattern 7 - Split/Tokenization Pattern

新しいパターン追加:

  • 可変ステップサポート
  • 複数キャリアi, start, accumulator
  • If-else での異なるステップ幅処理

検出条件:

  1. Loop condition: i <= expr - len
  2. Body has if statement:
    • Then: i = something_big (可変ジャンプ)
    • Else: i = i + 1 (定数ステップ)
  3. Accumulator への追加操作 (push など)

Option B: Pattern 2 拡張

既存 Pattern 2 を拡張:

  • ConstStep 要件を緩和
  • If-else で異なるステップ幅を許可
  • balanced_depth_scan_policy を拡張

Option C: Normalization 経路

ループ正規化で対応:

  • 可変ステップを定数ステップに変換
  • Carrier 追加で状態管理

次のステップ

  1. StepTree 詳細解析: split ループの完全な AST 構造確認
  2. 類似パターン調査: 他の可変ステップループindexOf, contains など)
  3. Option 選択: Pattern 7 新設 vs Pattern 2 拡張 vs Normalization
  4. 実装戦略策定: 選択した Option の詳細設計

備考

  • Phase 255 で loop_invariants が導入されたが、このケースは invariants 以前の問題(可変ステップ)
  • Phase 254-256 の流れで Pattern 6 → Pattern 7 の自然な進化が期待される
  • split/tokenization は一般的なパターンなので、汎用的な解決策が望ましい