Files
hakorune/CURRENT_TASK.md
nyash-codex ffd49ece91 docs(joinir): L-2.2 Step-5 - Verify Stage-B stability with generic ON
Verified NYASH_JOINIR_LOWER_GENERIC=1 does not break Stage-B:
- generic OFF: joinir_json 10 PASS, joinir_stageb 6 PASS
- generic ON: joinir_json 10 PASS, joinir_stageb 6 PASS,
  auto_lowering 2 PASS
- No guard additions needed - existing code is stable
- Stage-B execution remains on VM Route A (JoinIR as canary only)

L-2.2 is now fully complete (Step-1 through Step-5).

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 12:02:29 +09:00

1113 lines
86 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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

# Current Task — Phase 21.8 / 25 / 25.1 / 25.2 / 25.4 / 26-F / 26-G / 30 / 32 Snapshot2025-11-26 時点)
> このファイルは「今どこまで終わっていて、次に何をやるか」を 1000 行以内でざっくり把握するためのスナップショットだよ。
> 詳細な履歴やログは `git log CURRENT_TASK.md` からいつでも参照できるようにしておくね。
---
## 0. 現在地ざっくり
- フェーズ軸(ざっくり):
- **21.8**: Numeric Core / Core-15 まわりの安定化(既に日常的には安定運用)。
- **25.x**: Stage0/Stage1/StageB / Selfhost ラインのブートストラップと LoopForm v2 / LoopSSA v2 まわりの整備いまは「Rust側 LoopForm/PHI は維持しつつ、徐々に JoinIR に役割を移す」モード)。
- **25.1 系**: StageB / Stage1 / selfhost 向けに、Rust MIR / LoopForm v2 / LoopSSA v2 を段階的に整える長期ラインStage1 CLI / StageB / Ny compiler の Program(JSON v0) 境界はだいたい揃ったので、以降は JoinIR との接続が本線)。
- **26-F / 26-G**: Exit PHI / ExitLiveness 用の 4箱構成LoopVarClassBox / LoopExitLivenessBox / BodyLocalPhiBuilder / PhiInvariantsBoxと MirScanExitLiveness の準備(いまは「歴史的理由で残っている検証用レイヤ」で、将来 PHI レガシー削除時にまとめて畳む)。
- **26-H / 27.x**: JoinIR 設計+ミニ実験フェーズ → minimal/skip_ws/FuncScanner.trim/Stage1 UsingResolver minimal/FuncScanner.append_defs minimal/StageB minimal までを対象に、制御構造を関数呼び出しに正規化する IR とランナーを段階的に整備済み。
- 27.4 で Header φ を LoopHeaderShape 化、27.5 で Exit φ の意味を LoopExitShape として固定。
- 27.6-1/2/3 で ExitPhiBuilder 側にトグル付きバイパスを入れて A/B 観測まで完了、seal_phis と Header φ バイパスの整合性は別フェーズで refinement 済み。
- 27.8〜27.11/27.13/27.14 で skip_ws/trim/Stage1 UsingResolver minimal/FuncScanner.append_defs minimal/StageB minimal を Shared Builder PatternMIR-based lowering に移行し、ValueId 範囲管理も一元化。
- 27.8-27.11/27.13/27.15 で JoinIR Runner を JoinValue↔VMValue 変換+`MirInterpreter::execute_box_call` ラッパ経由で Rust VM の BoxCall 意味論と統合し、skip_ws/trim の JoinIR Runner スタンドアロン実行が安定GC/BoxRef も Arc ベースで統合済み)+ `joinir_runner_standalone_skip_ws` / `joinir_runner_standalone_trim` を NYASH_JOINIR_EXPERIMENT=1 で常時叩ける正常系テストとして昇格済み。
- Phase 27-shortterm の S1〜S5.4 は完了。Phase 28-midterm では per-loop lowering を増やさず、LoopFormLoopVarClassBoxLoopExitLivenessBox を入力にした「汎用 Loop→JoinIR ロワー」generic_case_a + LoopScopeShapeに畳み込む方針に切り替え済みjoin-ir.md に JoinIR ロワーが“やらないこと”チェックリストを明記)。
- Phase 29-longterm では LoopForm を「構造専任箱」とし、その隣に LoopScopeShapeLoopVarClassBox / ExitLiveness / LocalScopeInspector を統合したスコープ箱を置く形に整理。LoopVarClassBox/LoopExitLivenessBox/LocalScopeInspector は LoopScopeShape::from_existing_boxes 経由で細く呼ばれるだけのレガシーに寄せておき、将来的には LoopScopeShape に吸収して 1箱化する計画を TASKS に記録。
- **30.xJoinIR World**: JoinIR を「実行系の主IR」として採用し、PHI まわりのレガシーを段階的に縮退・削除していく足場整備フェーズ(早期削除候補と minimal ケースまで完了、残りは Phase 32 へ引き継ぎ)。
- L0.1〜0.2: jsonir v0JoinModule → JSONと v0_* スナップショットフィクスチャを整備済み。
- L0.3〜0.4: JoinIR VM Bridge を実装し、`Main.skip/1`minimal_ssa_skip_ws`FuncScannerBox.trim/1` で Route A (MIR→VM) の PHI バグを Route B (MIR→JoinIR→VM) で設計的に修正できることを実行レベルで実証済み。
- L0.5: Stage1UsingResolverBox.resolve_for_source/5 に対しても JoinIR VM Bridge A/B テストを追加し、Stage1 minimal について n=0 で `"init"` / n=3 で `"ABC"` が JoinIR 経由で正しく返ることを確認 → 「Stage1 ループでも JoinIR が PHI 問題を根治できる」ことを実行レベルで実証。
- F1: LoopScopeShape に LoopVarClassBox / LoopExitLivenessBox / LocalScopeInspectorBox の責務を委譲する準備が完了仕様SSOT化 classify/exit_live/inspector 委譲ヘルパー追加)。実際の呼び出し置換とファイル削除は F2 以降で行う。
- 現在は F系タスクF1〜F4を「**PHI レガシーを削りつつ、JoinIR 汎用 lowering と VM/LLVM 実行を JoinIR 前提にしていく」方向に倒すことを決定。ハードコードされた VM 分岐(`Main.skip/1``FuncScannerBox.trim/1` / Stage1 minimal 向けの特例)は Phase 30 の F4.4 で削除する前提で、今は「JoinIR World への橋」としてだけ残しておく。
- Rust 側:
- LoopForm v2 + ControlForm + Conservative PHI は、代表テストStage1 UsingResolver / StageB 最小ループ)ではほぼ安定。
- 静的メソッド呼び出し規約と `continue` 絡みの PHI は 25.1m までで根治済み。
- .hako 側:
- StageB コンパイラ本体 / LoopSSA v2 / BreakFinderBox / PhiInjectorBox はまだ部分実装。
- JSON v0 / selfhost ルートは Rust 側の LoopForm v2 規約に追いつかせる必要がある。StageB selfhost 経路は依然として Program JSON が取れていないが、直近の実行では `Unknown method 'main' on InstanceBox` は再現しておらず、別の箇所で落ちている可能性が高い。Stage1 CLI ブリッジ自体は Rust バイナリ経由で発火して `stage1_cli.hako` 実行までは到達しており、**ParserBox と StringHelpers まわりのすべての `loop(cont == 1) { ... cont = 0 }` パターンを `loop(true) { ...; continue / break }` に書き換えた結果、BuildBox.emit_program_json_v0 → `ParserBox.parse_program2` の無限ループstep budget 超過は解消済み**minimal_ssa_skip_ws.hako で Program(JSON v0) が RC=0 で出ることを確認済み。Rust VM の budget エラーは fn/bb/last_inst Span (あれば file:line:col) 付きで出るようにしたので、今後類似の問題が出ても位置特定は容易。
- StageB / FuncScanner ライン:
- Phase 25.3 をクローズし、`stageb_fib_program_defs_canary_vm.sh` が緑(`defs``TestBox.fib/Main.main`、fib.body.body[*] に `Loop`)。
- StageB は block パーサ優先 + defs を Block 包みで構造化。次手: Stage1 UsingResolver ループの Region+next_i 揃え / Stage1 CLI program-json selfhost 準備。
- Stage1 CLI 実験: `NYASH_USE_STAGE1_CLI=1 STAGE1_EMIT_PROGRAM_JSON=1 ./target/release/hakorune apps/tests/minimal_ssa_skip_ws.hako` でブリッジ発火・stage1_cli.hako 実行は確認できたが、VM が max_steps 2000000 を超過して中断プラグイン未ロード警告あり。budget 超過メッセージには `fn=ParserBox.parse_program2/1 ... (lang/src/runner/stage1_cli.hako:1:1)` まで出るようになったSpan 配線は通ったが位置精度はまだ粗い。selfhost ビルド経路tools/selfhost/build_stage1.shは StageBDriverBox.main 呼び出しで Unknown method 'main' on InstanceBox → Program JSON 未生成。
---
## 1. 最近完了した重要タスク
### 1-00v. Phase 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 実行はまだ有効化しない)。
**成果物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` JoinIRMIR Bridge の結果を取得
- 両者とも対象関数が存在しJoinIR 経由の MIR でも Block 数が 1 以上であることを確認
- JoinIR lowering JoinIRMIR 変換がエラーなく通ることを確認
- `joinir_stageb_funcscanner_structure_test`
- `StageBFuncScannerBox.scan_all_boxes/1` について同様のチェックを実施
- 現時点の検証範囲:
- **handwritten JoinIRMIR Bridge の健全性確認** が主目的`build_stageb_*_joinir()` 由来)。
- まだ `intake_loop_form` StageB ループ構造に非対応のため、「MIRJoinIR roundtripの完全 A/B ではなく、「handwritten JoinIR VM Bridge で安全に MIR に落とせているかをテストする位置付け
**VM Bridge 対応関数一覧Phase 32 時点のまとめ)**
- `Main.skip/1`:
- JoinIR lowering + JoinIRMIRVM 実行まで対応済み
- `NYASH_JOINIR_EXPERIMENT=1` / `NYASH_JOINIR_VM_BRIDGE=1` Route B を有効化trim と並ぶ最初期の A/B テスト対象)。
- `FuncScannerBox.trim/1`:
- JoinIR lowering + JoinIRVM 実行まで対応済み
- `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-35 / L-2.3 / Phase 29 L-3.x で段階的に進める
- Stage1 / StageB について JoinIRVM 実行まで有効化するには:
- JoinValue::BoxRef VM Box インスタンスの橋渡し引数マーシャリングを明示的に実装する必要あり
- JoinIR lowering 簡略版からVM Route A と意味論一致する版に引き上げA/B テストJSON / MIR / JoinIR / VM の比較を整備する必要がある
- Phase 32 TASKS のステータス2025-11-26 時点:
- L-1.1L-1.3: 完了LoopToJoinLowerer 汎用化の足場は整った)。
- L-2.1: 完了Stage1 UsingResolver 本線ループの JoinIR lowering + スナップショットテスト)。
- L-2.2: **全 Step 完了**StageB lowering 統一 + VM bridge dispatch + JoinIRMIR 構造テスト + ドキュメント更新 + 安定化確認
- 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 / L-3 / L-4: これからPHI レガシー削除とJoinIRVM/LLVM 前提ランナー構造への仕上げ)。
---
### 1-00a. Phase 30 F-1.4 — LoopScopeShape API 確定(**完了** 2025-11-25
**目的**
- LoopScopeShape ループ変数スコープ情報の唯一のソースに近づける
- Block IDs (header/body/latch/exit) LoopForm から伝播
- CaseAContext::from_scope() LoopScopeShape 直接受け取りAPI追加
- ユニットテスト追加8テスト全PASS
**成果物**
1. **LoopScopeShape 更新**: `src/mir/join_ir/lowering/loop_scope_shape.rs`
- Block IDs フィールド追加
- `from_existing_boxes()` シグネチャ更新LoopForm受け取り
- `CaseAContext::from_scope()` 新メソッド追加
2. **テスト追加**: 4本の新規テスト合計8テスト
- `test_from_scope_validation_header_eq_exit`
- `test_block_ids_preserved`
- `test_deterministic_order`
- `test_needs_phi_consistency`
3. **ドキュメント更新**: `docs/private/roadmap2/phases/phase-30-final-joinir-world/TASKS.md`
---
### 1-00b. Phase 29 小タスク — JoinIR/Stage-1 環境変数棚卸し(**完了** 2025-11-25
**目的**
- JoinIR/Stage-1 関連の環境変数を棚卸し整理
- 実装の統一確認env helper経由
- ドキュメント更新
**成果物**
1. **ENV_INVENTORY.md 作成**: `docs/private/roadmap2/phases/phase-29-longterm-joinir-full/ENV_INVENTORY.md`
- JoinIR 環境変数 6個`NYASH_JOINIR_*`
- Stage-1 環境変数Primary 7個 + Aliases 16個
- 分類: user-facing / dev-only / internal / alias
2. **実装統一確認**
- JoinIR: `env_flag_is_1()` ヘルパー経由で統一済み9ファイル
- Stage-1: `src/config/env/stage1.rs` SSOT モジュール経由で統一済み
3. **ドキュメント更新**: `docs/reference/environment-variables.md`
- JoinIR セクション追加6変数 + 使用例
**発見事項**
- `NYASH_RUN_JOINIR_MINIMAL` は既に削除済みgood
- Stage-1 alias群は警告付きで互換維持`warn_alias_once`
- テストファイルの直接env var参照はスキップガード用途で許容
---
### 1-01. Phase 26-E — PhiBuilderBox SSOT統一化**完了** 2025-11-22
**目的**
- PHI生成ロジックを単一責務箱PhiBuilderBoxに集約
- If/Loop両対応の統一インターフェース提供
- Conservative戦略 + BTreeSet/BTreeMap で決定性向上
**🎯 Phase 26-E 進捗状況**2025-11-22
- **✅ Phase 1**: PhiBuilderBox 骨格作成444行ControlForm対応
- **✅ Phase 2**: If PHI生成完全実装Conservative戦略決定的順序保証
- **✅ Phase 3**: PhiBuilderOps委譲実装has-a設計ChatGPT+Claude合意
- **⏳ Phase 4**: Legacy削除loop_phi.rs 287行将来タスク
**Phase 2 実装内容2025-11-22完了**
1. **PhiBuilderBox作成** (src/mir/phi_core/phi_builder_box.rs, 444行)
- If PHI生成: `generate_if_phis()` 完全実装
- Conservative戦略: void emission 含む完全対応
- 決定的順序: BTreeSet/BTreeMap で非決定性排除
2. **PhiBuilderOps trait** (7メソッド)
- 最小PHI生成インターフェース
- テスタビリティ向上モック可能
3. **loop_builder.rs 統合** (src/mir/loop_builder.rs)
- PhiBuilderOps trait 実装Ops構造体
- If PHI呼び出し箇所統合line 1136-1144
**Phase 3 実装内容2025-11-22完了**
1. **設計方針変更**: 継承is-a)→委譲has-aに変更ChatGPT+Claude合意
- PhiBuilderOps = 低レベル「PHI命令発行」道具箱
- LoopFormOps = 高レベル「ループ構造構築」作業場
- 関係: has-a委譲が正しい設計異なる抽象レベル
2. **LoopBuilder委譲実装** (src/mir/loop_builder.rs, +64行)
- `impl<'a> PhiBuilderOps for LoopBuilder<'a>` 個別実装
- 明示的 trait 修飾で自己再帰回避: `<Self as LoopFormOps>::method()`
- HashSet Vec 変換 + ソート決定性保証
- emit_phi 引数差吸収: set_current_block 経由
3. **設計文書更新** (src/mir/phi_core/loopform_builder.rs)
- has-a 設計根拠をコメント追加
- 継承試行の失敗理由を記録
**Phase 3 テスト結果2025-11-22**
- **決定性**: 10回実行で一貫した結果20% 成功率は既存 Loop PHI バグによるもの
- **退行なし**: Phase 26-E 実装前と同じ成功率
- **If PHI 生成**: 100% 動作Test 2 で確認済み
- **テスト詳細**:
- `mir_stage1_using_resolver_resolve_with_modules_map_verifies`: 2/10 成功20%、既存バグ
- `mir_stage1_using_resolver_modules_map_continue_break_with_lookup_verifies`: 10/10 成功100%
**Phase 3 コミット**
- `b9a03429`: Phase 26-E-2 - PhiBuilderBox If PHI生成完全実装
- `e0be01c1`: Phase 26-E-3 - PhiBuilderOps委譲実装has-a設計
**削減見込み**
- Phase 2: -80行If側重複削除
- Phase 4: -287行loop_phi.rs Legacy削除
- **合計**: -367行純削減
**次のステップPhase 4**
- loop_phi.rs Legacy削除287行
- Loop PHI バグ調査20% 成功率問題
- 100% 決定的テスト達成
**関連ファイル**
- [phi_builder_box.rs](src/mir/phi_core/phi_builder_box.rs) - 444行
- [loop_builder.rs](src/mir/loop_builder.rs) - PhiBuilderOps実装
- [exit_phi_builder.rs](src/mir/phi_core/exit_phi_builder.rs) - 779行Phase 26-D完成
- [header_phi_builder.rs](src/mir/phi_core/header_phi_builder.rs) - 548行Phase 26-C-2完成
---
### 1-00. Phase 21.7 — Static Box Methodization完了 2025-11-21, 既定ON に移行)
**目的**
- static box 内の呼び出しを NamingBox/Method まわりで一貫して扱えるようにする
- Global("BoxName.method/arity") Method{receiver = static singleton} に寄せるトグル制御)。
**🎯 Phase 21.7++ 計画**2025-11-22 全フェーズ完了!🎊)
- **NamingBox SSOT 統一化チェックリスト**: [phase-21.7-naming-ssot-checklist.md](docs/development/current/main/phase-21.7-naming-ssot-checklist.md)
- StringUtils using 解決バグ修正2025-11-22, commit f4ae1445を踏まえた改善計画
- **✅ Phase 0観測ライン**: Silent Failure 根絶完了commit 63012932
- **✅ Phase 1基盤整備**: StaticMethodId SSOT 基盤確立commit 96c1345e
- **✅ Phase 2VM統一**: VM 名前解決 SSOT 準拠commit 1b413da5
- **✅ Phase 3全体統一**: Builder 側統一素手 split 根絶commit c8ad1dae
- **✅ Phase 4ドキュメント**: READMEトラブルシューティングガイド整備commit 806e4d72
- **累計工数**: 10時間進捗率: 50-67%
**Phase 0-4 実装内容2025-11-22 全完了)**
1. **Phase 0: 観測ライン緊急構築** (commit 63012932)
- TOML parse エラー即座表示pipeline.rs
- VM 関数ルックアップDid you mean?」提案global.rs
- using not found 詳細化strip.rs
- **効果**: Silent Failure 根絶デバッグ時間が時間分に短縮
2. **Phase 1: StaticMethodId SSOT 基盤** (commit 96c1345e)
- `StaticMethodId` 構造体導入naming.rs:86-248
- parse/format/with_arity ヘルパー実装
- 包括的テスト13ケース全PASS
- **効果**: 関数名パース/フォーマット一元化型安全化
3. **Phase 2: VM 統一** (commit 1b413da5)
- global.rs StaticMethodId ベース化
- デバッグログ強化NYASH_DEBUG_FUNCTION_LOOKUP=1
- テスト全通過349 passed, 退行なし
- **効果**: arity バグ根治Hotfix 卒業
4. **Phase 3: 全体統一** (commit c8ad1dae)
- unified_emitter.rs methodization StaticMethodId
- known.rs split_once 全置き換え2箇所
- **効果**: 素手 split 根絶Builder 側完全統一コード50%削減
5. **Phase 4: ドキュメント整備** (commit 806e4d72)
- Phase 21.7 README に完了セクション追加60行
- トラブルシューティングガイド作成200+新規
- チェックリスト進捗サマリー更新
- **効果**: 再発防止開発者オンボーディング改善
**Phase 0-2 以前の実装内容**
1. **NamingBox decode 関数追加** (Step 1: commit a13f14ce)
- `decode_static_method(func_name: &str) -> Option<(&str, &str, usize)>`
- `is_static_method_name(func_name: &str) -> bool`
- "BoxName.method/arity" 形式をパースして (box_name, method, arity) に分解
2. **Hotfix 7 修正** (Step 2: commit a13f14ce)
- `unified_emitter.rs` receiver 追加ロジックを修正
- StaticCompiler box_kind のメソッドでstatic box method (decode可能な名前) の場合は receiver を追加しない
- instance method のみ receiver args の先頭に追加するようガード実装
3. **Methodization 実装** (Step 3: commit b5cd05c2)
- `builder.rs`: `static_box_singletons: HashMap<String, ValueId>` フィールド追加
- `unified_emitter.rs`: HAKO_MIR_BUILDER_METHODIZE=1 で有効化
- Callee::Global(name) decode して static box method 判定
- シングルトンインスタンス (NewBox) をキャッシュ生成
- Callee::Method{receiver=singleton} に変換
**動作確認**
- デフォルト (OFF): `call_global Calculator.add/2` (既存挙動維持)
- トグル (ON): `new Calculator() → call %singleton.add(...)` (methodization)
- RC=0 両モード動作確認済み: `apps/tests/phase217_methodize_test.hako`
**環境変数**
- `HAKO_MIR_BUILDER_METHODIZE=0/1`: methodization 制御既定ON未設定 or "1")、"0" のときのみ無効化
- `NYASH_METHODIZE_TRACE=1`: GlobalMethod 変換ログ出力
**次タスクPhase 21.7++ Phase 3-4**10-15時間見込み
- **Phase 3: 全体統一**
- MIR Builder 側を StaticMethodId 統一builder/calls/unified_emitter.rs
- 素手 split 置き換え`rg '"\."' --type rust src/mir/builder/`
- 100-200 行削減見込み
- **Phase 4: ドキュメント化**
- SSOT 設計書更新
- 移行ガイド作成
- チームレビュー
- **長期**: NamingBox/UnifiedCallEmitter/VM 3 点で名前と arity SSOT完全統一
### 1-02. Phase 27.4-A — JoinIR Header φ 統合LoopHeaderShape 導入、2025-11-23 完了)
**目的**
- HeaderPhiBuilder が担っていたloop header φPinned/Carrier の合流)」の意味をJoinIR 側に構造として持ち上げる足場を作る
- Rust 側の header φ 挙動は一切変えず本線 MIR/LoopFormVM を壊さないまま 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() pinnedcarriers 順で返すという契約を固定
**状態**
- 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/LoopFormVM の挙動には一切触れず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/LoopFormVM の挙動はトグル 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`を設計しenvConfig 変換の責務とフィールドを 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 本追加済みを基準にもう 12 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 化案
- どの構造を HashMapBTreeMap 化するかどのテスト`mir_funcscanner_skip_ws_vm_debug_flaky` などで決定性を確認するかを Phase25.x の設計メモとして書いておく
- D-2: dev 専用 / flaky テストの扱い方針
- どのテストが dev ignore手動実行用いつ/何を満たしたら常時有効に戻すかをこのファイルと関連 README に明確に残す
このセクションはすぐ実装する TODO ではなく25.125.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 計画側で扱う