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>
Phase 257: Loop with Early Return Pattern
Status: Active Scope: Pattern6(ScanWithInit)拡張で 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 Pattern6(ScanWithInit)to 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
}
}
分析
ループ構造
- 条件:
i >= 0(backward scan) - ボディ:
- If branch: マッチング検出時
return iで早期リターン
- After if: マッチなし
i = i - 1で 1 つ戻る(定数ステップ)
- If branch: マッチング検出時
- 特徴:
- Early return: break ではなく return を使用
- Backward scan:
i--で逆方向スキャン - Capabilities:
caps=If,Loop,Return(no Break) - Exits:
exits=return(両方の return を検出)
Pattern6(index_of)との比較
| Feature | Pattern6/index_of | last_index_of |
|---|---|---|
| 走査方向 | forward(i = i + 1) |
reverse(i = i - 1) |
| 早期終了 | 見つかったら return / exit PHI | 見つかったら return |
| 通常終了 | not-found return(例: -1) |
not-found return(-1) |
| JoinIR | main/loop_step/k_exit |
同じ語彙で表現できる |
提案される実装アプローチ
Option A: Pattern6 拡張(推奨 / 最小差分)
Pattern6(ScanWithInit)を “scan direction” を持つ形に一般化する(forward / reverse)。
-
検出条件:
- init:
i = s.length() - 1(reverse)またはi = 0(forward) - loop cond:
i >= 0(reverse)またはi < bound(forward) - body:
if s.substring(i, i + 1) == ch { return i }+ step update - step:
i = i - 1(reverse const step)またはi = i + 1(forward const step)
- init:
-
JoinIR lowering:
- Pattern6 の語彙(
main/loop_step/k_exit)を維持 - scan direction に応じて:
- stop 判定(
i < 0ori >= bound) - const step(
+1/-1)
- stop 判定(
- return は既存の “exit PHI + post-loop guard” を必要最小で再利用する(DCE回避は既存Box優先)
- Pattern6 の語彙(
-
Boundary construction:
- Phase 256 系の契約(
JumpArgsLayout/ contract checks)に従い、推測しない expr_resultはreturn iの経路でのみ使用(not-found は-1)
- Phase 256 系の契約(
Option B: Pattern8_ReverseScanReturn 新設
Pattern6 を触らずに、reverse scan 専用パターンとして箱を追加する。 (影響範囲は狭いが、scan 系が分裂する)
Option C: Normalization(将来)
return/break を正規化して共通語彙へ落とし、JoinIR patterns を縮退させる。 (Phase 257 ではやらない)
実装計画
推奨方針
Option A(Pattern6 拡張)を推奨。
理由:
- 既に index_of/find 系で “scan + not-found return” を Pattern6 が担っている
- last_index_of はその自然な派生(reverse scan + const step -1)
- Pattern2(break 前提)を膨らませずに済む
P0 タスク
-
Fixture & integration smokes(完了)
apps/tests/phase257_p0_last_index_of_min.hakotools/smokes/v2/profiles/integration/apps/phase257_p0_last_index_of_vm.shtools/smokes/v2/profiles/integration/apps/phase257_p0_last_index_of_llvm_exe.sh
-
Pattern6 detector/extractor 拡張(reverse scan)
src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs- reverse scan 形を accept し、parts(init/cond/step/return/not-found)を抽出
-
Pattern6 lowerer 拡張
- reverse scan の stop 判定・step を JoinIR へ落とす(語彙は既存 Pattern6 を維持)
-
検証
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)
次のステップ
- Pattern6 の forward scan parts と差分を整理(init/cond/step/return)
- reverse scan の extractor を実装(Fail-Fast)
- JoinIR lowerer へ reverse scan を追加(既存 contract に従う)
- integration smokes + quick profile を回して SSOT 更新
最終更新: 2025-12-20