Phase 33 NORM canon test: enforce normalized dev route for P1/P2/JP mini

This commit is contained in:
nyash-codex
2025-12-11 20:54:33 +09:00
parent 59a985b7fa
commit af6f95cd4b
170 changed files with 4423 additions and 1897 deletions

View File

@ -1106,7 +1106,65 @@ Normalized JoinIR では、制御構造を次の 3 要素だけで表現する
これにより、現在 JoinIR 層で苦労している「PHI 配線」「exit_bindings/jump_args 整合性」「評価順のねじれ」は、
Normalized JoinIR 側では「Env フィールドの更新順」と「どの継続を呼ぶか」に還元される想定だよ。
### 3.3 Phase 26-H のフェーズ分割(案)
### 3.3 Normalized JoinIR 導入のメリットと初期コスト
Normalized JoinIR を 1 段挟むと、開発の手触りがどう変わるかをここでまとめておくよ。
#### 3.3.1 楽になるポイント
- **デバッグ時の「問いの分離」**
- Structured JoinIR だけで `_parse_number` / `_atoi` を追うときは、常に同時に 4 つくらいの問いを抱えることになる:
1. Pattern 判定は正しいかP1/P2/P3/P4/P5
2. CarrierInfo / LoopState / LoopLocalZero / FromHost の設計は妥当か
3. 評価順header → body-init → break → updates → tailは正しいか
4. PHI / exit_bindings / jump_args / variable_map の整合は取れているか
- Normalized JoinIR を挟むと、これが層ごとに分割される:
- 上Structured: Pattern 判定 + CarrierInfo/Condition/Update の設計
- 中Normalized: EnvLayout + step/kont 合成(制御骨格)
- 下Bridge: TailCallEnv から MIR/VM の PHI/branch を作る
- これにより、「これは Pattern/Carrier 問題」「これは Env継続の組み立て問題」「これは Bridge 問題」と、デバッグ時にレイヤ単位で切り分けやすくなる。
- **PHI まわりの思考負荷軽減**
- Structured JoinIR では、LoopHeader PHI / Exit PHI / Select 展開 PHI と、exit_bindings/jump_args/variable_map 再接続が絡み合う。
- Normalized フェーズでは、キャリアや DigitPos/NumberAccumulation などはすべて Env フィールドになり、PHI は JoinIR には現れない:
- 「どこから値が来たか?」は「どの Env フィールドがどの順番で更新されたか」を見るだけでよくなる。
- PHI/exit_bindings は Bridge 側の責務として明示的に分離される。
- **while/if/break/continue/return の骨格が共通化される**
- Pattern15 ごとに似たロジックが散らばりがちな現在と違い、Normalized では:
- ループ: `loop_step(env, k_exit)` `loop_body(env, k_exit)`
- if: 条件判定 → then/else 継続 → join 継続
- break: `TailCallKont(k_exit, env)`
- continue: `TailCallFn(loop_step, env', k_exit)`
- こうした「世界共通のテンプレ」を 1 箱にまとめておけるので、新しい Pattern や JsonParser/selfhost のループを追加するときも、構造箱Structured側だけで頑張ればよくなる。
- **「1箱 = 1質問」を守りやすくなる**
- Structured JoinIR では、PatternLowerer が一時的に「構造評価順PHI 配線exit_bindings」を抱える瞬間がある。
- Normalized を挟むと:
- Structured: 「どの値を Env に載せるか」を決める箱
- Normalized: 「Env と継続にどう分解するか」を決める箱
- Bridge: 「TailCallEnv を MIR/VM にどう落とすか」を決める箱
- という分担にできるので、「この箱は何の質問に答えるか」が清潔に保ちやすくなる。
#### 3.3.2 初期コストと注意点
- **新しい IR モデルを 1 セット覚える必要がある**
- EnvFnKontTailCall の世界観を JoinIR 層の前提として追加することになる。
- 一度定着すれば Structured より思考負荷は下がるが、導入初期は「型・不変条件・ダンプの読み方」に慣れるまで少しコストが乗る。
- **移行期間中は 2 経路の比較が発生する**
- AST → JoinIR(Structured) → MIR
- AST → JoinIR(Structured) → JoinIR(Normalized) → MIR
- の 2 経路をしばらく併走させ、dev で結果比較をするフェーズが必要になる。
- Phase 26-H / 28-NORM-P2 のように「小さいサブセットから比較用にだけ回す」方針で進めることで、コストは制御可能にする。
- **導入フェーズごとに Fail-Fast とテストが必須**
- Structured→Normalized は対応範囲外の Structured JoinModule については必ず Fail-Fastpanic/Errし、サイレントに正規化を試みない。
- `normalized_dev` feature 配下で、Structured↔Normalized↔Structured の roundtrip と VM 実行結果比較を常に追加していく運用にする。
全体として、短期的には「新しいレイヤと比較テストを抱えるコスト」が増えるけれど、中長期では Pattern2〜4 や JsonParser 系の「評価順PHIDigitPos/num_str」が絡むループで、デバッグと設計の負荷をかなり下げることを狙った設計だよ。
### 3.4 Phase 26-H のフェーズ分割(案)
この正規化レイヤを一度に入れるのは重いので、Phase 26-H ラインとして小さく段階分けして進める方針だよ。
(番号は仮で、実際の Phase 番号は current の進行に合わせて調整する想定)
@ -1136,6 +1194,41 @@ Normalized JoinIR 側では「Env フィールドの更新順」と「どの継
各サブフェーズでは「既存意味論を変えない」「Fail-Fast 原則を維持する」「新旧経路の比較テストを先に用意する」をガードとして運用するよ。
詳細な API/型設計は、Phase 26-H.A/B の中で `EnvLayoutBox` / `LoopStepSynthesizer` / `JpVerifier` 等の箱として段階的に固めていく想定。
### 3.4 Phase 27-CLEAN Pattern24 の軽量整理
### 3.5 Phase 27-CLEAN Pattern24 の軽量整理
- Pattern2〜4/loop_with_break_minimal まわりで可視性・ログ・補助関数を整理し、joinir_dev フラグ配下のデバッグログに寄せる。意味論は変えずに「読みやすさ」「追いやすさ」を優先するクリーンアップフェーズだよ。
### 3.6 Phase 28-NORM-P2 Normalized JoinIR プロトタイプ拡張dev-only
- Phase 26-H で用意した Normalized JoinIR の極小サブセットを、Pattern1 に続いて Pattern2最小 break ループ)まで拡張。
- Structured → Normalized → Structured の往復と VM 実行比較を dev フィーチャ (`normalized_dev` + debug) でテスト済み。
- 対象は joinir_min_loop 相当の「ループ変数1つbreakのみ」のミニケースに限定し、本番経路Structured→MIRは不変。
- normalize_pattern2_minimal は対応外の Structured JoinModule では Fail-Fast するようにガードを追加し、対応範囲をテストで固定。
### 3.7 Phase 29-NORM-P2-APPLY Pattern2 実ループへの dev 適用
- Phase 34 の fixture `loop_frontend_break.program.json``i/acc/n` のシンプル break ループ)を Structured→Normalized→Structured の往復経路に載せ、VM 実行結果が Structured 直経路と一致することを dev テストで確認。
- `normalize_pattern2_minimal` のガードを 3 パラメータloop var + acc + hostまで許容する形に緩めつつ、DigitPos/Trim などの heavy carrier は依然として非対応に固定。
- すべて `normalized_dev` feature + debug_assertions 配下の実験経路に閉じ、本番 Structured→MIR パスの挙動は不変。
### 3.8 Phase 30-NORM-P2-DEV-RUN runner で Normalized を試走dev
- JoinIR runner に dev 専用の `NYASH_JOINIR_NORMALIZED_DEV_RUN=1` スイッチを追加し、Pattern1/2 のミニケースだけ Structured→Normalized→Structured を噛ませてから実行できるようにした(`normalized_dev` feature + debug ビルド限定)。
- runner 経路でも Structured 直実行との stdout/結果が一致することをテストloop_min_while と Phase 34 break fixtureで確認。フラグ OFF 時の挙動は従来と同じ。
### 3.9 Phase 31-NORM-JP-MINI JsonParser ミニ P2 を Normalized dev 経由で試走
- JsonParser 系のシンプルな P2 ループskip_whitespace 簡易版)を Structured→Normalized→Structured の dev ランナー経路に載せ、通常経路との実行結果一致を比較。
- `jsonparser_skip_ws_mini.program.json`docs/private/roadmap2/phases/normalized_dev/fixtures 配下)由来の JoinModule を使い、`NYASH_JOINIR_NORMALIZED_DEV_RUN=1` + `normalized_dev` + debug 限定で切替可能にした。
- 本番経路Structured→MIRは引き続き不変で、Normalized は dev 比較専用のまま。
### 3.10 Phase 32-NORM-CANON-PREP Normalized 本番導入の下地づくり
- JoinIR→MIR ブリッジに Structured/Normalized の入口を分けた `bridge_joinir_to_mir_*` を用意し、conversion pipeline / VM ランナーがこの 1 箇所で dev roundtrip を切替できるように整理。
- Normalized dev スイッチを `normalized_dev_enabled()` に集約feature `normalized_dev` + `NYASH_JOINIR_NORMALIZED_DEV_RUN=1`。P1/P2 ミニ + JsonParser mini で env ON/OFF の比較テストを追加し、いつでも canonical route に昇格できる状態を固めた。
### 3.11 Phase 33-NORM-CANON-TEST P1/P2/JP mini をテスト必須ラインへ
- `bridge_joinir_to_mir` / JoinIR runner は `shape_guard` で P1/P2 ミニ + JsonParser skip_ws mini を検知した場合、`normalized_dev_enabled()` が ON なら必ず Structured→Normalized→Structured の dev roundtrip を経由(正規化失敗は dev panic。未対応形状は静かに Structured 直通。
- tests/normalized_joinir_min.rs を Phase 33 前提に拡張し、P1/P2/JP mini の runner/VM 比較テストを env ON で実行。Normalized が壊れればこのスイートが必ず赤になる構造にしたfeature OFF の CI は従来どおり無関係)。
- 本番 CLI 挙動は Structured→MIR のまま維持しつつ、Normalized を canonical に昇格させる前段階として dev テストで SSOT 相当の役割を担わせている。

View File

@ -0,0 +1,65 @@
Status: Active
Scope: Phase 26H.C ― Normalized JoinIR (Pattern1) を MIR に落とす最小ブリッジの設計と比較テスト計画dev専用
# Phase 26H.C 指示書 — Normalized→MIR ブリッジPattern1 最小)+比較テスト
## ゴール
- Normalized JoinIRNormalizedModuleから MIR への「本物のブリッジ」を、Pattern1 最小ケースだけで実装する。
- Structured→MIR と Structured→Normalized→MIR の結果が一致することをテストで確認するdev 専用、CLI はまだ触らない)。
## A. Normalized→Structured or Normalized→MIR の経路方針
- Option 1: Normalized→Structured→既存 MIR ブリッジ
- 長所: 既存の JoinIR→MIR パイプラインを再利用できる。
- 短所: フェーズが一段増えるNormalized→Structured が joinir→joinir の一歩になる)。
- Option 2: Normalized→MIR を直接作るミニブリッジPattern1 限定) **←今回これを優先**
- 長所: TailCall/Env をそのまま MIR に落とす感触を掴める。
- 短所: 一部ロジックが JoinIR→MIR と重複する。
- どちらを選んでも「Pattern1 最小ケース限定」「テスト専用 helper」とする本番パスへは配線しない。今回は Option 2 で進め、必要があれば Option 1 のスケルトンも残す。
## B. Normalized→Structured ミニ変換Option 1 採用時のメモ)
- 追加場所: `src/mir/join_ir/normalized.rs`
- API 例: `pub fn normalize_pattern1_to_structured(norm: &NormalizedModule) -> JoinModule`
- 制約: `norm.phase == JoinIrPhase::Normalized`、関数は 1 つloop_step + 仮の k_exit 程度)を想定。
- 変換の要点:
- EnvLayout の fields から JoinFunction の params を再構成最小なら1引数でも可
- `JpInst::Let``JoinInst::Compute(MirLikeInst::Const/BinOp/UnaryOp/Compare)` に戻す。
- TailCallFn/TailCallKont/If を Pattern1 が生成していた `JoinInst::Call/Jump/Ret` 相当に戻す。
- `NormalizedModule.entry``JoinModule.entry` に写す。
- `NormalizedModule.structured_backup` は比較用に残すが、ブリッジでは本体から再構成する。
## C. Normalized→MIR ミニブリッジ(今回の主経路: Option 2
- 追加ファイル案: `src/mir/join_ir/lowering/normalized_pattern1_to_mir.rs`
- API 例:
```rust
pub fn lower_normalized_pattern1_to_mir(norm: &NormalizedModule) -> crate::mir::Module { ... }
```
- 最低限やること:
- EnvLayout の 1 フィールドを MIR のループ変数に対応させる。
- `JpFunction` 本体を 1 ブロック or 小ブロック列に変換Let→MIR Assign/Compute、TailCallFn→ループ末尾 Jump 等)。
- Pattern1 の `loop_min_while` 相当が生成していた MIR と構造的に一致するかをテストで確認。
- ガード: dev/テスト専用 helper から明示的に呼ぶ。ランナー/CLI には配線しない。
## D. 比較用テストの追加
- テストファイル案: `tests/normalized_pattern1_bridge.rs`
- シナリオ:
1. Structured JoinIR (Pattern1: loop_min_while 相当) を既存 lowerer で生成。
2. そのコピーを `normalize_pattern1_minimal` に通し NormalizedModule を得る。
3. 既存経路: Structured → 既存 JoinIR→MIR ブリッジ → 実行 or MIR dump。
4. 新経路: Structured → Normalized → C のブリッジ)→ MIR → 実行 or MIR dump。
5. 比較:
- 実行結果一致RC + stdout
- 余裕があれば MIR の基本ブロック数や命令種も比較し、構造乖離がないことを確認。
- テスト名例:
- `test_normalized_pattern1_minimal_roundtrip`
- `test_normalized_pattern1_exec_result_matches_structured`
## E. ガードと完了条件
- ガード:
- 新ブリッジはテスト専用(ランナー/CLI からは呼ばない)。
- JoinModule.phase が Structured のままでも既存経路は従来どおり動作。
- Normalized 経路は dev/テスト専用 helper からのみ呼ぶ。
- 完了条件:
- NormalizedModule から MIR を生成する経路Option 2が 1 本通る。
- Pattern1 最小ケースで Structured→MIR と Structured→Normalized→MIR が同じ結果になるテストが緑。
- 既存 `cargo test --release` が 0 FAIL のまま。