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>
6.7 KiB
Phase 188.3: Nested loop lowering (1-level) — make Pattern 6 real
Date: 2025-12-27 Status: In progress (Phase 2 done: selection / Phase 3: lowering) Prereq: Phase 188.2 Option A is complete (StepTree depth SSOT + strict Fail-Fast)
Goal
max_loop_depth == 2(1-level nested loop)を JoinIR lowering で実際に通す。
- 既知の事実:
LoopForm (=LoopShape)にはネスト情報が無いので、LoopFormベースの Pattern6 検出/ルーティングでは実装できない - 実装は **StepTree(AST側)**を SSOT として扱う(Phase 188.2 Option A を継続)
Definition: max_loop_depth (SSOT)
StepTreeFeatures.max_loop_depth は「その関数(or 評価対象ブロック)の中で、最も深い loop{} ネストの深さ」を表す。
- loop が無い:
0(※StepTree実装に依存。現状の運用では “loopがある前提”で扱う) - loop が 1 段:
1 - loop の中に loop が 1 つ:
2 loop{ loop{ loop{ ... } } }:3
Phase 188.2 の strict ガードは「depth > 2 を明示エラー」にしている。 これは言語仕様の制限ではなく、「JoinIR lowering の実装範囲(compiler capability)」の制限。
Scope (minimal)
対応するのは “NestedLoop Minimal” の 1形だけに限定する。
- depth:
max_loop_depth == 2のみ - inner loop: Pattern1相当(break/continue 無し)
- outer loop: Pattern1相当(break/continue 無し)を優先
- それ以外:
- strict mode: 明示エラー(Phase 188.2 の depth check とは別タグで良い)
- non-strict mode: 既存の fallback 経路に任せる(ただし silent fallback を増やさない)
Current Code Status (reality)
Phase 188.3 は “選択ロジック(Pattern6選定)” まで実装済みで、lowering が未実装(stub)な状態。
- Selection SSOT:
src/mir/builder/control_flow/joinir/routing.rsのchoose_pattern_kind()- cheap check → StepTree → AST validation
max_loop_depth == 2かつ “Pattern6 lowerable” のときだけPattern6NestedLoopMinimalを返す
- Lowering stub:
src/mir/builder/control_flow/joinir/patterns/pattern6_nested_minimal.rs- 現在は
Err("[Pattern6] ... not yet implemented")で Fail-Fast
- 現在は
この README のゴールは、stub を “実装済み” にして fixture を通すこと。
SSOT (what to rely on)
- nesting depth SSOT:
StepTreeFeatures.max_loop_depth - depth > 2: strict mode で
control_tree/nested_loop/depth_exceeded(Phase 188.2)
Scope & Variable Model (SSOT)
Nyash の変数スコープ方針に合わせて、nested loop lowering でも以下を SSOT として固定する。
Visibility (read)
- inner loop から outer の binding は参照できる(lexical scope: “1つ上は見える”)
- ただし JoinIR lowering では「見える」を 明示的な引数/継続で表現する(暗黙キャプチャを増やさない)
Mutation (write-back)
Phase 188.3 では段階的に進める:
- P188.3 (minimal): “最小の write-back” を 明示的な carrier として許す
- inner loop が outer 変数を更新する場合、その変数は carrier として引数で受けて、
k_inner_exit(...)で返す - 対応範囲は最小(fixtureが要求する 1 carrier 程度)に限定する
- inner loop が outer 変数を更新する場合、その変数は carrier として引数で受けて、
- P188.4+ (generalize): write-back を一般化(複数 carrier / 代入形 / break/continue を含む形)
この分離により、write-back の複雑さ(複数 carrier / break / continue / PHI の再接続)を Phase 188.3 に持ち込まずに済む。
Lowering sketch (how it should look)
Nested loop は JoinIR の「tail recursion + continuation」を再帰的に合成して表現する。
- outer:
outer_step(state..., k_outer_exit) - inner:
inner_step(state..., k_inner_exit) - inner が終わったら
k_inner_exit(...)で outer の“残り”へ戻る
この k_inner_exit がスコープ境界として働くので、将来の write-back もここに集約できる。
Minimal function graph (recommended)
Phase 188.3 の最小形は「outer の loop_step の中で inner の loop_step を呼び、inner が終わったら k_inner_exit で outer に戻る」。
推奨の JoinIR 関数群(概念):
main(i0, sum0):Call(loop_step, [i0, sum0])Ret 0(statement-position loop の場合)
loop_step(i, sum)(outer の step。canonical name はloop_stepに寄せる):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 + 1(fixture の最小形)j_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)(canonical namek_exit):Ret sum
重要:
- carrier はグローバル扱いしない。
sumは引数で運んで戻す。 - merge 側の “loop_step 関数の選定” を壊さないため、
inner_stepとk_inner_exitは boundary のcontinuation_func_idsに含めて除外する(詳細は指示書)。
Deliverables
-
Fixture + integration smoke(exit code SSOT)
- 1-level nested loop を最小で再現する
.hakoを追加 - integration で実行し、exit code で判定(stdout比較はしない)
- 1-level nested loop を最小で再現する
-
StepTree-based lowering implementation
- StepTree を辿って、inner loop を outer loop の中で正しく lowering できるようにする
- 入口は “StepTree→JoinIR” のどこか(LoopFormベースの router は使わない)
-
Docs update
- Phase 188.1 の “Pattern6 specification” が design であることは維持
- Phase 188.3 で “実装済み/未実装の境界” を明確に書く
Acceptance Criteria
./tools/smokes/v2/run.sh --profile quickが常にグリーン維持- integration selfhost が FAIL=0 を維持
- 追加した nested loop fixture が PASS(JoinIR lowering が使われたことをログ/タグで確認可能)
Instructions (handoff)
実装者(Claude Code)向けの手順書は docs/development/current/main/phases/phase-188.3/P1-INSTRUCTIONS.md を参照。
Next (schedule)
- Phase 188.3: depth=2 の最小形を “確実に通す” + PoC fixture を smoke 固定
- Phase 188.4+: write-back(outer carrier reconnection)と “再帰 lowering の一般化(depthを増やしても壊れない)” を docs-first で設計してから実装
Out of Scope
- nested loop + break/continue の一般対応
- LoopRegion を使った MIR-level nesting SSOT(Option B)