Files
hakorune/CURRENT_TASK.md
nyash-codex 03efa13e76 docs(phase-29): JoinIR/Stage-1 環境変数棚卸し完了
Step 1: ENV_INVENTORY.md 作成 (docs/private submodule)
Step 2: 実装統一確認 - 既にhelper経由で統一済み
Step 3: environment-variables.md に JoinIRセクション追加
Step 4: スモークテスト pass
Step 5: CURRENT_TASK.md に完了記録

発見事項:
- JoinIR: env_flag_is_1() ヘルパー経由で統一済み
- Stage-1: src/config/env/stage1.rs SSOT モジュール経由で統一済み
- NYASH_RUN_JOINIR_MINIMAL は既に削除済み

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 09:03:28 +09:00

835 lines
66 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 Snapshot2025-11-22 時点)
> このファイルは「今どこまで終わっていて、次に何をやるか」を 1000 行以内でざっくり把握するためのスナップショットだよ。
> 詳細な履歴やログは `git log CURRENT_TASK.md` からいつでも参照できるようにしておくね。
---
## 0. 現在地ざっくり
- フェーズ軸:
- **21.8**: Numeric Core / Core-15 まわりの安定化(既に日常的には安定運用)。
- **25.x**: Stage0/Stage1/StageB / Selfhost ラインのブートストラップと LoopForm v2 / LoopSSA v2 まわりの整備。
- **25.1 系**: StageB / Stage1 / selfhost 向けに、Rust MIR / LoopForm v2 / LoopSSA v2 を段階的に整える長期ライン。
- **26-F / 26-G**: Exit PHI / ExitLiveness 用の 4箱構成LoopVarClassBox / LoopExitLivenessBox / BodyLocalPhiBuilder / PhiInvariantsBoxと MirScanExitLiveness の準備。
- **26-H / 27.xNew**: 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` を NYASH_JOINIR_EXPERIMENT=1 で常時叩ける正常系テストとして昇格済み。Phase 27-shortterm の S1〜S5.4 は完了、Phase 28-midterm では per-loop lowering を増やさず、LoopFormLoopVarClassBoxLoopExitLivenessBox を入力にした「汎用 Loop→JoinIR ロワー」に畳み込んでいく方針に切り替え済みjoin-ir.md に JoinIR ロワーが“やらないこと”チェックリストを明記。Phase 29-longterm では LoopForm を「構造専任箱」とし、その隣に LoopScopeShapeLoopVarClassBox / ExitLiveness / LocalScopeInspector を統合したスコープ+進捗箱)と progress インバリアント(ゼロ進捗 backedge チェック)を Verifier 側に足して、JoinIR 側でループ進捗インバリアントまで構造的に扱う計画を TASKS に追加済み)。
- 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-00a. Phase 29 小タスク — JoinIR/Stage-1 環境変数棚卸し(**完了** 2025-11-25
**目的**
- JoinIR/Stage-1 関連の環境変数を棚卸し・整理
- 実装の統一確認env helper経由
- ドキュメント更新
**成果物**
1. **ENV_INVENTORY.md 作成**: `docs/private/roadmap2/phases/phase-29-longterm-joinir-full/ENV_INVENTORY.md`
- JoinIR 環境変数 6個`NYASH_JOINIR_*`
- Stage-1 環境変数Primary 7個 + Aliases 16個
- 分類: user-facing / dev-only / internal / alias
2. **実装統一確認**
- JoinIR: `env_flag_is_1()` ヘルパー経由で統一済み9ファイル
- Stage-1: `src/config/env/stage1.rs` SSOT モジュール経由で統一済み
3. **ドキュメント更新**: `docs/reference/environment-variables.md`
- JoinIR セクション追加6変数 + 使用例)
**発見事項**
- `NYASH_RUN_JOINIR_MINIMAL` は既に削除済みgood
- Stage-1 alias群は警告付きで互換維持`warn_alias_once`
- テストファイルの直接env var参照はスキップガード用途で許容
---
### 1-01. Phase 26-E — PhiBuilderBox SSOT統一化**完了** 2025-11-22
**目的**
- PHI生成ロジックを単一責務箱PhiBuilderBoxに集約
- If/Loop両対応の統一インターフェース提供
- Conservative戦略 + BTreeSet/BTreeMap で決定性向上
**🎯 Phase 26-E 進捗状況**2025-11-22
- **✅ Phase 1**: PhiBuilderBox 骨格作成444行、ControlForm対応
- **✅ Phase 2**: If PHI生成完全実装Conservative戦略、決定的順序保証
- **✅ Phase 3**: PhiBuilderOps委譲実装has-a設計、ChatGPT+Claude合意
- **⏳ Phase 4**: Legacy削除loop_phi.rs 287行、将来タスク
**Phase 2 実装内容2025-11-22完了**
1. **PhiBuilderBox作成** (src/mir/phi_core/phi_builder_box.rs, 444行)
- If PHI生成: `generate_if_phis()` 完全実装
- Conservative戦略: void emission 含む完全対応
- 決定的順序: BTreeSet/BTreeMap で非決定性排除
2. **PhiBuilderOps trait** (7メソッド)
- 最小PHI生成インターフェース
- テスタビリティ向上(モック可能)
3. **loop_builder.rs 統合** (src/mir/loop_builder.rs)
- PhiBuilderOps trait 実装Ops構造体
- If PHI呼び出し箇所統合line 1136-1144
**Phase 3 実装内容2025-11-22完了**
1. **設計方針変更**: 継承is-a→委譲has-aに変更ChatGPT+Claude合意
- PhiBuilderOps = 低レベル「PHI命令発行」道具箱
- LoopFormOps = 高レベル「ループ構造構築」作業場
- 関係: has-a委譲が正しい設計異なる抽象レベル
2. **LoopBuilder委譲実装** (src/mir/loop_builder.rs, +64行)
- `impl<'a> PhiBuilderOps for LoopBuilder<'a>` 個別実装
- 明示的 trait 修飾で自己再帰回避: `<Self as LoopFormOps>::method()`
- HashSet → Vec 変換 + ソート(決定性保証)
- emit_phi 引数差吸収: set_current_block 経由
3. **設計文書更新** (src/mir/phi_core/loopform_builder.rs)
- has-a 設計根拠をコメント追加
- 継承試行の失敗理由を記録
**Phase 3 テスト結果2025-11-22**
- **決定性**: 10回実行で一貫した結果20% 成功率は既存 Loop PHI バグによるもの)
- **退行なし**: Phase 26-E 実装前と同じ成功率
- **If PHI 生成**: 100% 動作Test 2 で確認済み)
- **テスト詳細**:
- `mir_stage1_using_resolver_resolve_with_modules_map_verifies`: 2/10 成功20%、既存バグ)
- `mir_stage1_using_resolver_modules_map_continue_break_with_lookup_verifies`: 10/10 成功100%
**Phase 3 コミット**
- `b9a03429`: Phase 26-E-2 - PhiBuilderBox If PHI生成完全実装
- `e0be01c1`: Phase 26-E-3 - PhiBuilderOps委譲実装has-a設計
**削減見込み**
- Phase 2: -80行If側重複削除
- Phase 4: -287行loop_phi.rs Legacy削除
- **合計**: -367行純削減
**次のステップPhase 4**
- loop_phi.rs Legacy削除287行
- Loop PHI バグ調査20% 成功率問題)
- 100% 決定的テスト達成
**関連ファイル**
- [phi_builder_box.rs](src/mir/phi_core/phi_builder_box.rs) - 444行
- [loop_builder.rs](src/mir/loop_builder.rs) - PhiBuilderOps実装
- [exit_phi_builder.rs](src/mir/phi_core/exit_phi_builder.rs) - 779行Phase 26-D完成
- [header_phi_builder.rs](src/mir/phi_core/header_phi_builder.rs) - 548行Phase 26-C-2完成
---
### 1-00. Phase 21.7 — Static Box Methodization完了 2025-11-21, 既定ON に移行)
**目的**
- static box 内の呼び出しを NamingBox/Method まわりで一貫して扱えるようにする。
- Global("BoxName.method/arity") を Method{receiver = static singleton} に寄せる(トグル制御)。
**🎯 Phase 21.7++ 計画**2025-11-22 全フェーズ完了!🎊)
- **NamingBox SSOT 統一化チェックリスト**: [phase-21.7-naming-ssot-checklist.md](docs/development/current/main/phase-21.7-naming-ssot-checklist.md)
- StringUtils using 解決バグ修正2025-11-22, commit f4ae1445を踏まえた改善計画
- **✅ Phase 0観測ライン**: Silent Failure 根絶完了commit 63012932
- **✅ Phase 1基盤整備**: StaticMethodId SSOT 基盤確立commit 96c1345e
- **✅ Phase 2VM統一**: VM 名前解決 SSOT 準拠commit 1b413da5
- **✅ Phase 3全体統一**: Builder 側統一、素手 split 根絶commit c8ad1dae
- **✅ Phase 4ドキュメント**: README・トラブルシューティングガイド整備commit 806e4d72
- **累計工数**: 10時間進捗率: 50-67%
**Phase 0-4 実装内容2025-11-22 全完了)**
1. **Phase 0: 観測ライン緊急構築** (commit 63012932)
- TOML parse エラー即座表示pipeline.rs
- VM 関数ルックアップ「Did you mean?」提案global.rs
- using not found 詳細化strip.rs
- **効果**: Silent Failure 根絶、デバッグ時間が時間→分に短縮
2. **Phase 1: StaticMethodId SSOT 基盤** (commit 96c1345e)
- `StaticMethodId` 構造体導入naming.rs:86-248
- parse/format/with_arity ヘルパー実装
- 包括的テスト13ケース全PASS
- **効果**: 関数名パース/フォーマット一元化、型安全化
3. **Phase 2: VM 統一** (commit 1b413da5)
- global.rs を StaticMethodId ベース化
- デバッグログ強化NYASH_DEBUG_FUNCTION_LOOKUP=1
- テスト全通過349 passed, 退行なし)
- **効果**: arity バグ根治、Hotfix 卒業
4. **Phase 3: 全体統一** (commit c8ad1dae)
- unified_emitter.rs の methodization を StaticMethodId 化
- known.rs の split_once 全置き換え2箇所
- **効果**: 素手 split 根絶、Builder 側完全統一、コード50%削減
5. **Phase 4: ドキュメント整備** (commit 806e4d72)
- Phase 21.7 README に完了セクション追加60行
- トラブルシューティングガイド作成200+行、新規)
- チェックリスト進捗サマリー更新
- **効果**: 再発防止、開発者オンボーディング改善
**Phase 0-2 以前の実装内容**
1. **NamingBox decode 関数追加** (Step 1: commit a13f14ce)
- `decode_static_method(func_name: &str) -> Option<(&str, &str, usize)>`
- `is_static_method_name(func_name: &str) -> bool`
- "BoxName.method/arity" 形式をパースして (box_name, method, arity) に分解。
2. **Hotfix 7 修正** (Step 2: commit a13f14ce)
- `unified_emitter.rs` の receiver 追加ロジックを修正。
- StaticCompiler box_kind のメソッドで、static box method (decode可能な名前) の場合は receiver を追加しない。
- instance method のみ receiver を args の先頭に追加するようガード実装。
3. **Methodization 実装** (Step 3: commit b5cd05c2)
- `builder.rs`: `static_box_singletons: HashMap<String, ValueId>` フィールド追加。
- `unified_emitter.rs`: HAKO_MIR_BUILDER_METHODIZE=1 で有効化。
- Callee::Global(name) を decode して static box method 判定。
- シングルトンインスタンス (NewBox) をキャッシュ生成。
- Callee::Method{receiver=singleton} に変換。
**動作確認**
- デフォルト (OFF): `call_global Calculator.add/2` (既存挙動維持)
- トグル (ON): `new Calculator() → call %singleton.add(...)` (methodization)
- RC=0 両モード動作確認済み: `apps/tests/phase217_methodize_test.hako`
**環境変数**
- `HAKO_MIR_BUILDER_METHODIZE=0/1`: methodization 制御。既定ON未設定 or "1")、"0" のときのみ無効化。
- `NYASH_METHODIZE_TRACE=1`: Global→Method 変換ログ出力
**次タスクPhase 21.7++ Phase 3-4**10-15時間見込み
- **Phase 3: 全体統一**
- MIR Builder 側を StaticMethodId 統一builder/calls/unified_emitter.rs 等)
- 素手 split 置き換え(`rg '"\."' --type rust src/mir/builder/`
- 100-200 行削減見込み
- **Phase 4: ドキュメント化**
- SSOT 設計書更新
- 移行ガイド作成
- チームレビュー
- **長期**: NamingBox/UnifiedCallEmitter/VM の 3 点で「名前と arity の SSOT」完全統一。
### 1-02. Phase 27.4-A — JoinIR Header φ 統合LoopHeaderShape 導入、2025-11-23 完了)
**目的**
- HeaderPhiBuilder が担っていた「loop header φPinned/Carrier の合流」の意味を、JoinIR 側に構造として持ち上げる足場を作る。
- Rust 側の header φ 挙動は一切変えず、本線 MIR/LoopForm→VM を壊さないまま JoinIR 経路の設計を進める。
**やったこと**
- `src/mir/join_ir.rs``LoopHeaderShape { pinned, carriers }` を追加し、skip_ws / FuncScanner.trim のループについて:
- skip_ws: Pinned = [s, n], Carrier = [i]`loop_step(s, i, n)` の設計をコメント付きで固定)。
- trim : Pinned = [str, b], Carrier = [e]`loop_step(str, b, e)` の設計をコメント付きで固定)。
- Header φ の意味を「loop_step 引数としてどう表現するか」をコードとコメントで一致させた。
- `src/mir/phi_core/header_phi_builder.rs``NYASH_JOINIR_HEADER_EXP=1` フラグチェックを追加(現在はログのみ、挙動は不変)。
- JoinIR テストまわり:
- skip_ws / min / trim の JoinIR 型・変換テストを維持しつつ、trim 側は `trim_main + loop_step + skip_leading` の 3 関数構成にテスト期待を合わせた。
- `LoopHeaderShape` 用のミニテストを追加し、「to_loop_step_params() は pinned→carriers 順で返す」という契約を固定。
**状態**
- JoinIR 側では Header φ の意味が LoopHeaderShape で表現され、対象ループの `loop_step` 引数に反映済み。
- HeaderPhiBuilder は従来挙動のままPhase 27.4-C 以降でトグル付き縮退を予定)。
- すべての JoinIR テスト 7/7 が PASS、既存本線テストの緑度には影響なし。
---
### 1-03. Phase 27.4-C — HeaderPhiBuilder バイパス実験JoinIR 経路限定、2025-11-23 完了)
**目的**
- Header φ の意味は JoinIR 側LoopHeaderShapeloop_step 引数に持ち上がったので、JoinIR 実験経路に限って Rust 側 HeaderPhiBuilder をバイパスできるか試す。
- 本線 MIR/LoopForm→VM の挙動には一切触れず、JoinIR runner テスト専用の縮退ステップとして運用する。
**やったこと**
- `src/mir/phi_core/header_phi_builder.rs` に 2 つのヘルパーを追加:
- `joinir_header_experiment_enabled()``NYASH_JOINIR_HEADER_EXP=1` チェック27.4-B で導入)。
- `joinir_header_bypass_enabled()``NYASH_JOINIR_EXPERIMENT=1 AND NYASH_JOINIR_HEADER_EXP=1` の両方が ON のとき true。
- `src/mir/loop_builder.rs` で HeaderPhiBuilder 利用箇所にバイパスロジックを追加:
- 現在の関数名が `Main.skip/1` または `FuncScannerBox.trim/1` のとき、
- かつ `joinir_header_bypass_enabled()` が true のときのみ `emit_header_phis()` をスキップ。
- `NYASH_LOOPFORM_DEBUG=1` 時はデバッグログを出す。
- JoinIR テスト側skip_ws / trimに、「NYASH_JOINIR_HEADER_EXP=1 を併用すると Header φ bypass が有効化される」旨のコメントを追加。
- `docs/private/roadmap2/phases/phase-27-joinir/IMPLEMENTATION_LOG.md` に Phase 27.4-C セクションを追加し、対象関数・トグル条件・挙動を記録。
- `phase-27-joinir/TASKS.md` で 27.4-C を完了扱いに更新。
**状態**
- JoinIR runner 実験時に `NYASH_JOINIR_EXPERIMENT=1` `NYASH_JOINIR_HEADER_EXP=1` を立てた場合のみ、Main.skip/1 / FuncScannerBox.trim/1 の Header φ がスキップされる。
- このモードでは VM 実行は使わず、JoinIR runner だけで意味が保たれているかを確認するテストとして運用。
- 本線 MIR/LoopForm→VM の挙動は、トグル OFF 時には従来どおりHeader φ あり)のまま。
---
### 1-04. Phase 27.5 — JoinIR Exit φ 統合設計着手、2025-11-24 現在)
**目的**
- ExitPhiBuilder が担っている「exit φbreak/early-exit の合流」の意味を、JoinIR 側の `k_exit` 呼び出し+引数として表現できるようにする設計フェーズ。
- Rust 側の ExitPhiBuilder 挙動は変えず、本線 VM を壊さないまま minimal/trim の 2 ケースで Exit φ の意味を整理する。
**やったこと(設計メモ反映済み)**
- `docs/private/roadmap2/phases/phase-27.5-joinir-exit/README.md` を追加し、minimal_ssa_skip_ws と FuncScanner.trim_min の exit 経路を整理。
- minimal: break 経路は `i>=n` / `ch!=" "`, Exit φ は `i` だけ → JoinIR では `k_exit(i)` を想定。
- trim: break 経路は `!(e>b)` / `!is_space`, Carrier=`e`, Pinned=`str,b`。ExitShape は Option A: `[e]`(第一候補) / Option B: `[str,b,e]` を比較と記述。
- `docs/private/roadmap2/phases/phase-27.5-joinir-exit/TASKS.md` の A-1/A-2 を完了にし、B 以降LoopExitShape 型/コメント追加、JoinIR 変換への反映、テスト/ログ追記)はこれから。
**次の一手**
- LoopExitShape の型 or コメントを JoinIR に追加して、minimal/trim の exit 引数セットを明示。
- JoinIR 変換の exit 部分に「k_exit で何を合流させるか」のコメントを足し、必要なら命令並びを軽く整える(意味は変えない)。
- JoinIR テストと IMPLEMENTATION_LOG に Exit φ 観点のメモを追記。
---
### 1-05. Phase 26-F — Loop Exit Liveness / BodyLocal PHI Guard箱とガードの整備、2025-11-22 時点)
**目的**
- LoopForm v2 / Exit PHI まわりで、BodyLocal 変数の未定義利用を「箱」と「Fail-Fast」で確実に検知・抑制できるようにする。
- MIR スキャン(本物の Exit Livenessは次フェーズに送りつつ、構造と受け口と環境変数ガードだけ先に整える。
**やったこと26-F 初期完了分)**
- 4箱構成の整理と実装Exit PHI 専用レイヤ):
- `LoopVarClassBox``loop_var_classifier.rs`:
- 変数のスコープ分類専用Pinned / Carrier / BodyLocalExit / BodyLocalInternal
- ここでは「どこで定義されているか」だけを見て、PHI 発行は行わない。
- `LoopExitLivenessBox``loop_exit_liveness.rs` 新設):
- ループ exit 後で「生きている可能性のある変数」の集合を返す箱。
- Phase 26-F 時点では中身は保守的近似+ダミー、実運用は環境変数ガードで OFF。
- 環境変数:
- `NYASH_EXIT_LIVE_ENABLE=1` で将来の MIR スキャン実装を opt-in で有効化(既定は 0/未設定)。
- `NYASH_EXIT_LIVENESS_TRACE=1` でトレース出力。
- `BodyLocalPhiBuilder``body_local_phi_builder.rs`:
- `LoopVarClassBox` の分類結果と `LoopExitLivenessBox``live_at_exit` を統合し、「どの BodyLocal に exit PHI が必要か」を決める箱。
- 既定挙動(ガード OFF:
- これまで通り `class.needs_exit_phi()` のみを見るPinned / Carrier / BodyLocalExit
- ガード ON の挙動(まだ実験段階):
- `BodyLocalInternal` かつ `live_at_exit` に含まれ、かつ `LocalScopeInspector::is_available_in_all` が true なものだけ、追加で exit PHI 候補に昇格できる OR ロジックを持つ。
- `PhiInvariantsBox``phi_invariants.rs`:
- Exit/If PHI 最後の Fail-Fast 検証箱。
- 「全 pred で定義されているか」「不正な incoming が無いか」をチェックし、構造バグはここで止める。
- Docs 整備:
- `docs/development/architecture/loops/loopform_ssot.md` に 4箱構成と環境変数ガード方針を追記。
- `docs/private/roadmap2/phases/phase-26-F/README.md` を新設し、26-F のスコープやらないこと次フェーズMIR スキャン本体)への橋渡しを書き切り。
**テスト状況(ガード OFF 時点)**
- Phase 26-F-3 → 26-F の流れで、一時的に退行したが、
- `NYASH_EXIT_LIVE_ENABLE` を既定 OFF にし、
- BodyLocalInternal 救済ロジックをガード付きに戻したことで、
- F3 ベースラインより PASS が増え、FAIL が減る状態(例: 365 PASS / 9 FAILまで持ち直し済み。
- FuncScanner 系:
- `mir_funcscanner_skip_ws_direct_vm`
- `mir_funcscanner_parse_params_trim_min_verify_and_vm`
は引き続き「BodyLocal / Exit Liveness のカナリア」として使う(未定義値が出た場合は 26-G 以降で追う)。
**このフェーズで残っていること**
- `ExitLivenessProvider` 相当のインターフェースを `ExitPhiBuilder` 周辺に導入し、「ExitLiveness を差し替え可能」な受け口だけ整える(中身は Legacy のまま)。→ **完了**`MirScanExitLiveness` も追加済み(現状は header/exit_snapshots の union を返す簡易版)。
- LoopFormOps / MirBuilder に MIR 命令列アクセスを追加する設計を 26-F の README にメモしておき、実装は 26-G 以降に分離する。
### 1-02+. Phase 26-G — Exit Liveness MIR Scan計画開始
- 26-F で作った差し替え口に、本物の use/def スキャン実装を載せるフェーズ。
- `NYASH_EXIT_LIVE_ENABLE=1` で MIR スキャン版を有効にし、FuncScanner カナリアskip_ws / parse_params_trimを緑にするのが目標。
- 新設 docs: `docs/private/roadmap2/phases/phase-26-G/README.md` に手順と受け入れ条件を記載済み。
### 1-03. Phase 25.3 — FuncScanner / StageB defs 安定化(完了)
**目的**
- StageB / FuncScanner ラインで defs が欠落したり Loop が脱落する問題を塞ぎ、selfhost 側の canary を緑に戻す。
**やったこと**
- StageBDriverBox.main:
- main 本文を `{…}` で包んだ block パーサ優先で Program(JSON) に組み立て、defs には `{"type":"Block","body":[…]}` 形式で埋め込むよう整理。
- Program パーサ fallback は `HAKO_STAGEB_PROGRAM_PARSE_FALLBACK=1` の opt-in に封じ込めskip_ws 崩れを回避)。
- StageBFuncScannerBox._scan_methods:
- block パーサ優先に統一し、Program パーサは `HAKO_STAGEB_FUNC_SCAN_PROG_FALLBACK=1` でのみ有効化。
- defs パラメータに必ず `me` を足す従来挙動は維持TestBox/Main いずれも同型で出力)。
- Rust 層の追加変更なしLoopForm v2 / LoopSnapshotMergeBox をそのまま利用)。
**結果**
- `tools/smokes/v2/profiles/quick/core/phase251/stageb_fib_program_defs_canary_vm.sh` が安定して PASS。
- `Program.kind == "Program"`
- `defs``TestBox.fib` / `Main.main` を保持していること。
- `TestBox.fib.body.body[*]``Loop` ノードが含まれること。
を満たした状態で `rc=0` になることを確認。
- FuncScanner / StageB 経由の fib defs ラインは LoopForm v2 + LoopSnapshotMergeBox 上で構造的に安定したとみなし、Phase 25.3 はクローズ。
- 次フェーズの入口が整理できたので、Stage1 UsingResolver ループRegion+next_i 形)と Stage1 CLI program-json/selfhost 導線に着手可能。
### 1-1. Phase 25.1m — Static Method / LoopForm v2 continue + PHI Fix完了
**目的**
- 静的メソッド呼び出し時の「暗黙レシーバ引数ずれ」バグと、LoopForm v2 経路における `continue` + header PHI の欠落を根本から直す。
**Rust 側(静的メソッド / 暗黙レシーバ / JSON v0 Bridge**
- `src/mir/function.rs::MirFunction::new`
- 暗黙 receiver 判定を是正し、**「第 1 パラメータが Box 型の関数だけ」をインスタンスメソッド with receiver** とみなす。
- 非 Box 型(`String`, `Integer` など)で始まるパラメータ列の関数は、暗黙レシーバなしの静的メソッド / Global 関数として扱うように変更。
- その結果:
- `static box TraceTest { method log(label) { ... } }` に対して `TraceTest.log("HELLO")` を呼ぶと、
`label``"HELLO"` が正しく入る(以前は `label = null` になっていた)。
- `src/mir/builder/decls.rs::build_static_main_box`
- `Main.main(args)` を「静的エントリ関数」に lower する経路を **`NYASH_BUILD_STATIC_MAIN_ENTRY=1` のときだけ有効** にし、
通常の VM 実行では wrapper `main()` を正規エントリとして扱うように整理。
- これにより、「`Main.main(args)` 版ではループが 1 度も回らず `return 0` で終わる」バグを解消。
- JSON v0 Bridge 経由の Box メソッドStage1/StageB defs:
- `src/runner/json_v0_bridge/lowering.rs``prog.defs` の降下ロジックを調整し、
- `box_name != "Main"` の関数定義をインスタンスメソッドとして扱って `signature.params` に「暗黙 `me` + 明示パラメータ」を載せる。
- `func_var_map``me``func.params[0]` を事前バインドし、残りのパラメータ名を `params[1..]` に対応づける。
- これにより、`Stage1UsingResolverFull._build_module_map()` のように JSON では `params: []` でも Hako 側で `me._push_module_entry(...)` を使う関数について、
Rust VM 実行時に `me` が未定義ValueId(0))になるケースを構造的に防止。
**LoopForm v2continue + header PHI**
- `src/mir/phi_core/loopform_builder.rs::LoopFormBuilder::seal_phis`
- シグネチャを `seal_phis(ops, latch_id)``seal_phis(ops, latch_id, &continue_snapshots)` に拡張。
- preheader と latch に加え、`LoopBuilder` 側で記録している **`continue_snapshots` からの値も header PHI の入力** として統合。
- pinned / carrier いずれも、header の全 predecessor:
- `(preheader, preheader_copy)`
- `(continue_bb, value_at_continue)`
- `(latch, value_at_latch)`
を入力として持つようになり、balanced scan など「continue を含むループ」の SSA が正しく構成される。
- `src/mir/loop_builder.rs::build_loop_with_loopform`
- `let continue_snaps = self.continue_snapshots.clone();`
- `loopform.seal_phis(self, actual_latch_id, &continue_snaps)?;`
- という形で、LoopBuilder → LoopForm 側に `continue` スナップショットを橋渡し。
**ControlForm / LoopShape invariant**
- `src/mir/control_form.rs::LoopShape::debug_validate`debug ビルドのみ)
- 既存の:
- `preheader -> header` エッジ必須
- `latch -> header` バックエッジ必須
- に加えて、次の invariant を追加:
- `continue_targets` の各ブロックから `header` へのエッジが存在すること。
- `break_targets` の各ブロックから `exit` へのエッジが存在すること。
- これにより、LoopForm / LoopBuilder が `continue` / `break` 経路を誤配線した場合に、構造レベルで早期検知できる。
**テスト / 検証**
- MIR ユニットテスト:
- `src/mir/phi_core/loopform_builder.rs::tests::test_seal_phis_includes_continue_snapshots`
- LoopFormBuilder 単体で「preheader + continue + latch」が PHI 入力に含まれることを固定。
- `src/tests/mir_stageb_loop_break_continue_verifies`
- StageB 風の `loop + break/continue` パターンで MirVerifier 緑。
- `src/tests/mir_stage1_using_resolver_verify.rs::mir_stage1_using_resolver_full_collect_entries_verifies`
- LoopForm v2/PHI v2 経路(現在は既定実装)で Stage1 UsingResolver フル版が MirVerifier 緑。
- 実行観測:
- 開発用 Hako `loop_continue_fixed.hako`:
- 期待 `RC=3` / 実測 `RC=3`、PHI pred mismatch なし。
- StageB balanced scan:
- `StageBBodyExtractorBox` のバランススキャンループに trace を入れて 228 回イテレーションが回ること、
`ch == "{"` ブランチで `depth` / `i` を更新 → `continue` → 次イテレーションに **確実に戻っている** ことを確認。
---
### 1-2. Phase 25.1k — LoopSSA v2 (.hako) & StageB harness 追従Rust 側はおおむね完了)
**目的**
- Rust 側の LoopForm v2 / ControlForm / Conservative PHI を SSOT としつつ、StageB / selfhost で使っている `.hako` 側 LoopSSA/BreakFinderBox/PhiInjectorBox をその規約に追従させる準備フェーズ。
**Rust 側でやったこと(サマリ)**
- Receiver / pinning:
- `CallMaterializerBox::materialize_receiver_in_callee` を事実上 no-op にし、
receiver の pinning / LocalSSA 連携を `receiver::finalize_method_receiver` に一本化。
- `MirBuilder``pin_slot_names: HashMap<ValueId, String>` を持たせ、
`LocalSSA.ensure` が「同じ slot にぶらさがる最新の ValueId」へ自動でリダイレクトできるようにした。
- Compiler Box の分類:
- `CalleeResolverBox::classify_box_kind``BreakFinderBox` / `PhiInjectorBox` / `LoopSSA` を追加し、
Stage1/StageB 用 LoopSSA 箱を `CalleeBoxKind::StaticCompiler` として明示。
**IfForm / empty else-branch の SSA fixStage1 UsingResolverFull 対応)**
- `src/mir/builder/if_form.rs`:
- `if cond { then }`else なし)のパターンで、
- else-entry 用に pre_if の `variable_map` から PHI ノードを生成したあと、
- その PHI 適用後の `variable_map``else_var_map_end_opt=Some(...)` として merge フェーズに渡すように修正。
- 以前は empty else の場合に `else_var_map_end_opt``None` になっており、
`merge_modified_vars` が pre_if の古い ValueId にフォールバックして、
merge ブロックで未定義の `%0` などを参照するケースがあった(`Stage1UsingResolverFull.main/0` の UndefinedValue
- 修正後は then/else 両ブランチで「PHI 適用後の variable_map」が merge に渡されるため、
empty else でも header/merge の SSA が崩れない。
- 検証:
- `src/tests/mir_stage1_using_resolver_verify.rs::mir_stage1_using_resolver_full_collect_entries_verifies`
`MirVerifier` 緑になり、`Stage1UsingResolverFull.main/0()` の merge ブロックで PHI 後の値(例: `%24`)を正しく参照していることを MIR dump で確認済み。
**.hako 側の今後25.1k 後半)**
- `LoopSSA.stabilize_merges(json)` を Rust LoopForm v2 の Carrier/Pinned 規約に合わせて実装する(現在はほぼ stub
- StageB Test2`tools/test_stageb_min.sh`)で得られる Program(JSON v0) に対し、
- Rust 側で `NYASH_VM_VERIFY_MIR=1` を立てた実行結果と、
- `.hako` 側 LoopSSA v2 適用後の JSON → Rust 実行結果
を比較し、BreakFinderBox / PhiInjectorBox / LoopSSA の責務を切り分けていく。
---
### 1-3. Phase 25.1e/f/g — LoopForm PHI v2 / ControlForm 統合(サマリ)
- 25.1eLoopForm PHI v2 migration:
- Local SSA`local a = ...`)の ValueId 分離を完了し、LoopForm v2 を「PHI/SSA の正」とする方向へ寄せた。
- Stage1 UsingResolver / 基本的な StageB ループは、LoopForm v2 経路で MirVerifier 緑。
- 25.1fControlForm 層の導入):
- `ControlForm` / `LoopShape` / `IfShape` を導入し、Loop / If を共通ビューとして扱う箱を定義。
- ここでは挙動を変えず、「構造だけを先に固定」する方針で設計を固めた。
- 25.1gConservative PHI ↔ ControlForm ブリッジ):
- `phi_core::loopform_builder::build_exit_phis_for_control` など、ControlForm から Conservative PHI を呼び出す薄いラッパを追加。
- 既存の PHI ロジックはそのままに、将来の置き換えポイントだけを明示している。
---
### 1-4. Phase 25.1A3 — Stage1 CLI bridgestub 実装)
- Rust 側: `src/runner/stage1_bridge.rs``run_refactored` 入口に組み込み、`NYASH_USE_STAGE1_CLI=1` かつ再入ガードなしのときに `lang/src/runner/stage1_cli.hako` を子プロセスで起動する。`STAGE1_EMIT_PROGRAM_JSON` / `STAGE1_EMIT_MIR_JSON` / `STAGE1_BACKEND` / `STAGE1_PROGRAM_JSON` で mode 選択。entry override は `STAGE1_CLI_ENTRY` / `HAKORUNE_STAGE1_ENTRY`
- .hako 側: `Stage1Cli` に最低限の本体を実装。
- `emit_program_json`: Stage1 UsingResolver で prefix を結合し、BuildBox.emit_program_json_v0 で Program(JSON v0) を返す。
- `emit_mir_json`: `MirBuilderBox.emit_from_program_json_v0` をそのまま呼ぶdelegate 未設定なら null
- `run_program_json`: backend==llvm の場合は `env.codegen.emit_object` まで通す。vm/pyvm は当面 MIR(JSON) を stdout に出すのみ(実行は Stage0 橋渡し未配線)。
- CLI: `emit program-json|mir-json` / `run --backend ... <src>` を受理。`NYASH_SCRIPT_ARGS_JSON` を JSON で best-effort 伝播。
- Docs: `docs/private/roadmap2/phases/phase-25.1/stage1-usingresolver-loopform.md` に stub 状態を追記run は暫定挙動)。
- Known gaps: vm/pyvm 実行はまだ Stage0 への橋渡し未着手。llvm も emit object 止まりlink/exec は後続)。
### 1-5. Phase 25.1A4 — Using SSOT 薄設計BuildBox include 除去
- SSOT: `lang/src/using/resolve_ssot_box.hako` に README 相当のコメントと I/Fresolve_modules/resolve_prefixを追加。現状は no-op だが「using をここで扱う」窓口を固定。
- Stage1UsingResolver: `resolve_for_program_json` を追加し、SSOT を呼ぶ導線だけ確保(現状は透過返し)。
- BuildBox: 先頭の `include` を削除し、`using lang.compiler.entry.bundle_resolver as BundleResolver` に置換StageB パーサが include を解せない問題の足場づくり)。
- 実行確認: `NYASH_ALLOW_NYASH=1 HAKO_ALLOW_NYASH=1 NYASH_USE_STAGE1_CLI=1 STAGE1_EMIT_PROGRAM_JSON=1 ... cargo run --bin hakorune -- basic_test.hako` が RC=0 で完走(ただし stdout は `RC: 0` のみ、program-json 出力は未配線)。`cargo run ... emit program-json` は Rust CLI 側の表面が未対応のためエラー(想定挙動)。
---
## 2. まだ残っている問題・課題2025-11-18 時点)
### 2-1. StageB 本体の型エラー(`String > Integer(13)`
- 症状:
- StageB の `Main.main` 実行時に、
`Type error: unsupported compare Gt on String(...) and Integer(13)` が発生。
- LoopForm v2 / PHI / continue 修正とは **独立の StageB 固有のロジック問題**
- 想定される原因:
- StageB 側の body 構築 / JSON 生成で、本来数値比較にすべき箇所で「生の文字列」と整数を比較している。
- もしくは、Parser/Scanner が `len` やインデックスを文字列のまま扱っている部分がある。
- 対応方針(次フェーズ向けメモ):
- `StageBBodyExtractorBox.build_body_src` が吐く `body_src` を最小ケースで抽出し、
その中から問題の比較式(`>`)がどのように生成されているかを特定する。
- StageB の box レベルで:
- 「どのフェーズで型を決めるか」(例: ParserBox / StageB / VM 手前)を決めてから修正する。
- このタスクは 25.1c 続き or 新フェーズ25.1n 相当として、StageB 箱の設計側で扱う。
---
### 2-2. StageB 再入ガード `env.set/2` の扱い
- 現状:
- 一部 StageB コードが `env.set/2` を使った再入ガードに依存しており、
Rust VM 側には `env.set/2` extern が定義されていないため、
`❌ VM error: Invalid instruction: extern function: Unknown: env.set/2` が発生するケースがある。
- 方針メモ:
- 選択肢 A: StageB 再入ガードを Box 内 stateフィールドに寄せて、`env.set/2` 依存をなくす。
- 選択肢 B: StageB ハーネス専用に、最小限の `env.set/2` extern を Rust 側に実装する(本番経路では使わない)。
- 25.1m では構造修正Loop/PHI/receiverを優先し、この extern の話は据え置き。
---
### 2-3. .hako 側 LoopSSA v2Rust LoopForm v2 との乖離)
- 現状:
- `lang/src/compiler/builder/ssa/loopssa.hako``stabilize_merges()` はまだ実質的に stub に近い。
- StageB Test2`compiler_stageb.hako` 経由では、Rust MIR 側で LoopForm v2 / PHI v2 が安定した後も、
`.hako` 側 LoopSSA 経由の JSON から生成した MIR で PHI/SSA 問題が残る可能性がある。
- 目標:
- Rust LoopForm v2 を「制御構造と PHI の SSOT」とみなし、
`.hako` 側 LoopSSA が同じ Carrier/Pinned / preheader/header/exit の規約を JSON レベルで再現する。
- やること(フェーズ 25.1k 後半〜 25.1f/g 連携):
- 特定の関数(例: `BreakFinderBox._find_loops/2`を対象に、Rust 側 / .hako 側のそれぞれで生成されるループ構造を比較。
- LoopSSA v2 の中に:
- Carrier 変数の検出、
- pinned 変数の扱い、
- exit PHI の構築
を Rust LoopForm v2 に合わせて実装。
- 2025-11-19 追記:
- `BreakFinderBox._find_loops/2` については、まず .hako 側を「region box」的に整理した。
- `header_pos` / `header_id` / `exit_pos` / `exit_id` まわりの異常系を `continue` ではなく
`next_i` ローカルへの代入で表現し、1 イテレーションの末尾で `i = next_i` に合流させる形に変更。
- これにより、LoopForm v2 / LoopSSA 側から見ると「単一 region 内での分岐+最後に合流」という構造になり、
carrier/pinned 検出や今後の SSA 解析が行いやすくなった(挙動は従来と同じ)。
### 2-5. static box / me セマンティクス(観測タスクへ移行)
- 現状:
- `static box StringHelpers` のようなユーティリティ箱で、`me.starts_with(src, i, kw)` のように
同一箱内のヘルパー(`starts_with`)を `me.` 経由で呼んでいたため、Stage3 降下時に引数ずれが発生していた。
- 具体的には、`StringHelpers.starts_with_kw/3``StringHelpers.starts_with/3` の降下で
実際の呼び出しが `starts_with("StringHelpers", src, i, kw)` のような 4 引数形になり、
`starts_with(src, i, pat)` 側では `src="StringHelpers"` / `i=<ソース全文>` となって、
`if i + m > n``String > Integer(13)` 比較に化けていた。
- 対応(完了済み・局所修正):
- `lang/src/shared/common/string_helpers.hako``starts_with_kw` を、
`if me.starts_with(src, i, kw) == 0` から `if starts_with(src, i, kw) == 0` に書き換え、
static box ユーティリティに対する `me` 依存を除去した。
- これにより、`starts_with` 内でのガード比較 `i + m > n` はすべて整数同士となり、
StageB fib ケースで発生していた `String("...") > Integer(13)` の TypeError は解消済み。
- 今後Phase 25.1p 以降):
- static box 全般における `me` セマンティクス(本当に「シングルトンインスタンス」として扱う箱と、
純粋な名前空間箱をどう区別するかは、25.1p の DebugLog フェーズで観測しながら設計を詰める。
- 実際に Rust 層(`build_me_expression` / `lower_static_method_as_function` / `FunctionDefBuilder::is_instance_method`)を
統一規約に寄せる作業は、25.1p 以降のサブタスクとして扱う(現時点では局所修正でバグのみ解消)。
---
### 2-4. Builder / Selfhost まわりの残タスク(超ざっくり)
- Builder 内部ルート20.43 系):
- `MirBuilderBox` 経由の internal ルートで、MIR を stdout 経由ではなく一時ファイル / FileBox に書き出し、
ハーネスがそこから読む形に揃える案が残タスク。
- Selfhost CLI / Stage1 CLI:
- StageB / Stage1 CLI を「Rust VM / LLVM / PyVM / selfhost」の 4 経路で安定確認するラインは進行中。
- 25.1m では Rust VM + StageB balanced scan を優先し、CLI 全体は次のフェーズで詰める。
---
## 3. 次にやること(候補タスク)
ここから先は「どのフェーズを進めるか」をそのときの優先度で選ぶ感じだよ。
Rust 側は LoopForm v2 / StageB fib / Stage1 UsingResolver 構造テストまで一通り整ったので、当面は Stage1 CLI / selfhost ラインの小さな箱から進める。
### A. Stage1 CLI / Stage0 ブリッジPhase 25.1 — いまここ)
- A1: Stage1 CLI stub を Stage0 Rust ランナーから呼び出すブリッジDONE
- 実装: `src/runner/stage1_bridge.rs` `src/runner/mod.rs``maybe_run_stage1_cli_stub` を追加。
- `NYASH_USE_STAGE1_CLI=1`(既定 OFFかつ `NYASH_STAGE1_CLI_CHILD!=1` のときにだけ有効。
- entry `.hako``STAGE1_CLI_ENTRY` / `HAKORUNE_STAGE1_ENTRY` で上書き可能(既定: `lang/src/runner/stage1_cli.hako`)。
- 入力ファイル / モード:
- `STAGE1_EMIT_PROGRAM_JSON=1`: `hakorune emit program-json <source.hako>` を子プロセスで実行。
- `STAGE1_EMIT_MIR_JSON=1`: `STAGE1_PROGRAM_JSON` があれば `--from-program-json`、なければ `<source.hako>` から `emit mir-json`
- 上記どちらも無い場合: `hakorune run --backend <backend> <source.hako>`backend は `STAGE1_BACKEND` か CLI の backend 設定)。
- `NYASH_SCRIPT_ARGS_JSON` を拾って `--` 以降の script 引数も Stage1 側に転送。
- 再入防止として子プロセスには `NYASH_STAGE1_CLI_CHILD=1` を付与(子側からは Rust ブリッジを素通り)。
- A2: Stage1 CLI skeleton の責務を doc に固定DONE
- `lang/src/runner/stage1_cli.hako``Stage1Cli.emit_program_json/emit_mir_json/run_program_json/stage1_main` のシグネチャとトグルトポロジーを固定。
- `docs/private/roadmap2/phases/phase-25.1/stage1-usingresolver-loopform.md` に Rust 側ブリッジの振る舞いとトグル名(`NYASH_USE_STAGE1_CLI` / `STAGE1_EMIT_*` / `STAGE1_BACKEND` / `NYASH_STAGE1_CLI_CHILD`)を追記。
- A3: 次ステップ(未着手)
- Stage1 CLI skeleton に StageB/BuildBox/MirBuilder 呼び出しを順に実装し、「Program(JSON)/MIR(JSON) を selfhost 経由で emit できる」状態まで持っていく。
- `tools/selfhost/run_stage1_cli.sh` から呼び出す selfhost パスと、Rust CLl からのブリッジパスの両方で JSON I/O 契約が同じになるように揃える。
### B. Stage1 UsingResolver / LoopForm v2 ラインPhase 25.1e 系フォロー)
- B1: entry UsingResolver の Region+next_i 化とコメント整備(おおむね DONE
- `lang/src/compiler/entry/using_resolver_box.hako` の 3 ループentries / JSON スキャン / modules_listは Region+next_i 形に揃え済み。
- 役割コメント: `resolve_for_source`StageB body_src を受けて prefix を返す)、`_collect_using_entries`JSON スキャンして entries を集める)、`_build_module_map`modules_list を map 化)を明記済み。
- `HAKO_STAGEB_APPLY_USINGS=0` の時は prefix を空 string にしつつ、depth ガードだけは走らせる仕様もコメントで固定。
- B2: UsingResolver 構造テスト(ループ形/PHIの拡充DONE
- `src/tests/mir_stage1_using_resolver_verify.rs` に Region+next_i ループと early-exit JSON スキャンパターンの軽量テストを追加。
- これらは cargo test 経路で常時緑を維持し、v2 quick スモークへの昇格は「実行時間とイズを見ながら後続フェーズで検討」という扱いにするdocs にメモ済み)。
- B3: pipeline_v2 UsingResolver との責務境界DONE
- `lang/src/compiler/pipeline_v2/using_resolver_box.hako` 冒頭に、「entry 側=ファイル I/O + using 収集」「pipeline_v2 側=modules_json 上の alias/path 解決」と役割メモを追加。
- RegexFlow ベースの単一路ループは Region 化不要stateful helperとし、LoopForm v2 の観点からも「観測対象外」とする。
### C. FuncScanner / Exit PHI カナリアPhase 26-F / 26-G への橋渡し)
- C1: FuncScanner trim/skip_ws/parse_params の最小再現ケース固定DONE
- `lang/src/compiler/tests/funcscanner_trim_min.hako``_trim` / `trim` / `skip_whitespace` を 1 回ずつ呼ぶ最小 Main を定義。
- `src/tests/mir_funcscanner_trim_min.rs` で:
- Stage3 / using 有効化したパーサ設定で func_scanner.hako + 上記テストを一体コンパイル。
- `MirVerifier` でモジュール全体の SSA/PHI を検証(`HAKO_MIR_BUILDER_METHODIZE=0` でも常に緑になることを確認)。
- VM 実行は `NYASH_TRIM_MIN_VM=1` のときだけ有効化(いまは MIR 側の根治が主目的)。
- C2: FuncScanner 側ロジックの構造整理DONE
- `lang/src/compiler/entry/func_scanner.hako`:
- `parse_params` を Region+next_i 形の 1 本ループに整理し、先頭スキップとカンマ探索をそれぞれ helper に寄せた。
- `trim` は先頭側を `skip_whitespace` に全面委譲し、末尾側のみ後ろ向きループで処理するように簡素化。
- `skip_whitespace``loop(i < n)` + if/continue だけにした最小形にし、過去の dev 向けログや loop(1==1) ワークアラウンドを撤去。
- これにより FuncScanner フロントは LoopForm v2 / LoopSSA v2 から見て「素直なループ+明確な next_i 形」になり、以後の PHI/ExitLiveness 側の根治作業が `.hako` に依存しづらい形になった。
- C3: Phase 26-F / 26-G のカナリアとして位置付け(進行中)
- 26-F 時点では `NYASH_EXIT_LIVE_ENABLE` 既定 OFF で、従来挙動のまま `mir_funcscanner_trim_min` が MIR verify 緑になることを確認済み。
- 26-G では:
- `NYASH_EXIT_LIVE_ENABLE=1` + MirScanExitLiveness 経由でも `mir_funcscanner_trim_min`(特に FuncScannerBox.trim/1 / skip_whitespace/2 / parse_params/1が常に緑になることを受け入れ条件にする。
- そのうえで `mir_funcscanner_skip_ws_direct_vm` / StageB / Stage1 UsingResolver 系のカナリアも順に緑に揃える。
### D. StageB / LoopSSA / Selfhost まわり(中期タスク)
- D1: StageB 再入ガード `env.set/2` の整理(据え置き)
- dev 専用 extern を Rust 側に追加するか、StageB 箱側で state に寄せるかを決める必要があるが、現在はループ/PHI ラインを優先し保留。
---
## 4. Phase 26-H — JoinIR 設計 & ミニ実験(新規フェーズ)
**目的**
- 制御構造if / loop / break / continue / return**関数呼び出し+継続** に正規化する中間層JoinIRを設計し、LoopForm v2 / PHI / ExitLiveness の負担を将来軽くする足場を作る。
- 25.1 / 26-F / 26-G の本線を止めずに、「設計+ごく小さな実験」だけを先に進める。
- スモーク/本線は既存の MIR/LoopForm 経路のまま維持しつつ、徐々に「関数型LoopFnIR/JoinIR」側に重心を移す。
**進捗26-H 完了分)**
- JoinIR 設計ドキュメント反映済み(`docs/development/architecture/join-ir.md`
- 26-H README/TASKS でスコープ・最終箱セット・次フェーズ境界を明記
- `src/mir/join_ir.rs` で JoinIR 型定義+ JoinIrMin 用のミニ自動変換を実装
- `apps/tests/joinir_min_loop.hako` + `src/tests/mir_joinir_min.rs`(トグル付きカナリア)を追加
- トグル: `NYASH_JOINIR_EXPERIMENT=1` で JoinIR 実験を有効化(デフォルトは既存 MIR/LoopForm のみ)
**次フェーズPhase 27 — JoinIR 実用化)**
- フォルダ: `docs/private/roadmap2/phases/phase-27-joinir/`
- 27.1: JoinIR 変換を FuncScanner/StageB の代表ループに拡張(トグル付き)
- 27.2: JoinIR → VM/LLVM ブリッジのプロトタイプを作り、A/B 実行を試す(トグル付き)
- 27.3: レガシー PHI/Loop 箱を段階削減Header/Exit/LoopPhi 系の吸収・削除計画を実行)
**やること26-H スコープ)**
- H1: JoinIR 設計ドキュメントの追加(**完了**
- `docs/development/architecture/join-ir.md` に命令セットと変換規則、対応表を記述済み。
- `docs/private/roadmap2/phases/phase-26-H/README.md` に、26-H のスコープ/やらないこと/他フェーズとの関係を記載済み。
- H2: JoinIR 型定義とミニ変換の骨格(**完了**
- `src/mir/join_ir.rs``JoinFunction/JoinInst/JoinContId/JoinModule` 等の型を定義済み。
- `lower_min_loop_to_joinir``JoinIrMin.main/0` 用の試験的な自動変換を実装Phase 27.x で一般化予定)。
- `src/tests/mir_joinir_min.rs``apps/tests/joinir_min_loop.hako` でカナリアテストを追加(`NYASH_JOINIR_EXPERIMENT=1` 時のみ有効)。
- H3: トグル付きミニ実験(**完了**
- `NYASH_JOINIR_EXPERIMENT=1` で JoinIR 実験テストを有効化。
- トグル OFF 時は既存の MIR/LoopForm 経路のみが動作することを確認(ゼロリグレッション)。
**やらないこと26-H では保留)**
- 既存の LoopForm v2 / PhiBuilderBox / ExitPhiBuilder を JoinIR ベースに全面移行すること。
- StageB / Stage1 / CLI / selfhost ラインの本線を JoinIR で差し替えること。
- 既存の SSA/PHI 実装を削除すること(全部別フェーズで検討)。
**優先度と位置付け**
- 本線(いま重視する順):
1. Phase 25.1 — Stage1 UsingResolver / Stage1 CLI program-json/mir-json を安定化。
2. Phase 26-F / 26-G — Exit PHI / ExitLiveness の根治LoopForm v2 / PHI SSOT / MirScanExitLiveness
- Phase 26-H は:
- 「本線の合間に進める設計フェーズ」として扱う。
- JoinIR が小さいケースでうまく動くことを確認できたら、27.x 以降で本格的な導入を検討する。
- このタスクに着手するときは「prod/CI 経路から完全に切り離した dev ガード」として設計する。
- C2: .hako LoopSSA v2 実装Rust LoopForm v2 への追従)
- Rust LoopForm v2 の Carrier/Pinned/BodyLocalInOut モデルを `.hako` の LoopSSA に輸入し、StageB Test2 / selfhost CLI でも MirVerifier 緑を目指す中〜長期タスク。
- 具体的な対象: `lang/src/compiler/builder/ssa/loopssa.hako` と StageB/BreakFinder 周辺の region 化済みループ。
- C3: Selfhost / CLI 周辺のテスト整理
- 代表的な selfhost / StageB / Stage1 CLI ケースを tests/tools 側でタグ付けquick/integration/selfhost、Phase 25.1 の「どこまでが Rust 側」「どこからが Stage1 側」を見える化する。
---
## 4. 履歴の見方メモ
- 以前の `CURRENT_TASK.md` は ~1900 行の長いログだったけど、読みやすさ重視でこのファイルはスナップショット形式にしたよ。
- 過去の詳細ログが必要になったら:
- `git log -p CURRENT_TASK.md`
- あるいは特定のコミット時点の `CURRENT_TASK.md``git show <commit>:CURRENT_TASK.md`
でいつでも復元できるよ。
---
# Phase 25.4 — Naming & Stage1 CLI Cleanupdesign only
- ねらい:
- static box / global 呼び出しの命名規約を NamingBox に集約し、VM 側のレガシーフォールバック経路を撤去する。
- Stage1 CLI の env/トグル解釈を 1 箇所の設定箱にまとめ、stage1_main の責務を薄く保つ。
- `__mir__.log` ベースの MIR ログ観測ポイントをドキュメントで一覧化し、将来の正式 API 化に備える。
- 現状:
- NamingBox`src/mir/naming.rs`は導入済みで、Builder 側の static メソッド名は `encode_static_method` 経由、VM 側の global 呼び出しは `normalize_static_global_name` 経由になっている。
- VM 側の「canonical 名で見つからなければ元名でもう一度探す」フォールバックは削除済みで、`mir_static_box_naming` テスト群が `Main._nop/0` 経路を固定している。
- Stage1 CLI は env-only 仕様argv 依存なし)で Stage0 ブリッジと接続済みだが、env 群の解釈はまだ stage1_main 内に散在している。
- このフェーズでやること(設計レベル):
- NamingBox を「static 名に触るすべてのコードの SSOT」として整理直接 `format!("Box.main")` する箇所を洗い出し)。
- `Stage1CliConfigBox`を設計し、env→Config 変換の責務とフィールドを docs に書き出す(実装は後続でも可)。
- `__mir__.log` のタグと用途を 1 ページの docs にまとめ、dev 用ログと残したい観測ログを分けておく。
---
以上が 2025-11-18 時点の Phase 21.8 / 25 / 25.1 / 25.2 / 25.4 ラインの「いまどこ」「なに済み」「なに残り」だよ。
次にどの箱から攻めるか決めたら、ここに箇条書きで足していこうね。
## 3. これからやるタスクのラフ一覧25.1 / Stage1 系)
ここから先は「まず設計と箱分割を書いてから実装」という方針で進めるタスク群だよ。
### A. Rust 層解析LoopForm v2 / JSON v0 / Stage1 観測)
- A-1: LoopForm v2 / LoopSnapshotMerge の入口確認
- `src/mir/loop_builder.rs` / `src/mir/phi_core/loopform_builder.rs` / `src/mir/phi_core/loop_snapshot_merge.rs` を「Stage1 から見た導線」として読み直し、どのレイヤで Carrier / Pinned / BodyLocalInOut が決まるかを short メモ化する。
- A-2: JSON v0 → MIR ブリッジの導線整理
- `src/runner/json_v0_bridge/lowering/`(特に `loop_.rs`を通して、Program(JSON v0).body / defs.body(Block) が LoopForm v2 までどう運ばれるかを図として docs に落とす。
- A-3: Stage1 UsingResolver の MIR 観測
- 既存テスト `src/tests/mir_stage1_using_resolver_verify.rs` の MIR dump を元に、「どのループが Region+next_i 化候補か」「既に問題なく LoopForm に乗っている場所はどこか」を箇条書きで整理する。
### B. Stage1 UsingResolver 箱化・ループ整理(.hako 側)
- B-1: Stage1UsingResolverBox の責務分割設計
- `lang/src/compiler/entry/using_resolver_box.hako` を、collect_entries / modules_map / file_read などの小さいロジック箱に概念的に分割し、「どの箱が何を責務とするか」を docs に書く(実際のファイル分割は後段)。
- B-2: すべてのループを Region+next_i 形に揃える計画
- entry の UsingResolver 内に残っている「pos++/continue 多発型」ループを洗い出し、Region+next_i 形にどう書き換えるかを phase-25.1 docs に追記する。
- 目的: LoopForm v2 / PHI から見たときに「1 region / 1 backedge / 明確な次位置決定」という形に統一する。
- 状況メモ: entry 側 3 ループentries/JSON scan/modules_listは Region+next_i 化済み。残りなし。
- B-3: pipeline_v2 UsingResolver との役割分担
- `lang/src/compiler/pipeline_v2/using_resolver_box.hako` と entry/Stage1UsingResolverBox のどちらが「テキスト / JSON / modules_map」のどこまでを担当するかを整理し、責務境界をドキュメントに固定する。
- 状況メモ: pipeline_v2 側は modules_json の alias 解決のみRegexFlow.find_from の単一路ループ。Region 化不要として据え置き、境界だけ明記。
- B-4: 構造テストの追加計画
- Region+next_i パターンの軽量 SSA テスト(すでに 1 本追加済み)を基準に、もう 1〜2 本、UsingResolver ソースに近いパターンJSON スキャンearly-exitを追加する方針を決める。
### C. Stage1 CLI / program-json / mir-json SelfHost 準備
- C-1: Stage1 CLI インターフェースの設計メモ
- `.hako → Program(JSON)` / `Program(JSON) → MIR(JSON)` / `MIR(JSON) → 実行` を Stage1 側からどう呼び出すか(関数名・引数レベル)を docs に先に書く。
- C-2: StageB → Stage1 データフロー図
- `compiler_stageb.hako` → Program(JSON v0) → Stage1 UsingResolver → MirBuilder までのパイプラインを Phase25.1 ドキュメントに 1 枚の図としてまとめる。
- C-3: Rust CLI 側ブリッジの最小ガード案
- Stage0/Rust CLI は「Program(JSON/MIR(JSON) を受け取り VM/LLVM に流すだけ」に縮退させる方針と、既定 OFF トグルselfhost 入口)をどう切るかを設計メモとして追加。
### D. 将来フェーズ向けメモvariable_map 決定化ライン)
- D-1: MirBuilder::variable_map / BoxCompilationContext::variable_map BTreeMap 化案
- どの構造を HashMap→BTreeMap 化するか、どのテスト(`mir_funcscanner_skip_ws_vm_debug_flaky` など)で決定性を確認するかを Phase25.x の設計メモとして書いておく。
- D-2: dev 専用 / flaky テストの扱い方針
- どのテストが dev ignore手動実行用で、いつ/何を満たしたら常時有効に戻すかを、このファイルと関連 README に明確に残す。
このセクションは「すぐ実装する TODO ではなく、25.1〜25.x ラインで踏むべき設計タスク一覧」として使う予定だよ。
(静的 me-call 修正の参考メモはここに残しつつ、別セクションは整理済み)
### E. Legacy Loop/PHI 経路の囲い込みと削除準備
- E-1: Legacy loop_phi.rs の役割と削除条件の明文化
- ファイル: `src/mir/phi_core/loop_phi.rs`
- 状態: LoopForm v2 + LoopSnapshotMergeBox への移行後は「legacy scaffold」としてのみ残存。
- やること:
- 現在の利用箇所を 2 種類に分類する:
- 本線経路LoopForm v2 / Stage1 / StageB から参照される部分)
- 互換レイヤ解析専用JSON v0 bridge, 旧 smokes, dev-only ヘルパ)
- 本線はすべて `loopform_builder.rs` + `header_phi_builder.rs` + `loop_snapshot_merge.rs` で賄えることを確認し、`loop_phi.rs` を「legacy 専用(新規利用禁止)」として CURRENT_TASK と docs に固定しておく。
- Phase 31.x の cleanup で実ファイル削除してよい条件(参照 0対応する smokes/テストの移行完了)を `docs/private/roadmap/phases/phase-31.2/legacy-loop-deletion-plan.md` 側と揃えておく。
- E-2: PHI/LoopForm 周辺で HashMap を使ってよい/いけない場所を線引き
- ファイル: `src/mir/builder.rs`, `src/mir/phi_core/*`, `src/mir/loop_builder.rs`
- やること:
- 「PHI 生成とスナップショット決定」に関わる構造では `BTreeMap` / `BTreeSet` / `Vec+sort` のみに限定し、`HashMap` は使わない、というルールを Phase 25.1 docs に明記する。
- それ以外のメタ情報plugin sigs, weak_fields など)は HashMap 維持可とし、「決定性」に影響しないことをコメントで示しておく。
- 代表として `loop_phi.rs` 内の `sanitize_phi_inputs` のように「一度 HashMap で集約してから sort する」パターンは、LoopForm v2 正系統では `PhiInputCollector` + BTree 系で代替されていることを確認し、legacy 側のみに閉じ込める。
- E-3: Legacy 経路の一覧と新規利用禁止ポリシーを docs に反映
- ファイル:
- `docs/private/roadmap2/phases/phase-25.1/README.md`(本線側のルール)
- `docs/private/roadmap/phases/phase-31.2/legacy-loop-deletion-plan.md`(削除計画側と整合させる)
- やること:
- Legacy として扱うモジュール(例: `phi_core::loop_phi`, 一部旧 JSON v0 bridge helperを一覧にして、「新しいコードからここを呼ばない」「Phase 31.x で削除予定」と明記する。
- 逆に、今後 PHI/Loop/If で使うべき SSOT 箱LoopForm v2 + HeaderPhiBuilder + BodyLocalPhiBuilder + if_phi + ControlFormを 1 セクションで列挙し、「ここだけを見ると設計が分かる」導線を作る。
### F. IfForm / Body-Local PHI 統合Phase 26-F 進捗メモ)
- F-1: IfBodyLocalMergeBox の導入(完了)
- ファイル: `src/mir/phi_core/if_body_local_merge.rs`
- 責務: if-merge 専用の body-local PHI 候補決定。
- 両腕に存在する変数のみを候補とし、`pre_if` から値が変化した変数だけを返す。
- empty else の場合は空リストを返し、従来の PhiBuilderBox ロジックに委ねる。
- F-2: LoopBuilder 内 if-merge への統合(進行中)
- ファイル: `src/mir/loop_builder.rs`, `src/mir/phi_core/phi_builder_box.rs`
- 状態: Phase 26-F-2 までで、loop 内 if の PHI 生成に IfBodyLocalMergeBox を噛ませるところまで実装。
- 代表テスト 1 本が新たに PASS になったが、Loop PHI 側に残る domination errorValue %48 / non-dominating useがまだ存在。
- メモ: この残件は LoopForm Exit PHI 側ExitPhiBuilder/LoopFormBuilderの古い Case に起因する既知バグとして扱い、次フェーズで Loop PHI 側の SSOT 化PhiBuilderBox への統合 or ExitPhiBuilder 根治)の入口とする。
## 3. Phase 25.1q — LoopForm Front UnificationDONE / follow-up 別タスクへ)
- 目的: Rust AST ルート (LoopBuilder) と JSON v0 ルート (`json_v0_bridge::lower_loop_stmt`) のループ lowering を “LoopFormBuilder + LoopSnapshotMergeBox” に一本化し、どの経路からでも同じ SSOT を見るだけで良い構造に揃える。
- 完了状態:
- AST ルート:
- `LoopBuilder::build_loop_with_loopform` で canonical `continue_merge_bb` を常時生成し、`continue_target` を header ではなく `continue_merge_bb` に統一済み。
- `LoopFormBuilder::seal_phis` / `LoopSnapshotMergeBox` の continue/exit 入力は「preheader + continue_merge + latch」「header + break snapshots」で完全管理。
- JSON v0 ルート:
- `loop_.rs``LoopFormJsonOps` を実装し、preheader/header/body/latch/continue_merge/exit の block ID を AST ルートと同じ形で生成。
- break/continue/exit の snapshot を `LoopSnapshotMergeBox` でマージし、canonical continue_merge → header backedge を JSON 側でも採用。
- `tests/json_program_loop.rs` で JSON v0 だけを入力にした軽量ループ(通常 / continue / body-local exit`MirVerifier` で確認するスモークを追加。
- Docs/README:
- `docs/private/roadmap2/phases/phase-25.1q/README.md` に「AST/JSON ともに LoopForm v2 + LoopSnapshotMergeBox が SSOT」と明記。
- `src/runner/json_v0_bridge/README.md` で “bridge は薄いアダプタであり、新しい PHI 仕様は loopform 側でのみ扱う” とガードを追記。
- `src/mir/phi_core/loop_phi.rs` には “legacy分析用のみ” コメントを追加。将来の cleanup (Phase 31.x) で削除対象とする。
- 残タスクは別フェーズへ:
- Stage1 UsingResolver 周りの SSA バグ(`tests::mir_stage1_using_resolver_verify::mir_stage1_using_resolver_full_collect_entries_verifies` の Undefined Valueは 25.1q の範囲外。LoopForm の SSOT 化は終わっているため、今後は Stage1 側の PHI/Env スナップショット設計タスクとして切り出す。
- JSON v0 → Nyash AST への統合案や loop_phi.rs の実ファイル削除は、Phase 31.x cleanup 計画側で扱う。