Files
hakorune/CURRENT_TASK.md
nyash-codex 588129db65 feat(joinir): Phase 34-6 MethodCall 構造と本物の substring 意味論
**Phase 34-6 実装完了**: MethodCall 構造を JoinIR に追加し、本物の substring
呼び出しを通すことに成功。

## 主要変更

### 1. MethodCall 構造追加 (34-6.1)
- `src/mir/join_ir/mod.rs`: JoinInst::MethodCall バリアント (+8 lines)
  - 構造: `{ dst, receiver, method, args }`
  - 設計原則: JoinIR は構造のみ、意味論は MIR レベル

### 2. extract_value 更新 (34-6.2)
- `src/mir/join_ir/frontend/ast_lowerer.rs`: Method 処理本物化 (+37 lines)
  - receiver/args を extract_value で再帰処理
  - ダミー Const(0) 削除 → 本物の MethodCall 生成
  - cond 処理修正: ValueId(0) ハードコード → extract_value で取得

### 3. JoinIR→MIR 変換実装 (34-6.3)
- `src/mir/join_ir_vm_bridge.rs`: MethodCall → BoxCall 変換 (+12 lines)
- `src/mir/join_ir/json.rs`: MethodCall JSON シリアライゼーション (+16 lines)
- `src/mir/join_ir_runner.rs`: MethodCall 未対応エラー (+7 lines)

### 4. テスト更新 (34-6.4)
- `docs/.../fixtures/json_shape_read_value.program.json`: 本物の substring 構造
- `src/tests/joinir_frontend_if_select.rs`: run_joinir_via_vm 使用
- テスト成功: v="hello", at=3 → "hel" 

## 成果

-  テスト全通過(1 passed; 0 failed)
-  設計原則確立: JoinIR = 構造 SSOT、意味論 = MIR レベル
-  Phase 33-10 原則との整合性: Method でも同じ原則適用

**ドキュメント更新**: CURRENT_TASK.md + TASKS.md(Phase 34-6 完了記録)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 17:05:46 +09:00

1770 lines
125 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Current Task — Phase 21.8 / 25 / 25.1 / 25.2 / 25.4 / 26-F / 26-G / 30 / 32 Snapshot2025-11-26 時点)
> このファイルは「今どこまで終わっていて、次に何をやるか」を 1000 行以内でざっくり把握するためのスナップショットだよ。
> 詳細な履歴やログは `git log CURRENT_TASK.md` からいつでも参照できるようにしておくね。
---
## 0. 現在地ざっくり
- フェーズ軸(ざっくり):
- **21.8**: Numeric Core / Core-15 まわりの安定化(既に日常的には安定運用)。
- **25.x**: Stage0/Stage1/StageB / Selfhost ラインのブートストラップと LoopForm v2 / LoopSSA v2 まわりの整備いまは「Rust側 LoopForm/PHI は維持しつつ、徐々に JoinIR に役割を移す」モード)。
- **25.1 系**: StageB / Stage1 / selfhost 向けに、Rust MIR / LoopForm v2 / LoopSSA v2 を段階的に整える長期ラインStage1 CLI / StageB / Ny compiler の Program(JSON v0) 境界はだいたい揃ったので、以降は JoinIR との接続が本線)。
- **26-F / 26-G**: Exit PHI / ExitLiveness 用の 4箱構成LoopVarClassBox / LoopExitLivenessBox / BodyLocalPhiBuilder / PhiInvariantsBoxと MirScanExitLiveness の準備(いまは「歴史的理由で残っている検証用レイヤ」で、将来 PHI レガシー削除時にまとめて畳む)。
- **26-H / 27.x**: JoinIR 設計+ミニ実験フェーズ → minimal/skip_ws/FuncScanner.trim/Stage1 UsingResolver minimal/FuncScanner.append_defs minimal/StageB minimal までを対象に、制御構造を関数呼び出しに正規化する IR とランナーを段階的に整備済み。
- 27.4 で Header φ を LoopHeaderShape 化、27.5 で Exit φ の意味を LoopExitShape として固定。
- 27.6-1/2/3 で ExitPhiBuilder 側にトグル付きバイパスを入れて A/B 観測まで完了、seal_phis と Header φ バイパスの整合性は別フェーズで refinement 済み。
- 27.8〜27.11/27.13/27.14 で skip_ws/trim/Stage1 UsingResolver minimal/FuncScanner.append_defs minimal/StageB minimal を Shared Builder PatternMIR-based lowering に移行し、ValueId 範囲管理も一元化。
- 27.8-27.11/27.13/27.15 で JoinIR Runner を JoinValue↔VMValue 変換+`MirInterpreter::execute_box_call` ラッパ経由で Rust VM の BoxCall 意味論と統合し、skip_ws/trim の JoinIR Runner スタンドアロン実行が安定GC/BoxRef も Arc ベースで統合済み)+ `joinir_runner_standalone_skip_ws` / `joinir_runner_standalone_trim` を NYASH_JOINIR_EXPERIMENT=1 で常時叩ける正常系テストとして昇格済み。
- Phase 27-shortterm の S1〜S5.4 は完了。Phase 28-midterm では per-loop lowering を増やさず、LoopFormLoopVarClassBoxLoopExitLivenessBox を入力にした「汎用 Loop→JoinIR ロワー」generic_case_a + LoopScopeShapeに畳み込む方針に切り替え済みjoin-ir.md に JoinIR ロワーが“やらないこと”チェックリストを明記)。
- Phase 29-longterm では LoopForm を「構造専任箱」とし、その隣に LoopScopeShapeLoopVarClassBox / ExitLiveness / LocalScopeInspector を統合したスコープ箱を置く形に整理。LoopVarClassBox/LoopExitLivenessBox/LocalScopeInspector は LoopScopeShape::from_existing_boxes 経由で細く呼ばれるだけのレガシーに寄せておき、将来的には LoopScopeShape に吸収して 1箱化する計画を TASKS に記録。
- **30.xJoinIR World**: JoinIR を「実行系の主IR」として採用し、PHI まわりのレガシーを段階的に縮退・削除していく足場整備フェーズ(早期削除候補と minimal ケースまで完了、残りは Phase 32 へ引き継ぎ)。
- L0.1〜0.2: jsonir v0JoinModule → JSONと v0_* スナップショットフィクスチャを整備済み。
- L0.3〜0.4: JoinIR VM Bridge を実装し、`Main.skip/1`minimal_ssa_skip_ws`FuncScannerBox.trim/1` で Route A (MIR→VM) の PHI バグを Route B (MIR→JoinIR→VM) で設計的に修正できることを実行レベルで実証済み。
- L0.5: Stage1UsingResolverBox.resolve_for_source/5 に対しても JoinIR VM Bridge A/B テストを追加し、Stage1 minimal について n=0 で `"init"` / n=3 で `"ABC"` が JoinIR 経由で正しく返ることを確認 → 「Stage1 ループでも JoinIR が PHI 問題を根治できる」ことを実行レベルで実証。
- F1: LoopScopeShape に LoopVarClassBox / LoopExitLivenessBox / LocalScopeInspectorBox の責務を委譲する準備が完了仕様SSOT化 classify/exit_live/inspector 委譲ヘルパー追加)。実際の呼び出し置換とファイル削除は F2 以降で行う。
- 現在は F系タスクF1〜F4を「**PHI レガシーを削りつつ、JoinIR 汎用 lowering と VM/LLVM 実行を JoinIR 前提にしていく」方向に倒すことを決定。ハードコードされた VM 分岐(`Main.skip/1``FuncScannerBox.trim/1` / Stage1 minimal 向けの特例)は Phase 30 の F4.4 で削除する前提で、今は「JoinIR World への橋」としてだけ残しておく。
- Rust 側:
- LoopForm v2 + ControlForm + Conservative PHI は、代表テストStage1 UsingResolver / StageB 最小ループ)ではほぼ安定。
- 静的メソッド呼び出し規約と `continue` 絡みの PHI は 25.1m までで根治済み。
- .hako 側:
- StageB コンパイラ本体 / LoopSSA v2 / BreakFinderBox / PhiInjectorBox はまだ部分実装。
- JSON v0 / selfhost ルートは Rust 側の LoopForm v2 規約に追いつかせる必要がある。StageB selfhost 経路は依然として Program JSON が取れていないが、直近の実行では `Unknown method 'main' on InstanceBox` は再現しておらず、別の箇所で落ちている可能性が高い。Stage1 CLI ブリッジ自体は Rust バイナリ経由で発火して `stage1_cli.hako` 実行までは到達しており、**ParserBox と StringHelpers まわりのすべての `loop(cont == 1) { ... cont = 0 }` パターンを `loop(true) { ...; continue / break }` に書き換えた結果、BuildBox.emit_program_json_v0 → `ParserBox.parse_program2` の無限ループstep budget 超過は解消済み**minimal_ssa_skip_ws.hako で Program(JSON v0) が RC=0 で出ることを確認済み。Rust VM の budget エラーは fn/bb/last_inst Span (あれば file:line:col) 付きで出るようにしたので、今後類似の問題が出ても位置特定は容易。
- StageB / FuncScanner ライン:
- Phase 25.3 をクローズし、`stageb_fib_program_defs_canary_vm.sh` が緑(`defs``TestBox.fib/Main.main`、fib.body.body[*] に `Loop`)。
- StageB は block パーサ優先 + defs を Block 包みで構造化。次手: Stage1 UsingResolver ループの Region+next_i 揃え / Stage1 CLI program-json selfhost 準備。
- Stage1 CLI 実験: `NYASH_USE_STAGE1_CLI=1 STAGE1_EMIT_PROGRAM_JSON=1 ./target/release/hakorune apps/tests/minimal_ssa_skip_ws.hako` でブリッジ発火・stage1_cli.hako 実行は確認できたが、VM が max_steps 2000000 を超過して中断プラグイン未ロード警告あり。budget 超過メッセージには `fn=ParserBox.parse_program2/1 ... (lang/src/runner/stage1_cli.hako:1:1)` まで出るようになったSpan 配線は通ったが位置精度はまだ粗い。selfhost ビルド経路tools/selfhost/build_stage1.shは StageBDriverBox.main 呼び出しで Unknown method 'main' on InstanceBox → Program JSON 未生成。
---
## 1. 最近完了した重要タスク
### 1-00v. Phase 29 L-5.3 — JoinIR generic_case_a との統合 (Phase 1)**完了** 2025-11-26
**目的**
- generic_case_a lowering に progress carrier チェックを追加
- 無限ループの可能性があるループprogress carrier なし)を事前にフォールバック
- 保守的アプローチで minimal 4 ケースの安定性確保
**実装内容**
1. `src/mir/join_ir/lowering/loop_to_join.rs``has_safe_progress()` helper 追加Phase 1 実装)
- `scope.progress_carrier.is_some()` で保守的チェック
- Phase 2 で MirQuery による Add 命令チェック予定
2. `is_supported_case_a_loop_view()` の末尾に progress guard 追加4 番目のチェック)
- progress carrier なしループは reject してフォールバック
- デバッグログで reject 理由を出力
**テスト結果**
-**25 passed; 0 failed** — JoinIR 関連テスト全通過
- ✅ skip_ws / trim / append_defs / Stage1 UsingResolver の全ケース PASS
- ✅ 既存テストへの影響なしprogress carrier が設定されているループは全て通過)
**技術メモ**
- Phase 1 は `progress_carrier.is_some()` だけチェック(保守的)
- LoopScopeShape で progress_carrier を carriers の先頭(典型的には 'i')として設定済み
- ignored テストjoinir_vm_bridge_skip_ws Route Aの失敗は MIR 自体の PHI バグで、本変更とは無関係git stash で確認済み)
**次のステップ**
- Phase 2: MirQuery で header→latch 間に Add 命令があるかの詳細チェック追加
- verify.rs の `verify_progress_for_skip_ws()` ロジックを MIR レベルに適用
**関連ファイル**
- `src/mir/join_ir/lowering/loop_to_join.rs` (has_safe_progress + progress guard)
- `docs/private/roadmap2/phases/phase-29-longterm-joinir-full/TASKS.md` (L-5.3 完了記録)
---
### 1-00w. Phase 33-3.1 — IfSelectTest.* を JoinIR 対応済みケースとして固定(**完了** 2025-11-26
**実施内容**
- `IfSelectTest.*` の単純 if/elsesimple/local パターン)は JoinIR Select 経路で実行可能になった
- dev 専用パス: `NYASH_JOINIR_IF_SELECT=1` + 関数名ガードで限定
- MIR Builder 本線の if_phi 呼び出しは未削除(既存経路を保持)
**JoinIR 対応範囲**
- ✅ simple パターン: `if cond { return 1 } else { return 2 }`
- ✅ local パターン: `if cond { x = a } else { x = b }; return x`
- ⏳ Stage-1/Stage-B/selfhost: Phase 33-4 で段階的拡大予定
**if_phi 系箱の削除計画**
- **削除条件**: Stage-1/Stage-B/selfhost の代表ケースを JoinIR Select/IfMerge に乗せてから再検討
- **対象箱**: if_phi.rs (316行) / phi_invariants.rs (92行) / conservative.rs (169行)
- **削減見込み**: 約 600 行Phase 33-3.2/3.3 で実施)
**次のステップ**
- Phase 33-3.2: `phi_invariants/conservative` の JoinIR 側への移譲設計(✅ 完了 2025-11-27
- Phase 33-3.3: 参照ゼロの箱から順次削除
**更新ドキュメント**
- `docs/private/roadmap2/phases/phase-30-final-joinir-world/PHI_BOX_INVENTORY.md` (Phase 33 進捗追加)
- `docs/private/roadmap2/phases/phase-33-joinir-if-phi-cleanup/TASKS.md` (33-3.1 完了)
---
### 1-00x. Phase 33-3.2 — phi_invariants / conservative の JoinIR 移譲(**完了** 2025-11-27
**実施内容**
- IfSelectTest.* の simple/local if/else について、JoinIR SelectVerifier 側に invariant を部分移譲
- phi_invariants.rs / conservative.rs の最小エッセンスを抽出
- `verify_select_minimal()` 実装(型一貫性・単一 PHI・完全性チェック
**移譲した責務**
- phi_invariants.rs::ensure_if_values_exist() → 型一貫性・完全性
- conservative.rs::ConservativeMerge → 単一 PHI チェック
**テスト結果**
- ✅ simple/local パターン: Verifier PASS
- ✅ 不正パターン(複数 Select: Verifier REJECT
- ✅ invariant チェック: conservative.rs / phi_invariants.rs からの移譲を明記
**削除計画**
- **現状**: IfSelectTest.* のみ JoinIR Verifier でカバー
- **条件**: Stage-1/Stage-B/selfhost の代表ケースへ適用後
- **対象**: if_phi.rs (316行) / phi_invariants.rs (92行) / conservative.rs (169行)
**次のステップ**
- Phase 33-3.3: 参照ゼロの箱から順次削除Stage-1/Stage-B/selfhost 適用後)
**更新ドキュメント**
- `src/mir/join_ir/verify.rs` (verify_select_minimal 実装)
- `src/tests/mir_joinir_if_select.rs` (4 テスト追加)
- `docs/private/roadmap2/phases/phase-33-joinir-if-phi-cleanup/if_joinir_design.md` (Select 用 Invariant セクション追加)
- `docs/private/roadmap2/phases/phase-30-final-joinir-world/PHI_BOX_INVENTORY.md` (Phase 33-3.2 進捗追加)
- `docs/private/roadmap2/phases/phase-33-joinir-if-phi-cleanup/TASKS.md` (33-3.2 完了)
---
### 1-00d. Phase 33-9.2 — IfSelect/IfMerge VM パイプライン統合(ドライラン)(**完了** 2025-11-27
**目的**
- IfSelect/IfMerge lowering を VM 実行ラインに「観測モード」で統合し、どの関数で JoinIR lowering が適用可能かをログと統計で可視化するMIR の書き換えや本番挙動は一切変更しない)。
**実装内容**
1. `src/runner/modes/vm.rs` に If lowering ドライランフックを追加(約 80 行、lines 502-585
- 有効条件: `NYASH_JOINIR_EXPERIMENT=1` かつ `NYASH_JOINIR_VM_BRIDGE=1` かつ `HAKO_JOINIR_IF_SELECT=1`
- `is_loop_lowered_function(name)` が true の関数skip_ws / trim / append_defs / Stage1 / StageB minimal 等)は対象外としてスキップ。
- 対象関数内の Branch terminator を走査し、`try_lower_if_to_joinir(&func, block_id, debug_level >= 3)` を呼び出して Select/IfMerge への lowering を試行。
- 成否・Select/IfMerge 種別・処理時間を集計し、`HAKO_JOINIR_DEBUG` のレベルに応じて統計ログLevel 1、関数別結果Level 2、詳細ダンプLevel 3を出力。
2. `src/mir/join_ir/lowering/if_select.rs` のパターンマッチ条件を現実の MIR 形に合わせて緩和。
- これまでの「then/else ブロックに命令が 1 つもないReturn のみ)」という制約を撤廃し、`Const` / `Copy` だけから成るブロックを「副作用なし」として許容する `is_side_effect_free()` ヘルパーを導入。
- ユニットテスト用の空ブロックinstructions.is_empty())も、実 MIR の `const + ret` も同じく安全パターンとして通しつつ、Call や Store など副作用のある命令が含まれる場合は従来どおり if_phi 経路にフォールバック。
3. `docs/private/roadmap2/phases/phase-33-joinir-if-phi-cleanup/if_joinir_design.md` に Section 12 を追加し、VM ドライラン統合の目的・実装ポイント・テスト結果・制約を記録。
**テスト結果**
- ✅ ユニットテスト: IfSelect/IfMerge/Verifier 系 7/7 PASSenv 競合を避けるためのテスト再編済み)。
- ✅ 実 MIR simple パターン: `apps/tests/joinir_if_select_simple.hako` で Branch=1 / Lowered=1 / 成功率 100% を確認(ログ例: `Lowered: 1 (100.0%), Select: 1, IfMerge: 0`)。
- ⏳ 実 MIR local パターン: `apps/tests/joinir_if_select_local.hako` はまだ lowering パターン未実装のため Lowered=0統計上 0%)。
- ⏳ 実 MIR IfMerge パターン: `apps/tests/joinir_if_merge_simple.hako` 等は Phase 33-7 の lowering は実装済みだが、VM ドライラン側ではまだマッチ条件を狭く保っており Lowered=0。
**制約 / 次のステップ**
- Phase 33-9.2 の範囲では **MIR の書き換えを一切行わず**、既定の Route Aif_phi 経路で実行する前提を維持するJoinIR lowering はあくまで解析+統計用)。
- local パターン / IfMerge パターンの実 MIR 対応、および Route BMIR→JoinIR→MIR')を用いた実行時 A/B 比較は、次フェーズPhase 33-10 以降)に引き継ぎ。
- 実装により「どの関数が安全に JoinIR If lowering できそうか」をランタイムで観測できるようになり、今後の本線統合と PHI レガシー削除の足場が整った。
---
### 1-00e. Phase 33-10 — IfLoweringDryRunner 箱化PHI設計原則の確立**完了** 2025-11-27
**目的**
- VM ラインに統合した If lowering ドライラン処理を専用の箱に切り出し、同時に「JoinIR は PHI 生成器SSOTであり、既存 PHI の変換器にはしない」という設計原則をコードとドキュメントの両方で固定する。
**実装内容**
1. `IfLoweringDryRunner` の導入と箱化
- `src/mir/join_ir/lowering/if_dry_runner.rs` を新規作成し、Phase 33-9.2 まで `src/runner/modes/vm.rs` に埋め込んでいた If lowering ドライラン処理を移設。
- VM 側からは `IfLoweringDryRunner::run(&module_vm)` を呼び出すだけの薄いフックに変更し、`vm.rs` 内の JoinIR If ドライランコードを 83 行 → 9 行に削減(約 89% 減)。
- これにより「VM 実行パイプライン」と「If lowering の解析ロジック」が別ファイル・別箱として分離され、責務境界が明確になった。
2. JoinIR = PHI 生成器という原則のコード化
- `src/mir/join_ir/lowering/if_select.rs` のパターンマッチロジックに、merge ブロックに既に `MirInstruction::Phi` が存在する場合は早期に `return None` するガードを追加。
- これにより、JoinIR If lowering は「PHI 未挿入の merge」だけを対象とし、`if_phi.rs` 等が生成済みの PHI を再解釈する“PHI 変換器”にはならないことを保証。
- 設計上の整理として:
- JoinIR は PHI の意味論を持つ SSOT 層PHI 生成器)。
- Rust MIR/PHI 層はあくまでエンコード先であり、既存 PHI を JoinIR に戻すラウンドトリップは禁止。
3. 挙動確認とテスト
- Simple pattern (`apps/tests/joinir_if_select_simple.hako`):
- merge ブロックを持たない形Return 形式)のため、従来どおり lowering 対象となり、dry-run 統計でも Branch=1 / Lowered=1 (100%) を維持。
- Local pattern (`apps/tests/joinir_if_select_local.hako`):
- 実 MIR の merge ブロックに `MirInstruction::Phi` が既に存在することが `--dump-mir` で確認され、PHI 早期チェックによって lowering 対象外Lowered=0, ログに「PHI already exists, skipping」として扱われる。
- これにより「Local pattern の現状は既存 if_phi.rs が正しい PHI を生成しており、JoinIR の本来の出番は PHI 生成前レイヤにある」ことが明示された。
4. ドキュメント更新
- `docs/private/roadmap2/phases/phase-33-joinir-if-phi-cleanup/if_joinir_design.md` に Section 13「Phase 33-10: 箱化PHI設計原則確立」を追加。
- IfLoweringDryRunner の役割と `vm.rs` の削減効果、PHI 早期チェックの実装、Simple/Local の実 MIR 構造と挙動を整理。
- 「Phase 33-10 以降、JoinIR If lowering は PHI 未挿入の merge だけを対象とする」という設計原則を明文化。
**位置づけと次のステップ**
- Phase 33-9.2 で導入した VM ドライラン統合を「箱 + 設計原則」として固めたことで、If lowering 基盤は十分に安定した実験レイヤになった。
- 今後は Phase 34 以降で AST/CFG 段階から JoinIR を挿入し、「PHI 生成前に JoinIR を通す本線経路」へ徐々に移行するか、IfMerge 側のカバレッジ拡大に進む。
---
### 1-00f. Phase 34-1 — JoinIR Frontend (AST→JoinIR) 設計フェーズ(**完了** 2025-11-27
**Phase 33 までの成果(簡潔要約)**
- **Loop JoinIR**: Phase 27〜30 で Loop→JoinIR lowering 基盤確立skip_ws, trim, append_defs, Stage-1 UsingResolver minimal
- **If JoinIR**: Phase 33 で If→Select/IfMerge lowering 実装、VM ドライラン統合、箱化完了
- **PHI 設計原則**: 「JoinIR = PHI 生成器SSOT、PHI 変換器ではない」をコードとドキュメントで確立Phase 33-10
**Phase 34-1 の目的**
- AST/CFG→JoinIR フロントエンド経路の設計と箱構造の固定
- **実装より設計docs 優先**: コードは skeleton のみdocコメントだけ
- **既定挙動は一切変更しない**: 新機能はデフォルト OFF、実験モードでのみ有効
**設計成果**
1. **パイプライン設計**`docs/private/roadmap2/phases/phase-34-joinir-frontend/README.md`
- 既存レガシー経路AST→MIR Builder→MIR+PHI→VM/LLVMの明文化
- 目標とする JoinIR 本線AST→JoinIR Frontend→JoinIR→MIR'+PHI→VM/LLVMの定義
- 責務分離の原則: 「PHI の意味論の SSOT は JoinIR 側、MIR の PHI は backend エンコード」
2. **対象ケースのスコープ決定**
- Phase 34-1: 設計のみ(実装なし)
- Phase 34-2: `IfSelectTest.*` 相当の tiny ケースSimple/Local pattern
- Phase 34-3 以降: Stage-1/Stage-B への段階的拡大
3. **Rust モジュール構成案**
- `src/mir/join_ir/frontend/mod.rs`: フロントエンド JoinIR lowering の入口
- `src/mir/join_ir/frontend/ast_lowerer.rs`: `AstToJoinIrLowerer` 構造体skeleton のみ)
- 既存の `lowering/` は MIR→JoinIR 補助として保持(責務分離)
4. **環境変数フラグ案**
- `HAKO_JOINIR_FRONTEND=1`(仮称): AST→JoinIR フロントエンド実験用
- 既存フラグとの関係: `NYASH_JOINIR_EXPERIMENT` (VM/LLVM bridge), `HAKO_JOINIR_IF_SELECT` (MIR If lowering)
**作成ドキュメント**
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/README.md` (パイプライン図、責務分離、フラグ案)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/TASKS.md` (タスクチェックリスト)
**Rust skeleton**
- `src/mir/join_ir/frontend/mod.rs` (docコメントのみ)
- `src/mir/join_ir/frontend/ast_lowerer.rs` (`AstToJoinIrLowerer` 宣言のみ、実装は `unimplemented!`)
- `src/mir/join_ir/mod.rs``pub mod frontend;` 追加(コメント: "Phase 34-1: Frontend (AST→JoinIR) — skeleton only"
**ガードレール遵守**
- ✅ 既定挙動は一切変更なし(新機能はデフォルト OFF
- ✅ JoinIR = PHI 生成器の原則を継続Phase 33-10 で確立)
- ✅ 実装より設計docs 優先(コードは最小限の skeleton のみ)
**次のステップPhase 34-2 以降)**
- Phase 34-2: `AstToJoinIrLowerer` 実装(`IfSelectTest.*` 相当の tiny ケース)
- Phase 34-3: Stage-1/Stage-B の純粋 if 関数への適用
- Phase 34-4: Loop/Break/Continue の AST→JoinIR 対応
**参照ドキュメント**
- 設計: `docs/private/roadmap2/phases/phase-34-joinir-frontend/README.md`
- タスク: `docs/private/roadmap2/phases/phase-34-joinir-frontend/TASKS.md`
- JoinIR 設計: `docs/development/architecture/join-ir.md`
- Phase 33 If 設計: `docs/private/roadmap2/phases/phase-33-joinir-if-phi-cleanup/if_joinir_design.md`
---
### 1-00g. Phase 34-2 — AstToJoinIrLowerer 実装(`IfSelectTest.*` simple pattern**完了** 2025-11-27
**Phase 34-2 の目的**
- `IfSelectTest.*` の simple pattern`if cond { return 1 } else { return 2 }`)で AST→JoinIR を実装
- Program(JSON v0) を AST 代わりに使用Stage-1 pipeline の安定境界活用)
- A/B テスト可能な形で JoinIR Frontend 経路を実証
**実装内容**
1. **入力ソース決定** (34-2.1)
- Program(JSON v0) を AST 代わりに使用する戦略を README に明文化
- 経路: `.hako` → Stage-1 → Program(JSON v0) → `AstToJoinIrLowerer` → JoinModule
2. **JSON v0 フィクスチャ準備** (34-2.2)
- `fixtures/joinir_if_select_simple.program.json` を手書き作成845 bytes
- `IfSelectTest.test(cond)` メソッドの JSON v0 表現
- If/Then/Else 構造 + Return 文 + Int リテラル値
3. **AstToJoinIrLowerer インターフェース実装** (34-2.3)
- `src/mir/join_ir/frontend/ast_lowerer.rs` に実装194 lines
- `lower_program_json(&serde_json::Value) -> JoinModule` メソッド
- `src/mir/join_ir/frontend/mod.rs` に使用例 docstring 追加
4. **Lowering ロジック実装** (34-2.4)
- 5段階変換: defs 抽出 → metadata 取得 → If stmt 検索 → then/else 値抽出 → JoinIR 組み立て
- JoinIR 命令列: `Const` (then) → `Const` (else) → `Select``Ret`
- パターン外は panictiny テスト専用で合理的)
5. **テストハーネス作成** (34-2.5)
- `src/tests/joinir_frontend_if_select.rs` 作成62 lines
- Route BJoinIR Frontend 経路)の動作確認
- cond=1 → 10, cond=0 → 20 の検証ロジック
**成果**
- ✅ コンパイル成功(`cargo build --release` PASS
-`AstToJoinIrLowerer` 完全実装simple pattern のみ)
- ✅ JoinIR 生成ロジック動作確認(型エラー全解決)
- ✅ JSON v0 → JoinModule 変換パイプライン確立
**技術的詳細**
- **型修正**: `VarId``ValueId`, `JoinValue::Integer``JoinValue::Int`
- **JoinFunction 構造**: `params: Vec<ValueId>`, `exit_cont: Option<JoinContId>`
- **JoinInst 構文**: `JoinInst::Compute(MirLikeInst::Const { dst, value })`
- **フィクスチャ形式**: Program(JSON v0) の body/defs 構造を手書き再現
**制約事項**
- ⚠️ テスト実行は panic strategy 問題で保留(コンパイルは成功)
- ✅ テストコードは完成済み(実行環境の設定問題のみ)
- ✅ Route A既存経路との比較は Phase 34-3 以降で実装予定
**ガードレール遵守**
- ✅ 既定挙動は一切変更なし(テスト専用実装)
- ✅ JoinIR = PHI 生成器の原則維持Phase 33-10 原則継続)
- ✅ パターン外は panictiny テスト専用で合理的)
**次のステップPhase 34-3 以降)**
- Phase 34-3: Local pattern 対応(`if cond { x = a } else { x = b }; return x`
- Phase 34-4: Stage-1/Stage-B の純粋 if 関数への適用
- Phase 34-5: Loop/Break/Continue の AST→JoinIR 対応
**実装ファイル**
- `src/mir/join_ir/frontend/ast_lowerer.rs` (194 lines, 完全実装)
- `src/mir/join_ir/frontend/mod.rs` (docstring 更新)
- `src/tests/joinir_frontend_if_select.rs` (62 lines, テストハーネス)
- `src/tests/mod.rs` (`pub mod joinir_frontend_if_select;` 追加)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/fixtures/joinir_if_select_simple.program.json` (845 bytes)
**更新ドキュメント**
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/README.md` (Phase 34-2 セクション追加)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/TASKS.md` (34-2.1 〜 34-2.5 完了)
---
### 1-00h. Phase 34-3 — JoinIR Frontend local pattern 対応(**完了** 2025-11-27
**Phase 34-3 の目的**
- simple に続き、local pattern で「値としての if」を JoinIR Select で正規化
- simple と local で **JoinIR 出力が同じ** になることを実証
**実装内容**
1. **Program(JSON v0) 形式調査** (34-3.0)
- `src/runner/json_v0_bridge/ast.rs` で構造確認
- **発見**: Assign ードが存在しないLocal は宣言のみ)
- **方針**: simple と同じ構造で local pattern を表現
2. **Local pattern フィクスチャ作成** (34-3.1)
- `fixtures/joinir_if_select_local.program.json` 作成845 bytes
- 関数名: `IfSelectTest.local` (simple は `test`)
- 構造は simple と同じIf + Return
3. **AstToJoinIrLowerer リファクタリング** (34-3.2a)
- `lower_program_json` を関数名分岐の入口に変更
- 既存ロジックを `lower_simple_if` に切り出し
- match 式で `"test"` / `"local"` 分岐
4. **Local pattern lowering 実装** (34-3.2b)
- `lower_local_if` 関数追加(`ast_lowerer.rs` 132 lines
- JoinIR 出力は simple と同じSelect ベース):
```
Const v1 = 10
Const v2 = 20
Select v3 = cond ? v1 : v2
Ret v3
```
5. **テストハーネス拡張** (34-3.3)
- `joinir_frontend_if_select.rs` に local テスト追加
- `joinir_frontend_if_select_local_ab_test` 実装
- 検証: cond=1 → 10, cond=0 → 20simple と同じ)
**成果**
- ✅ テスト両方成功2 passed; 0 failed
- ✅ simple と local で JoinIR 出力が同じことを実証
- ✅ 「値としての if」の本質が Select であることを確認
**技術的意義**
- **設計の美しさ**: simple/local という異なる構文パターンが、同じ JoinIR Select に正規化される
- **PHI 不要の証明**: 値としての if は Select だけで表現可能PHI は不要)
- **JoinIR = 意味論の SSOT**: 構文の違いを吸収し、意味論を統一的に表現
**ガードレール遵守**
- ✅ 既定挙動は一切変更なし(テスト専用実装)
- ✅ JoinIR = PHI 生成器の原則維持Phase 33-10 原則継続)
- ✅ パターン外は panictiny テスト専用で合理的)
**次のステップPhase 34-4 以降)**
- Phase 34-4: Stage-1/Stage-B の純粋 if 関数への適用
- Phase 34-5: Loop/Break/Continue の AST→JoinIR 対応
**実装ファイル**
- `src/mir/join_ir/frontend/ast_lowerer.rs` (関数名分岐 + `lower_local_if` 追加)
- `src/tests/joinir_frontend_if_select.rs` (local テスト追加、140 lines)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/fixtures/joinir_if_select_local.program.json` (845 bytes)
**更新ドキュメント**
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/README.md` (Phase 34-3 セクション追加)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/TASKS.md` (34-3.0 〜 34-3.4 完了)
---
### 1-00i. Phase 34-4 — JoinIR Frontend Stage-1/meta 実用関数対応(**完了** 2025-11-27
**Phase 34-4 の目的**
- Stage-1/meta 実用関数(`JsonShapeToMap._read_value_from_pair/1`)でも simple pattern が JoinIR Select に正規化されることを実証
- 構造確認フェーズMethod 呼び出し意味論は Phase 34-5 で対応)
**実装内容**
1. **スコープ確定** (34-4.1)
- README/TASKS 更新
- 対象関数: `JsonShapeToMap._read_value_from_pair/1`
- フィクスチャ簡略化方針決定Int 10/20 ダミー値)
2. **フィクスチャ作成** (34-4.2)
- `fixtures/json_shape_read_value.program.json` 作成
- 簡略版: `if at { return 10 } else { return 20 }`
- 関数名: `_read_value_from_pair`
3. **AstToJoinIrLowerer 拡張** (34-4.3)
- `ast_lowerer.rs` の match 分岐に `"_read_value_from_pair"` 追加(**1行変更**
- `"test" | "local" | "_read_value_from_pair"` の形式
- `lower_if_return_pattern` 再利用(新規関数なし)
4. **テスト追加** (34-4.4)
- `joinir_frontend_if_select.rs` に json_shape テスト追加
- `joinir_frontend_json_shape_read_value_ab_test` 実装
- 検証: at=1 → 10, at=0 → 20
5. **ドキュメント更新** (34-4.5)
- TASKS.md チェックボックス更新
- CURRENT_TASK.md にこのセクション追加
**成果**
- ✅ テスト全3つ成功3 passed; 0 failed
- `joinir_frontend_if_select_simple_ab_test` (Phase 34-2)
- `joinir_frontend_if_select_local_ab_test` (Phase 34-3)
- `joinir_frontend_json_shape_read_value_ab_test` (Phase 34-4)
- ✅ **Phase 34-3 refactoring の完全勝利**: 1行追加だけで実装完了
- ✅ Stage-1/meta 実用関数でも simple pattern が Select JoinIR に正規化されることを実証
**技術的意義**
- **DRY 原則の美しさ**: Phase 34-3 refactoring により、Phase 34-4 は **1行追加だけ** で実装完了
- **構造確認成功**: 実用関数でも simple pattern 検証が可能
- **Phase 分離の明確性**: Phase 34-4構造確認と Phase 34-5意味論実装の境界が明確
**ガードレール遵守**
- ✅ 既定挙動は一切変更なし(テスト専用実装)
- ✅ JoinIR = PHI 生成器の原則維持
- ✅ パターン外は panictiny テスト専用で合理的)
**次のステップPhase 34-5 以降)**
- Phase 34-5: Method 呼び出し/Var 対応(`extract_value` 汎用化)
- Phase 34-6: MethodCall 構造の JoinIR への明示 + JoinIR→MIR 変換側での Method/Call 意味論実装
**実装ファイル**
- `src/mir/join_ir/frontend/ast_lowerer.rs` (match 分岐 1行追加、68行目)
- `src/tests/joinir_frontend_if_select.rs` (json_shape テスト追加、206 lines)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/fixtures/json_shape_read_value.program.json` (手書き)
**更新ドキュメント**
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/README.md` (Phase 34-4 セクション追加)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/TASKS.md` (34-4.1 〜 34-4.5 完了)
---
### 1-00j. Phase 34-5 — extract_value 汎用化 & 実用 if の意味論対応(**完了** 2025-11-27
**Phase 34-5 の目的**
- Int ダミー値 → 本物の式Var / Method 呼び出し)への段階的移行
- `extract_value` ヘルパ関数で式処理を統一化
- `JsonShapeToMap._read_value_from_pair/1` の実用パターン対応準備
**実装内容**
1. **式の形の調査** (34-5.1)
- `ast.rs` の `ExprV0` 定義確認
- 対応 expr 形: Int / Var / Method
- README.md に対応 expr 形を記載
2. **extract_value の設計** (34-5.2)
- `ExtractCtx` 構造体設計ValueId カウンタ + 変数名マップ)
- `extract_value` ヘルパ関数シグネチャ設計
- 戻り値: `(ValueId, Vec<JoinInst>)`
3. **extract_value 実装** (34-5.3)
- **Int literal**: 新しい dst 割り当て + Const 命令生成
- **Var 参照**: var_map から既存 ValueId 取得(命令なし)
- **Method 呼び出し**: pattern match 実装substring のみ、ダミー出力)
4. **lower_if_return_pattern リファクタリング** (34-5.4)
- 既存の Int 前提コード削除(`extract_int_value` 削除)
- `extract_value` ベースに統一
- ExtractCtx でパラメータ管理cond, at など)
5. **テスト拡張と docs 更新** (34-5.5)
- 既存 3 テスト全通過確認3 passed; 0 failed
- README/TASKS/CURRENT_TASK 更新
**成果**
- ✅ テスト全通過3 passed; 0 failed
- `joinir_frontend_if_select_simple_ab_test` (Phase 34-2)
- `joinir_frontend_if_select_local_ab_test` (Phase 34-3)
- `joinir_frontend_json_shape_read_value_ab_test` (Phase 34-4)
- ✅ **extract_value 統一**: 式処理を単一ヘルパ関数に集約
- ✅ **DRY 原則**: extract_int_value 削除、重複コード削減
- ✅ **Var 対応**: 変数参照が JoinIR で扱えるようになった
**技術的意義**
- **段階的移行成功**: Int ダミー → 本物の式Varへの移行完了
- **拡張性向上**: `extract_value` 統一により、今後の expr 拡張が容易
- **Method 準備完了**: 構造レベルの pattern match 実装済みJoinIR→MIR 側での Method/Call 意味論実装Phase 34-6 への準備)
**ガードレール遵守**
- ✅ Phase 34 frontend テスト専用(既定経路不変)
- ✅ JoinIR = PHI 生成器の原則維持
- ✅ 未対応パターンは panictiny テスト専用で合理的)
**次のステップPhase 34-6 以降)**
- Phase 34-6: MethodCall 構造の JoinIR への明示 + JoinIR→MIR 変換での Method/Call 意味論実装
**実装ファイル**
- `src/mir/join_ir/frontend/ast_lowerer.rs` (ExtractCtx + extract_value + リファクタリング、~90 lines 追加)
**更新ドキュメント**
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/README.md` (Phase 34-5 セクション + 実装完了情報)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/TASKS.md` (34-5.1 〜 34-5.5 完了)
---
### 1-00k. Phase 34-6 — MethodCall 構造と本物の substring 意味論(**完了** 2025-11-27
**Phase 34-6 の目的**
- MethodCall 構造を JoinIR に明示的に追加
- JoinIR→MIR 変換で Method 呼び出し意味論を実装
- `JsonShapeToMap._read_value_from_pair/1` の本物の substring 呼び出しを通す
**実装内容**
1. **MethodCall 構造追加** (34-6.1)
- `src/mir/join_ir/mod.rs` に `JoinInst::MethodCall` バリアント追加
- 構造: `{ dst, receiver, method, args }`
- コメント: "Phase 34-6: メソッド呼び出し構造。意味論は JoinIR→MIR ブリッジで実装"
2. **extract_value 更新** (34-6.2)
- `ast_lowerer.rs` の Method 処理を本物の MethodCall 生成に変更
- receiver/args を extract_value で再帰的に処理
- ダミー Const(0) → 本物の MethodCall 構造
3. **JoinIR→MIR 変換実装** (34-6.3)
- `join_ir_vm_bridge.rs` に MethodCall → BoxCall 変換追加
- `join_ir/json.rs` に MethodCall JSON シリアライゼーション追加
- `join_ir_runner.rs` に MethodCall 未対応エラー追加JoinIR Runner は使用しない)
4. **テスト更新** (34-6.4)
- フィクスチャ更新: Int 10/20 → 本物の substring 呼び出し
- テスト更新: `run_joinir_function` → `run_joinir_via_vm`JoinIR→MIR→VM ブリッジ)
- 検証: v="hello", at=3 → "hel" / v="world", at=0 → "world"
- **cond 処理修正**: ValueId(0) ハードコード → extract_value で取得
5. **ドキュメント更新** (34-6.5)
- README/TASKS/CURRENT_TASK 更新
**成果**
- ✅ テスト全通過1 passed; 0 failed
- `joinir_frontend_json_shape_read_value_ab_test`: 本物の substring 呼び出し成功
- ✅ **MethodCall 構造**: JoinIR で Method 呼び出しを明示的に表現
- ✅ **意味論分離**: JoinIR = 構造 SSOT、JoinIR→MIR = 意味論実装
- ✅ **VM 実行**: 既存の StringBox.substring が正しく呼ばれる
**技術的意義**
- **設計原則確立**: JoinIR は構造のみ保持、意味論は MIR レベルで実装
- **Phase 33-10 原則との整合性**: "JoinIR = PHI 生成器" と同じ原則を Method にも適用
- **拡張性**: 今後の Method 種類追加が容易MethodCall → BoxCall/Call 変換の1箇所で対応
**修正ファイル**
- `src/mir/join_ir/mod.rs` (MethodCall バリアント +8 lines)
- `src/mir/join_ir/frontend/ast_lowerer.rs` (Method 処理 +37 lines, cond 処理修正)
- `src/mir/join_ir_vm_bridge.rs` (MethodCall → BoxCall 変換 +12 lines, dst 型修正)
- `src/mir/join_ir/json.rs` (MethodCall JSON +16 lines)
- `src/mir/join_ir_runner.rs` (MethodCall 未対応 +7 lines)
- `src/tests/joinir_frontend_if_select.rs` (import 更新, run_joinir_via_vm 使用)
- `docs/.../fixtures/json_shape_read_value.program.json` (本物の substring 構造)
**更新ドキュメント**
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/README.md` (Phase 34-6 セクション)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/TASKS.md` (34-6.1 〜 34-6.5 完了)
**次のステップPhase 34-7 以降)**
- Phase 34-7: Loop/Break/Continue の AST→JoinIR 対応
---
### 1-00u. Phase 32 L-4.3a — llvmlite ハーネスでの JoinIR 実験(**完了** 2025-11-26
**目的**
- LLVM 経路で JoinIR が PHI 問題を解決できることを実行レベルで実証
- Route A (MIR→LLVM) vs Route B (MIR→JoinIR→MIR'→LLVM) の比較検証
**実装内容**
1. `src/config/env.rs` に `joinir_llvm_experiment_enabled()` 追加
- `NYASH_JOINIR_LLVM_EXPERIMENT=1` で有効化
2. `src/runner/modes/llvm.rs` に JoinIR フック追加
- `inject_method_ids` の直後で JoinIR 変換を試行
- `Main.skip/1` を JoinIR 経由で変換し、元のモジュールとマージ
- 戦略: 元の `Main.skip/1`PHI問題ありを削除 → `join_func_0` を `Main.skip/1` にリネーム
**テスト結果**
- **Route A** (MIR→LLVM): PHI error ❌
```
PHINode should have one entry for each predecessor of its parent basic block!
%phi_11 = phi i64 [ %.1, %bb2 ], [ %.1, %bb5 ]
```
- **Route B** (MIR→JoinIR→MIR'→LLVM): **成功 ✅**
```
[joinir/llvm] ✅ Merged module (6 functions)
✅ LLVM (harness) execution completed (exit=0)
```
**次のステップ**
- L-4.3b: ny-llvmc への移行は **Phase 40 (LLVM Native Backend)** で扱う
---
### 1-00w. Phase 32 L-4.3b — ny-llvmc での JoinIR 実験 → Phase 40 に役割移管(**完了** 2025-11-26
**Phase 32 での結論**
- ny-llvmc 側のコード変更は行わず、llvmlite ハーネス経由の JoinIR 実験L-4.3a)まで完了。
- **実証済み**: Route B (MIR→JoinIR→MIR'→LLVM) が llvmlite ハーネス経由で動作確認済み。
- **構造**: JoinIR→MIR' 変換は Rust 本体(`src/runner/modes/llvm.rs`で実施。ny-llvmc は「JoinIR→MIR' 後の MIR JSON」を受け取るだけで、JoinIR を直接理解する必要なし。
**Phase 40 への引き継ぎ**
- ny-llvmc 本体の最適化・native backend 化と併せて、JoinIR 経路の統合を行う。
- 現時点では「上流nyash 本体)で JoinIR→MIR' 変換を済ませ、ny-llvmc は llvmlite ハーネスを呼ぶだけ」という構造を維持。
---
**使用方法**
```bash
env NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 \
NYASH_DISABLE_PLUGINS=1 NYASH_LLVM_USE_HARNESS=1 \
NYASH_JOINIR_EXPERIMENT=1 NYASH_JOINIR_LLVM_EXPERIMENT=1 \
./target/release/hakorune --backend llvm apps/tests/minimal_ssa_skip_ws.hako
```
**成果**: JoinIR が LLVM 経路でも PHI 問題を設計的に解決できることを実証
---
### 1-00t. Phase 32 L-3.2 — Medium 優先度 PHI 箱の削除検討(**完了** 2025-11-26
**目的**
- Medium 優先度の PHI 箱if_phi.rs / if_body_local_merge.rs / phi_invariants.rs / conservative.rsの削除可能性を調査
- 各箱の call site を A/B/C に分類A: テスト専用、B: JoinIR 候補、C: 本線必須)
**調査結果**
1. **if_phi.rs**: 9箇所で使用すべてカテゴリ C
- lifecycle.rs, if_form.rs, phi.rs, conservative.rs, loop_builder.rs で MIR Builder の If/Else PHI 生成に必須
2. **if_body_local_merge.rs**: phi_builder_box.rs で使用PhiBuilderBox に依存)
3. **phi_invariants.rs**: phi_builder_box.rs で検証用として使用PhiBuilderBox に依存)
4. **conservative.rs**: phi_merge.rs, phi.rs で MIR Builder の PHI マージに必須(カテゴリ C
**結論: Phase 32 時点では削除不可**
- すべての箱が MIR Builder の If/Else PHI 生成で必須(カテゴリ C
- if_body_local_merge.rs と phi_invariants.rs は PhiBuilderBoxHigh 優先度)に依存
- JoinIR は **Loop 専門**で、If PHI はカバーしていない
- 削除は **Phase 33+ で If JoinIR 対応後**に再検討
**ドキュメント更新**
- `PHI_BOX_INVENTORY.md` に L-3.2 詳細分析を追記
- 各箱の call site 分類テーブルと削除不可理由を明文化
---
### 1-00v. Phase 31 — LoopToJoinLowerer 統一箱(**完了** 2025-11-26
**目的**
- MIR LoopForm → JoinIR 変換を1つの統一箱に集約
- 個別手書き lowererskip_ws, trim, append_defs, stage1を LoopToJoinLowerer 経由に移行
**実装内容**
1. `src/mir/join_ir/lowering/loop_to_join.rs` 新規作成
- `LoopToJoinLowerer` 構造体
- `lower()` メソッド(汎用)
- `lower_minimal_skip_ws_case_a()`, `lower_minimal_trim_case_a()`, `lower_minimal_append_defs_case_a()`, `lower_minimal_stage1_case_a()` 専用メソッド
2. 4つの lowerer を LoopToJoinLowerer 経由に変更
- `skip_ws.rs`, `funcscanner_trim.rs`, `funcscanner_append_defs.rs`, `stage1_using_resolver.rs`
**成果**: ループ→JoinIR変換の単一エントリポイント確立
---
### 1-00w. Phase 30 F-3 レガシー削除 — 旧API関数とCaseAContext::new削除**完了** 2025-11-25
**目的**
- F-3.0 で `_with_scope` パターンに全lowererが移行完了したため、不要になった旧API関数を削除
- `CaseAContext::new()` を削除し、`from_scope()` のみに統一
**削除内容**
1. **generic_case_a.rs**: 旧API関数4個と未使用import削除
- `lower_case_a_loop_to_joinir_for_minimal_skip_ws`
- `lower_case_a_loop_to_joinir_for_trim_minimal`
- `lower_case_a_loop_to_joinir_for_append_defs_minimal`
- `lower_case_a_loop_to_joinir_for_stage1_usingresolver_minimal`
- 未使用import: `LoopForm`, `LoopExitLivenessBox`, `LoopVarClassBox`, `MirFunction`, `MirQuery`
2. **loop_scope_shape.rs**: `CaseAContext::new()` 関数約85行削除
- 未使用import: `intake_loop_form`, `MirFunction`
3. **mod.rs**: `pub use generic_case_a::lower_case_a_loop_to_joinir_for_minimal_skip_ws;` 削除
4. **`#[allow(dead_code)]`除去**: 5関数から除去現在使用中のため不要に
**成果**: ビルド警告ゼロ、コードサイズ削減約150行
---
### 1-00x. Phase 30 F-2.0 — PHI 箱インベントリ作成と削除順計画(**完了** 2025-11-25
**目的**
- PHI 関連の箱・モジュールを一覧化
- 「どの箱をどの順番で消すか」を PHI_BOX_INVENTORY.md に記録
- コード削除はまだ行わず、設計と棚卸しのみ
**成果物**
1. **PHI_BOX_INVENTORY.md 作成**: `docs/private/roadmap2/phases/phase-30-final-joinir-world/`
- 一覧テーブル: 13箱 + 補助構造体11個
- 削除順ポリシー: 早期/中期/最終の3段階
- 外部依存箇所: loop_builder.rs, json_v0_bridge, join_ir/lowering
2. **TASKS.md 更新**: F-2.0 完了、F-2.1〜F-2.3 再整理
**削除順ポリシー要約**:
1. **早期削除** (F-2.1): HeaderPhiBuilder / ExitPhiBuilder / BodyLocalPhiBuilder / LoopPhiManager
- 外部呼び出しがテストのみ、JoinIR で完全代替済み
2. **中期削除** (F-2.2): PhiBuilderBox / LoopSnapshotMerge / if_phi 等
- F-3/F-4 完了後に削除
3. **最終削除** (F-2.3): LoopVarClassBox / LoopExitLivenessBox / LocalScopeInspectorBox
- LoopScopeShape 完全移行後に削除
**次手**: 早期削除候補とテスト専用箱は削除済み。残りの中期〜最終候補は Phase 32`phase-32-joinir-complete-migration`)で扱う。
---
### 1-00y. Phase 30 F-3.0 — skip_ws で LoopScopeShape 実データ運用開始(**完了** 2025-11-25
**目的**
- LoopScopeShape を lowering で実データ運用に切り替えるskip_ws を最初のケースとして)
- `_with_scope + *_core` パターンを確立し、他の lowerertrim/append_defs/Stage-1に展開する準備
**成果物**
1. **generic_case_a.rs 更新**:
- `lower_case_a_skip_ws_with_scope(scope: LoopScopeShape)` 新関数追加
- `lower_case_a_skip_ws_core(ctx: &CaseAContext)` 共通ロジック抽出
- 既存の `lower_case_a_loop_to_joinir_for_minimal_skip_ws` は `*_core` に委譲する薄いラッパーに
2. **CaseAContext::from_scope() API**: LoopScopeShape を直接受け取り、CaseAContext を構築
3. **コード品質向上**:
- 未使用インポート削除BinaryOperator, VarId
- Phase 30 準備コードに `#[allow(dead_code)]` 追加
- visibility 修正(`pub` → `pub(crate)`)で警告ゼロ達成
**確立されたパターン**:
```
LoopScopeShape → CaseAContext::from_scope() → lower_case_a_X_core() → JoinModule
```
**次手**: trim / append_defs / Stage-1 minimal に同じパターンを広げていく
---
### 1-00z. Phase 30 F-1/F-3/F-4.4 準備調査 — generic_case_a 本線化準備(**完了** 2025-11-25, Phase 32 view 切り替え継続中)
**目的**
- Task A: 各 lowerer ファイルの Case A 判定フックと generic_case_a 呼び出し状況を棚卸し
- Task B: VM runner のハードコード分岐の縮退計画を TASKS.md F-4.4 に追記
- Task C: 本セクションとして CURRENT_TASK.md に橋渡しを記録
**調査結果: Lowerer ファイルの Case A 対応状況**2025-11-25 更新)
| ファイル | generic_case_a 呼び出し | LoopScopeShape 使用 | 状態 |
|---------|------------------------|-------------------|------|
| skip_ws.rs | ✅ `lower_case_a_skip_ws_with_scope` | ✅ `_with_scope` 配線済み | **F-3.0 完了** |
| funcscanner_trim.rs | ✅ `lower_case_a_trim_with_scope` | ✅ `_with_scope` 配線済み | **F-3.0.3 完了** |
| funcscanner_append_defs.rs | ✅ `lower_case_a_append_defs_with_scope` | ✅ `_with_scope` 配線済み | **F-3.0.4 完了** |
| stage1_using_resolver.rs | ✅ `lower_case_a_stage1_usingresolver_with_scope` | ✅ `_with_scope` 配線済み | **F-3.0.5 完了** |
| stageb_body.rs | ❌(プレースホルダのみ) | ❌ | 未実装 |
| stageb_funcscanner.rs | ❌(プレースホルダのみ) | ❌ | 未実装 |
**発見事項**
1. 全ファイルで `NYASH_JOINIR_LOWER_GENERIC` 環境変数トグル + `is_simple_case_a_loop()` で Case A 判定
2. **skip_ws / trim / append_defs / Stage-1 の4箇所で LoopScopeShape 実データ運用開始**F-3.0.23.0.5 完了)
3. `CaseAContext::from_scope()` API 確立 — LoopScopeShape を直接受け取り
4. stageb_body / stageb_funcscanner は generic_case_a 実装がない(プレースホルダのみ)
**次手F-3/F-4.4 の継続)**
- ✅ F-3.0.2: skip_ws で `_with_scope` パターン確立 → **完了**
- ✅ F-3.0.3: trim 用 `_with_scope` 実装と呼び出し切り替え → **完了** (2025-11-25)
- ✅ F-3.0.4: append_defs 用 `_with_scope` 実装と切り替え → **完了** (2025-11-25)
- ✅ F-3.0.5: Stage-1 minimal 用 `_with_scope` 実装と切り替え → **完了** (2025-11-25)
- F-3.0.6:必要ならStageB minimal 用 `_with_scope`
- F-4.4 方向: VM runner の関数名分岐約90行を generic_case_all 完成後に縮退
- 縮退計画詳細: `docs/private/roadmap2/phases/phase-30-final-joinir-world/TASKS.md` F-4.4 に記録済み
補足Phase 32 での追記):
- Phase 32 Step 3-B で LoopScopeShape::from_existing_boxes_legacy が LoopShape の view (`LoopRegion` / `LoopControlShape` / `ExitEdge`) 経由に切り替わった。
- Phase 32 Step 3-C で LoopToJoinLowerer 側も minimal 4 本skip_ws / trim / append_defs / Stage1 minimalについて LoopRegion/LoopControlShape view を使用するよう統一済み。以降の JoinIR 汎用化L-1.x, L-2.xはこの view ベース API を前提に進める。
---
### 1-00c. Phase 32 L-1/L-2 — JoinIR 汎用化と Stage-1/Stage-B Bridge 進捗2025-11-26 更新)
**目的**
- Phase 30〜31 で整えた LoopToJoinLowerer / LoopScopeShape / JoinIR VM Bridge を、「minimal 4 本」から **本線Stage1 / StageB / selfhostライン**に広げていくための足場を Phase 32 で固める。
- CaseA 汎用ループを構造ベースで検出し、JoinIR lowering → JoinIR→VM Bridge までを一貫して扱える状態に近づける。
**対応フェーズ**
- Phase 32 L-1.x: LoopToJoinLowerer 汎用化CaseA 拡張)
- Phase 32 L-2.1: Stage1 UsingResolver 本線ループの JoinIR 化
- Phase 32 L-2.2 Step-1/2: StageB FuncScanner / BodyExtractor の JoinIR loweringVM Bridge への配線
**成果物L-1: LoopToJoinLowerer 汎用化)**
1. **is_supported_case_a_loop_view 構造ベース化**
- `LoopShape::to_region_view()` / `to_control_view()` / `to_exit_edges()` で LoopRegion / LoopControlShape / ExitEdge view を取得し、CaseA 判定を view ベースに移行。
- 判定ロジック:
- 単一出口(`control.exits.len() == 1`)であること。
- header の succ が 2 本while(cond) 相当)であること。
- pinned/carriers が空でないこと(実質的に状態を運ぶループに限定)。
- 旧 `is_supported_case_a_loop` はレガシーとして温存しつつ、実際の判定は `*_view` 側に集約。
2. **汎用 CaseA フラグ `NYASH_JOINIR_LOWER_GENERIC` 導入**
- 既定: OFFminimal 4 本: skip_ws / trim / append_defs / Stage1 minimal のみ LoopToJoinLowerer 対象)。
- `NYASH_JOINIR_LOWER_GENERIC=1` のとき:
- 関数名フィルタを外し、「構造チェックを満たすループ全般」を CaseA 候補として扱う。
- `LoopToJoinLowerer::lower()` 内で構造条件+環境変数を見て汎用パスを有効化。
3. **minimal 専用メソッドの整理**
- `lower_minimal_*_case_a` を `lower_case_a_for_*` にリネームし、「CaseA 汎用 lowerer の薄いラッパ」として役割を明示。
- 呼び出し元skip_ws / trim / append_defs / Stage1 minimalをすべて新名称に統一。
4. **テスト/確認**
- `joinir_runner_standalone_skip_ws` / `joinir_runner_standalone_trim` / `joinir_vm_bridge_trim_*` / JSON v0 スナップショット系テストは全て PASS。
- Stage1 / StageB については、`NYASH_JOINIR_LOWER_GENERIC` 有効時も lowering 自体は通ることを確認(意味論は簡略版のままなので VM 実行はまだ有効化しない)。
5. **ExitGroup ベースの Case-A 判定 refinementL-1.4 完了 2025-11-26**
- `src/mir/control_form.rs` に `ExitGroup { target, edges, has_break }` と `ExitAnalysis { loop_exit_groups, nonlocal_exits }` を追加し、ExitEdge の生配列から「出口ブロック単位のグループ」と「非局所 exitReturn/Throw 等)」を切り出すビューを実装。
- `analyze_exits(exits: &[ExitEdge]) -> ExitAnalysis` で:
- `loop_exit_groups`: ループ本体からループ外の同じ after-block へ出る出口グループExitKind::ConditionFalse / Break のみ)
- `nonlocal_exits`: ExitKind::Return / Throw / outer break など非局所 exit
に分類。
- `loop_to_join.rs` の `is_supported_case_a_loop_view` を ExitAnalysis ベースに差し替え:
- 旧ロジック: `control.exits.len() == 1` で単一 ExitEdge を要求していたため、複雑条件 break短絡 && でブロックが分かれる)を Case-A から外していた。
- 新ロジック: `exit_analysis.loop_exit_groups.len() == 1`(出口ブロック集合が 1 種類)かつ `exit_analysis.nonlocal_exits.is_empty()`(非局所 exit なし)であれば Case-A とみなすように変更。
- 効果:
- `if c != ' ' && c != '\t' && c != '\n' { break }` のような複雑条件 break を含むループでも、出口ブロックが 1 種類であれば Case-A として LoopToJoinLowerer/JoinIR lowering の対象にできるようになった。
- Stage1 JoinIR VM bridge テスト 4 件(`joinir_vm_bridge_stage1_*`)は全て PASS を維持。
- skip_ws 実物については、JoinIR 経由の PHI 形状は引き続き正しく表現できているが、VM 実行側では `StepBudgetExceeded`(既知の別問題)が残っているため、挙動は L-4/L-5VM / progress carrier まわり)のフェーズで継続調査する。
**成果物L-2.1: Stage1 UsingResolver 本線ループ)**
1. **本線ループを LoopToJoinLowerer 対象に昇格**
- 対象: `Stage1UsingResolverBox.resolve_for_source/5` 内の entries ループ(`lang/src/compiler/entry/using_resolver_box.hako:46-91`)。
- CFG から `construct_simple_while_loopform(entry_is_preheader=false, has_break=false)` で LoopForm を構築し、CaseA 条件while(i < n), break なし, 単一出口)を満たすことを確認。
2. **MIR-based lowering hook**
- `lower_from_mir()` 内で:
- 軽量 CFG チェックentry に `Const 0`、ArrayBox.length の存在など)を通過した場合に LoopForm を構築。
- `LoopToJoinLowerer::lower_case_a_for_stage1_resolver` を呼び、成功時は LoopToJoinLowerer 由来の JoinIR を優先採用。
- 失敗時/非対応時は `build_stage1_using_resolver_joinir()`handwritten JoinIRにフォールバック。
3. **JoinIR lowering の現状(簡略版)**
- `build_stage1_using_resolver_joinir()` は Phase 27.12 の最小版のまま:
- `entries.get(i)` → `prefix + entry` の文字列連結に限定。
- `should_emit` や path 解決など UsingResolver 本来のロジックはまだ JoinIR 側に持ち込んでいない。
- Phase 32 では「CaseA 構造PHI 形状が JoinIR 側で正しく表現できること」を優先し、意味論フル移植は L-2.x / Phase 29 L-3.x で段階的に進める前提。
4. **テスト**
- JSON スナップショット: 8/8 PASS`joinir_json_v0_stage1_usingresolver_min_matches_fixture` 含む)。
- VM Bridge: Stage1 は **JoinIR lowering 検証のみ**(後述の VM bridge dispatch にて実行は VM フォールバック)。
**成果物L-2.2 Step-1〜3: StageB FuncScanner / BodyExtractor**
1. **Step-1 — StageB lowering を LoopToJoinLowerer 経由に統一commit 9d769e92**
- 対象関数:
- `StageBBodyExtractorBox.build_body_src/2`
- `StageBFuncScannerBox.scan_all_boxes/1`
- 既存の StageB ループを CaseA 前提で LoopForm + LoopScopeShape view に載せ、LoopToJoinLowerer から JoinIR を生成する経路を追加。
- もともとの handwritten JoinIR がある場合も、Shared Builder Pattern`lower_from_mir` / `lower_handwritten` → `build_*_joinir`)に揃える方針は Stage1 と同じ。
2. **Step-2 — StageB を VM bridge dispatch に追加commit e61e7a2b**
- `src/mir/join_ir_vm_bridge_dispatch.rs` に StageB 向けエントリを追加:
- `try_run_stageb_body``StageBBodyExtractorBox.build_body_src/2` 用)
- `try_run_stageb_funcscanner``StageBFuncScannerBox.scan_all_boxes/1` 用)
- 挙動:
- JoinIR lowering を試みる(`lower_stageb_body_to_joinir` / `lower_stageb_funcscanner_to_joinir`)。
- JoinIR モジュールが生成できた場合は関数数などをログ出力し、**実際の実行は行わず `false` を返して VM 経路にフォールバック**。
- Stage1 UsingResolver と同一パターンJoinIR lowering の健全性検証専用ブリッジ)として運用。
- テスト:
- JSON v0 スナップショット: 6/6 PASSStageB 関連を含む)。
- `cargo build`: 成功、警告なし。
3. **Step-3 — StageB JoinIR→MIR 構造テスト追加commit 1eea4045**
- `src/mir/join_ir_vm_bridge.rs` の `convert_joinir_to_mir` を `pub(crate)` 化し、テストから直接呼び出し可能に。
- StageB 向けの構造テストを追加(ファイル名: `src/tests/joinir_json_min.rs` 内):
- `joinir_stageb_body_structure_test`
- `StageBBodyExtractorBox.build_body_src/2` について:
- Route A: 既存パイプラインで MirModuleBaselineを生成。
- Route B: `lower_stageb_body_to_joinir` → `convert_joinir_to_mir` で JoinIR→MIR Bridge の結果を取得。
- 両者とも対象関数が存在し、JoinIR 経由の MIR でも Block 数が 1 以上であることを確認。
- JoinIR lowering と JoinIR→MIR 変換がエラーなく通ることを確認。
- `joinir_stageb_funcscanner_structure_test`
- `StageBFuncScannerBox.scan_all_boxes/1` について同様のチェックを実施。
- 現時点の検証範囲:
- **handwritten JoinIR→MIR Bridge の健全性確認** が主目的(`build_stageb_*_joinir()` 由来)。
- まだ `intake_loop_form` が StageB ループ構造に非対応のため、「MIR→JoinIR roundtrip」の完全 A/B ではなく、「handwritten JoinIR を VM Bridge で安全に MIR に落とせているか」をテストする位置付け。
**VM Bridge 対応関数一覧Phase 32 時点のまとめ)**
- `Main.skip/1`:
- JoinIR lowering + JoinIR→MIR→VM 実行まで対応済み。
- `NYASH_JOINIR_EXPERIMENT=1` / `NYASH_JOINIR_VM_BRIDGE=1` で Route B を有効化trim と並ぶ最初期の A/B テスト対象)。
- `FuncScannerBox.trim/1`:
- JoinIR lowering + JoinIR→VM 実行まで対応済み。
- `NYASH_JOINIR_INPUT` で入力文字列を差し替え可能(未指定時 `" abc "`)。
- `Stage1UsingResolverBox.resolve_for_source/5`:
- JoinIR lowering 検証のみ。ArrayBox / MapBox の引数を JoinValue::BoxRef として橋渡しする部分がまだ未実装のため、実行は VM にフォールバックする設計。
- `StageBBodyExtractorBox.build_body_src/2`:
- NEW: JoinIR lowering 検証のみ。Stage1 と同様、JoinIR モジュール生成に成功しても実行は VM 経路に任せる。
- `StageBFuncScannerBox.scan_all_boxes/1`:
- NEW: JoinIR lowering 検証のみ。同上。
**制限と今後のタスク(忘れないためのメモ)**
- JoinIR lowering の多くは Phase 27.x 時点の「簡略版」のままで、本番ロジックshould_emit / path 解決 / JSON マージなど)はまだ .hako 側 MIR に残っている。
- Phase 32 では「CaseA 構造PHI 形状の正規化」を優先し、意味論フル移植は L-2.2 Step-3〜5 / L-2.3 / Phase 29 L-3.x で段階的に進める。
- Stage1 / StageB について JoinIR→VM 実行まで有効化するには:
- JoinValue::BoxRef と VM 側 Box インスタンスの橋渡し(引数マーシャリング)を明示的に実装する必要あり。
- JoinIR lowering を「簡略版」から「VM Route A と意味論一致する版」に引き上げ、A/B テストJSON / MIR / JoinIR / VM の比較)を整備する必要がある。
- Phase 32 TASKS のステータス2025-11-26 時点):
- L-1.1〜L-1.3: 完了LoopToJoinLowerer 汎用化の足場は整った)。
- L-2.1: 完了Stage1 UsingResolver 本線ループの JoinIR lowering + スナップショットテスト)。
- L-2.2: **全 Step 完了**StageB lowering 統一 + VM bridge dispatch + JoinIR→MIR 構造テスト + ドキュメント更新 + 安定化確認)
- Step-3: `convert_joinir_to_mir` を pub(crate) 化、Stage-B 構造テスト2本追加 (commit 1eea4045)
- Step-4: Phase 32 README / env リファレンスを更新し、StageB の JoinIR 利用範囲loweringBridgeのみと各トグルの役割を明文化
- Step-5: `NYASH_JOINIR_LOWER_GENERIC=1` 時の StageB lowering/bridge 安定性確認済み18 tests PASS、ガード追加不要。StageB 実行は依然として VM Route A
- L-2.3: **完了2025-11-26** — Route A vs Route B の A/B 比較で JoinIR が PHI 問題を設計的に解決していることを実行レベルで実証
- 最小ケース `minimal_ssa_skip_ws.hako` で `Main.skip(" abc")` を実行
- Route AVM 直接): 結果 `0` ❌PHI バグで値消失)
- Route BJoinIR: 結果 `Int(3)` ✅(正解)
- L-3.1 Step-1: **完了2025-11-26** — PHI レガシー箱の call site 分析
- PHI_BOX_INVENTORY.md を Phase 32 向けに更新(削除優先度 High/Medium/Low 分類)
- PhiBuilderBox / PhiInputCollector / LoopSnapshotMergeBox の call site を A/B/C 分類
- 結果: カテゴリ Aテスト専用はコメント参照 1 箇所のみ
- L-3.1 Step-2: **完了2025-11-26** — PHI 経路削除可能性分析
- loopform_builder.rs: PhiInputCollector × 3, LoopSnapshotMergeBox × 1 → 非 JoinIR 経路で必須(削除不可)
- json_v0_bridge/loop_.rs: PhiInputCollector × 1 → selfhost 経路で必須(削除不可)
- **結論**: JoinIR は既に PHI 箱をバイパス。削除は L-4JoinIR 本線化)完了後
- L-3.2 / L-3.3: これからJoinIR 本線化後に PHI 箱削除)。
- L-4.1/L-4.2: **完了2025-11-26** — VM Bridge テーブル化 & 本線明示
- `join_ir_vm_bridge_dispatch.rs` に Descriptor テーブル(`JOINIR_TARGETS`)を導入し、関数名→役割のマッピングを一元管理。
- `JoinIrBridgeKind` 列挙型: **Exec**JoinIR→VM 実行まで対応)と **LowerOnly**lowering 検証のみ、実行は VM Route Aを明確に区別。
- 対象関数ごとの状態:
| 関数 | Kind | デフォルト有効 | 状態 |
|-----|------|---------------|------|
| `Main.skip/1` | Exec | ❌ | PHI canary のため本線化しないenv 必須) |
| `FuncScannerBox.trim/1` | Exec | ✅ | **唯一の本線昇格候補**A/B 実証済み) |
| `Stage1UsingResolverBox.resolve_for_source/5` | LowerOnly | ❌ | 構造検証のみ |
| `StageBBodyExtractorBox.build_body_src/2` | LowerOnly | ❌ | 構造検証のみ |
| `StageBFuncScannerBox.scan_all_boxes/1` | LowerOnly | ❌ | 構造検証のみ |
- **結論**: 現状は **trim だけが「JoinIR 実行まで含めて安全」として昇格候補**。skip は PHI canary として残し、Stage-1/Stage-B は ArrayBox/MapBox 引数の JoinValue 対応が揃うまで LowerOnly。
- L-4.3: これからLLVM ライン JoinIR 実験: まず llvmlite ハーネスで JoinIR→MIR'→LLVM の最小ケース 1 本を通し、その後 llvmc 側にミラーする計画。いずれも dev トグル前提の実験扱い)。
---
### 1-00a. Phase 30 F-1.4 — LoopScopeShape API 確定(**完了** 2025-11-25
**目的**
- LoopScopeShape を「ループ変数スコープ情報の唯一のソース」に近づける
- Block IDs (header/body/latch/exit) を LoopForm から伝播
- CaseAContext::from_scope() で LoopScopeShape 直接受け取りAPI追加
- ユニットテスト追加8テスト全PASS
**成果物**
1. **LoopScopeShape 更新**: `src/mir/join_ir/lowering/loop_scope_shape.rs`
- Block IDs フィールド追加
- `from_existing_boxes()` シグネチャ更新LoopForm受け取り
- `CaseAContext::from_scope()` 新メソッド追加
2. **テスト追加**: 4本の新規テスト合計8テスト
- `test_from_scope_validation_header_eq_exit`
- `test_block_ids_preserved`
- `test_deterministic_order`
- `test_needs_phi_consistency`
3. **ドキュメント更新**: `docs/private/roadmap2/phases/phase-30-final-joinir-world/TASKS.md`
---
### 1-00b. Phase 29 小タスク — JoinIR/Stage-1 環境変数棚卸し(**完了** 2025-11-25
**目的**
- JoinIR/Stage-1 関連の環境変数を棚卸し・整理
- 実装の統一確認env helper経由
- ドキュメント更新
**成果物**
1. **ENV_INVENTORY.md 作成**: `docs/private/roadmap2/phases/phase-29-longterm-joinir-full/ENV_INVENTORY.md`
- JoinIR 環境変数 6個`NYASH_JOINIR_*`
- Stage-1 環境変数Primary 7個 + Aliases 16個
- 分類: user-facing / dev-only / internal / alias
2. **実装統一確認**
- JoinIR: `env_flag_is_1()` ヘルパー経由で統一済み9ファイル
- Stage-1: `src/config/env/stage1.rs` SSOT モジュール経由で統一済み
3. **ドキュメント更新**: `docs/reference/environment-variables.md`
- JoinIR セクション追加6変数 + 使用例)
**発見事項**
- `NYASH_RUN_JOINIR_MINIMAL` は既に削除済みgood
- Stage-1 alias群は警告付きで互換維持`warn_alias_once`
- テストファイルの直接env var参照はスキップガード用途で許容
---
### 1-01. Phase 26-E — PhiBuilderBox SSOT統一化**完了** 2025-11-22
**目的**
- PHI生成ロジックを単一責務箱PhiBuilderBoxに集約
- If/Loop両対応の統一インターフェース提供
- Conservative戦略 + BTreeSet/BTreeMap で決定性向上
**🎯 Phase 26-E 進捗状況**2025-11-22
- **✅ Phase 1**: PhiBuilderBox 骨格作成444行、ControlForm対応
- **✅ Phase 2**: If PHI生成完全実装Conservative戦略、決定的順序保証
- **✅ Phase 3**: PhiBuilderOps委譲実装has-a設計、ChatGPT+Claude合意
- **⏳ Phase 4**: Legacy削除loop_phi.rs 287行、将来タスク
**Phase 2 実装内容2025-11-22完了**
1. **PhiBuilderBox作成** (src/mir/phi_core/phi_builder_box.rs, 444行)
- If PHI生成: `generate_if_phis()` 完全実装
- Conservative戦略: void emission 含む完全対応
- 決定的順序: BTreeSet/BTreeMap で非決定性排除
2. **PhiBuilderOps trait** (7メソッド)
- 最小PHI生成インターフェース
- テスタビリティ向上(モック可能)
3. **loop_builder.rs 統合** (src/mir/loop_builder.rs)
- PhiBuilderOps trait 実装Ops構造体
- If PHI呼び出し箇所統合line 1136-1144
**Phase 3 実装内容2025-11-22完了**
1. **設計方針変更**: 継承is-a→委譲has-aに変更ChatGPT+Claude合意
- PhiBuilderOps = 低レベル「PHI命令発行」道具箱
- LoopFormOps = 高レベル「ループ構造構築」作業場
- 関係: has-a委譲が正しい設計異なる抽象レベル
2. **LoopBuilder委譲実装** (src/mir/loop_builder.rs, +64行)
- `impl<'a> PhiBuilderOps for LoopBuilder<'a>` 個別実装
- 明示的 trait 修飾で自己再帰回避: `<Self as LoopFormOps>::method()`
- HashSet → Vec 変換 + ソート(決定性保証)
- emit_phi 引数差吸収: set_current_block 経由
3. **設計文書更新** (src/mir/phi_core/loopform_builder.rs)
- has-a 設計根拠をコメント追加
- 継承試行の失敗理由を記録
**Phase 3 テスト結果2025-11-22**
- **決定性**: 10回実行で一貫した結果20% 成功率は既存 Loop PHI バグによるもの)
- **退行なし**: Phase 26-E 実装前と同じ成功率
- **If PHI 生成**: 100% 動作Test 2 で確認済み)
- **テスト詳細**:
- `mir_stage1_using_resolver_resolve_with_modules_map_verifies`: 2/10 成功20%、既存バグ)
- `mir_stage1_using_resolver_modules_map_continue_break_with_lookup_verifies`: 10/10 成功100%
**Phase 3 コミット**
- `b9a03429`: Phase 26-E-2 - PhiBuilderBox If PHI生成完全実装
- `e0be01c1`: Phase 26-E-3 - PhiBuilderOps委譲実装has-a設計
**削減見込み**
- Phase 2: -80行If側重複削除
- Phase 4: -287行loop_phi.rs Legacy削除
- **合計**: -367行純削減
**次のステップPhase 4**
- loop_phi.rs Legacy削除287行
- Loop PHI バグ調査20% 成功率問題)
- 100% 決定的テスト達成
**関連ファイル**
- [phi_builder_box.rs](src/mir/phi_core/phi_builder_box.rs) - 444行
- [loop_builder.rs](src/mir/loop_builder.rs) - PhiBuilderOps実装
- [exit_phi_builder.rs](src/mir/phi_core/exit_phi_builder.rs) - 779行Phase 26-D完成
- [header_phi_builder.rs](src/mir/phi_core/header_phi_builder.rs) - 548行Phase 26-C-2完成
---
### 1-00. Phase 21.7 — Static Box Methodization完了 2025-11-21, 既定ON に移行)
**目的**
- static box 内の呼び出しを NamingBox/Method まわりで一貫して扱えるようにする。
- Global("BoxName.method/arity") を Method{receiver = static singleton} に寄せる(トグル制御)。
**🎯 Phase 21.7++ 計画**2025-11-22 全フェーズ完了!🎊)
- **NamingBox SSOT 統一化チェックリスト**: [phase-21.7-naming-ssot-checklist.md](docs/development/current/main/phase-21.7-naming-ssot-checklist.md)
- StringUtils using 解決バグ修正2025-11-22, commit f4ae1445を踏まえた改善計画
- **✅ Phase 0観測ライン**: Silent Failure 根絶完了commit 63012932
- **✅ Phase 1基盤整備**: StaticMethodId SSOT 基盤確立commit 96c1345e
- **✅ Phase 2VM統一**: VM 名前解決 SSOT 準拠commit 1b413da5
- **✅ Phase 3全体統一**: Builder 側統一、素手 split 根絶commit c8ad1dae
- **✅ Phase 4ドキュメント**: README・トラブルシューティングガイド整備commit 806e4d72
- **累計工数**: 10時間進捗率: 50-67%
**Phase 0-4 実装内容2025-11-22 全完了)**
1. **Phase 0: 観測ライン緊急構築** (commit 63012932)
- TOML parse エラー即座表示pipeline.rs
- VM 関数ルックアップ「Did you mean?」提案global.rs
- using not found 詳細化strip.rs
- **効果**: Silent Failure 根絶、デバッグ時間が時間→分に短縮
2. **Phase 1: StaticMethodId SSOT 基盤** (commit 96c1345e)
- `StaticMethodId` 構造体導入naming.rs:86-248
- parse/format/with_arity ヘルパー実装
- 包括的テスト13ケース全PASS
- **効果**: 関数名パース/フォーマット一元化、型安全化
3. **Phase 2: VM 統一** (commit 1b413da5)
- global.rs を StaticMethodId ベース化
- デバッグログ強化NYASH_DEBUG_FUNCTION_LOOKUP=1
- テスト全通過349 passed, 退行なし)
- **効果**: arity バグ根治、Hotfix 卒業
4. **Phase 3: 全体統一** (commit c8ad1dae)
- unified_emitter.rs の methodization を StaticMethodId 化
- known.rs の split_once 全置き換え2箇所
- **効果**: 素手 split 根絶、Builder 側完全統一、コード50%削減
5. **Phase 4: ドキュメント整備** (commit 806e4d72)
- Phase 21.7 README に完了セクション追加60行
- トラブルシューティングガイド作成200+行、新規)
- チェックリスト進捗サマリー更新
- **効果**: 再発防止、開発者オンボーディング改善
**Phase 0-2 以前の実装内容**
1. **NamingBox decode 関数追加** (Step 1: commit a13f14ce)
- `decode_static_method(func_name: &str) -> Option<(&str, &str, usize)>`
- `is_static_method_name(func_name: &str) -> bool`
- "BoxName.method/arity" 形式をパースして (box_name, method, arity) に分解。
2. **Hotfix 7 修正** (Step 2: commit a13f14ce)
- `unified_emitter.rs` の receiver 追加ロジックを修正。
- StaticCompiler box_kind のメソッドで、static box method (decode可能な名前) の場合は receiver を追加しない。
- instance method のみ receiver を args の先頭に追加するようガード実装。
3. **Methodization 実装** (Step 3: commit b5cd05c2)
- `builder.rs`: `static_box_singletons: HashMap<String, ValueId>` フィールド追加。
- `unified_emitter.rs`: HAKO_MIR_BUILDER_METHODIZE=1 で有効化。
- Callee::Global(name) を decode して static box method 判定。
- シングルトンインスタンス (NewBox) をキャッシュ生成。
- Callee::Method{receiver=singleton} に変換。
**動作確認**
- デフォルト (OFF): `call_global Calculator.add/2` (既存挙動維持)
- トグル (ON): `new Calculator() → call %singleton.add(...)` (methodization)
- RC=0 両モード動作確認済み: `apps/tests/phase217_methodize_test.hako`
**環境変数**
- `HAKO_MIR_BUILDER_METHODIZE=0/1`: methodization 制御。既定ON未設定 or "1")、"0" のときのみ無効化。
- `NYASH_METHODIZE_TRACE=1`: Global→Method 変換ログ出力
**次タスクPhase 21.7++ Phase 3-4**10-15時間見込み
- **Phase 3: 全体統一**
- MIR Builder 側を StaticMethodId 統一builder/calls/unified_emitter.rs 等)
- 素手 split 置き換え(`rg '"\."' --type rust src/mir/builder/`
- 100-200 行削減見込み
- **Phase 4: ドキュメント化**
- SSOT 設計書更新
- 移行ガイド作成
- チームレビュー
- **長期**: NamingBox/UnifiedCallEmitter/VM の 3 点で「名前と arity の SSOT」完全統一。
### 1-02. Phase 27.4-A — JoinIR Header φ 統合LoopHeaderShape 導入、2025-11-23 完了)
**目的**
- HeaderPhiBuilder が担っていた「loop header φPinned/Carrier の合流」の意味を、JoinIR 側に構造として持ち上げる足場を作る。
- Rust 側の header φ 挙動は一切変えず、本線 MIR/LoopForm→VM を壊さないまま JoinIR 経路の設計を進める。
**やったこと**
- `src/mir/join_ir.rs` に `LoopHeaderShape { pinned, carriers }` を追加し、skip_ws / FuncScanner.trim のループについて:
- skip_ws: Pinned = [s, n], Carrier = [i]`loop_step(s, i, n)` の設計をコメント付きで固定)。
- trim : Pinned = [str, b], Carrier = [e]`loop_step(str, b, e)` の設計をコメント付きで固定)。
- Header φ の意味を「loop_step 引数としてどう表現するか」をコードとコメントで一致させた。
- `src/mir/phi_core/header_phi_builder.rs` に `NYASH_JOINIR_HEADER_EXP=1` フラグチェックを追加(現在はログのみ、挙動は不変)。
- JoinIR テストまわり:
- skip_ws / min / trim の JoinIR 型・変換テストを維持しつつ、trim 側は `trim_main + loop_step + skip_leading` の 3 関数構成にテスト期待を合わせた。
- `LoopHeaderShape` 用のミニテストを追加し、「to_loop_step_params() は pinned→carriers 順で返す」という契約を固定。
**状態**
- JoinIR 側では Header φ の意味が LoopHeaderShape で表現され、対象ループの `loop_step` 引数に反映済み。
- HeaderPhiBuilder は従来挙動のままPhase 27.4-C 以降でトグル付き縮退を予定)。
- すべての JoinIR テスト 7/7 が PASS、既存本線テストの緑度には影響なし。
---
### 1-03. Phase 27.4-C — HeaderPhiBuilder バイパス実験JoinIR 経路限定、2025-11-23 完了)
**目的**
- Header φ の意味は JoinIR 側LoopHeaderShapeloop_step 引数に持ち上がったので、JoinIR 実験経路に限って Rust 側 HeaderPhiBuilder をバイパスできるか試す。
- 本線 MIR/LoopForm→VM の挙動には一切触れず、JoinIR runner テスト専用の縮退ステップとして運用する。
**やったこと**
- `src/mir/phi_core/header_phi_builder.rs` に 2 つのヘルパーを追加:
- `joinir_header_experiment_enabled()` … `NYASH_JOINIR_HEADER_EXP=1` チェック27.4-B で導入)。
- `joinir_header_bypass_enabled()` … `NYASH_JOINIR_EXPERIMENT=1 AND NYASH_JOINIR_HEADER_EXP=1` の両方が ON のとき true。
- `src/mir/loop_builder.rs` で HeaderPhiBuilder 利用箇所にバイパスロジックを追加:
- 現在の関数名が `Main.skip/1` または `FuncScannerBox.trim/1` のとき、
- かつ `joinir_header_bypass_enabled()` が true のときのみ `emit_header_phis()` をスキップ。
- `NYASH_LOOPFORM_DEBUG=1` 時はデバッグログを出す。
- JoinIR テスト側skip_ws / trimに、「NYASH_JOINIR_HEADER_EXP=1 を併用すると Header φ bypass が有効化される」旨のコメントを追加。
- `docs/private/roadmap2/phases/phase-27-joinir/IMPLEMENTATION_LOG.md` に Phase 27.4-C セクションを追加し、対象関数・トグル条件・挙動を記録。
- `phase-27-joinir/TASKS.md` で 27.4-C を完了扱いに更新。
**状態**
- JoinIR runner 実験時に `NYASH_JOINIR_EXPERIMENT=1` `NYASH_JOINIR_HEADER_EXP=1` を立てた場合のみ、Main.skip/1 / FuncScannerBox.trim/1 の Header φ がスキップされる。
- このモードでは VM 実行は使わず、JoinIR runner だけで意味が保たれているかを確認するテストとして運用。
- 本線 MIR/LoopForm→VM の挙動は、トグル OFF 時には従来どおりHeader φ あり)のまま。
---
### 1-04. Phase 27.5 — JoinIR Exit φ 統合設計着手、2025-11-24 現在)
**目的**
- ExitPhiBuilder が担っている「exit φbreak/early-exit の合流」の意味を、JoinIR 側の `k_exit` 呼び出し+引数として表現できるようにする設計フェーズ。
- Rust 側の ExitPhiBuilder 挙動は変えず、本線 VM を壊さないまま minimal/trim の 2 ケースで Exit φ の意味を整理する。
**やったこと(設計メモ反映済み)**
- `docs/private/roadmap2/phases/phase-27.5-joinir-exit/README.md` を追加し、minimal_ssa_skip_ws と FuncScanner.trim_min の exit 経路を整理。
- minimal: break 経路は `i>=n` / `ch!=" "`, Exit φ は `i` だけ → JoinIR では `k_exit(i)` を想定。
- trim: break 経路は `!(e>b)` / `!is_space`, Carrier=`e`, Pinned=`str,b`。ExitShape は Option A: `[e]`(第一候補) / Option B: `[str,b,e]` を比較と記述。
- `docs/private/roadmap2/phases/phase-27.5-joinir-exit/TASKS.md` の A-1/A-2 を完了にし、B 以降LoopExitShape 型/コメント追加、JoinIR 変換への反映、テスト/ログ追記)はこれから。
**次の一手**
- LoopExitShape の型 or コメントを JoinIR に追加して、minimal/trim の exit 引数セットを明示。
- JoinIR 変換の exit 部分に「k_exit で何を合流させるか」のコメントを足し、必要なら命令並びを軽く整える(意味は変えない)。
- JoinIR テストと IMPLEMENTATION_LOG に Exit φ 観点のメモを追記。
---
### 1-05. Phase 26-F — Loop Exit Liveness / BodyLocal PHI Guard箱とガードの整備、2025-11-22 時点)
**目的**
- LoopForm v2 / Exit PHI まわりで、BodyLocal 変数の未定義利用を「箱」と「Fail-Fast」で確実に検知・抑制できるようにする。
- MIR スキャン(本物の Exit Livenessは次フェーズに送りつつ、構造と受け口と環境変数ガードだけ先に整える。
**やったこと26-F 初期完了分)**
- 4箱構成の整理と実装Exit PHI 専用レイヤ):
- `LoopVarClassBox``loop_var_classifier.rs`:
- 変数のスコープ分類専用Pinned / Carrier / BodyLocalExit / BodyLocalInternal
- ここでは「どこで定義されているか」だけを見て、PHI 発行は行わない。
- `LoopExitLivenessBox``loop_exit_liveness.rs` 新設):
- ループ exit 後で「生きている可能性のある変数」の集合を返す箱。
- Phase 26-F 時点では中身は保守的近似+ダミー、実運用は環境変数ガードで OFF。
- 環境変数:
- `NYASH_EXIT_LIVE_ENABLE=1` で将来の MIR スキャン実装を opt-in で有効化(既定は 0/未設定)。
- `NYASH_EXIT_LIVENESS_TRACE=1` でトレース出力。
- `BodyLocalPhiBuilder``body_local_phi_builder.rs`:
- `LoopVarClassBox` の分類結果と `LoopExitLivenessBox` の `live_at_exit` を統合し、「どの BodyLocal に exit PHI が必要か」を決める箱。
- 既定挙動(ガード OFF:
- これまで通り `class.needs_exit_phi()` のみを見るPinned / Carrier / BodyLocalExit
- ガード ON の挙動(まだ実験段階):
- `BodyLocalInternal` かつ `live_at_exit` に含まれ、かつ `LocalScopeInspector::is_available_in_all` が true なものだけ、追加で exit PHI 候補に昇格できる OR ロジックを持つ。
- `PhiInvariantsBox``phi_invariants.rs`:
- Exit/If PHI 最後の Fail-Fast 検証箱。
- 「全 pred で定義されているか」「不正な incoming が無いか」をチェックし、構造バグはここで止める。
- Docs 整備:
- `docs/development/architecture/loops/loopform_ssot.md` に 4箱構成と環境変数ガード方針を追記。
- `docs/private/roadmap2/phases/phase-26-F/README.md` を新設し、26-F のスコープやらないこと次フェーズMIR スキャン本体)への橋渡しを書き切り。
**テスト状況(ガード OFF 時点)**
- Phase 26-F-3 → 26-F の流れで、一時的に退行したが、
- `NYASH_EXIT_LIVE_ENABLE` を既定 OFF にし、
- BodyLocalInternal 救済ロジックをガード付きに戻したことで、
- F3 ベースラインより PASS が増え、FAIL が減る状態(例: 365 PASS / 9 FAILまで持ち直し済み。
- FuncScanner 系:
- `mir_funcscanner_skip_ws_direct_vm`
- `mir_funcscanner_parse_params_trim_min_verify_and_vm`
は引き続き「BodyLocal / Exit Liveness のカナリア」として使う(未定義値が出た場合は 26-G 以降で追う)。
**このフェーズで残っていること**
- `ExitLivenessProvider` 相当のインターフェースを `ExitPhiBuilder` 周辺に導入し、「ExitLiveness を差し替え可能」な受け口だけ整える(中身は Legacy のまま)。→ **完了**。`MirScanExitLiveness` も追加済み(現状は header/exit_snapshots の union を返す簡易版)。
- LoopFormOps / MirBuilder に MIR 命令列アクセスを追加する設計を 26-F の README にメモしておき、実装は 26-G 以降に分離する。
### 1-02+. Phase 26-G — Exit Liveness MIR Scan計画開始
- 26-F で作った差し替え口に、本物の use/def スキャン実装を載せるフェーズ。
- `NYASH_EXIT_LIVE_ENABLE=1` で MIR スキャン版を有効にし、FuncScanner カナリアskip_ws / parse_params_trimを緑にするのが目標。
- 新設 docs: `docs/private/roadmap2/phases/phase-26-G/README.md` に手順と受け入れ条件を記載済み。
### 1-03. Phase 25.3 — FuncScanner / StageB defs 安定化(完了)
**目的**
- StageB / FuncScanner ラインで defs が欠落したり Loop が脱落する問題を塞ぎ、selfhost 側の canary を緑に戻す。
**やったこと**
- StageBDriverBox.main:
- main 本文を `{…}` で包んだ block パーサ優先で Program(JSON) に組み立て、defs には `{"type":"Block","body":[…]}` 形式で埋め込むよう整理。
- Program パーサ fallback は `HAKO_STAGEB_PROGRAM_PARSE_FALLBACK=1` の opt-in に封じ込めskip_ws 崩れを回避)。
- StageBFuncScannerBox._scan_methods:
- block パーサ優先に統一し、Program パーサは `HAKO_STAGEB_FUNC_SCAN_PROG_FALLBACK=1` でのみ有効化。
- defs パラメータに必ず `me` を足す従来挙動は維持TestBox/Main いずれも同型で出力)。
- Rust 層の追加変更なしLoopForm v2 / LoopSnapshotMergeBox をそのまま利用)。
**結果**
- `tools/smokes/v2/profiles/quick/core/phase251/stageb_fib_program_defs_canary_vm.sh` が安定して PASS。
- `Program.kind == "Program"`
- `defs` に `TestBox.fib` / `Main.main` を保持していること。
- `TestBox.fib.body.body[*]` に `Loop` ノードが含まれること。
を満たした状態で `rc=0` になることを確認。
- FuncScanner / StageB 経由の fib defs ラインは LoopForm v2 + LoopSnapshotMergeBox 上で構造的に安定したとみなし、Phase 25.3 はクローズ。
- 次フェーズの入口が整理できたので、Stage1 UsingResolver ループRegion+next_i 形)と Stage1 CLI program-json/selfhost 導線に着手可能。
### 1-1. Phase 25.1m — Static Method / LoopForm v2 continue + PHI Fix完了
**目的**
- 静的メソッド呼び出し時の「暗黙レシーバ引数ずれ」バグと、LoopForm v2 経路における `continue` + header PHI の欠落を根本から直す。
**Rust 側(静的メソッド / 暗黙レシーバ / JSON v0 Bridge**
- `src/mir/function.rs::MirFunction::new`
- 暗黙 receiver 判定を是正し、**「第 1 パラメータが Box 型の関数だけ」をインスタンスメソッド with receiver** とみなす。
- 非 Box 型(`String`, `Integer` など)で始まるパラメータ列の関数は、暗黙レシーバなしの静的メソッド / Global 関数として扱うように変更。
- その結果:
- `static box TraceTest { method log(label) { ... } }` に対して `TraceTest.log("HELLO")` を呼ぶと、
`label` に `"HELLO"` が正しく入る(以前は `label = null` になっていた)。
- `src/mir/builder/decls.rs::build_static_main_box`
- `Main.main(args)` を「静的エントリ関数」に lower する経路を **`NYASH_BUILD_STATIC_MAIN_ENTRY=1` のときだけ有効** にし、
通常の VM 実行では wrapper `main()` を正規エントリとして扱うように整理。
- これにより、「`Main.main(args)` 版ではループが 1 度も回らず `return 0` で終わる」バグを解消。
- JSON v0 Bridge 経由の Box メソッドStage1/StageB defs:
- `src/runner/json_v0_bridge/lowering.rs` で `prog.defs` の降下ロジックを調整し、
- `box_name != "Main"` の関数定義をインスタンスメソッドとして扱って `signature.params` に「暗黙 `me` + 明示パラメータ」を載せる。
- `func_var_map` に `me` → `func.params[0]` を事前バインドし、残りのパラメータ名を `params[1..]` に対応づける。
- これにより、`Stage1UsingResolverFull._build_module_map()` のように JSON では `params: []` でも Hako 側で `me._push_module_entry(...)` を使う関数について、
Rust VM 実行時に `me` が未定義ValueId(0))になるケースを構造的に防止。
**LoopForm v2continue + header PHI**
- `src/mir/phi_core/loopform_builder.rs::LoopFormBuilder::seal_phis`
- シグネチャを `seal_phis(ops, latch_id)` → `seal_phis(ops, latch_id, &continue_snapshots)` に拡張。
- preheader と latch に加え、`LoopBuilder` 側で記録している **`continue_snapshots` からの値も header PHI の入力** として統合。
- pinned / carrier いずれも、header の全 predecessor:
- `(preheader, preheader_copy)`
- `(continue_bb, value_at_continue)`
- `(latch, value_at_latch)`
を入力として持つようになり、balanced scan など「continue を含むループ」の SSA が正しく構成される。
- `src/mir/loop_builder.rs::build_loop_with_loopform`
- `let continue_snaps = self.continue_snapshots.clone();`
- `loopform.seal_phis(self, actual_latch_id, &continue_snaps)?;`
- という形で、LoopBuilder → LoopForm 側に `continue` スナップショットを橋渡し。
**ControlForm / LoopShape invariant**
- `src/mir/control_form.rs::LoopShape::debug_validate`debug ビルドのみ)
- 既存の:
- `preheader -> header` エッジ必須
- `latch -> header` バックエッジ必須
- に加えて、次の invariant を追加:
- `continue_targets` の各ブロックから `header` へのエッジが存在すること。
- `break_targets` の各ブロックから `exit` へのエッジが存在すること。
- これにより、LoopForm / LoopBuilder が `continue` / `break` 経路を誤配線した場合に、構造レベルで早期検知できる。
**テスト / 検証**
- MIR ユニットテスト:
- `src/mir/phi_core/loopform_builder.rs::tests::test_seal_phis_includes_continue_snapshots`
- LoopFormBuilder 単体で「preheader + continue + latch」が PHI 入力に含まれることを固定。
- `src/tests/mir_stageb_loop_break_continue_verifies`
- StageB 風の `loop + break/continue` パターンで MirVerifier 緑。
- `src/tests/mir_stage1_using_resolver_verify.rs::mir_stage1_using_resolver_full_collect_entries_verifies`
- LoopForm v2/PHI v2 経路(現在は既定実装)で Stage1 UsingResolver フル版が MirVerifier 緑。
- 実行観測:
- 開発用 Hako `loop_continue_fixed.hako`:
- 期待 `RC=3` / 実測 `RC=3`、PHI pred mismatch なし。
- StageB balanced scan:
- `StageBBodyExtractorBox` のバランススキャンループに trace を入れて 228 回イテレーションが回ること、
`ch == "{"` ブランチで `depth` / `i` を更新 → `continue` → 次イテレーションに **確実に戻っている** ことを確認。
---
### 1-2. Phase 25.1k — LoopSSA v2 (.hako) & StageB harness 追従Rust 側はおおむね完了)
**目的**
- Rust 側の LoopForm v2 / ControlForm / Conservative PHI を SSOT としつつ、StageB / selfhost で使っている `.hako` 側 LoopSSA/BreakFinderBox/PhiInjectorBox をその規約に追従させる準備フェーズ。
**Rust 側でやったこと(サマリ)**
- Receiver / pinning:
- `CallMaterializerBox::materialize_receiver_in_callee` を事実上 no-op にし、
receiver の pinning / LocalSSA 連携を `receiver::finalize_method_receiver` に一本化。
- `MirBuilder` に `pin_slot_names: HashMap<ValueId, String>` を持たせ、
`LocalSSA.ensure` が「同じ slot にぶらさがる最新の ValueId」へ自動でリダイレクトできるようにした。
- Compiler Box の分類:
- `CalleeResolverBox::classify_box_kind` に `BreakFinderBox` / `PhiInjectorBox` / `LoopSSA` を追加し、
Stage1/StageB 用 LoopSSA 箱を `CalleeBoxKind::StaticCompiler` として明示。
**IfForm / empty else-branch の SSA fixStage1 UsingResolverFull 対応)**
- `src/mir/builder/if_form.rs`:
- `if cond { then }`else なし)のパターンで、
- else-entry 用に pre_if の `variable_map` から PHI ノードを生成したあと、
- その PHI 適用後の `variable_map` を `else_var_map_end_opt=Some(...)` として merge フェーズに渡すように修正。
- 以前は empty else の場合に `else_var_map_end_opt` が `None` になっており、
`merge_modified_vars` が pre_if の古い ValueId にフォールバックして、
merge ブロックで未定義の `%0` などを参照するケースがあった(`Stage1UsingResolverFull.main/0` の UndefinedValue
- 修正後は then/else 両ブランチで「PHI 適用後の variable_map」が merge に渡されるため、
empty else でも header/merge の SSA が崩れない。
- 検証:
- `src/tests/mir_stage1_using_resolver_verify.rs::mir_stage1_using_resolver_full_collect_entries_verifies` が
`MirVerifier` 緑になり、`Stage1UsingResolverFull.main/0()` の merge ブロックで PHI 後の値(例: `%24`)を正しく参照していることを MIR dump で確認済み。
**.hako 側の今後25.1k 後半)**
- `LoopSSA.stabilize_merges(json)` を Rust LoopForm v2 の Carrier/Pinned 規約に合わせて実装する(現在はほぼ stub
- StageB Test2`tools/test_stageb_min.sh`)で得られる Program(JSON v0) に対し、
- Rust 側で `NYASH_VM_VERIFY_MIR=1` を立てた実行結果と、
- `.hako` 側 LoopSSA v2 適用後の JSON → Rust 実行結果
を比較し、BreakFinderBox / PhiInjectorBox / LoopSSA の責務を切り分けていく。
---
### 1-3. Phase 25.1e/f/g — LoopForm PHI v2 / ControlForm 統合(サマリ)
- 25.1eLoopForm PHI v2 migration:
- Local SSA`local a = ...`)の ValueId 分離を完了し、LoopForm v2 を「PHI/SSA の正」とする方向へ寄せた。
- Stage1 UsingResolver / 基本的な StageB ループは、LoopForm v2 経路で MirVerifier 緑。
- 25.1fControlForm 層の導入):
- `ControlForm` / `LoopShape` / `IfShape` を導入し、Loop / If を共通ビューとして扱う箱を定義。
- ここでは挙動を変えず、「構造だけを先に固定」する方針で設計を固めた。
- 25.1gConservative PHI ↔ ControlForm ブリッジ):
- `phi_core::loopform_builder::build_exit_phis_for_control` など、ControlForm から Conservative PHI を呼び出す薄いラッパを追加。
- 既存の PHI ロジックはそのままに、将来の置き換えポイントだけを明示している。
---
### 1-4. Phase 25.1A3 — Stage1 CLI bridgestub 実装)
- Rust 側: `src/runner/stage1_bridge.rs` を `run_refactored` 入口に組み込み、`NYASH_USE_STAGE1_CLI=1` かつ再入ガードなしのときに `lang/src/runner/stage1_cli.hako` を子プロセスで起動する。`STAGE1_EMIT_PROGRAM_JSON` / `STAGE1_EMIT_MIR_JSON` / `STAGE1_BACKEND` / `STAGE1_PROGRAM_JSON` で mode 選択。entry override は `STAGE1_CLI_ENTRY` / `HAKORUNE_STAGE1_ENTRY`。
- .hako 側: `Stage1Cli` に最低限の本体を実装。
- `emit_program_json`: Stage1 UsingResolver で prefix を結合し、BuildBox.emit_program_json_v0 で Program(JSON v0) を返す。
- `emit_mir_json`: `MirBuilderBox.emit_from_program_json_v0` をそのまま呼ぶdelegate 未設定なら null
- `run_program_json`: backend==llvm の場合は `env.codegen.emit_object` まで通す。vm/pyvm は当面 MIR(JSON) を stdout に出すのみ(実行は Stage0 橋渡し未配線)。
- CLI: `emit program-json|mir-json` / `run --backend ... <src>` を受理。`NYASH_SCRIPT_ARGS_JSON` を JSON で best-effort 伝播。
- Docs: `docs/private/roadmap2/phases/phase-25.1/stage1-usingresolver-loopform.md` に stub 状態を追記run は暫定挙動)。
- Known gaps: vm/pyvm 実行はまだ Stage0 への橋渡し未着手。llvm も emit object 止まりlink/exec は後続)。
### 1-5. Phase 25.1A4 — Using SSOT 薄設計BuildBox include 除去
- SSOT: `lang/src/using/resolve_ssot_box.hako` に README 相当のコメントと I/Fresolve_modules/resolve_prefixを追加。現状は no-op だが「using をここで扱う」窓口を固定。
- Stage1UsingResolver: `resolve_for_program_json` を追加し、SSOT を呼ぶ導線だけ確保(現状は透過返し)。
- BuildBox: 先頭の `include` を削除し、`using lang.compiler.entry.bundle_resolver as BundleResolver` に置換StageB パーサが include を解せない問題の足場づくり)。
- 実行確認: `NYASH_ALLOW_NYASH=1 HAKO_ALLOW_NYASH=1 NYASH_USE_STAGE1_CLI=1 STAGE1_EMIT_PROGRAM_JSON=1 ... cargo run --bin hakorune -- basic_test.hako` が RC=0 で完走(ただし stdout は `RC: 0` のみ、program-json 出力は未配線)。`cargo run ... emit program-json` は Rust CLI 側の表面が未対応のためエラー(想定挙動)。
---
## 2. まだ残っている問題・課題2025-11-18 時点)
### 2-1. StageB 本体の型エラー(`String > Integer(13)`
- 症状:
- StageB の `Main.main` 実行時に、
`Type error: unsupported compare Gt on String(...) and Integer(13)` が発生。
- LoopForm v2 / PHI / continue 修正とは **独立の StageB 固有のロジック問題**。
- 想定される原因:
- StageB 側の body 構築 / JSON 生成で、本来数値比較にすべき箇所で「生の文字列」と整数を比較している。
- もしくは、Parser/Scanner が `len` やインデックスを文字列のまま扱っている部分がある。
- 対応方針(次フェーズ向けメモ):
- `StageBBodyExtractorBox.build_body_src` が吐く `body_src` を最小ケースで抽出し、
その中から問題の比較式(`>`)がどのように生成されているかを特定する。
- StageB の box レベルで:
- 「どのフェーズで型を決めるか」(例: ParserBox / StageB / VM 手前)を決めてから修正する。
- このタスクは 25.1c 続き or 新フェーズ25.1n 相当として、StageB 箱の設計側で扱う。
---
### 2-2. StageB 再入ガード `env.set/2` の扱い
- 現状:
- 一部 StageB コードが `env.set/2` を使った再入ガードに依存しており、
Rust VM 側には `env.set/2` extern が定義されていないため、
`❌ VM error: Invalid instruction: extern function: Unknown: env.set/2` が発生するケースがある。
- 方針メモ:
- 選択肢 A: StageB 再入ガードを Box 内 stateフィールドに寄せて、`env.set/2` 依存をなくす。
- 選択肢 B: StageB ハーネス専用に、最小限の `env.set/2` extern を Rust 側に実装する(本番経路では使わない)。
- 25.1m では構造修正Loop/PHI/receiverを優先し、この extern の話は据え置き。
---
### 2-3. .hako 側 LoopSSA v2Rust LoopForm v2 との乖離)
- 現状:
- `lang/src/compiler/builder/ssa/loopssa.hako` の `stabilize_merges()` はまだ実質的に stub に近い。
- StageB Test2`compiler_stageb.hako` 経由では、Rust MIR 側で LoopForm v2 / PHI v2 が安定した後も、
`.hako` 側 LoopSSA 経由の JSON から生成した MIR で PHI/SSA 問題が残る可能性がある。
- 目標:
- Rust LoopForm v2 を「制御構造と PHI の SSOT」とみなし、
`.hako` 側 LoopSSA が同じ Carrier/Pinned / preheader/header/exit の規約を JSON レベルで再現する。
- やること(フェーズ 25.1k 後半〜 25.1f/g 連携):
- 特定の関数(例: `BreakFinderBox._find_loops/2`を対象に、Rust 側 / .hako 側のそれぞれで生成されるループ構造を比較。
- LoopSSA v2 の中に:
- Carrier 変数の検出、
- pinned 変数の扱い、
- exit PHI の構築
を Rust LoopForm v2 に合わせて実装。
- 2025-11-19 追記:
- `BreakFinderBox._find_loops/2` については、まず .hako 側を「region box」的に整理した。
- `header_pos` / `header_id` / `exit_pos` / `exit_id` まわりの異常系を `continue` ではなく
`next_i` ローカルへの代入で表現し、1 イテレーションの末尾で `i = next_i` に合流させる形に変更。
- これにより、LoopForm v2 / LoopSSA 側から見ると「単一 region 内での分岐+最後に合流」という構造になり、
carrier/pinned 検出や今後の SSA 解析が行いやすくなった(挙動は従来と同じ)。
### 2-5. static box / me セマンティクス(観測タスクへ移行)
- 現状:
- `static box StringHelpers` のようなユーティリティ箱で、`me.starts_with(src, i, kw)` のように
同一箱内のヘルパー(`starts_with`)を `me.` 経由で呼んでいたため、Stage3 降下時に引数ずれが発生していた。
- 具体的には、`StringHelpers.starts_with_kw/3` → `StringHelpers.starts_with/3` の降下で
実際の呼び出しが `starts_with("StringHelpers", src, i, kw)` のような 4 引数形になり、
`starts_with(src, i, pat)` 側では `src="StringHelpers"` / `i=<ソース全文>` となって、
`if i + m > n` が `String > Integer(13)` 比較に化けていた。
- 対応(完了済み・局所修正):
- `lang/src/shared/common/string_helpers.hako` の `starts_with_kw` を、
`if me.starts_with(src, i, kw) == 0` から `if starts_with(src, i, kw) == 0` に書き換え、
static box ユーティリティに対する `me` 依存を除去した。
- これにより、`starts_with` 内でのガード比較 `i + m > n` はすべて整数同士となり、
StageB fib ケースで発生していた `String("...") > Integer(13)` の TypeError は解消済み。
- 今後Phase 25.1p 以降):
- static box 全般における `me` セマンティクス(本当に「シングルトンインスタンス」として扱う箱と、
純粋な名前空間箱をどう区別するかは、25.1p の DebugLog フェーズで観測しながら設計を詰める。
- 実際に Rust 層(`build_me_expression` / `lower_static_method_as_function` / `FunctionDefBuilder::is_instance_method`)を
統一規約に寄せる作業は、25.1p 以降のサブタスクとして扱う(現時点では局所修正でバグのみ解消)。
---
### 2-4. Builder / Selfhost まわりの残タスク(超ざっくり)
- Builder 内部ルート20.43 系):
- `MirBuilderBox` 経由の internal ルートで、MIR を stdout 経由ではなく一時ファイル / FileBox に書き出し、
ハーネスがそこから読む形に揃える案が残タスク。
- Selfhost CLI / Stage1 CLI:
- StageB / Stage1 CLI を「Rust VM / LLVM / PyVM / selfhost」の 4 経路で安定確認するラインは進行中。
- 25.1m では Rust VM + StageB balanced scan を優先し、CLI 全体は次のフェーズで詰める。
---
## 3. 次にやること(候補タスク)
ここから先は「どのフェーズを進めるか」をそのときの優先度で選ぶ感じだよ。
Rust 側は LoopForm v2 / StageB fib / Stage1 UsingResolver 構造テストまで一通り整ったので、当面は Stage1 CLI / selfhost ラインの小さな箱から進める。
### A. Stage1 CLI / Stage0 ブリッジPhase 25.1 — いまここ)
- A1: Stage1 CLI stub を Stage0 Rust ランナーから呼び出すブリッジDONE
- 実装: `src/runner/stage1_bridge.rs` `src/runner/mod.rs` に `maybe_run_stage1_cli_stub` を追加。
- `NYASH_USE_STAGE1_CLI=1`(既定 OFFかつ `NYASH_STAGE1_CLI_CHILD!=1` のときにだけ有効。
- entry `.hako` は `STAGE1_CLI_ENTRY` / `HAKORUNE_STAGE1_ENTRY` で上書き可能(既定: `lang/src/runner/stage1_cli.hako`)。
- 入力ファイル / モード:
- `STAGE1_EMIT_PROGRAM_JSON=1`: `hakorune emit program-json <source.hako>` を子プロセスで実行。
- `STAGE1_EMIT_MIR_JSON=1`: `STAGE1_PROGRAM_JSON` があれば `--from-program-json`、なければ `<source.hako>` から `emit mir-json`。
- 上記どちらも無い場合: `hakorune run --backend <backend> <source.hako>`backend は `STAGE1_BACKEND` か CLI の backend 設定)。
- `NYASH_SCRIPT_ARGS_JSON` を拾って `--` 以降の script 引数も Stage1 側に転送。
- 再入防止として子プロセスには `NYASH_STAGE1_CLI_CHILD=1` を付与(子側からは Rust ブリッジを素通り)。
- A2: Stage1 CLI skeleton の責務を doc に固定DONE
- `lang/src/runner/stage1_cli.hako` で `Stage1Cli.emit_program_json/emit_mir_json/run_program_json/stage1_main` のシグネチャとトグルトポロジーを固定。
- `docs/private/roadmap2/phases/phase-25.1/stage1-usingresolver-loopform.md` に Rust 側ブリッジの振る舞いとトグル名(`NYASH_USE_STAGE1_CLI` / `STAGE1_EMIT_*` / `STAGE1_BACKEND` / `NYASH_STAGE1_CLI_CHILD`)を追記。
- A3: 次ステップ(未着手)
- Stage1 CLI skeleton に StageB/BuildBox/MirBuilder 呼び出しを順に実装し、「Program(JSON)/MIR(JSON) を selfhost 経由で emit できる」状態まで持っていく。
- `tools/selfhost/run_stage1_cli.sh` から呼び出す selfhost パスと、Rust CLl からのブリッジパスの両方で JSON I/O 契約が同じになるように揃える。
### B. Stage1 UsingResolver / LoopForm v2 ラインPhase 25.1e 系フォロー)
- B1: entry UsingResolver の Region+next_i 化とコメント整備(おおむね DONE
- `lang/src/compiler/entry/using_resolver_box.hako` の 3 ループentries / JSON スキャン / modules_listは Region+next_i 形に揃え済み。
- 役割コメント: `resolve_for_source`StageB body_src を受けて prefix を返す)、`_collect_using_entries`JSON スキャンして entries を集める)、`_build_module_map`modules_list を map 化)を明記済み。
- `HAKO_STAGEB_APPLY_USINGS=0` の時は prefix を空 string にしつつ、depth ガードだけは走らせる仕様もコメントで固定。
- B2: UsingResolver 構造テスト(ループ形/PHIの拡充DONE
- `src/tests/mir_stage1_using_resolver_verify.rs` に Region+next_i ループと early-exit JSON スキャンパターンの軽量テストを追加。
- これらは cargo test 経路で常時緑を維持し、v2 quick スモークへの昇格は「実行時間とイズを見ながら後続フェーズで検討」という扱いにするdocs にメモ済み)。
- B3: pipeline_v2 UsingResolver との責務境界DONE
- `lang/src/compiler/pipeline_v2/using_resolver_box.hako` 冒頭に、「entry 側=ファイル I/O + using 収集」「pipeline_v2 側=modules_json 上の alias/path 解決」と役割メモを追加。
- RegexFlow ベースの単一路ループは Region 化不要stateful helperとし、LoopForm v2 の観点からも「観測対象外」とする。
### C. FuncScanner / Exit PHI カナリアPhase 26-F / 26-G への橋渡し)
- C1: FuncScanner trim/skip_ws/parse_params の最小再現ケース固定DONE
- `lang/src/compiler/tests/funcscanner_trim_min.hako` で `_trim` / `trim` / `skip_whitespace` を 1 回ずつ呼ぶ最小 Main を定義。
- `src/tests/mir_funcscanner_trim_min.rs` で:
- Stage3 / using 有効化したパーサ設定で func_scanner.hako + 上記テストを一体コンパイル。
- `MirVerifier` でモジュール全体の SSA/PHI を検証(`HAKO_MIR_BUILDER_METHODIZE=0` でも常に緑になることを確認)。
- VM 実行は `NYASH_TRIM_MIN_VM=1` のときだけ有効化(いまは MIR 側の根治が主目的)。
- C2: FuncScanner 側ロジックの構造整理DONE
- `lang/src/compiler/entry/func_scanner.hako`:
- `parse_params` を Region+next_i 形の 1 本ループに整理し、先頭スキップとカンマ探索をそれぞれ helper に寄せた。
- `trim` は先頭側を `skip_whitespace` に全面委譲し、末尾側のみ後ろ向きループで処理するように簡素化。
- `skip_whitespace` は `loop(i < n)` + if/continue だけにした最小形にし、過去の dev 向けログや loop(1==1) ワークアラウンドを撤去。
- これにより FuncScanner フロントは LoopForm v2 / LoopSSA v2 から見て「素直なループ+明確な next_i 形」になり、以後の PHI/ExitLiveness 側の根治作業が `.hako` に依存しづらい形になった。
- C3: Phase 26-F / 26-G のカナリアとして位置付け(進行中)
- 26-F 時点では `NYASH_EXIT_LIVE_ENABLE` 既定 OFF で、従来挙動のまま `mir_funcscanner_trim_min` が MIR verify 緑になることを確認済み。
- 26-G では:
- `NYASH_EXIT_LIVE_ENABLE=1` + MirScanExitLiveness 経由でも `mir_funcscanner_trim_min`(特に FuncScannerBox.trim/1 / skip_whitespace/2 / parse_params/1が常に緑になることを受け入れ条件にする。
- そのうえで `mir_funcscanner_skip_ws_direct_vm` / StageB / Stage1 UsingResolver 系のカナリアも順に緑に揃える。
### D. StageB / LoopSSA / Selfhost まわり(中期タスク)
- D1: StageB 再入ガード `env.set/2` の整理(据え置き)
- dev 専用 extern を Rust 側に追加するか、StageB 箱側で state に寄せるかを決める必要があるが、現在はループ/PHI ラインを優先し保留。
---
## 4. Phase 26-H — JoinIR 設計 & ミニ実験(新規フェーズ)
**目的**
- 制御構造if / loop / break / continue / returnを **関数呼び出し+継続** に正規化する中間層JoinIRを設計し、LoopForm v2 / PHI / ExitLiveness の負担を将来軽くする足場を作る。
- 25.1 / 26-F / 26-G の本線を止めずに、「設計+ごく小さな実験」だけを先に進める。
- スモーク/本線は既存の MIR/LoopForm 経路のまま維持しつつ、徐々に「関数型LoopFnIR/JoinIR」側に重心を移す。
**進捗26-H 完了分)**
- JoinIR 設計ドキュメント反映済み(`docs/development/architecture/join-ir.md`
- 26-H README/TASKS でスコープ・最終箱セット・次フェーズ境界を明記
- `src/mir/join_ir.rs` で JoinIR 型定義+ JoinIrMin 用のミニ自動変換を実装
- `apps/tests/joinir_min_loop.hako` + `src/tests/mir_joinir_min.rs`(トグル付きカナリア)を追加
- トグル: `NYASH_JOINIR_EXPERIMENT=1` で JoinIR 実験を有効化(デフォルトは既存 MIR/LoopForm のみ)
**次フェーズPhase 27 — JoinIR 実用化)**
- フォルダ: `docs/private/roadmap2/phases/phase-27-joinir/`
- 27.1: JoinIR 変換を FuncScanner/StageB の代表ループに拡張(トグル付き)
- 27.2: JoinIR → VM/LLVM ブリッジのプロトタイプを作り、A/B 実行を試す(トグル付き)
- 27.3: レガシー PHI/Loop 箱を段階削減Header/Exit/LoopPhi 系の吸収・削除計画を実行)
**やること26-H スコープ)**
- H1: JoinIR 設計ドキュメントの追加(**完了**
- `docs/development/architecture/join-ir.md` に命令セットと変換規則、対応表を記述済み。
- `docs/private/roadmap2/phases/phase-26-H/README.md` に、26-H のスコープ/やらないこと/他フェーズとの関係を記載済み。
- H2: JoinIR 型定義とミニ変換の骨格(**完了**
- `src/mir/join_ir.rs` に `JoinFunction/JoinInst/JoinContId/JoinModule` 等の型を定義済み。
- `lower_min_loop_to_joinir` で `JoinIrMin.main/0` 用の試験的な自動変換を実装Phase 27.x で一般化予定)。
- `src/tests/mir_joinir_min.rs` と `apps/tests/joinir_min_loop.hako` でカナリアテストを追加(`NYASH_JOINIR_EXPERIMENT=1` 時のみ有効)。
- H3: トグル付きミニ実験(**完了**
- `NYASH_JOINIR_EXPERIMENT=1` で JoinIR 実験テストを有効化。
- トグル OFF 時は既存の MIR/LoopForm 経路のみが動作することを確認(ゼロリグレッション)。
**やらないこと26-H では保留)**
- 既存の LoopForm v2 / PhiBuilderBox / ExitPhiBuilder を JoinIR ベースに全面移行すること。
- StageB / Stage1 / CLI / selfhost ラインの本線を JoinIR で差し替えること。
- 既存の SSA/PHI 実装を削除すること(全部別フェーズで検討)。
**優先度と位置付け**
- 本線(いま重視する順):
1. Phase 25.1 — Stage1 UsingResolver / Stage1 CLI program-json/mir-json を安定化。
2. Phase 26-F / 26-G — Exit PHI / ExitLiveness の根治LoopForm v2 / PHI SSOT / MirScanExitLiveness
- Phase 26-H は:
- 「本線の合間に進める設計フェーズ」として扱う。
- JoinIR が小さいケースでうまく動くことを確認できたら、27.x 以降で本格的な導入を検討する。
- このタスクに着手するときは「prod/CI 経路から完全に切り離した dev ガード」として設計する。
- C2: .hako LoopSSA v2 実装Rust LoopForm v2 への追従)
- Rust LoopForm v2 の Carrier/Pinned/BodyLocalInOut モデルを `.hako` の LoopSSA に輸入し、StageB Test2 / selfhost CLI でも MirVerifier 緑を目指す中〜長期タスク。
- 具体的な対象: `lang/src/compiler/builder/ssa/loopssa.hako` と StageB/BreakFinder 周辺の region 化済みループ。
- C3: Selfhost / CLI 周辺のテスト整理
- 代表的な selfhost / StageB / Stage1 CLI ケースを tests/tools 側でタグ付けquick/integration/selfhost、Phase 25.1 の「どこまでが Rust 側」「どこからが Stage1 側」を見える化する。
---
## 4. 履歴の見方メモ
- 以前の `CURRENT_TASK.md` は ~1900 行の長いログだったけど、読みやすさ重視でこのファイルはスナップショット形式にしたよ。
- 過去の詳細ログが必要になったら:
- `git log -p CURRENT_TASK.md`
- あるいは特定のコミット時点の `CURRENT_TASK.md` を `git show <commit>:CURRENT_TASK.md`
でいつでも復元できるよ。
---
# Phase 25.4 — Naming & Stage1 CLI Cleanupdesign only
- ねらい:
- static box / global 呼び出しの命名規約を NamingBox に集約し、VM 側のレガシーフォールバック経路を撤去する。
- Stage1 CLI の env/トグル解釈を 1 箇所の設定箱にまとめ、stage1_main の責務を薄く保つ。
- `__mir__.log` ベースの MIR ログ観測ポイントをドキュメントで一覧化し、将来の正式 API 化に備える。
- 現状:
- NamingBox`src/mir/naming.rs`は導入済みで、Builder 側の static メソッド名は `encode_static_method` 経由、VM 側の global 呼び出しは `normalize_static_global_name` 経由になっている。
- VM 側の「canonical 名で見つからなければ元名でもう一度探す」フォールバックは削除済みで、`mir_static_box_naming` テスト群が `Main._nop/0` 経路を固定している。
- Stage1 CLI は env-only 仕様argv 依存なし)で Stage0 ブリッジと接続済みだが、env 群の解釈はまだ stage1_main 内に散在している。
- このフェーズでやること(設計レベル):
- NamingBox を「static 名に触るすべてのコードの SSOT」として整理直接 `format!("Box.main")` する箇所を洗い出し)。
- `Stage1CliConfigBox`を設計し、env→Config 変換の責務とフィールドを docs に書き出す(実装は後続でも可)。
- `__mir__.log` のタグと用途を 1 ページの docs にまとめ、dev 用ログと残したい観測ログを分けておく。
---
以上が 2025-11-18 時点の Phase 21.8 / 25 / 25.1 / 25.2 / 25.4 ラインの「いまどこ」「なに済み」「なに残り」だよ。
次にどの箱から攻めるか決めたら、ここに箇条書きで足していこうね。
## 3. これからやるタスクのラフ一覧25.1 / Stage1 系)
ここから先は「まず設計と箱分割を書いてから実装」という方針で進めるタスク群だよ。
### A. Rust 層解析LoopForm v2 / JSON v0 / Stage1 観測)
- A-1: LoopForm v2 / LoopSnapshotMerge の入口確認
- `src/mir/loop_builder.rs` / `src/mir/phi_core/loopform_builder.rs` / `src/mir/phi_core/loop_snapshot_merge.rs` を「Stage1 から見た導線」として読み直し、どのレイヤで Carrier / Pinned / BodyLocalInOut が決まるかを short メモ化する。
- A-2: JSON v0 → MIR ブリッジの導線整理
- `src/runner/json_v0_bridge/lowering/`(特に `loop_.rs`を通して、Program(JSON v0).body / defs.body(Block) が LoopForm v2 までどう運ばれるかを図として docs に落とす。
- A-3: Stage1 UsingResolver の MIR 観測
- 既存テスト `src/tests/mir_stage1_using_resolver_verify.rs` の MIR dump を元に、「どのループが Region+next_i 化候補か」「既に問題なく LoopForm に乗っている場所はどこか」を箇条書きで整理する。
### B. Stage1 UsingResolver 箱化・ループ整理(.hako 側)
- B-1: Stage1UsingResolverBox の責務分割設計
- `lang/src/compiler/entry/using_resolver_box.hako` を、collect_entries / modules_map / file_read などの小さいロジック箱に概念的に分割し、「どの箱が何を責務とするか」を docs に書く(実際のファイル分割は後段)。
- B-2: すべてのループを Region+next_i 形に揃える計画
- entry の UsingResolver 内に残っている「pos++/continue 多発型」ループを洗い出し、Region+next_i 形にどう書き換えるかを phase-25.1 docs に追記する。
- 目的: LoopForm v2 / PHI から見たときに「1 region / 1 backedge / 明確な次位置決定」という形に統一する。
- 状況メモ: entry 側 3 ループentries/JSON scan/modules_listは Region+next_i 化済み。残りなし。
- B-3: pipeline_v2 UsingResolver との役割分担
- `lang/src/compiler/pipeline_v2/using_resolver_box.hako` と entry/Stage1UsingResolverBox のどちらが「テキスト / JSON / modules_map」のどこまでを担当するかを整理し、責務境界をドキュメントに固定する。
- 状況メモ: pipeline_v2 側は modules_json の alias 解決のみRegexFlow.find_from の単一路ループ。Region 化不要として据え置き、境界だけ明記。
- B-4: 構造テストの追加計画
- Region+next_i パターンの軽量 SSA テスト(すでに 1 本追加済み)を基準に、もう 1〜2 本、UsingResolver ソースに近いパターンJSON スキャンearly-exitを追加する方針を決める。
### C. Stage1 CLI / program-json / mir-json SelfHost 準備
- C-1: Stage1 CLI インターフェースの設計メモ
- `.hako → Program(JSON)` / `Program(JSON) → MIR(JSON)` / `MIR(JSON) → 実行` を Stage1 側からどう呼び出すか(関数名・引数レベル)を docs に先に書く。
- C-2: StageB → Stage1 データフロー図
- `compiler_stageb.hako` → Program(JSON v0) → Stage1 UsingResolver → MirBuilder までのパイプラインを Phase25.1 ドキュメントに 1 枚の図としてまとめる。
- C-3: Rust CLI 側ブリッジの最小ガード案
- Stage0/Rust CLI は「Program(JSON/MIR(JSON) を受け取り VM/LLVM に流すだけ」に縮退させる方針と、既定 OFF トグルselfhost 入口)をどう切るかを設計メモとして追加。
### D. 将来フェーズ向けメモvariable_map 決定化ライン)
- D-1: MirBuilder::variable_map / BoxCompilationContext::variable_map BTreeMap 化案
- どの構造を HashMap→BTreeMap 化するか、どのテスト(`mir_funcscanner_skip_ws_vm_debug_flaky` など)で決定性を確認するかを Phase25.x の設計メモとして書いておく。
- D-2: dev 専用 / flaky テストの扱い方針
- どのテストが dev ignore手動実行用で、いつ/何を満たしたら常時有効に戻すかを、このファイルと関連 README に明確に残す。
このセクションは「すぐ実装する TODO ではなく、25.1〜25.x ラインで踏むべき設計タスク一覧」として使う予定だよ。
(静的 me-call 修正の参考メモはここに残しつつ、別セクションは整理済み)
### E. Legacy Loop/PHI 経路の囲い込みと削除準備
- E-1: Legacy loop_phi.rs の役割と削除条件の明文化
- ファイル: `src/mir/phi_core/loop_phi.rs`
- 状態: LoopForm v2 + LoopSnapshotMergeBox への移行後は「legacy scaffold」としてのみ残存。
- やること:
- 現在の利用箇所を 2 種類に分類する:
- 本線経路LoopForm v2 / Stage1 / StageB から参照される部分)
- 互換レイヤ解析専用JSON v0 bridge, 旧 smokes, dev-only ヘルパ)
- 本線はすべて `loopform_builder.rs` + `header_phi_builder.rs` + `loop_snapshot_merge.rs` で賄えることを確認し、`loop_phi.rs` を「legacy 専用(新規利用禁止)」として CURRENT_TASK と docs に固定しておく。
- Phase 31.x の cleanup で実ファイル削除してよい条件(参照 0対応する smokes/テストの移行完了)を `docs/private/roadmap/phases/phase-31.2/legacy-loop-deletion-plan.md` 側と揃えておく。
- E-2: PHI/LoopForm 周辺で HashMap を使ってよい/いけない場所を線引き
- ファイル: `src/mir/builder.rs`, `src/mir/phi_core/*`, `src/mir/loop_builder.rs`
- やること:
- 「PHI 生成とスナップショット決定」に関わる構造では `BTreeMap` / `BTreeSet` / `Vec+sort` のみに限定し、`HashMap` は使わない、というルールを Phase 25.1 docs に明記する。
- それ以外のメタ情報plugin sigs, weak_fields など)は HashMap 維持可とし、「決定性」に影響しないことをコメントで示しておく。
- 代表として `loop_phi.rs` 内の `sanitize_phi_inputs` のように「一度 HashMap で集約してから sort する」パターンは、LoopForm v2 正系統では `PhiInputCollector` + BTree 系で代替されていることを確認し、legacy 側のみに閉じ込める。
- E-3: Legacy 経路の一覧と新規利用禁止ポリシーを docs に反映
- ファイル:
- `docs/private/roadmap2/phases/phase-25.1/README.md`(本線側のルール)
- `docs/private/roadmap/phases/phase-31.2/legacy-loop-deletion-plan.md`(削除計画側と整合させる)
- やること:
- Legacy として扱うモジュール(例: `phi_core::loop_phi`, 一部旧 JSON v0 bridge helperを一覧にして、「新しいコードからここを呼ばない」「Phase 31.x で削除予定」と明記する。
- 逆に、今後 PHI/Loop/If で使うべき SSOT 箱LoopForm v2 + HeaderPhiBuilder + BodyLocalPhiBuilder + if_phi + ControlFormを 1 セクションで列挙し、「ここだけを見ると設計が分かる」導線を作る。
### F. IfForm / Body-Local PHI 統合Phase 26-F 進捗メモ)
- F-1: IfBodyLocalMergeBox の導入(完了)
- ファイル: `src/mir/phi_core/if_body_local_merge.rs`
- 責務: if-merge 専用の body-local PHI 候補決定。
- 両腕に存在する変数のみを候補とし、`pre_if` から値が変化した変数だけを返す。
- empty else の場合は空リストを返し、従来の PhiBuilderBox ロジックに委ねる。
- F-2: LoopBuilder 内 if-merge への統合(進行中)
- ファイル: `src/mir/loop_builder.rs`, `src/mir/phi_core/phi_builder_box.rs`
- 状態: Phase 26-F-2 までで、loop 内 if の PHI 生成に IfBodyLocalMergeBox を噛ませるところまで実装。
- 代表テスト 1 本が新たに PASS になったが、Loop PHI 側に残る domination errorValue %48 / non-dominating useがまだ存在。
- メモ: この残件は LoopForm Exit PHI 側ExitPhiBuilder/LoopFormBuilderの古い Case に起因する既知バグとして扱い、次フェーズで Loop PHI 側の SSOT 化PhiBuilderBox への統合 or ExitPhiBuilder 根治)の入口とする。
## 3. Phase 25.1q — LoopForm Front UnificationDONE / follow-up 別タスクへ)
- 目的: Rust AST ルート (LoopBuilder) と JSON v0 ルート (`json_v0_bridge::lower_loop_stmt`) のループ lowering を “LoopFormBuilder + LoopSnapshotMergeBox” に一本化し、どの経路からでも同じ SSOT を見るだけで良い構造に揃える。
- 完了状態:
- AST ルート:
- `LoopBuilder::build_loop_with_loopform` で canonical `continue_merge_bb` を常時生成し、`continue_target` を header ではなく `continue_merge_bb` に統一済み。
- `LoopFormBuilder::seal_phis` / `LoopSnapshotMergeBox` の continue/exit 入力は「preheader + continue_merge + latch」「header + break snapshots」で完全管理。
- JSON v0 ルート:
- `loop_.rs` で `LoopFormJsonOps` を実装し、preheader/header/body/latch/continue_merge/exit の block ID を AST ルートと同じ形で生成。
- break/continue/exit の snapshot を `LoopSnapshotMergeBox` でマージし、canonical continue_merge → header backedge を JSON 側でも採用。
- `tests/json_program_loop.rs` で JSON v0 だけを入力にした軽量ループ(通常 / continue / body-local exitを `MirVerifier` で確認するスモークを追加。
- Docs/README:
- `docs/private/roadmap2/phases/phase-25.1q/README.md` に「AST/JSON ともに LoopForm v2 + LoopSnapshotMergeBox が SSOT」と明記。
- `src/runner/json_v0_bridge/README.md` で “bridge は薄いアダプタであり、新しい PHI 仕様は loopform 側でのみ扱う” とガードを追記。
- `src/mir/phi_core/loop_phi.rs` には “legacy分析用のみ” コメントを追加。将来の cleanup (Phase 31.x) で削除対象とする。
- 残タスクは別フェーズへ:
- Stage1 UsingResolver 周りの SSA バグ(`tests::mir_stage1_using_resolver_verify::mir_stage1_using_resolver_full_collect_entries_verifies` の Undefined Valueは 25.1q の範囲外。LoopForm の SSOT 化は終わっているため、今後は Stage1 側の PHI/Env スナップショット設計タスクとして切り出す。
- JSON v0 → Nyash AST への統合案や loop_phi.rs の実ファイル削除は、Phase 31.x cleanup 計画側で扱う。