Phase 3-1 完了: extract_inner_loop_ast() 実装 - 外側 loop body から内側 loop を抽出 - 正確に 1 つの inner loop を検証 - 0 個 or 2+ 個の場合は明示エラー (Fail-Fast) Phase 3-2 完了: validate_strict_mode() プレースホルダー - 現在は大半の検証を is_pattern6_lowerable() で実施 - 将来の strict mode チェック用の足場 ドキュメント追加: - P1-INSTRUCTIONS.md: Phase 3-3 実装指示書 (4関数モデル詳細) - README.md: max_loop_depth 定義の明確化 - 10-Now.md: Phase 188.3 方針の整合性維持 次タスク: Phase 3-3 (Continuation 生成) - P1-INSTRUCTIONS.md 参照 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
5.1 KiB
Phase 188.3 P1: Pattern6(NestedLoopMinimal)lowering を “実装済み” にする
Date: 2025-12-27
Scope: Pattern6 の lowering 実装(fixture を PASS)
Non-goals: 汎用 nested loop / break/continue / 多段ネスト / 多重 inner loop
✅ Success Criteria
apps/tests/phase1883_nested_minimal.hakoが--backend vmで exit=9./tools/smokes/v2/run.sh --profile quickが 154/154 PASS- integration selfhost が FAIL=0 維持
- Pattern6 を選んだ後に silent fallback しない(Fail-Fast)
SSOT / Constraints
- ネスト深さ SSOT:
StepTreeFeatures.max_loop_depth - Pattern6 選択 SSOT:
src/mir/builder/control_flow/joinir/routing.rs::choose_pattern_kind() - Phase 188.2: strict では depth > 2 を明示エラー
- 本タスクで触るのは “lowering stub を実装” のみ
Fixture(目標)
apps/tests/phase1883_nested_minimal.hako を SSOT とする(Add/Compare のみ)。
実装方針(重要)
1) sum は carrier として渡す(グローバル禁止)
inner loop が outer の sum を更新しているので、JoinIR では sum を引数で運び、k_inner_exit で outer に戻す。
sumを “グローバル変数” 扱いにしてはいけない(箱理論と Fail-Fast 的にも事故る)
2) merge の「loop_step 選定」を壊さない
JoinIR merge は「main でも continuation でもない関数」を 1つ選んで “loop header” とみなす。
nested loop では inner_step が混ざるので、inner_step / k_inner_exit を boundary の continuation_func_ids に入れて除外する。
これをしないと、merge が誤って inner_step を loop header として選び、PHI/exit binding が壊れる。
実装タスク(順番)
Task A: lowering を実装する
対象: src/mir/builder/control_flow/joinir/patterns/pattern6_nested_minimal.rs
やること:
lower()でErrしている stub を置き換えて、JoinIR pipeline を呼ぶ- 最小形だけ対応し、外れた形は Pattern6 選択前に落とす(
is_pattern6_lowerable()側を強化)か、ここで明示エラー
推奨構成(Pattern1 と同じ流儀):
- JoinIR 生成は
src/mir/join_ir/lowering/nested_loop_minimal.rs(新規)に切り出す - builder 側は「context 作成 → JoinModule → boundary → conversion pipeline」のみ
(ただし PoC なので builder 側に直書きでも可。差分は最小に。)
Task B: JoinIR(nested minimal)を生成する
参考: src/mir/join_ir/lowering/simple_while_minimal.rs
最小形の JoinIR 関数構成(推奨):
main(i0, sum0):Call(loop_step, [i0, sum0])Ret 0(statement-position)
loop_step(i, sum)(outer):exit_cond = !(i < N_outer)Jump(k_exit, [sum], cond=exit_cond)Call(inner_step, [j0, i, sum])
inner_step(j, i_outer, sum):exit_cond = !(j < N_inner)Jump(k_inner_exit, [i_outer, sum], cond=exit_cond)sum_next = sum + 1j_next = j + 1Call(inner_step, [j_next, i_outer, sum_next])
k_inner_exit(i, sum):i_next = i + 1Call(loop_step, [i_next, sum])
k_exit(sum):Ret sum
命名:
k_exitは canonical name:src/mir/join_ir/lowering/canonical_names.rs::K_EXITloop_stepは canonical name:...::LOOP_STEPinner_step/k_inner_exitは Phase 188.3 で追加(文字列でよいが、可能なら canonical_names に追加)
Task C: boundary(exit binding + continuation funcs)を正しく構築する
参考: src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs
必須:
- join_inputs/host_inputs は JoinModule.entry.params と同順・同数
exit_bindingsはsumを host slot に reconnect- join_exit_value は
k_exitの param(Pattern1 と同じくjoin_module.require_function("k_exit").params[0]から取得)
- join_exit_value は
continuation_func_idsに以下を含める:k_exitk_inner_exitinner_step
Task D: integration smoke を追加する(exit code SSOT)
新規:
tools/smokes/v2/profiles/integration/joinir/phase1883_nested_minimal_vm.shapps/tests/phase1883_nested_minimal.hakoを実行し、exit code == 9 で PASS- stdout 比較はしない
注意:
tools/smokes/v2はmanifest.txt方式ではない(findベース)- 既存の helper を使う:
source tools/smokes/v2/lib/test_runner.sh
検証手順
cargo build --release./target/release/hakorune --backend vm apps/tests/phase1883_nested_minimal.hako(exit=9)./tools/smokes/v2/run.sh --profile integration --filter "phase1883_nested_minimal"./tools/smokes/v2/run.sh --profile quick(154/154 PASS)./tools/smokes/v2/run.sh --profile integration --filter "selfhost_"(FAIL=0)
追加の注意(Fail-Fast)
- Pattern6 が選ばれたあとに
Ok(None)で他パターンに流すのは禁止(silent fallback) - “選ぶ前に落とす” が最も安全:
is_pattern6_lowerable()を「lowering が確実に通る形だけ true」に強化する