Files
hakorune/CURRENT_TASK.md
nyash-codex aa0b3972b9 docs(phase40-2): CURRENT_TASK.md Phase 40-2セクション追加
Phase 40-2設計フェーズ完了記録:
- パイプライン移行設計(現状→目標)
- 対象関数確定(array_ext.filter)
- 経路切替ポイント(loop_builder.rs:1069, 1075)
- A/Bテスト方針
- 削除条件(3条件)

コード変更: なし(docs-only)
次のステップ: Phase 40-3マイグレーションフェーズ

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 10:13:53 +09:00

2278 lines
154 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-00y. Phase 35-5 — PHI Box Reduction第1波削除HIGH安全度 430行**完了** 2025-11-28
**実施内容**
- Phase 34 JoinIR Frontend 実装完了を受け、証拠に基づく段階的削除を実施
- **if_body_local_merge.rs** (339行): PhiBuilderBox に吸収(外部呼び出し 0
- **phi_invariants.rs** (91行): JoinIR Verifier に移譲1 callsite のみ)
**技術的成果**
1. **frontend_covered カラム追加**: PHI_BOX_INVENTORY.md で進捗可視化
- `none`, `tiny_only`, `stage1_partial`, `isolated`, `full` の 5 段階
2. **Runner 経路統一文書化**: Route A (JoinIR→MIR→VM) を SSOT として確立
- Route B (direct Runner) は構造検証専用に限定
3. **安全な削除実行**: 調査報告書に基づく証拠駆動の段階的削除
- if_body_local_merge.rs: ロジックを `compute_modified_names_if()` に inline
- phi_invariants.rs: Fail-Fast チェックを削除JoinIR Verifier が責務を継承)
**テスト結果**
- ✅ cargo build --release: クリーンビルド430行削減達成
- ✅ JoinIR Frontend tests: 全PASSPhase 34 機能退行なし)
- ✅ PHI 関連テスト: 全PASSjoinir_frontend_if_select, test_if_merge_simple_pattern 等)
**削除対象外Phase 36+ へ延期)**
- **MEDIUM 安全度**: LoopSnapshotMergeBox (470行)、PhiBuilderBox (970行)
- **LOW 安全度**: conservative.rs / if_phi.rs (483行、11 重要呼び出し箇所)
- **アーキテクチャリファクタ**: Classifier Trio → LoopScopeShape 吸収 (1,352行)
**総削減ポテンシャル**: ~3,705行Phase 35: 430行 + Phase 36+: 3,275行
**関連ドキュメント**
- `docs/private/roadmap2/phases/phase-35-phi-reduction/README.md` (Phase 35 戦略)
- `docs/private/roadmap2/phases/phase-35-phi-reduction-investigation-report.md` (調査根拠)
- `docs/private/roadmap2/phases/phase-30-final-joinir-world/PHI_BOX_INVENTORY.md` (削除記録)
---
### 1-00z. Phase 38 — If-Side PHI Level 1 削除LOW安全度 90行**完了** 2025-11-28
**目的**
- Phase 37 設計完了を受け、Level 1Tiny/pure If JoinIR Frontend 完全カバー)の削除を実施
- 目標: 68行削減14%)、実績: 90行削減28.6%
**削除内容**
1. **merge_modified_with_control** (51行, dead code)
- Phase 25.1g の実験的 ControlForm wrapper未使用
- 呼び出し箇所: 0完全なデッドコード
- リスク: ZERO
2. **extract_assigned_var** (39行, JoinIR AST lowering に置換済み)
- If/else AST からの変数名抽出pre-analysis hints 用)
- 呼び出し箇所: 4 (if_form.rs:2, phi.rs:2)
- 置換: `None` で統一JoinIR AST lowering が責務を継承)
- リスク: LOWPhase 33 IfSelect 完全カバー検証済み)
**技術的成果**
1. **JoinIR カバレッジ検証**: extract_assigned_var を `None` 返しに置換してもテスト全PASS
2. **デッドコード完全削除**: merge_modified_with_control の 0 呼び出し確認
3. **段階的削除戦略実証**: Phase 37 設計の 3 レベル削減計画が有効と確認
**テスト結果**
- ✅ cargo build --release: クリーンビルド90行削減達成
- ✅ PHI tests: 58/58 PASS退行なし
- ✅ JoinIR Frontend tests: 37/38 PASS1 失敗は pre-existing test ordering issue
- ✅ Full lib tests: 399-400/460 PASSbaseline 10-12 non-deterministic failures confirmed unrelated
**ファイル変更**
- `src/mir/phi_core/if_phi.rs`: 315行 → 225行90行削減、28.6%
- `src/mir/builder/if_form.rs`: extract_assigned_var 呼び出し 2箇所を `None` に置換
- `src/mir/builder/phi.rs`: extract_assigned_var 呼び出し 2箇所を `None` に置換
**Phase 35-38 累計削減**: 605行430+107+68 → 430+107+90に上方修正
**Phase 39+ 削減ポテンシャル**: 415行Level 2: 115行, Level 3: 300行
**次のステップPhase 39**
- **Level 2 削減** (MEDIUM 安全度, 115行)
- 前提条件: Stage-1/Stage-B 代表関数 1-2個を JoinIR Frontend 経由で A/B テスト成功
- 削除候補: `collect_assigned_vars`, `compute_modified_names`, `merge_with_reset_at_merge_with`
- conservative.rs: 30行縮退struct inline化
**関連ドキュメント**
- `docs/private/roadmap2/phases/phase-37-if-phi-reduction/` (Phase 37 設計、Phase 38 実施記録)
- `docs/private/roadmap2/phases/phase-38-if-phi-level1/README.md` (Phase 38 詳細)
- `docs/private/roadmap2/phases/phase-30-final-joinir-world/PHI_BOX_INVENTORY.md` (削減記録更新)
---
### 1-00m. Phase 40-2 — array_ext.filter JoinIR統合設計docs-only**完了** 2025-11-28
**目的**
- Phase 40-1インフラ完成を受け、旧AST→MIRパスをJoinIR経路に折り畳む設計を確定
- array_ext.filterに特化した経路切替ポイント・A/Bテスト方針・削除条件を明確化
- Phase 40-3実装フェーズへの具体的な橋渡し
**設計内容**
**パイプライン見取り図作成**:
- **現状パイプライン(レガシー経路)**:
```
AST → MIR Builder (loop_builder.rs)
→ collect_assigned_vars() (Line 1069, 1075)
→ PHI生成 → VM/LLVM
```
- **目標パイプラインJoinIR SSOT**:
```
AST → Program (JSON v0)
→ JoinIR Frontend (AstToJoinIrLowerer)
→ JoinModule + JoinFuncMetaMap
→ JoinIR→MIR Bridge (convert_with_meta)
→ VM/LLVM
```
- **利点**: JoinIR Frontendがif-in-loop変数追跡のSSOT、PHI生成がBridgeに集約、二重管理解消
**対象関数と経路マッピング**:
- **対象関数**: array_ext.filter唯一Phase 39選定済み、8/10点
- **パターン**: if-in-loop with conditional modification`if fn(v) { out.push(v) }`
- **経路切替ポイント**:
- loop_builder.rs:1069 (then body) → JoinFuncMeta.if_modified_vars
- loop_builder.rs:1075 (else body) → JoinFuncMeta.if_modified_vars
- **現状責務**: collect_assigned_vars(&then_prog, &mut vars)でHashSet<String>蓄積→exit PHI生成
- **目標責務**: JoinFuncMeta.if_modified_vars = {"out"}→convert_with_meta()でexit PHI生成
**JoinIR Frontend入口API設計**:
- **使用API**: Phase 40-1作成済みの`lower_loop_with_if_meta(program_json) → (JoinModule, JoinFuncMetaMap)`
- **JoinFuncMeta使い方**:
```rust
JoinFuncMeta {
if_modified_vars: Some(HashSet::from(["out".to_string()])),
// "out"はif文内で変更される変数
}
```
- **載せる変数**: `out`if文内で`out.push(v)`により変更)
- **載せない変数**: `i`if文内で変更なし、`v`local変数、exit不要
**二重管理防止ポリシー**:
- **SSOT宣言**: array_ext.filterについては
- if-in-loop modified変数集合のSSOT: `JoinFuncMeta.if_modified_vars`
- exit PHI生成のSSOT: `convert_with_meta()`
- `collect_assigned_vars`: 削除候補Phase 40-4
- **現時点**: 両経路併存A/Bテストのため
**JoinIR→MIR Bridge利用設計**:
- **切り分けロジック**:
```rust
if is_array_ext_filter(func_name) {
convert_with_meta(module, meta) // Route B
} else {
convert_join_module_to_mir(module) // Route A
}
```
**A/Bテスト設計**:
- **Route A**: AST → MIR Builder → collect_assigned_vars → PHI → VMレガシー
- **Route B**: AST → JoinIR Frontend → JoinFuncMeta → convert_with_meta → VMSSOT
- **比較対象**:
1. 実行結果: 入力`[1,2,3,4,5,6]`, predicate`x % 2 == 0` → 期待出力`[2,4,6]`
2. Exit PHI形状: dumpしたときのφの有無と形ソースブロック数・変数名一致
- **成功基準**: 実行結果一致 + Exit PHI形状一致 + 5回連続PASS
**array_ext.filter専用削除条件**:
1. **A/Bテスト安定PASS**: Route A == Route B、Exit PHI形状一致、5回連続PASS
2. **JoinIR経由のみでPHI生成**: array_ext.filterのループPHIはJoinIR→MIR bridgeだけで生成
3. **callsite unused確認**: `rg collect_assigned_vars`でarray_ext.filter絡みの分岐が完全にunused
**削除タイミング**:
- **Phase 40-3**: 経路切り替え実装、A/Bテスト実行
- **Phase 40-4**: 上記条件すべて満たした後に削除
**Phase 40-3での dev flag 案**:
```rust
// loop_builder.rs:1069, 1075
if std::env::var("NYASH_LEGACY_COLLECT_ASSIGNED_VARS").is_ok() {
collect_assigned_vars(&then_prog, &mut vars); // Route A
} else {
// JoinFuncMetaから取得 // Route B
}
```
**技術的成果**
1. **パイプライン移行設計確定**: 現状レガシー→目標JoinIR SSOTの具体的変換手順明確化
2. **二重管理防止ポリシー確立**: SSOT宣言により責務分離明確化
3. **A/Bテスト方針確定**: Route A vs Route B比較基準、成功基準、PASS回数5回
4. **array_ext.filter専用削除条件定義**: 3条件チェックリスト化
**成果物**
- ✅ README.md: パイプライン見取り図(~70行
- ✅ README.md: 対象関数と経路切替ポイント表(~60行
- ✅ README.md: JoinIR Frontend入口API設計~50行
- ✅ README.md: 二重管理防止ポリシー(~20行
- ✅ README.md: Bridge利用設計~30行
- ✅ README.md: A/Bテスト設計~50行
- ✅ deletion_criteria_checklist.md: array_ext.filter専用削除条件~50行
- ✅ PHI_BOX_INVENTORY.md: Phase 40-2更新~50行
**Rustコード変更**
- **ゼロ**docs-only、Phase 40-2完遂
**追加行数合計**: ~380行ドキュメント
**Phase 35-40-2 累計削減**: 605行Phase 35: 430行, Phase 38: 90行, Phase 39: 0行設計, Phase 40-2: 0行設計
**Phase 40-3/4 削減ポテンシャル**: 115行Level 2、MEDIUM安全度
**次のステップPhase 40-3**
- **経路切り替え実装**: loop_builder.rs:1069, 1075にdev flag追加
- **JoinIR Frontend拡張**: array_ext.filter向けif-in-loop variable tracking実装
- **A/Bテスト実行**: Route A vs Route B完全検証
- **callsite unused確認**: `rg collect_assigned_vars`で分岐unused確認
**関連ドキュメント**
- `docs/private/roadmap2/phases/phase-40-if-phi-level2/README.md` (Phase 40-2詳細、~330行追加)
- `docs/private/roadmap2/phases/phase-39-if-phi-level2/deletion_criteria_checklist.md` (Phase 40-2更新、~50行追加)
- `docs/private/roadmap2/phases/phase-30-final-joinir-world/PHI_BOX_INVENTORY.md` (Phase 40-2結論、~50行追加)
---
### 1-00l. Phase 39 — If側PHI Level 2削減準備設計中心、docs-only**完了** 2025-11-28
**目的**
- Phase 37設計 → Phase 40実装の橋渡し
- Stage-1/Stage-B代表関数を具体選定し、JoinIR Frontend拡張の具体設計を行う
- Level 2削除基準を抽象的基準から検証可能チェックリストに精緻化
**設計内容**
**Phase 37成果深堀り**:
- コンテキスト詳細分析: if-in-loop vs ループ外if使い分け明確化
- JoinIR Frontend coverage: 現在30%collect_assigned_vars、50%compute_modified_names、80%merge_with_reset_at_merge_with
- 削減ブロッカー特定: if-in-loop variable capture70% gap、conservative strategy migration50% gap、reset semantics20% gap
**Stage-1/Stage-B代表関数選定**:
- 候補リスト: 5個の関数を評価array_ext.filter, json_cur.skip_ws, json_cur.read_quoted_from, byte_cursor.find_bytes, test_assert.assert_true
- 評価マトリックス: 実装難易度1-5点、削減効果1-3点、collect_assigned_vars依存度0.5-2点、合計10点満点
- 最優先選定: **array_ext.filter**スコア8/10、if-in-loop with conditional push
- パターン: loop内でif文による配列の条件付き追加`if fn(v) { out.push(v) }`
- PHI生成: loop exit PHI for `out` variable
- collect_assigned_vars直接テスト: HIGH dependency
- 次点: **json_cur.read_quoted_from**スコア7/10、包括的検証用
- パターン: guard if + loop + if-in-loop with break + string accumulation
- 用途: 一次実装後の包括的検証(複雑パターン全カバー)
**JoinIR Frontend拡張設計**:
- 追加機能: if-in-loop AST lowering with variable tracking
- AST lowering変更:
- `extract_if_in_loop_modified_vars()`: loop body内のif文から変更変数抽出~40行
- `extract_assigned_vars_from_body()`: AST再帰走査で代入変数検出~40行
- `lower_loop_with_body()` 拡張: 変数追跡metadata付きJoinIR生成
- JoinIR命令拡張: 不要既存Select/Jump/Call/Retで対応可能
- MIR lowering変更: `lower_loop_to_mir()` で loop exit PHI生成~40行
- 合計追加見込み: ~120行Phase 40実装時
**削除条件精緻化**:
- Level 2チェックリスト: 検証可能基準Prerequisites 5項目、Safety Checks 3項目、Deletion Safe Conditions 4段階
- 検証手順:
- Procedure 1: collect_assigned_vars Callsite Analysis2 callsites表形式分析
- Procedure 2: A/B Test Execution6 step手順、identical output確認、performance ≤110%
- Procedure 3: Grep-Based Safety Checkdeletion前後のgrep検証、undefined symbol確認
- 削除順序: Phase 40-1collect_assigned_vars 32行→ 40-2compute_modified_names 26行 + conservative migration→ 40-3merge_with_reset_at_merge_with 27行→ 40-4conservative.rs inline 30行
**技術的成果**
1. **具体的実装計画**: Phase 37抽象設計→Phase 39具体設計→Phase 40実装の橋渡し完成
2. **証拠ベース選定**: 3段階評価候補リスト→評価マトリックス→最優先選定で最適な代表関数選択
3. **検証可能基準**: 抽象的基準("Stage-1/B代表1-2関数成功"→チェックリスト化具体的Prerequisites/Safety Checks/Procedures
**成果物**
- ✅ callsite_context_analysis.md: 呼び出しコンテキスト詳細if-in-loop vs ループ外if、なぜ呼ばれるか、JoinIR代替策
- ✅ gap_analysis.md: JoinIR Frontend未カバー範囲明確化パターンギャップ3個、機能ギャップ3個、実装見込み180行
- ✅ representative_function_candidates.md: 候補5個→最優先2個選定Primary: array_ext.filter 8/10点、Secondary: json_cur.read_quoted_from 7/10点
- ✅ joinir_extension_design.md: AST/JoinIR/MIR拡張具体設計~120行追加見込み、A/B test plan 5 fixtures
- ✅ deletion_criteria_checklist.md: 検証可能削除基準Prerequisites 5項目、3 Verification Procedures、4-phase deletion safe conditions
- ✅ deletion_sequence_detailed.md: Phase 40-1→40-4詳細手順各phase毎の8 step手順、rollback plan、expected reduction
**Rustコード変更**
- **ゼロ**docs-only、Phase 39完遂
**Phase 35-39 累計削減**: 605行Phase 35: 430行, Phase 38: 90行, Phase 39: 0行設計のみ
**Phase 40 削減ポテンシャル**: 115行Level 2、MEDIUM安全度
**Phase 40+ 削減ポテンシャル**: 300行Level 3、HIGH安全度
**次のステップPhase 40**
- **Phase 40-1**: collect_assigned_vars削除32行
- 前提: array_ext.filter JoinIR Frontend実装 + A/B test PASS
- 実装: if-in-loop AST lowering (~80行) + loop exit PHI generation (~40行)
- 検証: Procedure 1/2/3 完全実行
- **Phase 40-2**: compute_modified_names削除26行+ conservative migration
- 前提: Phase 40-1完了 + JoinIR Verifier conservative analyzer実装
- 実装: JoinIrConservativeAnalyzer (~60行)
- **Phase 40-3**: merge_with_reset_at_merge_with削除27行
- 前提: Phase 40-1/2完了 + IfMerge reset semantics実装
- **Phase 40-4**: conservative.rs struct inline30行
- 前提: Phase 40-1/2/3完了 + ConservativeMerge → JoinIrConservativeAnalyzer移行
**関連ドキュメント**
- `docs/private/roadmap2/phases/phase-39-if-phi-level2/README.md` (Phase 39 overview)
- `docs/private/roadmap2/phases/phase-39-if-phi-level2/callsite_context_analysis.md` (callsite詳細)
- `docs/private/roadmap2/phases/phase-39-if-phi-level2/gap_analysis.md` (未カバー範囲)
- `docs/private/roadmap2/phases/phase-39-if-phi-level2/representative_function_candidates.md` (代表関数選定)
- `docs/private/roadmap2/phases/phase-39-if-phi-level2/joinir_extension_design.md` (JoinIR拡張設計)
- `docs/private/roadmap2/phases/phase-39-if-phi-level2/deletion_criteria_checklist.md` (削除基準)
- `docs/private/roadmap2/phases/phase-39-if-phi-level2/deletion_sequence_detailed.md` (Phase 40手順)
- `docs/private/roadmap2/phases/phase-30-final-joinir-world/PHI_BOX_INVENTORY.md` (Phase 39完了記録更新)
---
### 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-7 — tiny while ループの AST→JoinIR 対応(**完了** 2025-11-27
**目的**
- ASTProgram JSON v0の Loop ノードを JoinIR に lowering する frontend 実装
- Phase 31 の LoopToJoinLowererMIR→JoinIRと同型の JoinIR 生成を達成
- AST→JoinIR→MIR→VM の完全な経路を実証
**実装内容**
1. `ast_lowerer.rs` に `lower_loop_case_a_simple()` 実装3関数構造: entry/loop_step/k_exit
- entry: 初期化i=0, acc=0→ Call(loop_step)
- loop_step: Jump(k_exit, cond=!(i<n)) で早期 return → body 処理 → Call(loop_step) で末尾再帰
- k_exit: Ret acc
2. `extract_value()` に Binary/Compare 対応追加(算術演算・比較演算)
3. Local ード処理実装SSA スタイルの再代入)
**技術的発見**
- **Jump vs Call セマンティクス**: Jump = 早期 return条件付き関数脱出、Call = 末尾再帰(関数呼び出し)
- **k_next パラメータ**: Phase 31 でも常に `None`JoinIR→MIR bridge が未対応)
- **SSA スタイル再代入**: Program(JSON v0) の同名 Local 宣言は新 ValueId を生成var_map 自然更新)
**テスト結果**
- ✅ n=0 → 0, n=3 → 3, n=5 → 5 すべて PASS
- ✅ `joinir_frontend_loop_simple_ab_test` 実行成功(`run_joinir_via_vm` 経由)
**更新ドキュメント**
- `src/mir/join_ir/frontend/ast_lowerer.rs` (lower_loop_case_a_simple 実装, 246行)
- `src/tests/joinir_frontend_if_select.rs` (test 追加)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/TASKS.md` (Phase 34-7 完了記録)
**次のステップ**
- Phase 34-8: Break/Continue 付きループへの拡張
- Phase 34 完了後: AST→JoinIR frontend を標準経路に統合
---
### 1-00g. Phase 34-8 — Break/Continue 付きループの AST→JoinIR 対応(**完了** 2025-11-27
**目的**
- Break/Continue を含む tiny while loop を AST→JoinIR に lowering
- 制御フロー構造を完全に JoinIR で表現
- Phase 31 の LoopToJoinLowerer と同型の JoinIR 生成
**実装内容**
1. `ast_lowerer.rs` に Break/Continue pattern の lowering 実装
- Break: Jump(k_exit, cond=i>=n) で早期 return
- Continue: Select条件付き値更新+ Call末尾再帰
- 3関数構造entry/loop_step/k_exitを維持
2. JSON v0 フィクスチャ作成break/continue 2パターン
3. テスト追加JoinIrFrontendTestRunner 使用)
**技術的発見**
- **Continue に k_continue 継続は不要**3関数構造で十分
- Continue = 「値更新をスキップして末尾再帰」
- Select 命令で条件付き値更新を表現
- Break = Phase 34-7 の早期 return と完全同型
- Phase 31 (MIR→JoinIR) と Phase 34 (AST→JoinIR) が同じ JoinIR を生成
**テスト結果**
- ✅ Break: n=5 → acc=10, n=3 → acc=3 PASS
- ✅ Continue: n=5 → acc=12, n=2 → acc=3 PASS
- ✅ 全6テストPhase 34-2/3/6/7/8PASS
**更新ドキュメント**
- `src/mir/join_ir/frontend/ast_lowerer.rs` (Break/Continue lowering 追加, 約200行)
- `src/tests/joinir_frontend_if_select.rs` (2テスト追加)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/README.md` (Phase 34-8 追加)
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/TASKS.md` (Phase 34-8 完了)
**次のステップ**
- Phase 34-9: ネストループ・複雑な式への拡張
- Phase 35: PHI 箱削減JoinIR Frontend が主経路化したら実施)
**マイルストーン達成**
If/Loop/Break/Continue の最小パターンが全て AST→JoinIR→MIR→VM で説明可能になり、Phase 35 での PHI 箱削減の布石が完成。
---
### 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+行、新規)
- チェックリスト進捗サマリー更新
- **効果**: 再発防止、開発者オンボーディング改善
---
### 1-00k. Phase 36 — PHI Box Midrange ReductionLoopSnapshotMergeBox & PhiBuilderBox**完了** 2025-11-29
**目的**
- Phase 35 での HIGH 安全度削除(`if_body_local_merge.rs` / `phi_invariants.rs` 計 430 行に続き、MEDIUM 安全度帯の PHI 箱のうち「Loop 側のみ」を縮退させて、JoinIR FrontendPhase 34 LoopScopeShape を PHI 生成 SSOT とする流れをさらに強化する。
- 行数削減だけでなく、「どの箱がどの責務を持つか」を明文化し、将来の Phase 37+If 側 PHI 削減 / Classifier Trio リファクタ)への足場を固める。
**実装内容サマリ**
1. **LoopSnapshotMergeBox の縮退dead code 100% 削除+純粋静的化)**
- 対象: `src/mir/phi_core/loop_snapshot_merge.rs`(元 470 行)。
- 完了内容:
- 未使用メソッド `merge_continue_for_header()` とそのテスト群を削除(本番 callsite 0
- PhiInputCollector 側に移譲済みの `optimize_same_value()` / `sanitize_inputs()` とテストを削除。
- 未使用フィールド `header_phi_inputs` / `exit_phi_inputs` と `new/Default` 実装を削除し、状態を一切持たない構造に整理。
- 本質ロジック `merge_exit_with_classification()` のみを残し、「Exit PHI マージの名前空間」としての静的ユーティリティに縮退。
- 結果:
- 行数: **470 → 363 行107 行削減, 22.8% reduction**
- Exit PHI Option CPHI pred mismatch 防止)ロジックは完全温存。
- Phase 36 専用テスト 3 本を追加し、Case A/B/Option C を包括的にカバー。
2. **PhiBuilderBox の責務マッピングLoop/If/Common の見える化)**
- 対象: `src/mir/phi_core/phi_builder_box.rs`。
- 実装方針:
- Loop 側専用の薄いラッパLoopPhiBuilderは作らず、既存の API をそのまま維持。
- 代わりにファイル先頭と各メソッドに「Responsibility: Loop-only / If-only / Common / Stub」といったマーカーコメントを追加。
- 主な分類:
- Loop-only: `set_if_context()`(ループ内 if の PHI 文脈設定)、`generate_loop_phis()`(将来の実装ターゲット)。
- If-only: `generate_if_phis()`, `compute_modified_names_if()`, `get_conservative_if_values()`。
- Common: `new()`, `generate_phis()`。
- 効果:
- PhiBuilderBox が「Loop PHI 本体」ではなく **if-in-loop PHI 用の共通箱** であることを明示。
- Phase 37 以降の If 側 PHI 削減のターゲット範囲がクリアになった。
3. **ドキュメント更新と PHI インベントリ反映**
- `docs/private/roadmap2/phases/phase-36-phi-midrange/README.md`:
- 目標 30% に対して実際は 22.8% 削減であること、行数よりも「dead code 全削除+本質ロジック温存」を優先した判断を明文化。
- LoopSnapshotMergeBox の役割を「Exit PHI マージ専用の静的ユーティリティ」として再定義。
- `docs/private/roadmap2/phases/phase-36-phi-midrange/TASKS.md`:
- 36-1.3/1.4, 36-6.1〜6.5 を含む全タスクを完了にマーク。
- `docs/private/roadmap2/phases/phase-30-final-joinir-world/PHI_BOX_INVENTORY.md`:
- LoopSnapshotMergeBox 行に Phase 36 の縮退内容dead code 削除+静的化)を追記し、将来 JoinIR/LoopScopeShape 側へ段階移行する計画を明示。
4. **テスト&リグレッション確認**
- LoopSnapshotMergeBox 単体テスト: 3/3 PASSsimple carrier / body-local internal skip / break-only loop
- LoopFormBuilder 周辺テスト: 14/14 PASS。
- Phase 34 JoinIR Frontend テストIf/Loop/Break/Continue: 6/6 PASS。
- `cargo build --release`: 警告ゼロで完了。
**成果と位置づけ**
- Phase 35-5HIGH 安全度 430 行削除に続き、Phase 36 で LoopSnapshotMergeBox を安全に 22.8% 縮退し、PHI レイヤの責務を「JoinIR + LoopScopeShape 側が SSOT、本レガシー箱は Exit PHI マージ専用の補助ユーティリティ」として再配置できた。
- PhiBuilderBox については Loop 側からの利用を「if-in-loop PHI 用」として位置づけ直し、実体削減は Phase 37+ の If 側 JoinIR 移行完了後に行う方針を固定した。
- これにより PHI 関連コードの総削減量は Phase 35-5430 行)+ Phase 36107 行)で **合計 537 行** に到達しつつ、Exit/If PHI の本質ロジックと JoinIR Frontend の安定性は維持されている。
**次のステップPhase 37+ の入口メモ)**
- Phase 37: If 側 PHI`conservative.rs` + `if_phi.rs`)の削減検討。
- 前提: Phase 33〜34 で整備した IfSelect/IfMerge lowering と JoinIR Frontend の適用範囲を広げること。
- Phase 38+: PhiBuilderBox 内部の実体削減Loop 側は完了済み / If 側の join 関数化)。
- Phase 39: Classifier TrioLoopVarClassBox / LoopExitLivenessBox / LocalScopeInspectorBoxを LoopScopeShape に吸収するアーキテクチャ作業。
**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-00j. Phase 37 If-Side PHI Design Phase**完了** 2025-11-28
**日付**: 2025-11-28
**達成**: If側PHI完全設計書作成削除前の地図完成
#### 設計内容
**if_phi.rs完全棚卸し**:
- パターン分類: simple/local/nested/if-in-loop/composite
- 12重要呼び出し箇所特定: lifecycle.rs (2), if_form.rs (2), phi.rs (2), loop_builder.rs (3), conservative.rs (1), phi_merge.rs (2)
- 難易度評価: LOW/MEDIUM/HIGH per callsite
**conservative.rs責務分解**:
- JoinIR移行済み: Select パターン用最小エッセンスを JoinIR Verifier に移譲済みPhase 33-3.2
- 未移行(本質的): 複雑パターン解析Conservative value propagation, void emission, predecessor fallback
- 移行境界明確化: tiny/simple vs complex patterns
**JoinIRカバレッジマップ**:
- Phase 33成果: IfSelect/IfMergesimple/local/2-3変数
- Phase 34成果: Tiny Frontend cases (AST→JoinIR→MIR→VM full path)
- ギャップ分析: カバー済み vs 未カバーStage-1/B完全実装待ち
**3レベル削除基準**:
- **Level 1** (Phase 38, LOW risk): tiny/pure IfIfSelectTest.* 全PASS via JoinIR Frontend
- 削減量: 68行 (14% of 483 lines)
- 候補: `merge_modified_with_control` (33行, 未使用), `extract_assigned_var` (35行)
- 前提: ✅ Phase 33 IfSelect実装, ✅ Phase 34 Frontend実装
- **Level 2** (Phase 39, MEDIUM risk): Stage-1/B代表関数1-2個A/Bテスト
- 削減量: 115行 (24% of 483 lines, 38% cumulative)
- 候補: `collect_assigned_vars` (32行), `compute_modified_names` (26行), `merge_with_reset_at_merge_with` (27行), conservative.rs struct inline (30行)
- 前提: ⚠️ Stage-1/B代表関数実装, ⚠️ Loop+If JoinIR統合, ⚠️ conservative.rs移行設計
- **Level 3** (Phase 40+, HIGH risk): Stage-1/B/selfhost大半JoinIR依存完全移行
- 削減量: 300行 total (62% of 483 lines, 94% cumulative)
- 候補: `merge_modified_at_merge_with` (63行), `PhiMergeOps` trait (14行), `get_conservative_values` (30行), conservative.rs完全削除 (70行)
- 前提: ❌ PhiMergeOps trait代替, ❌ 完全IfMerge void handling, ❌ Stage-1/B/selfhost大半カバー
#### 技術的成果
1. **docs-only完遂**: Rustコード変更ゼロ設計のみ
2. **証拠ベース設計**: 12箇所特定・難易度評価で削除可能性判断
3. **段階的削減計画**: Phase 38/39/40+への明確なロードマップ
#### 成果物
- ✅ if_phi_responsibility_table.md: 10関数分類・12呼び出し箇所・難易度評価
- ✅ conservative_responsibility_table.md: JoinIR移行済み vs 未移行境界
- ✅ joinir_coverage_map.md: Phase 33-34カバレッジ vs if_phi/conservativeマッピング
- ✅ deletion_sequence_plan.md: Phase 38/39/40+削減計画
- ✅ deliverables_checklist.md: 品質確認完了
- ✅ PHI_BOX_INVENTORY.md: Phase 37リンク追加、frontend_covered詳細コメント追加
#### Rustコード変更
- **ゼロ**docs-only、コメント追加なし
#### 次のステップ (Phase 38+)
- **Phase 38**: Level 1達成 → if_phi.rs Level 1関数削除~68行、14%削減、LOW risk
- **Phase 39**: Level 2達成 → if_phi.rs Level 2関数削除 + conservative.rs縮退~115行、24%削減、MEDIUM risk
- **Phase 40+**: Level 3達成 → conservative.rs完全削除~300行total、62%削減、HIGH risk
**Phase 37設計完了**: 削除前の完全地図作成達成
**Phase 38+削減ポテンシャル**: ~483行Level 1+2+3合計、if_phi.rs + conservative.rs
**Phase 35-37累計削減量**: 537行 (Phase 35: 430行, Phase 36: 107行) + Phase 37設計完了Phase 38+で483行削減予定
---
### 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 計画側で扱う。