Files
hakorune/docs/development/current/main/phases/phase-257
tomoaki edc7355937 refactor(joinir): unify boundary join_inputs SSOT (pattern4/6/7)
Apply Phase 256.8 SSOT fix to Pattern4/6/7:
- Use join_module.entry.params.clone() instead of hardcoded ValueIds
- Add fail-fast validation for params count mismatch
- Remove ValueId(0), ValueId(PARAM_MIN + k) patterns
- Clean up unused PARAM_MIN imports

This prevents entry_param_mismatch errors structurally and maintains
consistency with Pattern2/3.

Changes:
- pattern4_with_continue.rs: Lines 442-476 (SSOT extraction + validation)
- pattern6_scan_with_init.rs: Lines 447-471 (SSOT extraction + validation)
- pattern7_split_scan.rs: Lines 495-526 (SSOT extraction + validation)

All patterns now use the same SSOT principle:
1. Extract entry function (priority: join_module.entry → fallback "main")
2. Use params as SSOT: join_inputs = entry_func.params.clone()
3. Build host_inputs in expected order (pattern-specific)
4. Fail-fast validation: join_inputs.len() == host_inputs.len()

Verification:
- cargo build --release:  PASS (no PARAM_MIN warnings)
- Quick profile:  First FAIL still json_lint_vm (baseline maintained)
- Pattern6 smoke:  PASS (index_of test)
- Pattern7 smoke: Pre-existing phi pred mismatch (not introduced by SSOT)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-20 20:05:11 +09:00
..

Phase 257: Loop with Early Return Pattern

Status: Active Scope: Pattern6ScanWithInit拡張で reverse scan + early return を受理する Related:

  • Phase 256 完了Pattern2 boundary SSOT 化、entry_param_mismatch 根治)
  • North star: docs/development/current/main/design/join-explicit-cfg-construction.md

Current Status (SSOT)

  • Target first FAIL: json_lint_vm / StringUtils.last_index_of/2
  • Pattern: Loop with early return (backward scan)
  • Approach: Extend Pattern6ScanWithInitto support reverse scan + early return

Background

失敗詳細

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

エラーメッセージ全体

[phase143/debug] Attempting loop_true_if_break/continue pattern (P0/P1)
[phase143/debug] Pattern out-of-scope: NotLoopTrue
[trace:dev] phase121/shadow: shadow=skipped signature_basis=kinds=Block,Stmt(local(i)),Loop,Block,If,Block,Stmt(return(value)),Stmt(assign(i)),Stmt(return(value));exits=return;writes=i;reads=ch,i,s;caps=If,Loop,Return;conds=(var:i >= lit:int:0)|(other:MethodCall == var:ch)
[trace:dev] loop_canonicalizer: Function: StringUtils.last_index_of/2
[trace:dev] loop_canonicalizer:   Skeleton steps: 0
[trace:dev] loop_canonicalizer:   Carriers: 0
[trace:dev] loop_canonicalizer:   Has exits: false
[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.last_index_of/2
Hint: This loop pattern is not supported. All loops must use JoinIR lowering.

期待される動作

StringUtils.last_index_of(s, ch) が正常にコンパイルされ、文字列内の最後の文字位置を返すこと。

実際の動作

Loop canonicalizer が ConstStep を要求しているが、このループは早期リターンを持つため Pattern2Break として認識されていない。

最小再現コード

// apps/tests/phase257_p0_last_index_of_min.hako
static box Main {
    main() {
        local s = "hello world"
        local ch = "o"

        // Find last occurrence of ch in s
        local i = s.length() - 1

        loop(i >= 0) {
            if s.substring(i, i + 1) == ch {
                return i  // Early return when found
            }
            i = i - 1  // Backward scan
        }

        return -1  // Not found
    }
}

分析

ループ構造

  1. 条件: i >= 0 (backward scan)
  2. ボディ:
    • If branch: マッチング検出時
      • return i で早期リターン
    • After if: マッチなし
      • i = i - 1 で 1 つ戻る(定数ステップ)
  3. 特徴:
    • Early return: break ではなく return を使用
    • Backward scan: i-- で逆方向スキャン
    • Capabilities: caps=If,Loop,Return (no Break)
    • Exits: exits=return (両方の return を検出)

Pattern6index_ofとの比較

Feature Pattern6/index_of last_index_of
走査方向 forwardi = i + 1 reversei = i - 1
早期終了 見つかったら return / exit PHI 見つかったら return
通常終了 not-found return例: -1 not-found return-1
JoinIR main/loop_step/k_exit 同じ語彙で表現できる

提案される実装アプローチ

Option A: Pattern6 拡張(推奨 / 最小差分)

Pattern6ScanWithInitを “scan direction” を持つ形に一般化するforward / reverse

  1. 検出条件:

    • init: i = s.length() - 1reverseまたは i = 0forward
    • loop cond: i >= 0reverseまたは i < boundforward
    • body: if s.substring(i, i + 1) == ch { return i } + step update
    • step: i = i - 1reverse const stepまたは i = i + 1forward const step
  2. JoinIR lowering:

    • Pattern6 の語彙(main/loop_step/k_exit)を維持
    • scan direction に応じて:
      • stop 判定(i < 0 or i >= bound
      • const step+1 / -1
    • return は既存の “exit PHI + post-loop guard” を必要最小で再利用するDCE回避は既存Box優先
  3. Boundary construction:

    • Phase 256 系の契約(JumpArgsLayout / contract checksに従い、推測しない
    • expr_resultreturn i の経路でのみ使用not-found は -1

Option B: Pattern8_ReverseScanReturn 新設

Pattern6 を触らずに、reverse scan 専用パターンとして箱を追加する。 影響範囲は狭いが、scan 系が分裂する)

Option C: Normalization将来

return/break を正規化して共通語彙へ落とし、JoinIR patterns を縮退させる。 Phase 257 ではやらない)

実装計画

推奨方針

Option APattern6 拡張)を推奨。

理由:

  • 既に index_of/find 系で “scan + not-found return” を Pattern6 が担っている
  • last_index_of はその自然な派生reverse scan + const step -1
  • Pattern2break 前提)を膨らませずに済む

P0 タスク

  1. Fixture & integration smokes(完了)

    • apps/tests/phase257_p0_last_index_of_min.hako
    • tools/smokes/v2/profiles/integration/apps/phase257_p0_last_index_of_vm.sh
    • tools/smokes/v2/profiles/integration/apps/phase257_p0_last_index_of_llvm_exe.sh
  2. Pattern6 detector/extractor 拡張reverse scan

    • src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs
    • reverse scan 形を accept し、partsinit/cond/step/return/not-foundを抽出
  3. Pattern6 lowerer 拡張

    • reverse scan の stop 判定・step を JoinIR へ落とす(語彙は既存 Pattern6 を維持)
  4. 検証

    • bash tools/smokes/v2/profiles/integration/apps/phase257_p0_last_index_of_vm.sh
    • ./tools/smokes/v2/run.sh --profile quick(最初の FAIL が次へ進む)

注意P0ではやらない

  • 正規化での return/break 統一Phase 257 ではやらない)
  • scan 系の大改造(まずは reverse scan の受理を最小差分で固定)

備考

  • Phase 256 で Pattern2Break の境界構築が SSOT 化済みentry_param_mismatch 根治)
  • この知見を活かし、Pattern2Return も同じ原則で実装する
  • "Early exit" として break と return を統一的に扱うことで、将来の拡張性も高まる

進捗P0

次のステップ

  1. Pattern6 の forward scan parts と差分を整理init/cond/step/return
  2. reverse scan の extractor を実装Fail-Fast
  3. JoinIR lowerer へ reverse scan を追加(既存 contract に従う)
  4. integration smokes + quick profile を回して SSOT 更新

最終更新: 2025-12-20