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

@ -31,6 +31,20 @@
- DigitPos promotion を二重値化し、`digit_pos` から boolean carrier `is_digit_pos`ConditionOnlyと integer carrier `digit_value`LoopStateを生成。 - DigitPos promotion を二重値化し、`digit_pos` から boolean carrier `is_digit_pos`ConditionOnlyと integer carrier `digit_value`LoopStateを生成。
- UpdateEnv で `digit_pos` 解決時に `digit_value` を優先し、NumberAccumulation`result = result * 10 + digit_pos`)と break 条件の両方で DigitPos パターンが安全に利用可能に。 - UpdateEnv で `digit_pos` 解決時に `digit_value` を優先し、NumberAccumulation`result = result * 10 + digit_pos`)と break 条件の両方で DigitPos パターンが安全に利用可能に。
- 現在: `cargo test --release --lib` で 931/931 テスト PASS既知 FAIL なし)。 - 現在: `cargo test --release --lib` で 931/931 テスト PASS既知 FAIL なし)。
- Phase 28-NORM-P2dev-only:
- Normalized JoinIR のミニ実装を Pattern1 に続き Pattern2 最小ケースまで拡張Structured→Normalized→Structured を比較)。
- 対応外の Structured JoinModule では normalize_pattern2_minimal が Fail-Fast するようガードを追加し、normalized_dev テストで固定。
- Phase 29-NORM-P2-APPLYdev-only:
- Phase 34 の break fixturei/acc/n の単純 break ループ)を Structured→Normalized→Structured の往復に通し、VM 実行結果が Structured 直経路と一致することを dev テストで固定。
- ガードは 3 パラメータまで緩和しつつ、DigitPos/Trim などの重いキャリアはまだ非対応のまま。
- Phase 30-NORM-P2-DEV-RUNdev-only:
- JoinIR runner に `NYASH_JOINIR_NORMALIZED_DEV_RUN=1` を追加し、Pattern1/2 ミニケースだけ Structured→Normalized→Structured を挟んで dev 実行できるようにした(`normalized_dev` + debug 限定。通常経路Structured→MIRは不変。
- Phase 31-NORM-JP-MINIdev-only:
- JsonParser 系のシンプルな P2 ループskip_whitespace ミニ fixtureを Structured→Normalized→Structured 経由でも実行し、runner dev スイッチの比較テストで Structured 直経路と一致することを固定。
- Phase 32-NORM-CANON-PREPdev-only:
- JoinIR→MIR ブリッジの入口を `bridge_joinir_to_mir` に一本化し、normalized_dev スイッチfeature + envで Structured→Normalized→Structured の dev roundtrip を切り替える準備を整えた。P1/P2/JP mini の比較テストも VM ブリッジ経路で追加。
- Phase 33-NORM-CANON-TESTdev-only:
- P1/P2Phase 34 break fixture/JsonParser skip_ws mini について、normalized_dev ON 時は shape_guard 経由で必ず Normalized roundtrip を通すようブリッジと runner を固めた。normalized_joinir_min.rs の runner/VM 比較テストを拡張し、Normalized が壊れたら dev スイートが必ず赤になるようにした(本番 CLI は従来どおり Structured→MIR
### 1. いまコード側で意識しておきたいフォーカス ### 1. いまコード側で意識しておきたいフォーカス

View File

@ -30,6 +30,8 @@ gui-examples = ["gui"]
all-examples = ["gui-examples"] all-examples = ["gui-examples"]
dynamic-file = [] dynamic-file = []
wasm-backend = ["dep:wasmtime", "dep:wabt"] wasm-backend = ["dep:wasmtime", "dep:wabt"]
# Dev-only normalized JoinIR experiment (Pattern1)
normalized_dev = []
# TLV C shim wiring (default OFF): enables optional dependency `nyash-tlv` # TLV C shim wiring (default OFF): enables optional dependency `nyash-tlv`
tlv-shim = ["dep:nyash-tlv"] tlv-shim = ["dep:nyash-tlv"]
# Core C shims (design-stage; default OFF) # Core C shims (design-stage; default OFF)

View File

@ -1106,7 +1106,65 @@ Normalized JoinIR では、制御構造を次の 3 要素だけで表現する
これにより、現在 JoinIR 層で苦労している「PHI 配線」「exit_bindings/jump_args 整合性」「評価順のねじれ」は、 これにより、現在 JoinIR 層で苦労している「PHI 配線」「exit_bindings/jump_args 整合性」「評価順のねじれ」は、
Normalized JoinIR 側では「Env フィールドの更新順」と「どの継続を呼ぶか」に還元される想定だよ。 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 26-H ラインとして小さく段階分けして進める方針だよ。
(番号は仮で、実際の Phase 番号は current の進行に合わせて調整する想定) (番号は仮で、実際の Phase 番号は current の進行に合わせて調整する想定)
@ -1136,6 +1194,41 @@ Normalized JoinIR 側では「Env フィールドの更新順」と「どの継
各サブフェーズでは「既存意味論を変えない」「Fail-Fast 原則を維持する」「新旧経路の比較テストを先に用意する」をガードとして運用するよ。 各サブフェーズでは「既存意味論を変えない」「Fail-Fast 原則を維持する」「新旧経路の比較テストを先に用意する」をガードとして運用するよ。
詳細な API/型設計は、Phase 26-H.A/B の中で `EnvLayoutBox` / `LoopStepSynthesizer` / `JpVerifier` 等の箱として段階的に固めていく想定。 詳細な 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 フラグ配下のデバッグログに寄せる。意味論は変えずに「読みやすさ」「追いやすさ」を優先するクリーンアップフェーズだよ。 - 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 のまま。

View File

@ -527,8 +527,8 @@ pub enum ASTNode {
/// 値・型は右辺と同じ、副作用として左辺に代入 /// 値・型は右辺と同じ、副作用として左辺に代入
/// 使用例: local y = (x = x + 1), if (x = next()) != null { } /// 使用例: local y = (x = x + 1), if (x = next()) != null { }
GroupedAssignmentExpr { GroupedAssignmentExpr {
lhs: String, // 変数名 lhs: String, // 変数名
rhs: Box<ASTNode>, // 右辺式 rhs: Box<ASTNode>, // 右辺式
span: Span, span: Span,
}, },

View File

@ -68,16 +68,14 @@ impl MirInterpreter {
}) })
.unwrap_or(1_000_000); .unwrap_or(1_000_000);
let mut steps: u64 = 0; let mut steps: u64 = 0;
let trace_log_path: Option<String> = std::env::var("NYASH_VM_TRACE_LOG") let trace_log_path: Option<String> = std::env::var("NYASH_VM_TRACE_LOG").ok().map(|v| {
.ok() let trimmed = v.trim();
.map(|v| { if trimmed.is_empty() || trimmed == "1" {
let trimmed = v.trim(); "__mir__.log".to_string()
if trimmed.is_empty() || trimmed == "1" { } else {
"__mir__.log".to_string() v
} else { }
v });
}
});
loop { loop {
steps += 1; steps += 1;

View File

@ -313,7 +313,10 @@ impl MirInterpreter {
("ConsoleBox", 400) => { ("ConsoleBox", 400) => {
// log/println // log/println
if let VMValue::BoxRef(bx) = receiver { if let VMValue::BoxRef(bx) = receiver {
if let Some(console) = bx.as_any().downcast_ref::<crate::boxes::console_box::ConsoleBox>() { if let Some(console) = bx
.as_any()
.downcast_ref::<crate::boxes::console_box::ConsoleBox>()
{
let message = if args.len() > 1 { let message = if args.len() > 1 {
self.reg_load(args[1])?.to_string() self.reg_load(args[1])?.to_string()
} else if args.len() > 0 { } else if args.len() > 0 {
@ -330,7 +333,10 @@ impl MirInterpreter {
("ConsoleBox", 401) => { ("ConsoleBox", 401) => {
// warn // warn
if let VMValue::BoxRef(bx) = receiver { if let VMValue::BoxRef(bx) = receiver {
if let Some(console) = bx.as_any().downcast_ref::<crate::boxes::console_box::ConsoleBox>() { if let Some(console) = bx
.as_any()
.downcast_ref::<crate::boxes::console_box::ConsoleBox>()
{
let message = if args.len() > 1 { let message = if args.len() > 1 {
self.reg_load(args[1])?.to_string() self.reg_load(args[1])?.to_string()
} else if args.len() > 0 { } else if args.len() > 0 {
@ -347,7 +353,10 @@ impl MirInterpreter {
("ConsoleBox", 402) => { ("ConsoleBox", 402) => {
// error // error
if let VMValue::BoxRef(bx) = receiver { if let VMValue::BoxRef(bx) = receiver {
if let Some(console) = bx.as_any().downcast_ref::<crate::boxes::console_box::ConsoleBox>() { if let Some(console) = bx
.as_any()
.downcast_ref::<crate::boxes::console_box::ConsoleBox>()
{
let message = if args.len() > 1 { let message = if args.len() > 1 {
self.reg_load(args[1])?.to_string() self.reg_load(args[1])?.to_string()
} else if args.len() > 0 { } else if args.len() > 0 {
@ -364,7 +373,10 @@ impl MirInterpreter {
("ConsoleBox", 403) => { ("ConsoleBox", 403) => {
// clear // clear
if let VMValue::BoxRef(bx) = receiver { if let VMValue::BoxRef(bx) = receiver {
if let Some(console) = bx.as_any().downcast_ref::<crate::boxes::console_box::ConsoleBox>() { if let Some(console) = bx
.as_any()
.downcast_ref::<crate::boxes::console_box::ConsoleBox>()
{
console.clear(); console.clear();
return Ok(VMValue::Void); return Ok(VMValue::Void);
} }
@ -405,7 +417,10 @@ impl MirInterpreter {
// Plugin Box methods (slot >= 1000) // Plugin Box methods (slot >= 1000)
(_, slot) if slot >= 1000 => { (_, slot) if slot >= 1000 => {
if let VMValue::BoxRef(bx) = receiver { if let VMValue::BoxRef(bx) = receiver {
if let Some(_p) = bx.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() { if let Some(_p) = bx
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host(); let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
let _host = host.read().unwrap(); let _host = host.read().unwrap();
let _argv = self.load_args_as_boxes(args)?; let _argv = self.load_args_as_boxes(args)?;
@ -447,22 +462,20 @@ impl MirInterpreter {
// 2. Lookup type in TypeRegistry and get slot // 2. Lookup type in TypeRegistry and get slot
// Note: Try exact arity first, then try with args.len()-1 (in case receiver is duplicated in args) // Note: Try exact arity first, then try with args.len()-1 (in case receiver is duplicated in args)
let slot = crate::runtime::type_registry::resolve_slot_by_name( let slot =
type_name, crate::runtime::type_registry::resolve_slot_by_name(type_name, method, args.len())
method, .or_else(|| {
args.len(), // Fallback: try with one less argument (receiver might be in args)
).or_else(|| { if args.len() > 0 {
// Fallback: try with one less argument (receiver might be in args) crate::runtime::type_registry::resolve_slot_by_name(
if args.len() > 0 { type_name,
crate::runtime::type_registry::resolve_slot_by_name( method,
type_name, args.len() - 1,
method, )
args.len() - 1, } else {
) None
} else { }
None });
}
});
if let Some(slot) = slot { if let Some(slot) = slot {
// 3. Use unified dispatch // 3. Use unified dispatch
@ -489,9 +502,8 @@ impl MirInterpreter {
if let Some(arg_id) = args.get(0) { if let Some(arg_id) = args.get(0) {
let ch = self.reg_load(*arg_id)?.to_string(); let ch = self.reg_load(*arg_id)?.to_string();
let c = ch.chars().next().unwrap_or('\0'); let c = ch.chars().next().unwrap_or('\0');
let is_alpha = ('A'..='Z').contains(&c) let is_alpha =
|| ('a'..='z').contains(&c) ('A'..='Z').contains(&c) || ('a'..='z').contains(&c) || c == '_';
|| c == '_';
return Ok(VMValue::Bool(is_alpha)); return Ok(VMValue::Bool(is_alpha));
} else { } else {
return Err(self.err_invalid("is_alpha requires 1 argument")); return Err(self.err_invalid("is_alpha requires 1 argument"));
@ -520,12 +532,7 @@ impl MirInterpreter {
let host = crate::runtime::plugin_loader_unified::get_global_plugin_host(); let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
let host = host.read().unwrap(); let host = host.read().unwrap();
let argv = self.load_args_as_boxes(args)?; let argv = self.load_args_as_boxes(args)?;
match host.invoke_instance_method( match host.invoke_instance_method(&p.box_type, method, p.inner.instance_id, &argv) {
&p.box_type,
method,
p.inner.instance_id,
&argv,
) {
Ok(Some(ret)) => return Ok(VMValue::from_nyash_box(ret)), Ok(Some(ret)) => return Ok(VMValue::from_nyash_box(ret)),
Ok(None) => return Ok(VMValue::Void), Ok(None) => return Ok(VMValue::Void),
Err(e) => { Err(e) => {

View File

@ -253,11 +253,7 @@ impl MirInterpreter {
} }
}; };
if std::env::var("NYASH_EMIT_MIR_TRACE") if std::env::var("NYASH_EMIT_MIR_TRACE").ok().as_deref() == Some("1") {
.ok()
.as_deref()
== Some("1")
{
if let Some(name) = chosen_name { if let Some(name) = chosen_name {
eprintln!("[vm/entry] main={}", name); eprintln!("[vm/entry] main={}", name);
} }

View File

@ -194,9 +194,7 @@ impl StaticBoxRegistry {
/// 登録済み/検出済みの静的Box名一覧 /// 登録済み/検出済みの静的Box名一覧
pub fn all_box_names(&self) -> impl Iterator<Item = &String> { pub fn all_box_names(&self) -> impl Iterator<Item = &String> {
self.declarations self.declarations.keys().chain(self.detected_boxes.iter())
.keys()
.chain(self.detected_boxes.iter())
} }
/// declarations への直接アクセス (既存コードとの互換性) /// declarations への直接アクセス (既存コードとの互換性)
@ -250,7 +248,7 @@ mod tests {
"JsonParserBox.parse/1".to_string(), "JsonParserBox.parse/1".to_string(),
"JsonParserBox.toString/0".to_string(), "JsonParserBox.toString/0".to_string(),
"ProgramJSONBox.get/1".to_string(), "ProgramJSONBox.get/1".to_string(),
"Main.main/0".to_string(), // 除外される "Main.main/0".to_string(), // 除外される
"StringBox.length/0".to_string(), // 除外される "StringBox.length/0".to_string(), // 除外される
]; ];

View File

@ -190,7 +190,8 @@ impl UnifiedBoxRegistry {
// core_required (6個) + 特殊型の一部を予約型として保護 // core_required (6個) + 特殊型の一部を予約型として保護
CoreBoxId::from_name(name) CoreBoxId::from_name(name)
.map(|id| { .map(|id| {
id.is_core_required() || matches!(id, CoreBoxId::Result | CoreBoxId::Method) id.is_core_required()
|| matches!(id, CoreBoxId::Result | CoreBoxId::Method)
}) })
.unwrap_or(false) .unwrap_or(false)
} }

View File

@ -29,7 +29,11 @@ macro_rules! ny_wrap_string {
pub fn $name(&self) -> StringBox { pub fn $name(&self) -> StringBox {
match self.$inner() { match self.$inner() {
Ok(result) => StringBox::new(result), Ok(result) => StringBox::new(result),
Err(e) => panic!("FileHandleBox.{}() failed: {}", stringify!($name).trim_start_matches("ny_"), e), Err(e) => panic!(
"FileHandleBox.{}() failed: {}",
stringify!($name).trim_start_matches("ny_"),
e
),
} }
} }
}; };
@ -40,7 +44,11 @@ macro_rules! ny_wrap_bool {
pub fn $name(&self) -> BoolBox { pub fn $name(&self) -> BoolBox {
match self.$inner() { match self.$inner() {
Ok(result) => BoolBox::new(result), Ok(result) => BoolBox::new(result),
Err(e) => panic!("FileHandleBox.{}() failed: {}", stringify!($name).trim_start_matches("ny_"), e), Err(e) => panic!(
"FileHandleBox.{}() failed: {}",
stringify!($name).trim_start_matches("ny_"),
e
),
} }
} }
}; };
@ -51,7 +59,11 @@ macro_rules! ny_wrap_integer {
pub fn $name(&self) -> crate::box_trait::IntegerBox { pub fn $name(&self) -> crate::box_trait::IntegerBox {
match self.$inner() { match self.$inner() {
Ok(result) => crate::box_trait::IntegerBox::new(result as i64), Ok(result) => crate::box_trait::IntegerBox::new(result as i64),
Err(e) => panic!("FileHandleBox.{}() failed: {}", stringify!($name).trim_start_matches("ny_"), e), Err(e) => panic!(
"FileHandleBox.{}() failed: {}",
stringify!($name).trim_start_matches("ny_"),
e
),
} }
} }
}; };
@ -156,8 +168,8 @@ impl FileHandleBox {
} }
// Get FileIo provider to check capabilities // Get FileIo provider to check capabilities
let provider = provider_lock::get_filebox_provider() let provider =
.ok_or_else(provider_not_initialized)?; provider_lock::get_filebox_provider().ok_or_else(provider_not_initialized)?;
// NoFs profile check (Fail-Fast) // NoFs profile check (Fail-Fast)
let caps = provider.caps(); let caps = provider.caps();
@ -171,8 +183,8 @@ impl FileHandleBox {
// Create NEW independent Ring0FsFileIo instance for this handle // Create NEW independent Ring0FsFileIo instance for this handle
// IMPORTANT: We must create a new instance, not clone the Arc // IMPORTANT: We must create a new instance, not clone the Arc
// because Ring0FsFileIo has internal state (path field) // because Ring0FsFileIo has internal state (path field)
use crate::runtime::get_global_ring0;
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo; use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
use crate::runtime::get_global_ring0;
let ring0 = get_global_ring0(); let ring0 = get_global_ring0();
@ -281,11 +293,12 @@ impl FileHandleBox {
/// ///
/// Unified metadata access through FileIo trait. /// Unified metadata access through FileIo trait.
fn metadata_internal(&self) -> Result<crate::boxes::file::provider::FileStat, String> { fn metadata_internal(&self) -> Result<crate::boxes::file::provider::FileStat, String> {
let io = self.io.as_ref() let io = self
.io
.as_ref()
.ok_or_else(|| "FileHandleBox is not open".to_string())?; .ok_or_else(|| "FileHandleBox is not open".to_string())?;
io.stat() io.stat().map_err(|e| format!("Metadata failed: {}", e))
.map_err(|e| format!("Metadata failed: {}", e))
} }
/// Get file size in bytes /// Get file size in bytes
@ -308,7 +321,9 @@ impl FileHandleBox {
/// ///
/// Uses FileIo::exists() for direct check. /// Uses FileIo::exists() for direct check.
pub fn exists(&self) -> Result<bool, String> { pub fn exists(&self) -> Result<bool, String> {
let io = self.io.as_ref() let io = self
.io
.as_ref()
.ok_or_else(|| "FileHandleBox is not open".to_string())?; .ok_or_else(|| "FileHandleBox is not open".to_string())?;
Ok(io.exists()) Ok(io.exists())
@ -395,7 +410,9 @@ impl NyashBox for FileHandleBox {
fn to_string_box(&self) -> StringBox { fn to_string_box(&self) -> StringBox {
StringBox::new(format!( StringBox::new(format!(
"FileHandleBox(path={}, mode={}, open={})", "FileHandleBox(path={}, mode={}, open={})",
self.path, self.mode, self.is_open() self.path,
self.mode,
self.is_open()
)) ))
} }
@ -442,8 +459,8 @@ mod tests {
/// Helper: Initialize FileBox provider for tests /// Helper: Initialize FileBox provider for tests
fn init_test_provider() { fn init_test_provider() {
use crate::runtime::ring0::{default_ring0, init_global_ring0};
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo; use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
use crate::runtime::ring0::{default_ring0, init_global_ring0};
use std::panic; use std::panic;
use std::sync::Arc; use std::sync::Arc;
@ -720,7 +737,7 @@ mod tests {
init_test_provider(); init_test_provider();
let path = "/tmp/phase111_append_test.txt"; let path = "/tmp/phase111_append_test.txt";
let _ = fs::remove_file(path); // cleanup let _ = fs::remove_file(path); // cleanup
// First write (truncate) // First write (truncate)
let mut handle = FileHandleBox::new(); let mut handle = FileHandleBox::new();
@ -751,7 +768,7 @@ mod tests {
// Write test file // Write test file
let mut handle = FileHandleBox::new(); let mut handle = FileHandleBox::new();
handle.open(path, "w").unwrap(); handle.open(path, "w").unwrap();
handle.write_all("hello").unwrap(); // 5 bytes handle.write_all("hello").unwrap(); // 5 bytes
handle.close().unwrap(); handle.close().unwrap();
// Check size // Check size
@ -967,7 +984,9 @@ mod tests {
// Test metadata_internal() via stat() // Test metadata_internal() via stat()
handle.open(path, "r").unwrap(); handle.open(path, "r").unwrap();
let stat = handle.metadata_internal().expect("metadata_internal should succeed"); let stat = handle
.metadata_internal()
.expect("metadata_internal should succeed");
assert!(stat.is_file); assert!(stat.is_file);
assert!(!stat.is_dir); assert!(!stat.is_dir);
assert_eq!(stat.size, 5); assert_eq!(stat.size, 5);

View File

@ -89,8 +89,8 @@ impl FileBox {
// Create NEW independent provider instance for this FileBox // Create NEW independent provider instance for this FileBox
// IMPORTANT: We must create a new instance, not clone the Arc, // IMPORTANT: We must create a new instance, not clone the Arc,
// because Ring0FsFileIo has internal state (path field) // because Ring0FsFileIo has internal state (path field)
use crate::runtime::get_global_ring0;
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo; use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
use crate::runtime::get_global_ring0;
let ring0 = get_global_ring0(); let ring0 = get_global_ring0();
@ -126,7 +126,8 @@ impl FileBox {
} }
// Phase 108: UTF-8 conversion (text-oriented design) // Phase 108: UTF-8 conversion (text-oriented design)
let text = String::from_utf8_lossy(buf).to_string(); let text = String::from_utf8_lossy(buf).to_string();
provider.write(&text) provider
.write(&text)
.map_err(|e| format!("Write failed: {:?}", e)) .map_err(|e| format!("Write failed: {:?}", e))
} else { } else {
Err(no_provider_available()) Err(no_provider_available())
@ -160,7 +161,10 @@ impl FileBox {
Err(e) => Box::new(StringBox::new(format!("Error: {:?}", e))), Err(e) => Box::new(StringBox::new(format!("Error: {:?}", e))),
} }
} else { } else {
Box::new(StringBox::new(format!("Error: {}", no_provider_available()))) Box::new(StringBox::new(format!(
"Error: {}",
no_provider_available()
)))
} }
} }
@ -230,8 +234,8 @@ impl std::fmt::Display for FileBox {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::runtime::ring0::{default_ring0, GLOBAL_RING0};
use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo; use crate::providers::ring1::file::ring0_fs_fileio::Ring0FsFileIo;
use crate::runtime::ring0::{default_ring0, GLOBAL_RING0};
use std::fs; use std::fs;
use std::io::Write; use std::io::Write;
@ -345,11 +349,7 @@ mod tests {
let fb = FileBox::open(tmp_path).expect("open failed"); let fb = FileBox::open(tmp_path).expect("open failed");
let exists_box = fb.exists(); let exists_box = fb.exists();
let exists = exists_box let exists = exists_box.as_any().downcast_ref::<BoolBox>().unwrap().value;
.as_any()
.downcast_ref::<BoolBox>()
.unwrap()
.value;
assert!(exists); assert!(exists);

View File

@ -60,7 +60,8 @@ impl FileCaps {
return Err("Read not supported by FileBox provider".to_string()); return Err("Read not supported by FileBox provider".to_string());
} }
} }
"w" | "a" => { // Phase 111: "a" added "w" | "a" => {
// Phase 111: "a" added
if !self.write { if !self.write {
return Err("Write not supported by FileBox provider".to_string()); return Err("Write not supported by FileBox provider".to_string());
} }

View File

@ -99,6 +99,24 @@ pub fn read_quoted_ifmerge_enabled() -> bool {
env_bool("HAKO_JOINIR_READ_QUOTED_IFMERGE") env_bool("HAKO_JOINIR_READ_QUOTED_IFMERGE")
} }
/// NYASH_JOINIR_NORMALIZED_DEV_RUN=1 - Run JoinIR runner through Normalized roundtrip (dev only)
pub fn joinir_normalized_dev_run_enabled() -> bool {
// Feature gate at call sites still applies; this helper just centralizes the env read.
env_bool("NYASH_JOINIR_NORMALIZED_DEV_RUN")
}
/// Unified switch for Normalized dev experiments (feature + env).
///
/// - Requires `--features normalized_dev`
/// - Requires `NYASH_JOINIR_NORMALIZED_DEV_RUN=1`
pub fn normalized_dev_enabled() -> bool {
if cfg!(feature = "normalized_dev") {
joinir_normalized_dev_run_enabled()
} else {
false
}
}
/// Phase 82: NYASH_PHI_FALLBACK_DISABLED=1 - Disable if_phi fallback (dev mode) /// Phase 82: NYASH_PHI_FALLBACK_DISABLED=1 - Disable if_phi fallback (dev mode)
/// ///
/// lifecycle.rs の infer_type_from_phi* callsite を封じて、 /// lifecycle.rs の infer_type_from_phi* callsite を封じて、
@ -124,7 +142,7 @@ pub fn read_quoted_ifmerge_enabled() -> bool {
/// Phase 84-5: if_phi.rs 削除後は常に true /// Phase 84-5: if_phi.rs 削除後は常に true
/// 環境変数は統計目的でのみ残す /// 環境変数は統計目的でのみ残す
pub fn phi_fallback_disabled() -> bool { pub fn phi_fallback_disabled() -> bool {
true // Phase 84-5: Always disabled after if_phi.rs deletion true // Phase 84-5: Always disabled after if_phi.rs deletion
} }
/// Phase 84-5: 統計・メトリクス用(オプション) /// Phase 84-5: 統計・メトリクス用(オプション)

View File

@ -71,7 +71,7 @@ fn main() {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use nyash_rust::box_trait::{NyashBox, StringBox}; use nyash_rust::box_trait::{NyashBox, StringBox};
#[test] #[test]

View File

@ -30,10 +30,10 @@ mod exprs_peek; // peek expression
mod exprs_qmark; // ?-propagate mod exprs_qmark; // ?-propagate
mod fields; // field access/assignment lowering split mod fields; // field access/assignment lowering split
mod if_form; mod if_form;
mod lifecycle;
mod loop_frontend_binding; // Phase 50: Loop Frontend Binding (JoinIR variable mapping)
mod joinir_id_remapper; // Phase 189: JoinIR ID remapping (ValueId/BlockId translation) mod joinir_id_remapper; // Phase 189: JoinIR ID remapping (ValueId/BlockId translation)
mod joinir_inline_boundary_injector; // Phase 189: JoinInlineBoundary Copy instruction injector mod joinir_inline_boundary_injector; // Phase 189: JoinInlineBoundary Copy instruction injector
mod lifecycle;
mod loop_frontend_binding; // Phase 50: Loop Frontend Binding (JoinIR variable mapping)
pub(crate) mod loops; pub(crate) mod loops;
mod ops; mod ops;
mod phi; mod phi;
@ -285,7 +285,7 @@ impl MirBuilder {
method_tail_index_source_len: 0, method_tail_index_source_len: 0,
current_region_stack: Vec::new(), current_region_stack: Vec::new(),
fn_body_ast: None, // Phase 200-C: Initialize to None fn_body_ast: None, // Phase 200-C: Initialize to None
reserved_value_ids: HashSet::new(), // Phase 201-A: Initialize to empty reserved_value_ids: HashSet::new(), // Phase 201-A: Initialize to empty
loop_header_stack: Vec::new(), loop_header_stack: Vec::new(),

View File

@ -144,9 +144,7 @@ impl<'a> CalleeGuardBox<'a> {
// Previously this converted to Global("JsonParserBox.parse") which failed // Previously this converted to Global("JsonParserBox.parse") which failed
// because the function table expected "JsonParserBox.parse/1" with arity. // because the function table expected "JsonParserBox.parse/1" with arity.
if trace_enabled { if trace_enabled {
eprintln!( eprintln!("[static-runtime-guard] StaticCompiler trusting methodize:");
"[static-runtime-guard] StaticCompiler trusting methodize:"
);
eprintln!( eprintln!(
" {}.{} receiver %{} - passing through as static box call", " {}.{} receiver %{} - passing through as static box call",
box_name, method, recv.0 box_name, method, recv.0

View File

@ -344,7 +344,11 @@ impl MirBuilder {
body: Vec<ASTNode>, body: Vec<ASTNode>,
) -> Result<(), String> { ) -> Result<(), String> {
// Phase 200-C: Store fn_body for capture analysis // Phase 200-C: Store fn_body for capture analysis
eprintln!("[lower_static_method_as_function] Storing fn_body with {} nodes for '{}'", body.len(), func_name); eprintln!(
"[lower_static_method_as_function] Storing fn_body with {} nodes for '{}'",
body.len(),
func_name
);
self.fn_body_ast = Some(body.clone()); self.fn_body_ast = Some(body.clone());
// Step 1: Context準備 // Step 1: Context準備

View File

@ -5,9 +5,9 @@
//! //!
//! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 159-194) //! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 159-194)
use crate::mir::MirModule;
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
use super::super::trace; use super::super::trace;
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
use crate::mir::MirModule;
/// Phase 1: Allocate new block IDs for ALL functions (Phase 189) /// Phase 1: Allocate new block IDs for ALL functions (Phase 189)
/// ///
@ -25,10 +25,16 @@ pub(super) fn allocate_blocks(
// This exit_block_id will be returned and used by instruction_rewriter and exit_phi_builder // This exit_block_id will be returned and used by instruction_rewriter and exit_phi_builder
let exit_block_id = builder.block_gen.next(); let exit_block_id = builder.block_gen.next();
eprintln!("[cf_loop/joinir/block_allocator] Phase 177-3: Allocated exit_block_id = {:?}", exit_block_id); eprintln!(
"[cf_loop/joinir/block_allocator] Phase 177-3: Allocated exit_block_id = {:?}",
exit_block_id
);
// Phase 195: Use unified trace // Phase 195: Use unified trace
trace::trace().blocks("allocator", "Phase 189: Allocating block IDs for all functions"); trace::trace().blocks(
"allocator",
"Phase 189: Allocating block IDs for all functions",
);
// DETERMINISM FIX: Sort functions by name to ensure consistent iteration order // DETERMINISM FIX: Sort functions by name to ensure consistent iteration order
let mut functions: Vec<_> = mir_module.functions.iter().collect(); let mut functions: Vec<_> = mir_module.functions.iter().collect();
@ -50,7 +56,10 @@ pub(super) fn allocate_blocks(
// Phase 195: Use unified trace // Phase 195: Use unified trace
trace::trace().blocks( trace::trace().blocks(
"allocator", "allocator",
&format!("Block remap: {}:{:?}{:?}", func_name, old_block_id, new_block_id), &format!(
"Block remap: {}:{:?}{:?}",
func_name, old_block_id, new_block_id
),
); );
} }

View File

@ -1,7 +1,7 @@
use super::LoopHeaderPhiInfo;
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
use crate::mir::join_ir::lowering::join_value_space::{LOCAL_MAX, PARAM_MAX, PARAM_MIN}; use crate::mir::join_ir::lowering::join_value_space::{LOCAL_MAX, PARAM_MAX, PARAM_MIN};
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId}; use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
use super::LoopHeaderPhiInfo;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub(super) fn verify_loop_header_phis( pub(super) fn verify_loop_header_phis(
@ -99,10 +99,7 @@ pub(super) fn verify_exit_line(
} }
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub(super) fn verify_valueid_regions( pub(super) fn verify_valueid_regions(loop_info: &LoopHeaderPhiInfo, boundary: &JoinInlineBoundary) {
loop_info: &LoopHeaderPhiInfo,
boundary: &JoinInlineBoundary,
) {
fn region_name(id: ValueId) -> &'static str { fn region_name(id: ValueId) -> &'static str {
if id.0 < PARAM_MIN { if id.0 < PARAM_MIN {
"PHI Reserved" "PHI Reserved"

View File

@ -81,21 +81,25 @@ impl ExitMetaCollector {
debug: bool, debug: bool,
) -> Vec<LoopExitBinding> { ) -> Vec<LoopExitBinding> {
let mut bindings = Vec::new(); let mut bindings = Vec::new();
let verbose = debug || crate::config::env::joinir_dev_enabled(); let dev_on = crate::config::env::joinir_dev_enabled();
let verbose = debug || dev_on;
let strict = crate::config::env::joinir_strict_enabled() || dev_on;
if debug { if verbose {
eprintln!( eprintln!(
"[cf_loop/exit_line] ExitMetaCollector: Collecting {} exit values", "[joinir/exit-line] ExitMetaCollector: Collecting {} exit values",
exit_meta.exit_values.len() exit_meta.exit_values.len()
); );
} }
// Iterate over ExitMeta entries and build bindings // Iterate over ExitMeta entries and build bindings
for (carrier_name, join_exit_value) in &exit_meta.exit_values { for (carrier_name, join_exit_value) in &exit_meta.exit_values {
eprintln!( if verbose {
"[cf_loop/exit_line] ExitMetaCollector DEBUG: Checking carrier '{}' in variable_map", eprintln!(
carrier_name "[joinir/exit-line] checking carrier '{}' in variable_map",
); carrier_name
);
}
// Look up host slot from variable_map // Look up host slot from variable_map
if let Some(&host_slot) = builder.variable_map.get(carrier_name) { if let Some(&host_slot) = builder.variable_map.get(carrier_name) {
@ -103,7 +107,8 @@ impl ExitMetaCollector {
// Phase 228-8: Look up role from carrier_info if available // Phase 228-8: Look up role from carrier_info if available
let role = if let Some(ci) = carrier_info { let role = if let Some(ci) = carrier_info {
ci.carriers.iter() ci.carriers
.iter()
.find(|c| c.name == *carrier_name) .find(|c| c.name == *carrier_name)
.map(|c| c.role) .map(|c| c.role)
.unwrap_or(CarrierRole::LoopState) .unwrap_or(CarrierRole::LoopState)
@ -118,18 +123,21 @@ impl ExitMetaCollector {
role, role,
}; };
eprintln!( if verbose {
"[cf_loop/exit_line] ExitMetaCollector: Collected '{}' JoinIR {:?} → HOST {:?}, role={:?}", eprintln!(
carrier_name, join_exit_value, host_slot, role "[joinir/exit-line] collected '{}' JoinIR {:?} → HOST {:?}, role={:?}",
); carrier_name, join_exit_value, host_slot, role
);
}
bindings.push(binding); bindings.push(binding);
} else { } else {
// Phase 228-8: Check if this is a ConditionOnly carrier // Phase 228-8: Check if this is a ConditionOnly carrier
// Phase 247-EX: Also check if this is a FromHost carrier (e.g., digit_value) // Phase 247-EX: Also check if this is a FromHost carrier (e.g., digit_value)
use crate::mir::join_ir::lowering::carrier_info::{CarrierRole, CarrierInit}; use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole};
let carrier_meta = if let Some(ci) = carrier_info { let carrier_meta = if let Some(ci) = carrier_info {
ci.carriers.iter() ci.carriers
.iter()
.find(|c| c.name == *carrier_name) .find(|c| c.name == *carrier_name)
.map(|c| (c.role, c.init)) .map(|c| (c.role, c.init))
} else { } else {
@ -147,10 +155,12 @@ impl ExitMetaCollector {
role: CarrierRole::ConditionOnly, role: CarrierRole::ConditionOnly,
}; };
eprintln!( if verbose {
"[cf_loop/exit_line] Phase 228-8: Collected ConditionOnly carrier '{}' JoinIR {:?} (not in variable_map)", eprintln!(
carrier_name, join_exit_value "[joinir/exit-line] collected ConditionOnly carrier '{}' JoinIR {:?} (not in variable_map)",
); carrier_name, join_exit_value
);
}
bindings.push(binding); bindings.push(binding);
} }
@ -164,10 +174,12 @@ impl ExitMetaCollector {
role: CarrierRole::LoopState, role: CarrierRole::LoopState,
}; };
eprintln!( if verbose {
"[cf_loop/exit_line] Phase 247-EX: Collected FromHost carrier '{}' JoinIR {:?} (not in variable_map)", eprintln!(
carrier_name, join_exit_value "[joinir/exit-line] collected FromHost carrier '{}' JoinIR {:?} (not in variable_map)",
); carrier_name, join_exit_value
);
}
bindings.push(binding); bindings.push(binding);
} }
@ -180,18 +192,25 @@ impl ExitMetaCollector {
role: CarrierRole::LoopState, role: CarrierRole::LoopState,
}; };
eprintln!( if verbose {
"[cf_loop/exit_line] Phase 247-EX: Collected loop-local carrier '{}' JoinIR {:?} (no host slot)", eprintln!(
carrier_name, join_exit_value "[joinir/exit-line] collected loop-local carrier '{}' JoinIR {:?} (no host slot)",
); carrier_name, join_exit_value
);
}
bindings.push(binding); bindings.push(binding);
} }
_ => { _ => {
eprintln!( let msg = format!(
"[cf_loop/exit_line] ExitMetaCollector DEBUG: Carrier '{}' not in variable_map and not ConditionOnly/FromHost (skip)", "[joinir/exit-line] carrier '{}' not in variable_map and not ConditionOnly/FromHost (skip)",
carrier_name carrier_name
); );
if strict {
panic!("{}", msg);
} else if verbose {
eprintln!("{}", msg);
}
} }
} }
} }
@ -199,7 +218,7 @@ impl ExitMetaCollector {
if verbose { if verbose {
eprintln!( eprintln!(
"[cf_loop/exit_line] ExitMetaCollector: Collected {} bindings: {:?}", "[joinir/exit-line] ExitMetaCollector: collected {} bindings: {:?}",
bindings.len(), bindings.len(),
bindings bindings
); );
@ -211,7 +230,6 @@ impl ExitMetaCollector {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[test] #[test]
fn test_empty_exit_meta() { fn test_empty_exit_meta() {

View File

@ -48,14 +48,14 @@
//! ``` //! ```
//! No changes to exit_line module needed! //! No changes to exit_line module needed!
pub mod reconnector;
pub mod meta_collector; pub mod meta_collector;
pub mod reconnector;
pub use reconnector::ExitLineReconnector;
pub use meta_collector::ExitMetaCollector; pub use meta_collector::ExitMetaCollector;
pub use reconnector::ExitLineReconnector;
use std::collections::BTreeMap;
use crate::mir::ValueId; use crate::mir::ValueId;
use std::collections::BTreeMap;
/// Phase 33-10-Refactor-P2: ExitLineOrchestrator facade /// Phase 33-10-Refactor-P2: ExitLineOrchestrator facade
/// ///
@ -84,9 +84,10 @@ impl ExitLineOrchestrator {
carrier_phis: &BTreeMap<String, ValueId>, carrier_phis: &BTreeMap<String, ValueId>,
debug: bool, debug: bool,
) -> Result<(), String> { ) -> Result<(), String> {
if debug { let verbose = debug || crate::config::env::joinir_dev_enabled();
if verbose {
eprintln!( eprintln!(
"[cf_loop/joinir/exit_line] ExitLineOrchestrator: Starting Phase 6 reconnection with {} carrier PHIs", "[joinir/exit-line] orchestrator start: {} carrier PHIs",
carrier_phis.len() carrier_phis.len()
); );
} }
@ -94,8 +95,8 @@ impl ExitLineOrchestrator {
// Phase 33-13: Delegate to ExitLineReconnector with carrier_phis // Phase 33-13: Delegate to ExitLineReconnector with carrier_phis
ExitLineReconnector::reconnect(builder, boundary, carrier_phis, debug)?; ExitLineReconnector::reconnect(builder, boundary, carrier_phis, debug)?;
if debug { if verbose {
eprintln!("[cf_loop/joinir/exit_line] ExitLineOrchestrator: Phase 6 complete"); eprintln!("[joinir/exit-line] orchestrator complete");
} }
Ok(()) Ok(())

View File

@ -79,26 +79,39 @@ impl ExitLineReconnector {
carrier_phis: &BTreeMap<String, ValueId>, carrier_phis: &BTreeMap<String, ValueId>,
debug: bool, debug: bool,
) -> Result<(), String> { ) -> Result<(), String> {
let strict = crate::config::env::joinir_strict_enabled(); let dev_on = crate::config::env::joinir_dev_enabled();
let strict = crate::config::env::joinir_strict_enabled() || dev_on;
let verbose = debug || dev_on;
// Phase 177-STRUCT: Always log for debugging if verbose {
eprintln!( eprintln!(
"[DEBUG-177/reconnect] ExitLineReconnector: {} exit bindings, {} carrier PHIs", "[joinir/exit-line] reconnect: {} exit bindings, {} carrier PHIs",
boundary.exit_bindings.len(), boundary.exit_bindings.len(),
carrier_phis.len() carrier_phis.len()
); );
if !boundary.exit_bindings.is_empty() {
eprintln!(
"[joinir/exit-line] bindings {:?}",
boundary
.exit_bindings
.iter()
.map(|b| (&b.carrier_name, b.role, b.join_exit_value))
.collect::<Vec<_>>()
);
}
}
// Early return for empty exit_bindings // Early return for empty exit_bindings
if boundary.exit_bindings.is_empty() { if boundary.exit_bindings.is_empty() {
if debug { if verbose {
eprintln!("[cf_loop/joinir/exit_line] ExitLineReconnector: No exit bindings, skipping reconnect"); eprintln!("[joinir/exit-line] reconnect: no exit bindings, skip");
} }
return Ok(()); return Ok(());
} }
if debug { if verbose {
eprintln!( eprintln!(
"[cf_loop/joinir/exit_line] ExitLineReconnector: Reconnecting {} exit bindings with {} carrier PHIs", "[joinir/exit-line] reconnecting {} exit bindings with {} carrier PHIs",
boundary.exit_bindings.len(), boundary.exit_bindings.len(),
carrier_phis.len() carrier_phis.len()
); );
@ -109,9 +122,9 @@ impl ExitLineReconnector {
// Phase 228-8: Skip ConditionOnly carriers (no variable_map update needed) // Phase 228-8: Skip ConditionOnly carriers (no variable_map update needed)
use crate::mir::join_ir::lowering::carrier_info::CarrierRole; use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
if binding.role == CarrierRole::ConditionOnly { if binding.role == CarrierRole::ConditionOnly {
if debug { if verbose {
eprintln!( eprintln!(
"[cf_loop/joinir/exit_line] Phase 228-8: Skipping ConditionOnly carrier '{}' (no variable_map update)", "[joinir/exit-line] skip ConditionOnly carrier '{}' (no variable_map update)",
binding.carrier_name binding.carrier_name
); );
} }
@ -121,9 +134,9 @@ impl ExitLineReconnector {
// Phase 33-13: Look up the PHI dst for this carrier // Phase 33-13: Look up the PHI dst for this carrier
let phi_dst = carrier_phis.get(&binding.carrier_name); let phi_dst = carrier_phis.get(&binding.carrier_name);
if debug { if verbose {
eprintln!( eprintln!(
"[cf_loop/joinir/exit_line] ExitLineReconnector: Carrier '{}' → phi_dst={:?}", "[joinir/exit-line] carrier '{}' → phi_dst={:?}",
binding.carrier_name, phi_dst binding.carrier_name, phi_dst
); );
} }
@ -132,33 +145,34 @@ impl ExitLineReconnector {
if let Some(&phi_value) = phi_dst { if let Some(&phi_value) = phi_dst {
if let Some(var_vid) = builder.variable_map.get_mut(&binding.carrier_name) { if let Some(var_vid) = builder.variable_map.get_mut(&binding.carrier_name) {
// Phase 177-STRUCT: Always log for debugging // Phase 177-STRUCT: Always log for debugging
eprintln!( if verbose {
"[DEBUG-177/reconnect] Updated variable_map['{}'] {:?}{:?}", eprintln!(
binding.carrier_name, *var_vid, phi_value "[joinir/exit-line] variable_map['{}'] {:?}{:?}",
); binding.carrier_name, *var_vid, phi_value
);
}
*var_vid = phi_value; *var_vid = phi_value;
} else if debug { } else if verbose {
eprintln!( eprintln!(
"[cf_loop/joinir/exit_line] ExitLineReconnector WARNING: Carrier '{}' not found in variable_map", "[joinir/exit-line] warning: carrier '{}' not found in variable_map",
binding.carrier_name binding.carrier_name
); );
} else if strict { } else if strict {
return Err(format!( return Err(format!(
"[pattern2/exit_line] Missing variable_map entry for carrier '{}' (exit reconnection)", "[joinir/exit-line] missing variable_map entry for carrier '{}' (exit reconnection)",
binding.carrier_name binding.carrier_name
)); ));
} }
} else { } else {
if strict && binding.role != CarrierRole::ConditionOnly { if strict && binding.role != CarrierRole::ConditionOnly {
return Err(format!( return Err(format!(
"[pattern2/exit_line] Missing PHI dst for carrier '{}' ({} PHIs available)", "[joinir/exit-line] missing PHI dst for carrier '{}' ({} PHIs available)",
binding.carrier_name, binding.carrier_name,
carrier_phis.len() carrier_phis.len()
)); ));
} } else if verbose {
if debug {
eprintln!( eprintln!(
"[cf_loop/joinir/exit_line] ExitLineReconnector WARNING: No PHI dst for carrier '{}' (may be condition-only variable)", "[joinir/exit-line] warning: No PHI dst for carrier '{}' (may be condition-only variable)",
binding.carrier_name binding.carrier_name
); );
} }
@ -169,7 +183,7 @@ impl ExitLineReconnector {
#[allow(deprecated)] #[allow(deprecated)]
if !boundary.host_outputs.is_empty() && debug { if !boundary.host_outputs.is_empty() && debug {
eprintln!( eprintln!(
"[cf_loop/joinir/exit_line] WARNING: Using deprecated host_outputs. Migrate to exit_bindings." "[joinir/exit-line] WARNING: Using deprecated host_outputs. Migrate to exit_bindings."
); );
} }

View File

@ -85,7 +85,8 @@ pub(super) fn build_exit_phi(
if debug { if debug {
eprintln!( eprintln!(
"[cf_loop/joinir] Created exit block: {:?} with {} carrier PHIs", "[cf_loop/joinir] Created exit block: {:?} with {} carrier PHIs",
exit_block_id, carrier_phis.len() exit_block_id,
carrier_phis.len()
); );
} }
phi_result phi_result

View File

@ -12,10 +12,10 @@
//! 4. 非対応の場合 → remapper.get_value(expr_result) を返す //! 4. 非対応の場合 → remapper.get_value(expr_result) を返す
//! 5. expr_result が None → None を返す //! 5. expr_result が None → None を返す
use std::collections::BTreeMap;
use crate::mir::ValueId;
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper; use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
use crate::mir::ValueId;
use std::collections::BTreeMap;
pub struct ExprResultResolver; pub struct ExprResultResolver;
@ -60,7 +60,10 @@ impl ExprResultResolver {
eprintln!( eprintln!(
"[cf_loop/joinir] Phase 221: Resolving expr_result {:?}, exit_bindings={:?}", "[cf_loop/joinir] Phase 221: Resolving expr_result {:?}, exit_bindings={:?}",
expr_result_id, expr_result_id,
exit_bindings.iter().map(|b| (b.carrier_name.as_str(), b.join_exit_value)).collect::<Vec<_>>() exit_bindings
.iter()
.map(|b| (b.carrier_name.as_str(), b.join_exit_value))
.collect::<Vec<_>>()
); );
} }
@ -114,13 +117,7 @@ mod tests {
let carrier_phis = BTreeMap::new(); let carrier_phis = BTreeMap::new();
let remapper = JoinIrIdRemapper::new(); let remapper = JoinIrIdRemapper::new();
let result = ExprResultResolver::resolve( let result = ExprResultResolver::resolve(None, &[], &carrier_phis, &remapper, false);
None,
&[],
&carrier_phis,
&remapper,
false,
);
assert_eq!(result.unwrap(), None); assert_eq!(result.unwrap(), None);
} }
@ -156,13 +153,8 @@ mod tests {
let mut remapper = JoinIrIdRemapper::new(); let mut remapper = JoinIrIdRemapper::new();
remapper.set_value(ValueId(42), ValueId(200)); remapper.set_value(ValueId(42), ValueId(200));
let result = ExprResultResolver::resolve( let result =
Some(ValueId(42)), ExprResultResolver::resolve(Some(ValueId(42)), &[], &carrier_phis, &remapper, false);
&[],
&carrier_phis,
&remapper,
false,
);
assert_eq!(result.unwrap(), Some(ValueId(200))); assert_eq!(result.unwrap(), Some(ValueId(200)));
} }
@ -172,13 +164,8 @@ mod tests {
let carrier_phis = BTreeMap::new(); let carrier_phis = BTreeMap::new();
let remapper = JoinIrIdRemapper::new(); let remapper = JoinIrIdRemapper::new();
let result = ExprResultResolver::resolve( let result =
Some(ValueId(999)), ExprResultResolver::resolve(Some(ValueId(999)), &[], &carrier_phis, &remapper, false);
&[],
&carrier_phis,
&remapper,
false,
);
assert!(result.is_err()); assert!(result.is_err());
assert!(result.unwrap_err().contains("not found in remapper")); assert!(result.unwrap_err().contains("not found in remapper"));

View File

@ -61,7 +61,8 @@ pub(super) fn merge_and_rewrite(
let boundary_input_set: std::collections::HashSet<ValueId> = boundary let boundary_input_set: std::collections::HashSet<ValueId> = boundary
.map(|b| b.join_inputs.iter().copied().collect()) .map(|b| b.join_inputs.iter().copied().collect())
.unwrap_or_default(); .unwrap_or_default();
let strict_exit = crate::config::env::joinir_strict_enabled(); let strict_exit =
crate::config::env::joinir_strict_enabled() || crate::config::env::joinir_dev_enabled();
// Phase 189-Fix: Collect return values from JoinIR functions for exit PHI // Phase 189-Fix: Collect return values from JoinIR functions for exit PHI
let mut exit_phi_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new(); let mut exit_phi_inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
@ -614,7 +615,7 @@ pub(super) fn merge_and_rewrite(
let expected_args = carrier_info.carriers.len() + 1; // loop_var + carriers let expected_args = carrier_info.carriers.len() + 1; // loop_var + carriers
if remapped_args.len() < expected_args { if remapped_args.len() < expected_args {
let msg = format!( let msg = format!(
"[pattern2/exit_line] jump_args length mismatch: expected at least {} (loop_var + carriers) but got {} in block {:?}", "[joinir/exit-line] jump_args length mismatch: expected at least {} (loop_var + carriers) but got {} in block {:?}",
expected_args, expected_args,
remapped_args.len(), remapped_args.len(),
old_block.id old_block.id
@ -673,7 +674,7 @@ pub(super) fn merge_and_rewrite(
); );
} else { } else {
let msg = format!( let msg = format!(
"[pattern2/exit_line] Missing jump_args entry for carrier '{}' at index {} in block {:?}", "[joinir/exit-line] Missing jump_args entry for carrier '{}' at index {} in block {:?}",
carrier.name, jump_args_idx, old_block.id carrier.name, jump_args_idx, old_block.id
); );
if strict_exit { if strict_exit {

View File

@ -27,8 +27,8 @@
//! Called from merge pipeline between Phase 3 (remap_values) and Phase 4 //! Called from merge pipeline between Phase 3 (remap_values) and Phase 4
//! (instruction_rewriter). //! (instruction_rewriter).
use crate::mir::{MirInstruction, ValueId, BasicBlockId}; use super::loop_header_phi_info::{CarrierPhiEntry, LoopHeaderPhiInfo};
use super::loop_header_phi_info::{LoopHeaderPhiInfo, CarrierPhiEntry}; use crate::mir::{BasicBlockId, MirInstruction, ValueId};
/// Builder for loop header PHIs /// Builder for loop header PHIs
/// ///
@ -64,7 +64,12 @@ impl LoopHeaderPhiBuilder {
entry_block: BasicBlockId, entry_block: BasicBlockId,
loop_var_name: &str, loop_var_name: &str,
loop_var_init: ValueId, loop_var_init: ValueId,
carriers: &[(String, ValueId, crate::mir::join_ir::lowering::carrier_info::CarrierInit, crate::mir::join_ir::lowering::carrier_info::CarrierRole)], // Phase 228: Added CarrierInit and CarrierRole carriers: &[(
String,
ValueId,
crate::mir::join_ir::lowering::carrier_info::CarrierInit,
crate::mir::join_ir::lowering::carrier_info::CarrierRole,
)], // Phase 228: Added CarrierInit and CarrierRole
expr_result_is_loop_var: bool, expr_result_is_loop_var: bool,
debug: bool, debug: bool,
) -> Result<LoopHeaderPhiInfo, String> { ) -> Result<LoopHeaderPhiInfo, String> {
@ -188,6 +193,7 @@ impl LoopHeaderPhiBuilder {
info: &LoopHeaderPhiInfo, info: &LoopHeaderPhiInfo,
debug: bool, debug: bool,
) -> Result<(), String> { ) -> Result<(), String> {
let dev_debug = debug || crate::config::env::joinir_dev_enabled();
if debug { if debug {
eprintln!( eprintln!(
"[cf_loop/joinir] Phase 33-16: Finalizing header PHIs at {:?}", "[cf_loop/joinir] Phase 33-16: Finalizing header PHIs at {:?}",
@ -206,16 +212,20 @@ impl LoopHeaderPhiBuilder {
} }
// Get the header block from current function // Get the header block from current function
let current_func = builder.current_function.as_mut().ok_or( let current_func = builder
"Phase 33-16: No current function when finalizing header PHIs" .current_function
)?; .as_mut()
.ok_or("Phase 33-16: No current function when finalizing header PHIs")?;
let header_block = current_func.blocks.get_mut(&info.header_block).ok_or_else(|| { let header_block = current_func
format!( .blocks
"Phase 33-16: Header block {:?} not found in current function", .get_mut(&info.header_block)
info.header_block .ok_or_else(|| {
) format!(
})?; "Phase 33-16: Header block {:?} not found in current function",
info.header_block
)
})?;
// Insert PHIs at the beginning of the header block (before other instructions) // Insert PHIs at the beginning of the header block (before other instructions)
// Sorted by carrier name for determinism // Sorted by carrier name for determinism
@ -233,11 +243,11 @@ impl LoopHeaderPhiBuilder {
phi_instructions.push(phi); phi_instructions.push(phi);
if debug { if dev_debug {
eprintln!( eprintln!(
"[cf_loop/joinir] Finalized carrier '{}' PHI: {:?} = phi [({:?}, {:?}), ({:?}, {:?})]", "[joinir/header-phi] Finalized carrier '{}' PHI: {:?} = phi [({:?}, {:?}), ({:?}, {:?})]",
name, entry.phi_dst, entry_block, entry_val, latch_block, latch_val name, entry.phi_dst, entry_block, entry_val, latch_block, latch_val
); );
} }
} }
@ -253,9 +263,9 @@ impl LoopHeaderPhiBuilder {
new_spans.append(&mut header_block.instruction_spans); new_spans.append(&mut header_block.instruction_spans);
header_block.instruction_spans = new_spans; header_block.instruction_spans = new_spans;
if debug { if dev_debug {
eprintln!( eprintln!(
"[cf_loop/joinir] Header block now has {} instructions", "[joinir/header-phi] Header block now has {} instructions",
header_block.instructions.len() header_block.instructions.len()
); );
} }

View File

@ -10,8 +10,8 @@
//! - exit_phi_builder: to reference current loop values //! - exit_phi_builder: to reference current loop values
//! - ExitLineReconnector: to update variable_map with final values //! - ExitLineReconnector: to update variable_map with final values
use crate::mir::{BasicBlockId, ValueId};
use crate::mir::join_ir::lowering::carrier_info::CarrierRole; use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
use crate::mir::{BasicBlockId, ValueId};
use std::collections::BTreeMap; use std::collections::BTreeMap;
/// Information about loop header PHIs /// Information about loop header PHIs
@ -104,7 +104,9 @@ impl LoopHeaderPhiInfo {
/// Check if all carriers have latch incoming set /// Check if all carriers have latch incoming set
#[allow(dead_code)] #[allow(dead_code)]
pub fn all_latch_set(&self) -> bool { pub fn all_latch_set(&self) -> bool {
self.carrier_phis.values().all(|e| e.latch_incoming.is_some()) self.carrier_phis
.values()
.all(|e| e.latch_incoming.is_some())
} }
/// Phase 201-A: Get reserved ValueIds (PHI dsts that must not be overwritten) /// Phase 201-A: Get reserved ValueIds (PHI dsts that must not be overwritten)

View File

@ -38,7 +38,12 @@ impl MergeResult {
/// Add a carrier input /// Add a carrier input
#[allow(dead_code)] #[allow(dead_code)]
pub fn add_carrier_input(&mut self, carrier_name: String, from_block: BasicBlockId, value: ValueId) { pub fn add_carrier_input(
&mut self,
carrier_name: String,
from_block: BasicBlockId,
value: ValueId,
) {
self.carrier_inputs self.carrier_inputs
.entry(carrier_name) .entry(carrier_name)
.or_insert_with(Vec::new) .or_insert_with(Vec::new)

View File

@ -13,24 +13,24 @@
//! Phase 4 Refactoring: Breaking down 714-line merge_joinir_mir_blocks() into focused modules //! Phase 4 Refactoring: Breaking down 714-line merge_joinir_mir_blocks() into focused modules
mod block_allocator; mod block_allocator;
mod value_collector;
mod instruction_rewriter;
mod exit_phi_builder;
pub mod exit_line;
mod loop_header_phi_info;
mod loop_header_phi_builder;
mod tail_call_classifier;
mod merge_result;
mod expr_result_resolver;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
mod contract_checks; mod contract_checks;
pub mod exit_line;
mod exit_phi_builder;
mod expr_result_resolver;
mod instruction_rewriter;
mod loop_header_phi_builder;
mod loop_header_phi_info;
mod merge_result;
mod tail_call_classifier;
mod value_collector;
// Phase 33-17: Re-export for use by other modules // Phase 33-17: Re-export for use by other modules
pub use loop_header_phi_info::LoopHeaderPhiInfo;
pub use loop_header_phi_builder::LoopHeaderPhiBuilder; pub use loop_header_phi_builder::LoopHeaderPhiBuilder;
pub use loop_header_phi_info::LoopHeaderPhiInfo;
use crate::mir::{MirModule, ValueId};
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
use crate::mir::{MirModule, ValueId};
/// Phase 49-3.2: Merge JoinIR-generated MIR blocks into current_function /// Phase 49-3.2: Merge JoinIR-generated MIR blocks into current_function
/// ///
@ -100,7 +100,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
let cond_summary: Vec<String> = boundary let cond_summary: Vec<String> = boundary
.condition_bindings .condition_bindings
.iter() .iter()
.map(|b| format!("{}: host {:?} → join {:?}", b.name, b.host_value, b.join_value)) .map(|b| {
format!(
"{}: host {:?} → join {:?}",
b.name, b.host_value, b.join_value
)
})
.collect(); .collect();
eprintln!( eprintln!(
@ -123,8 +128,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
let carriers: Vec<String> = ci.carriers.iter().map(|c| c.name.clone()).collect(); let carriers: Vec<String> = ci.carriers.iter().map(|c| c.name.clone()).collect();
eprintln!( eprintln!(
"[cf_loop/joinir] Boundary carrier_info: loop_var='{}', carriers={:?}", "[cf_loop/joinir] Boundary carrier_info: loop_var='{}', carriers={:?}",
ci.loop_var_name, ci.loop_var_name, carriers
carriers
); );
} }
} else { } else {
@ -134,7 +138,8 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Phase 1: Allocate block IDs for all functions // Phase 1: Allocate block IDs for all functions
// Phase 177-3: block_allocator now returns exit_block_id to avoid conflicts // Phase 177-3: block_allocator now returns exit_block_id to avoid conflicts
let (mut remapper, exit_block_id) = block_allocator::allocate_blocks(builder, mir_module, debug)?; let (mut remapper, exit_block_id) =
block_allocator::allocate_blocks(builder, mir_module, debug)?;
// Phase 2: Collect values from all functions // Phase 2: Collect values from all functions
let (mut used_values, value_to_func_name, function_params) = let (mut used_values, value_to_func_name, function_params) =
@ -181,35 +186,55 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
.ok_or("JoinIR module has no functions (Phase 201-A)")?; .ok_or("JoinIR module has no functions (Phase 201-A)")?;
let entry_block_remapped = remapper let entry_block_remapped = remapper
.get_block(entry_func_name, entry_func.entry_block) .get_block(entry_func_name, entry_func.entry_block)
.ok_or_else(|| format!("Entry block not found for {} (Phase 201-A)", entry_func_name))?; .ok_or_else(|| {
format!(
"Entry block not found for {} (Phase 201-A)",
entry_func_name
)
})?;
// Get host's current block as the entry edge // Get host's current block as the entry edge
let host_entry_block = builder.current_block.ok_or( let host_entry_block = builder
"Phase 201-A: No current block when building header PHIs" .current_block
)?; .ok_or("Phase 201-A: No current block when building header PHIs")?;
// Get loop variable's initial value from HOST // Get loop variable's initial value from HOST
let loop_var_init = boundary.host_inputs.first().copied().ok_or( let loop_var_init = boundary
"Phase 201-A: No host_inputs in boundary for loop_var_init" .host_inputs
)?; .first()
.copied()
.ok_or("Phase 201-A: No host_inputs in boundary for loop_var_init")?;
// Phase 228-4: Extract carriers with their initialization strategy // Phase 228-4: Extract carriers with their initialization strategy
let other_carriers: Vec<(String, ValueId, crate::mir::join_ir::lowering::carrier_info::CarrierInit, crate::mir::join_ir::lowering::carrier_info::CarrierRole)> = let other_carriers: Vec<(
if let Some(ref carrier_info) = boundary.carrier_info { String,
// Use carrier_info if available (Phase 228) ValueId,
carrier_info.carriers crate::mir::join_ir::lowering::carrier_info::CarrierInit,
.iter() crate::mir::join_ir::lowering::carrier_info::CarrierRole,
.filter(|c| c.name != *loop_var_name) )> = if let Some(ref carrier_info) = boundary.carrier_info {
.map(|c| (c.name.clone(), c.host_id, c.init, c.role)) // Use carrier_info if available (Phase 228)
.collect() carrier_info
} else { .carriers
// Fallback: exit_bindings から取得(既存動作) .iter()
boundary.exit_bindings .filter(|c| c.name != *loop_var_name)
.iter() .map(|c| (c.name.clone(), c.host_id, c.init, c.role))
.filter(|b| b.carrier_name != *loop_var_name) .collect()
.map(|b| (b.carrier_name.clone(), b.host_slot, crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState)) } else {
.collect() // Fallback: exit_bindings から取得(既存動作)
}; boundary
.exit_bindings
.iter()
.filter(|b| b.carrier_name != *loop_var_name)
.map(|b| {
(
b.carrier_name.clone(),
b.host_slot,
crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost,
crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
)
})
.collect()
};
if debug { if debug {
eprintln!( eprintln!(
@ -219,7 +244,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
eprintln!( eprintln!(
"[cf_loop/joinir] loop_var_init={:?}, carriers={:?}", "[cf_loop/joinir] loop_var_init={:?}, carriers={:?}",
loop_var_init, loop_var_init,
other_carriers.iter().map(|(n, _, _, _)| n.as_str()).collect::<Vec<_>>() other_carriers
.iter()
.map(|(n, _, _, _)| n.as_str())
.collect::<Vec<_>>()
); );
} }
@ -235,33 +263,53 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
debug, debug,
)? )?
} else { } else {
LoopHeaderPhiInfo::empty(remapper.get_block( LoopHeaderPhiInfo::empty(
mir_module.functions.iter().next().unwrap().0, remapper
mir_module.functions.iter().next().unwrap().1.entry_block .get_block(
).unwrap()) mir_module.functions.iter().next().unwrap().0,
mir_module.functions.iter().next().unwrap().1.entry_block,
)
.unwrap(),
)
} }
} else { } else {
LoopHeaderPhiInfo::empty(remapper.get_block( LoopHeaderPhiInfo::empty(
mir_module.functions.iter().next().unwrap().0, remapper
mir_module.functions.iter().next().unwrap().1.entry_block .get_block(
).unwrap()) mir_module.functions.iter().next().unwrap().0,
mir_module.functions.iter().next().unwrap().1.entry_block,
)
.unwrap(),
)
}; };
// Phase 201-A: Get reserved PHI dst ValueIds and set in MirBuilder // Phase 201-A: Get reserved PHI dst ValueIds and set in MirBuilder
let reserved_phi_dsts = loop_header_phi_info.reserved_value_ids(); let reserved_phi_dsts = loop_header_phi_info.reserved_value_ids();
if debug && !reserved_phi_dsts.is_empty() { if debug && !reserved_phi_dsts.is_empty() {
eprintln!("[cf_loop/joinir] Phase 201-A: Reserved PHI dsts: {:?}", reserved_phi_dsts); eprintln!(
"[cf_loop/joinir] Phase 201-A: Reserved PHI dsts: {:?}",
reserved_phi_dsts
);
} }
// Phase 201-A: Set reserved IDs in MirBuilder so next_value_id() skips them // Phase 201-A: Set reserved IDs in MirBuilder so next_value_id() skips them
// This protects against carrier corruption when break conditions emit Const instructions // This protects against carrier corruption when break conditions emit Const instructions
builder.reserved_value_ids = reserved_phi_dsts.clone(); builder.reserved_value_ids = reserved_phi_dsts.clone();
if debug && !builder.reserved_value_ids.is_empty() { if debug && !builder.reserved_value_ids.is_empty() {
eprintln!("[cf_loop/joinir] Phase 201-A: Set builder.reserved_value_ids = {:?}", builder.reserved_value_ids); eprintln!(
"[cf_loop/joinir] Phase 201-A: Set builder.reserved_value_ids = {:?}",
builder.reserved_value_ids
);
} }
// Phase 3: Remap ValueIds (with reserved PHI dsts protection) // Phase 3: Remap ValueIds (with reserved PHI dsts protection)
remap_values(builder, &used_values, &mut remapper, &reserved_phi_dsts, debug)?; remap_values(
builder,
&used_values,
&mut remapper,
&reserved_phi_dsts,
debug,
)?;
// Phase 177-3 DEBUG: Verify remapper state after Phase 3 // Phase 177-3 DEBUG: Verify remapper state after Phase 3
eprintln!("[DEBUG-177] === Remapper state after Phase 3 ==="); eprintln!("[DEBUG-177] === Remapper state after Phase 3 ===");
@ -372,12 +420,18 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
eprintln!( eprintln!(
"[DEBUG-177] Phase 33-21: carrier_phis count: {}, names: {:?}", "[DEBUG-177] Phase 33-21: carrier_phis count: {}, names: {:?}",
loop_header_phi_info.carrier_phis.len(), loop_header_phi_info.carrier_phis.len(),
loop_header_phi_info.carrier_phis.iter().map(|(n, _)| n.as_str()).collect::<Vec<_>>() loop_header_phi_info
.carrier_phis
.iter()
.map(|(n, _)| n.as_str())
.collect::<Vec<_>>()
); );
// Map main's parameters to header PHI dsts // Map main's parameters to header PHI dsts
// main params: [i_init, carrier1_init, ...] // main params: [i_init, carrier1_init, ...]
// carrier_phis: [("i", entry), ("sum", entry), ...] // carrier_phis: [("i", entry), ("sum", entry), ...]
for (idx, (carrier_name, entry)) in loop_header_phi_info.carrier_phis.iter().enumerate() { for (idx, (carrier_name, entry)) in
loop_header_phi_info.carrier_phis.iter().enumerate()
{
if let Some(&main_param) = main_params.get(idx) { if let Some(&main_param) = main_params.get(idx) {
// Phase 177-3: Don't override condition_bindings // Phase 177-3: Don't override condition_bindings
if condition_binding_ids.contains(&main_param) { if condition_binding_ids.contains(&main_param) {
@ -401,7 +455,11 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// They appear in condition_bindings (added by Phase 176-5) but need PHI remapping. // They appear in condition_bindings (added by Phase 176-5) but need PHI remapping.
for (carrier_name, entry) in &loop_header_phi_info.carrier_phis { for (carrier_name, entry) in &loop_header_phi_info.carrier_phis {
// Check if this carrier has a condition_binding // Check if this carrier has a condition_binding
if let Some(binding) = boundary.condition_bindings.iter().find(|cb| cb.name == *carrier_name) { if let Some(binding) = boundary
.condition_bindings
.iter()
.find(|cb| cb.name == *carrier_name)
{
// Skip if it's a true condition-only variable (already protected above) // Skip if it's a true condition-only variable (already protected above)
if condition_binding_ids.contains(&binding.join_value) { if condition_binding_ids.contains(&binding.join_value) {
continue; continue;
@ -451,8 +509,11 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Find which carrier this param belongs to by matching join_value // Find which carrier this param belongs to by matching join_value
// Check if this param was already handled by Phase 177-3-B // Check if this param was already handled by Phase 177-3-B
let already_mapped = boundary.condition_bindings.iter().any(|cb| { let already_mapped = boundary.condition_bindings.iter().any(|cb| {
cb.join_value == *loop_step_param && cb.join_value == *loop_step_param
loop_header_phi_info.carrier_phis.iter().any(|(name, _)| name == &cb.name) && loop_header_phi_info
.carrier_phis
.iter()
.any(|(name, _)| name == &cb.name)
}); });
if already_mapped { if already_mapped {
eprintln!( eprintln!(
@ -467,7 +528,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// generates params in exit_bindings order. // generates params in exit_bindings order.
// //
// Solution: Use carrier_order (Vec<String>) which preserves insertion order. // Solution: Use carrier_order (Vec<String>) which preserves insertion order.
if let Some(param_idx) = loop_step_params.iter().position(|p| p == loop_step_param) { if let Some(param_idx) =
loop_step_params.iter().position(|p| p == loop_step_param)
{
// Map params[i] to carrier_order[i] // Map params[i] to carrier_order[i]
if let (Some(carrier_name), Some(entry)) = ( if let (Some(carrier_name), Some(entry)) = (
loop_header_phi_info.get_carrier_at_index(param_idx), loop_header_phi_info.get_carrier_at_index(param_idx),
@ -483,7 +546,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
} }
} }
if function_params.get(main_func_name).is_none() && function_params.get(loop_step_func_name).is_none() { if function_params.get(main_func_name).is_none()
&& function_params.get(loop_step_func_name).is_none()
{
// Fallback: Use old behavior (ValueId(0), ValueId(1), ...) // Fallback: Use old behavior (ValueId(0), ValueId(1), ...)
// This handles patterns that don't have loop_step function // This handles patterns that don't have loop_step function
if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(loop_var_name) { if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(loop_var_name) {
@ -570,11 +635,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
loop_header_phi_info.carrier_phis.len() loop_header_phi_info.carrier_phis.len()
); );
} }
LoopHeaderPhiBuilder::finalize( LoopHeaderPhiBuilder::finalize(builder, &loop_header_phi_info, debug)?;
builder,
&loop_header_phi_info,
debug,
)?;
} }
// Phase 5: Build exit PHI (expr result only, not carrier PHIs) // Phase 5: Build exit PHI (expr result only, not carrier PHIs)
@ -633,12 +694,18 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
let entry_block = loop_header_phi_info.header_block; let entry_block = loop_header_phi_info.header_block;
if debug { if debug {
eprintln!("[cf_loop/joinir] Entry block (from loop_header_phi_info): {:?}", entry_block); eprintln!(
"[cf_loop/joinir] Entry block (from loop_header_phi_info): {:?}",
entry_block
);
eprintln!( eprintln!(
"[cf_loop/joinir] Current block before emit_jump: {:?}", "[cf_loop/joinir] Current block before emit_jump: {:?}",
builder.current_block builder.current_block
); );
eprintln!("[cf_loop/joinir] Jumping to entry block: {:?}", entry_block); eprintln!(
"[cf_loop/joinir] Jumping to entry block: {:?}",
entry_block
);
} }
crate::mir::builder::emission::branch::emit_jump(builder, entry_block)?; crate::mir::builder::emission::branch::emit_jump(builder, entry_block)?;
@ -684,7 +751,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Future loops will set their own reserved IDs // Future loops will set their own reserved IDs
if !builder.reserved_value_ids.is_empty() { if !builder.reserved_value_ids.is_empty() {
if debug { if debug {
eprintln!("[cf_loop/joinir] Phase 201-A: Clearing reserved_value_ids (was {:?})", builder.reserved_value_ids); eprintln!(
"[cf_loop/joinir] Phase 201-A: Clearing reserved_value_ids (was {:?})",
builder.reserved_value_ids
);
} }
builder.reserved_value_ids.clear(); builder.reserved_value_ids.clear();
} }
@ -698,7 +768,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Check if expr_result is the loop variable // Check if expr_result is the loop variable
if let Some(loop_var_name) = &b.loop_var_name { if let Some(loop_var_name) = &b.loop_var_name {
// Find the exit binding for the loop variable // Find the exit binding for the loop variable
let loop_var_binding = b.exit_bindings.iter() let loop_var_binding = b
.exit_bindings
.iter()
.find(|binding| binding.carrier_name == *loop_var_name); .find(|binding| binding.carrier_name == *loop_var_name);
if let Some(binding) = loop_var_binding { if let Some(binding) = loop_var_binding {
@ -751,7 +823,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Return expr_result if present, otherwise fall back to exit_phi_result_id // Return expr_result if present, otherwise fall back to exit_phi_result_id
if let Some(resolved) = expr_result_value { if let Some(resolved) = expr_result_value {
if debug { if debug {
eprintln!("[cf_loop/joinir] Phase 246-EX-FIX: Returning expr_result_value {:?}", resolved); eprintln!(
"[cf_loop/joinir] Phase 246-EX-FIX: Returning expr_result_value {:?}",
resolved
);
} }
Ok(Some(resolved)) Ok(Some(resolved))
} else { } else {
@ -779,8 +854,11 @@ fn remap_values(
debug: bool, debug: bool,
) -> Result<(), String> { ) -> Result<(), String> {
if debug { if debug {
eprintln!("[cf_loop/joinir] Phase 3: Remapping {} ValueIds (reserved: {})", eprintln!(
used_values.len(), reserved_ids.len()); "[cf_loop/joinir] Phase 3: Remapping {} ValueIds (reserved: {})",
used_values.len(),
reserved_ids.len()
);
} }
for old_value in used_values { for old_value in used_values {
@ -792,7 +870,10 @@ fn remap_values(
} }
// Skip reserved ID - will try next one // Skip reserved ID - will try next one
if debug { if debug {
eprintln!("[cf_loop/joinir] Phase 201-A: Skipping reserved PHI dst {:?}", candidate); eprintln!(
"[cf_loop/joinir] Phase 201-A: Skipping reserved PHI dst {:?}",
candidate
);
} }
}; };
@ -943,7 +1024,12 @@ fn verify_phi_inputs_defined(
}); });
for instr in &header_block_data.instructions { for instr in &header_block_data.instructions {
if let crate::mir::MirInstruction::Phi { dst, inputs, type_hint: _ } = instr { if let crate::mir::MirInstruction::Phi {
dst,
inputs,
type_hint: _,
} = instr
{
for (value_id, pred_block) in inputs { for (value_id, pred_block) in inputs {
// Conservative sanity check: ValueId should not be suspiciously large // Conservative sanity check: ValueId should not be suspiciously large
// Phase 201 JoinValueSpace uses regions: // Phase 201 JoinValueSpace uses regions:

View File

@ -68,9 +68,9 @@ mod tests {
#[test] #[test]
fn test_classify_loop_entry() { fn test_classify_loop_entry() {
let result = classify_tail_call( let result = classify_tail_call(
true, // is_entry_func_entry_block true, // is_entry_func_entry_block
true, // has_loop_header_phis true, // has_loop_header_phis
true, // has_boundary true, // has_boundary
); );
assert_eq!(result, TailCallKind::LoopEntry); assert_eq!(result, TailCallKind::LoopEntry);
} }

View File

@ -5,10 +5,10 @@
//! //!
//! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 202-246) //! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 202-246)
use crate::mir::{MirInstruction, MirModule, ValueId};
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper; use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
use std::collections::BTreeSet; // Phase 222.5-E: HashMap → BTreeMap for determinism use crate::mir::{MirInstruction, MirModule, ValueId};
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism use std::collections::BTreeMap;
use std::collections::BTreeSet; // Phase 222.5-E: HashMap → BTreeMap for determinism // Phase 222.5-E: HashMap → BTreeMap for determinism
/// Phase 2: Collect all ValueIds used across ALL functions (Phase 189) /// Phase 2: Collect all ValueIds used across ALL functions (Phase 189)
/// ///
@ -22,15 +22,13 @@ pub(super) fn collect_values(
) -> Result< ) -> Result<
( (
BTreeSet<ValueId>, BTreeSet<ValueId>,
BTreeMap<ValueId, String>, // Phase 222.5-E: HashMap → BTreeMap for determinism BTreeMap<ValueId, String>, // Phase 222.5-E: HashMap → BTreeMap for determinism
BTreeMap<String, Vec<ValueId>>, // Phase 222.5-E: HashMap → BTreeMap for determinism BTreeMap<String, Vec<ValueId>>, // Phase 222.5-E: HashMap → BTreeMap for determinism
), ),
String, String,
> { > {
if debug { if debug {
eprintln!( eprintln!("[cf_loop/joinir] Phase 189: Collecting value IDs from all functions");
"[cf_loop/joinir] Phase 189: Collecting value IDs from all functions"
);
} }
let mut used_values: BTreeSet<ValueId> = BTreeSet::new(); let mut used_values: BTreeSet<ValueId> = BTreeSet::new();

View File

@ -6,8 +6,8 @@
//! - MIR block merging (merge/) ✅ Phase 4 //! - MIR block merging (merge/) ✅ Phase 4
//! - Unified tracing (trace.rs) ✅ Phase 195 //! - Unified tracing (trace.rs) ✅ Phase 195
pub(in crate::mir::builder) mod merge;
pub(in crate::mir::builder) mod patterns; pub(in crate::mir::builder) mod patterns;
pub(in crate::mir::builder) mod routing; pub(in crate::mir::builder) mod routing;
pub(in crate::mir::builder) mod routing_legacy_binding; pub(in crate::mir::builder) mod routing_legacy_binding;
pub(in crate::mir::builder) mod merge;
pub(in crate::mir::builder) mod trace; pub(in crate::mir::builder) mod trace;

View File

@ -27,13 +27,13 @@
//! - **Testability**: Can test conversion independently //! - **Testability**: Can test conversion independently
//! - **Reduces duplication**: Eliminates 120 lines across Pattern 1-4 //! - **Reduces duplication**: Eliminates 120 lines across Pattern 1-4
use crate::mir::ValueId;
use crate::mir::builder::MirBuilder; use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::JoinModule;
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
use crate::mir::join_ir::JoinModule;
use crate::mir::ValueId;
use std::collections::BTreeMap; use std::collections::BTreeMap;
pub struct JoinIRConversionPipeline; pub(crate) struct JoinIRConversionPipeline;
impl JoinIRConversionPipeline { impl JoinIRConversionPipeline {
/// Execute unified conversion pipeline /// Execute unified conversion pipeline
@ -82,41 +82,28 @@ impl JoinIRConversionPipeline {
pattern_name: &str, pattern_name: &str,
debug: bool, debug: bool,
) -> Result<Option<ValueId>, String> { ) -> Result<Option<ValueId>, String> {
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta;
use crate::mir::join_ir::frontend::JoinFuncMetaMap;
use super::super::trace; use super::super::trace;
use crate::mir::join_ir::frontend::JoinFuncMetaMap;
use crate::mir::join_ir_vm_bridge::bridge_joinir_to_mir_with_meta;
// Step 1: Log JoinIR stats (functions and blocks) // Step 1: Log JoinIR stats (functions and blocks)
trace::trace().joinir_stats( trace::trace().joinir_stats(
pattern_name, pattern_name,
join_module.functions.len(), join_module.functions.len(),
join_module join_module.functions.values().map(|f| f.body.len()).sum(),
.functions
.values()
.map(|f| f.body.len())
.sum(),
); );
// Step 2: JoinModule → MirModule conversion // Step 2: JoinModule → MirModule conversion
// Pass empty meta map since minimal lowerers don't use metadata // Pass empty meta map since minimal lowerers don't use metadata
let empty_meta: JoinFuncMetaMap = BTreeMap::new(); let empty_meta: JoinFuncMetaMap = BTreeMap::new();
let mir_module = convert_join_module_to_mir_with_meta(&join_module, &empty_meta) let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &empty_meta)
.map_err(|e| { .map_err(|e| format!("[{}/pipeline] MIR conversion failed: {:?}", pattern_name, e))?;
format!(
"[{}/pipeline] MIR conversion failed: {:?}",
pattern_name, e
)
})?;
// Step 3: Log MIR stats (functions and blocks) // Step 3: Log MIR stats (functions and blocks)
trace::trace().joinir_stats( trace::trace().joinir_stats(
pattern_name, pattern_name,
mir_module.functions.len(), mir_module.functions.len(),
mir_module mir_module.functions.values().map(|f| f.blocks.len()).sum(),
.functions
.values()
.map(|f| f.blocks.len())
.sum(),
); );
// Step 4: Merge into current function // Step 4: Merge into current function

View File

@ -10,17 +10,17 @@
//! //!
//! This orchestrator coordinates the three modules for a complete workflow. //! This orchestrator coordinates the three modules for a complete workflow.
use crate::mir::ValueId;
use crate::mir::join_ir::lowering::inline_boundary::{
JoinInlineBoundary, LoopExitBinding,
};
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta}; use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
use crate::mir::join_ir::lowering::inline_boundary::{JoinInlineBoundary, LoopExitBinding};
use crate::mir::ValueId;
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
// Phase 222.5-C: Import modular components // Phase 222.5-C: Import modular components
use super::exit_binding_validator::validate_exit_binding; use super::exit_binding_applicator::{
apply_exit_bindings_to_boundary, create_loop_var_exit_binding,
};
use super::exit_binding_constructor::build_loop_exit_bindings; use super::exit_binding_constructor::build_loop_exit_bindings;
use super::exit_binding_applicator::{apply_exit_bindings_to_boundary, create_loop_var_exit_binding}; use super::exit_binding_validator::validate_exit_binding;
/// Builder for generating loop exit bindings /// Builder for generating loop exit bindings
/// ///
@ -29,7 +29,7 @@ use super::exit_binding_applicator::{apply_exit_bindings_to_boundary, create_loo
/// ///
/// Eliminates hardcoded variable names and ValueId plumbing scattered across lowerers. /// Eliminates hardcoded variable names and ValueId plumbing scattered across lowerers.
#[allow(dead_code)] #[allow(dead_code)]
pub struct ExitBindingBuilder<'a> { pub(crate) struct ExitBindingBuilder<'a> {
carrier_info: &'a CarrierInfo, carrier_info: &'a CarrierInfo,
exit_meta: &'a ExitMeta, exit_meta: &'a ExitMeta,
variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
@ -60,7 +60,7 @@ impl<'a> ExitBindingBuilder<'a> {
/// ///
/// ExitBindingBuilder instance, or error if metadata is inconsistent /// ExitBindingBuilder instance, or error if metadata is inconsistent
#[allow(dead_code)] #[allow(dead_code)]
pub fn new( pub(crate) fn new(
carrier_info: &'a CarrierInfo, carrier_info: &'a CarrierInfo,
exit_meta: &'a ExitMeta, exit_meta: &'a ExitMeta,
variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism variable_map: &'a mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
@ -86,7 +86,7 @@ impl<'a> ExitBindingBuilder<'a> {
/// ///
/// Vec of LoopExitBinding, one per carrier, sorted by carrier name /// Vec of LoopExitBinding, one per carrier, sorted by carrier name
#[allow(dead_code)] #[allow(dead_code)]
pub fn build_loop_exit_bindings(&mut self) -> Result<Vec<LoopExitBinding>, String> { pub(crate) fn build_loop_exit_bindings(&mut self) -> Result<Vec<LoopExitBinding>, String> {
// Phase 222.5-C: Delegate to constructor module // Phase 222.5-C: Delegate to constructor module
build_loop_exit_bindings(self.carrier_info, self.exit_meta, self.variable_map) build_loop_exit_bindings(self.carrier_info, self.exit_meta, self.variable_map)
} }
@ -106,7 +106,7 @@ impl<'a> ExitBindingBuilder<'a> {
/// ///
/// Success or error if boundary cannot be updated /// Success or error if boundary cannot be updated
#[allow(dead_code)] #[allow(dead_code)]
pub fn apply_to_boundary(&self, boundary: &mut JoinInlineBoundary) -> Result<(), String> { pub(crate) fn apply_to_boundary(&self, boundary: &mut JoinInlineBoundary) -> Result<(), String> {
// Phase 222.5-C: Delegate to applicator module // Phase 222.5-C: Delegate to applicator module
apply_exit_bindings_to_boundary( apply_exit_bindings_to_boundary(
self.carrier_info, self.carrier_info,
@ -160,7 +160,9 @@ mod tests {
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map) let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
.expect("Failed to create builder"); .expect("Failed to create builder");
let bindings = builder.build_loop_exit_bindings().expect("Failed to build bindings"); let bindings = builder
.build_loop_exit_bindings()
.expect("Failed to build bindings");
assert_eq!(bindings.len(), 1); assert_eq!(bindings.len(), 1);
assert_eq!(bindings[0].carrier_name, "sum"); assert_eq!(bindings[0].carrier_name, "sum");
@ -213,7 +215,9 @@ mod tests {
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map) let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
.expect("Failed to create builder"); .expect("Failed to create builder");
let bindings = builder.build_loop_exit_bindings().expect("Failed to build bindings"); let bindings = builder
.build_loop_exit_bindings()
.expect("Failed to build bindings");
assert_eq!(bindings.len(), 2); assert_eq!(bindings.len(), 2);
// Bindings should be sorted by carrier name // Bindings should be sorted by carrier name
@ -345,24 +349,27 @@ mod tests {
let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map) let mut builder = ExitBindingBuilder::new(&carrier_info, &exit_meta, &mut variable_map)
.expect("Failed to create builder"); .expect("Failed to create builder");
let _ = builder.build_loop_exit_bindings().expect("Failed to build bindings"); let _ = builder
.build_loop_exit_bindings()
.expect("Failed to build bindings");
let mut boundary = JoinInlineBoundary { let mut boundary = JoinInlineBoundary {
host_inputs: vec![], host_inputs: vec![],
join_inputs: vec![], join_inputs: vec![],
exit_bindings: vec![], // Phase 171: Add missing field exit_bindings: vec![], // Phase 171: Add missing field
#[allow(deprecated)] #[allow(deprecated)]
host_outputs: vec![], // legacy, unused in new assertions host_outputs: vec![], // legacy, unused in new assertions
join_outputs: vec![], join_outputs: vec![],
#[allow(deprecated)] #[allow(deprecated)]
condition_inputs: vec![], // Phase 171: Add missing field condition_inputs: vec![], // Phase 171: Add missing field
condition_bindings: vec![], // Phase 171-fix: Add missing field condition_bindings: vec![], // Phase 171-fix: Add missing field
expr_result: None, // Phase 33-14: Add missing field expr_result: None, // Phase 33-14: Add missing field
loop_var_name: None, // Phase 33-16: Add missing field loop_var_name: None, // Phase 33-16: Add missing field
carrier_info: None, // Phase 228: Add missing field carrier_info: None, // Phase 228: Add missing field
}; };
builder.apply_to_boundary(&mut boundary) builder
.apply_to_boundary(&mut boundary)
.expect("Failed to apply to boundary"); .expect("Failed to apply to boundary");
// Should have loop_var + sum carrier in exit_bindings // Should have loop_var + sum carrier in exit_bindings

View File

@ -38,7 +38,7 @@ use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
use crate::mir::BasicBlockId; use crate::mir::BasicBlockId;
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
pub struct LoopScopeShapeBuilder; pub(crate) struct LoopScopeShapeBuilder;
impl LoopScopeShapeBuilder { impl LoopScopeShapeBuilder {
/// Create LoopScopeShape with empty body_locals /// Create LoopScopeShape with empty body_locals

View File

@ -46,20 +46,20 @@ pub(in crate::mir::builder) mod common_init;
pub(in crate::mir::builder) mod condition_env_builder; pub(in crate::mir::builder) mod condition_env_builder;
pub(in crate::mir::builder) mod conversion_pipeline; pub(in crate::mir::builder) mod conversion_pipeline;
pub(in crate::mir::builder) mod exit_binding; pub(in crate::mir::builder) mod exit_binding;
pub(in crate::mir::builder) mod exit_binding_validator; // Phase 222.5-C pub(in crate::mir::builder) mod exit_binding_applicator; // Phase 222.5-C
pub(in crate::mir::builder) mod exit_binding_constructor; // Phase 222.5-C pub(in crate::mir::builder) mod exit_binding_constructor; // Phase 222.5-C
pub(in crate::mir::builder) mod exit_binding_applicator; // Phase 222.5-C pub(in crate::mir::builder) mod exit_binding_validator; // Phase 222.5-C
pub(in crate::mir::builder) mod loop_scope_shape_builder; pub(in crate::mir::builder) mod loop_scope_shape_builder;
pub(in crate::mir::builder) mod pattern_pipeline;
pub(in crate::mir::builder) mod pattern1_minimal; pub(in crate::mir::builder) mod pattern1_minimal;
pub(in crate::mir::builder) mod pattern2_with_break; pub(in crate::mir::builder) mod pattern2_with_break;
pub(in crate::mir::builder) mod pattern3_with_if_phi; pub(in crate::mir::builder) mod pattern3_with_if_phi;
pub(in crate::mir::builder) mod pattern4_carrier_analyzer; pub(in crate::mir::builder) mod pattern4_carrier_analyzer;
pub(in crate::mir::builder) mod pattern4_with_continue; pub(in crate::mir::builder) mod pattern4_with_continue;
pub(in crate::mir::builder) mod pattern_pipeline;
pub(in crate::mir::builder) mod router; pub(in crate::mir::builder) mod router;
pub(in crate::mir::builder) mod trim_loop_lowering; // Phase 180: Dedicated Trim/P5 lowering module pub(in crate::mir::builder) mod trim_loop_lowering; // Phase 180: Dedicated Trim/P5 lowering module
pub(in crate::mir::builder) mod trim_pattern_validator;
pub(in crate::mir::builder) mod trim_pattern_lowerer; pub(in crate::mir::builder) mod trim_pattern_lowerer;
pub(in crate::mir::builder) mod trim_pattern_validator;
// Re-export router for convenience // Re-export router for convenience
pub(in crate::mir::builder) use router::{route_loop_pattern, LoopPatternContext}; pub(in crate::mir::builder) use router::{route_loop_pattern, LoopPatternContext};

View File

@ -5,10 +5,10 @@
//! - Trim break condition generation //! - Trim break condition generation
//! - Carrier binding setup in ConditionEnv //! - Carrier binding setup in ConditionEnv
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper; use crate::ast::{ASTNode, Span, UnaryOperator};
use crate::mir::join_ir::lowering::condition_env::ConditionBinding; use crate::mir::join_ir::lowering::condition_env::ConditionBinding;
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
use crate::mir::ValueId; use crate::mir::ValueId;
use crate::ast::{ASTNode, UnaryOperator, Span};
pub(in crate::mir::builder::control_flow::joinir::patterns) struct TrimPatternLowerer; pub(in crate::mir::builder::control_flow::joinir::patterns) struct TrimPatternLowerer;
@ -61,8 +61,12 @@ impl TrimPatternLowerer {
let carrier_name = &trim_helper.carrier_name; let carrier_name = &trim_helper.carrier_name;
// Get host ValueId for carrier // Get host ValueId for carrier
let host_value_id = get_host_value(carrier_name) let host_value_id = get_host_value(carrier_name).ok_or_else(|| {
.ok_or_else(|| format!("[TrimPatternLowerer] Carrier '{}' not in variable_map", carrier_name))?; format!(
"[TrimPatternLowerer] Carrier '{}' not in variable_map",
carrier_name
)
})?;
// Allocate JoinIR ValueId // Allocate JoinIR ValueId
let joinir_value_id = alloc_join_value(); let joinir_value_id = alloc_join_value();
@ -94,7 +98,8 @@ impl TrimPatternLowerer {
insert_to_env: impl FnOnce(String, ValueId), insert_to_env: impl FnOnce(String, ValueId),
alloc_join_value: &mut dyn FnMut() -> ValueId, alloc_join_value: &mut dyn FnMut() -> ValueId,
) -> Result<ConditionBinding, String> { ) -> Result<ConditionBinding, String> {
let binding = Self::setup_trim_carrier_binding(trim_helper, get_host_value, alloc_join_value)?; let binding =
Self::setup_trim_carrier_binding(trim_helper, get_host_value, alloc_join_value)?;
// Insert into env // Insert into env
insert_to_env(binding.name.clone(), binding.join_value); insert_to_env(binding.name.clone(), binding.join_value);
@ -122,7 +127,9 @@ mod tests {
// Should be UnaryOp::Not // Should be UnaryOp::Not
match result { match result {
ASTNode::UnaryOp { operator, operand, .. } => { ASTNode::UnaryOp {
operator, operand, ..
} => {
assert_eq!(operator, UnaryOperator::Not); assert_eq!(operator, UnaryOperator::Not);
// Operand should be Variable with carrier name // Operand should be Variable with carrier name
match *operand { match *operand {
@ -151,11 +158,7 @@ mod tests {
}; };
let get_value = |name: &str| variable_map.get(name).copied(); let get_value = |name: &str| variable_map.get(name).copied();
let result = TrimPatternLowerer::setup_trim_carrier_binding( let result = TrimPatternLowerer::setup_trim_carrier_binding(&helper, get_value, &mut alloc);
&helper,
get_value,
&mut alloc,
);
assert!(result.is_ok()); assert!(result.is_ok());
let binding = result.unwrap(); let binding = result.unwrap();
@ -178,11 +181,7 @@ mod tests {
}; };
let get_value = |name: &str| variable_map.get(name).copied(); let get_value = |name: &str| variable_map.get(name).copied();
let result = TrimPatternLowerer::setup_trim_carrier_binding( let result = TrimPatternLowerer::setup_trim_carrier_binding(&helper, get_value, &mut alloc);
&helper,
get_value,
&mut alloc,
);
assert!(result.is_err()); assert!(result.is_err());
assert!(result.unwrap_err().contains("not in variable_map")); assert!(result.unwrap_err().contains("not in variable_map"));
@ -209,12 +208,8 @@ mod tests {
env.insert(name, value); env.insert(name, value);
}; };
let result = TrimPatternLowerer::add_to_condition_env( let result =
&helper, TrimPatternLowerer::add_to_condition_env(&helper, get_value, insert, &mut alloc);
get_value,
insert,
&mut alloc,
);
assert!(result.is_ok()); assert!(result.is_ok());
let binding = result.unwrap(); let binding = result.unwrap();

View File

@ -31,10 +31,10 @@ impl TrimPatternValidator {
ch_value: ValueId, ch_value: ValueId,
whitespace_chars: &[String], whitespace_chars: &[String],
) -> Result<ValueId, String> { ) -> Result<ValueId, String> {
use crate::mir::builder::emission::constant::emit_string;
use crate::mir::builder::emission::compare::emit_eq_to; use crate::mir::builder::emission::compare::emit_eq_to;
use crate::mir::types::BinaryOp; use crate::mir::builder::emission::constant::emit_string;
use crate::mir::instruction::MirInstruction; use crate::mir::instruction::MirInstruction;
use crate::mir::types::BinaryOp;
if whitespace_chars.is_empty() { if whitespace_chars.is_empty() {
return Err("[emit_whitespace_check] Empty whitespace_chars".to_string()); return Err("[emit_whitespace_check] Empty whitespace_chars".to_string());
@ -89,18 +89,32 @@ impl TrimPatternValidator {
) -> Option<(String, Box<ASTNode>)> { ) -> Option<(String, Box<ASTNode>)> {
for stmt in loop_body { for stmt in loop_body {
// Look for: local ch = ... // Look for: local ch = ...
if let ASTNode::Local { variables, initial_values, .. } = stmt { if let ASTNode::Local {
variables,
initial_values,
..
} = stmt
{
for (i, var) in variables.iter().enumerate() { for (i, var) in variables.iter().enumerate() {
if var == var_name { if var == var_name {
if let Some(Some(init_expr_box)) = initial_values.get(i) { if let Some(Some(init_expr_box)) = initial_values.get(i) {
// Check if it's a substring method call // Check if it's a substring method call
if let ASTNode::MethodCall { object, method, arguments, .. } = init_expr_box.as_ref() { if let ASTNode::MethodCall {
object,
method,
arguments,
..
} = init_expr_box.as_ref()
{
if method == "substring" && arguments.len() == 2 { if method == "substring" && arguments.len() == 2 {
// Extract object name // Extract object name
if let ASTNode::Variable { name, .. } = object.as_ref() { if let ASTNode::Variable { name, .. } = object.as_ref() {
// Return object name and start expression // Return object name and start expression
// (We assume second arg is start+1, first arg is start) // (We assume second arg is start+1, first arg is start)
return Some((name.clone(), Box::new(arguments[0].clone()))); return Some((
name.clone(),
Box::new(arguments[0].clone()),
));
} }
} }
} }
@ -116,45 +130,41 @@ impl TrimPatternValidator {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::ast::{LiteralValue, Span, BinaryOperator}; use crate::ast::{BinaryOperator, LiteralValue, Span};
#[test] #[test]
fn test_extract_substring_args_valid() { fn test_extract_substring_args_valid() {
// Create: local ch = s.substring(start, start+1) // Create: local ch = s.substring(start, start+1)
let body = vec![ let body = vec![ASTNode::Local {
ASTNode::Local { variables: vec!["ch".to_string()],
variables: vec!["ch".to_string()], initial_values: vec![Some(Box::new(ASTNode::MethodCall {
initial_values: vec![ object: Box::new(ASTNode::Variable {
Some(Box::new(ASTNode::MethodCall { name: "s".to_string(),
object: Box::new(ASTNode::Variable { span: Span::unknown(),
name: "s".to_string(), }),
method: "substring".to_string(),
arguments: vec![
ASTNode::Variable {
name: "start".to_string(),
span: Span::unknown(),
},
ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(ASTNode::Variable {
name: "start".to_string(),
span: Span::unknown(),
}),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(), span: Span::unknown(),
}), }),
method: "substring".to_string(),
arguments: vec![
ASTNode::Variable {
name: "start".to_string(),
span: Span::unknown(),
},
ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(ASTNode::Variable {
name: "start".to_string(),
span: Span::unknown(),
}),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
},
],
span: Span::unknown(), span: Span::unknown(),
})), },
], ],
span: Span::unknown(), span: Span::unknown(),
}, }))],
]; span: Span::unknown(),
}];
let result = TrimPatternValidator::extract_substring_args(&body, "ch"); let result = TrimPatternValidator::extract_substring_args(&body, "ch");
assert!(result.is_some()); assert!(result.is_some());
@ -173,32 +183,28 @@ mod tests {
#[test] #[test]
fn test_extract_substring_args_wrong_var() { fn test_extract_substring_args_wrong_var() {
// local other_var = s.substring(0, 1) // local other_var = s.substring(0, 1)
let body = vec![ let body = vec![ASTNode::Local {
ASTNode::Local { variables: vec!["other_var".to_string()],
variables: vec!["other_var".to_string()], initial_values: vec![Some(Box::new(ASTNode::MethodCall {
initial_values: vec![ object: Box::new(ASTNode::Variable {
Some(Box::new(ASTNode::MethodCall { name: "s".to_string(),
object: Box::new(ASTNode::Variable { span: Span::unknown(),
name: "s".to_string(), }),
span: Span::unknown(), method: "substring".to_string(),
}), arguments: vec![
method: "substring".to_string(), ASTNode::Literal {
arguments: vec![ value: LiteralValue::Integer(0),
ASTNode::Literal {
value: LiteralValue::Integer(0),
span: Span::unknown(),
},
ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
},
],
span: Span::unknown(), span: Span::unknown(),
})), },
ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
},
], ],
span: Span::unknown(), span: Span::unknown(),
}, }))],
]; span: Span::unknown(),
}];
let result = TrimPatternValidator::extract_substring_args(&body, "ch"); let result = TrimPatternValidator::extract_substring_args(&body, "ch");
assert!(result.is_none()); assert!(result.is_none());
@ -207,28 +213,22 @@ mod tests {
#[test] #[test]
fn test_extract_substring_args_wrong_method() { fn test_extract_substring_args_wrong_method() {
// local ch = s.charAt(0) // local ch = s.charAt(0)
let body = vec![ let body = vec![ASTNode::Local {
ASTNode::Local { variables: vec!["ch".to_string()],
variables: vec!["ch".to_string()], initial_values: vec![Some(Box::new(ASTNode::MethodCall {
initial_values: vec![ object: Box::new(ASTNode::Variable {
Some(Box::new(ASTNode::MethodCall { name: "s".to_string(),
object: Box::new(ASTNode::Variable { span: Span::unknown(),
name: "s".to_string(), }),
span: Span::unknown(), method: "charAt".to_string(),
}), arguments: vec![ASTNode::Literal {
method: "charAt".to_string(), value: LiteralValue::Integer(0),
arguments: vec![ span: Span::unknown(),
ASTNode::Literal { }],
value: LiteralValue::Integer(0),
span: Span::unknown(),
},
],
span: Span::unknown(),
})),
],
span: Span::unknown(), span: Span::unknown(),
}, }))],
]; span: Span::unknown(),
}];
let result = TrimPatternValidator::extract_substring_args(&body, "ch"); let result = TrimPatternValidator::extract_substring_args(&body, "ch");
assert!(result.is_none()); assert!(result.is_none());

View File

@ -1,9 +1,9 @@
//! JoinIR routing logic for loop lowering //! JoinIR routing logic for loop lowering
use super::trace;
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder; use crate::mir::builder::MirBuilder;
use crate::mir::ValueId; use crate::mir::ValueId;
use super::trace;
impl MirBuilder { impl MirBuilder {
/// Phase 49: Try JoinIR Frontend for mainline integration /// Phase 49: Try JoinIR Frontend for mainline integration
@ -42,16 +42,17 @@ impl MirBuilder {
// Phase 196: Default to structure-first routing now that LoopBuilder is removed. // Phase 196: Default to structure-first routing now that LoopBuilder is removed.
// - Default: ON (structure_only = true) to allow JoinIR patterns to run for all loops. // - Default: ON (structure_only = true) to allow JoinIR patterns to run for all loops.
// - To revert to the previous whitelist-only behavior, set NYASH_JOINIR_STRUCTURE_ONLY=0. // - To revert to the previous whitelist-only behavior, set NYASH_JOINIR_STRUCTURE_ONLY=0.
let structure_only = match std::env::var("NYASH_JOINIR_STRUCTURE_ONLY") let structure_only = match std::env::var("NYASH_JOINIR_STRUCTURE_ONLY").ok().as_deref() {
.ok()
.as_deref()
{
Some("0") | Some("off") => false, Some("0") | Some("off") => false,
_ => true, _ => true,
}; };
if structure_only { if structure_only {
trace::trace().routing("router", &func_name, "Structure-only mode enabled, skipping whitelist"); trace::trace().routing(
"router",
&func_name,
"Structure-only mode enabled, skipping whitelist",
);
} else { } else {
// Phase 49-4 + Phase 80: Multi-target routing (legacy whitelist) // Phase 49-4 + Phase 80: Multi-target routing (legacy whitelist)
// - JoinIR は常時 ON。legacy LoopBuilder は削除済み。 // - JoinIR は常時 ON。legacy LoopBuilder は削除済み。
@ -60,25 +61,25 @@ impl MirBuilder {
// Phase 188: Add "main" routing for loop pattern expansion // Phase 188: Add "main" routing for loop pattern expansion
// Phase 170: Add JsonParserBox methods for selfhost validation // Phase 170: Add JsonParserBox methods for selfhost validation
let is_target = match func_name.as_str() { let is_target = match func_name.as_str() {
"main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1) "main" => true, // Phase 188-Impl-1: Enable JoinIR for main function (Pattern 1)
"JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2) "JoinIrMin.main/0" => true, // Phase 188-Impl-2: Enable JoinIR for JoinIrMin.main/0 (Pattern 2)
"JsonTokenizer.print_tokens/0" => true, "JsonTokenizer.print_tokens/0" => true,
"ArrayExtBox.filter/2" => true, "ArrayExtBox.filter/2" => true,
// Phase 170-A-1: Enable JsonParserBox methods for JoinIR routing // Phase 170-A-1: Enable JsonParserBox methods for JoinIR routing
"JsonParserBox._trim/1" => true, "JsonParserBox._trim/1" => true,
"JsonParserBox._skip_whitespace/2" => true, "JsonParserBox._skip_whitespace/2" => true,
"JsonParserBox._match_literal/3" => true, // Phase 182: Fixed arity (s, pos, literal) "JsonParserBox._match_literal/3" => true, // Phase 182: Fixed arity (s, pos, literal)
"JsonParserBox._parse_string/2" => true, "JsonParserBox._parse_string/2" => true,
"JsonParserBox._parse_array/2" => true, "JsonParserBox._parse_array/2" => true,
"JsonParserBox._parse_object/2" => true, "JsonParserBox._parse_object/2" => true,
// Phase 182: Add simple loop methods // Phase 182: Add simple loop methods
"JsonParserBox._parse_number/2" => true, // P2 Break (s, pos) "JsonParserBox._parse_number/2" => true, // P2 Break (s, pos)
"JsonParserBox._atoi/1" => true, // P2 Break (s) "JsonParserBox._atoi/1" => true, // P2 Break (s)
// Phase 170-A-1: Test methods (simplified versions) // Phase 170-A-1: Test methods (simplified versions)
"TrimTest.trim/1" => true, "TrimTest.trim/1" => true,
"Main.trim/1" => true, // Phase 171-fix: Main box variant "Main.trim/1" => true, // Phase 171-fix: Main box variant
"Main.trim_string_simple/1" => true, // Phase 33-13: Simple trim variant "Main.trim_string_simple/1" => true, // Phase 33-13: Simple trim variant
"TrimTest.main/0" => true, // Phase 170: TrimTest.main for loop pattern test "TrimTest.main/0" => true, // Phase 170: TrimTest.main for loop pattern test
// Phase 173: JsonParser P5 expansion test // Phase 173: JsonParser P5 expansion test
"JsonParserTest._skip_whitespace/3" => true, "JsonParserTest._skip_whitespace/3" => true,
"JsonParserTest.main/0" => true, "JsonParserTest.main/0" => true,
@ -99,7 +100,11 @@ impl MirBuilder {
// Debug log when routing through JoinIR Frontend // Debug log when routing through JoinIR Frontend
// Phase 195: Check trace flags directly from JoinLoopTrace // Phase 195: Check trace flags directly from JoinLoopTrace
let debug = trace::trace().is_loopform_enabled() || trace::trace().is_mainline_enabled(); let debug = trace::trace().is_loopform_enabled() || trace::trace().is_mainline_enabled();
trace::trace().routing("router", &func_name, "Routing through JoinIR Frontend mainline"); trace::trace().routing(
"router",
&func_name,
"Routing through JoinIR Frontend mainline",
);
// Phase 49-3: Implement JoinIR Frontend integration // Phase 49-3: Implement JoinIR Frontend integration
self.cf_loop_joinir_impl(condition, body, &func_name, debug) self.cf_loop_joinir_impl(condition, body, &func_name, debug)
@ -123,9 +128,20 @@ impl MirBuilder {
// Phase 200-C: Pass fn_body_ast to LoopPatternContext if available // Phase 200-C: Pass fn_body_ast to LoopPatternContext if available
// Clone fn_body_ast to avoid borrow checker issues // Clone fn_body_ast to avoid borrow checker issues
let fn_body_clone = self.fn_body_ast.clone(); let fn_body_clone = self.fn_body_ast.clone();
eprintln!("[routing] fn_body_ast is {} for '{}'", if fn_body_clone.is_some() { "SOME" } else { "NONE" }, func_name); eprintln!(
"[routing] fn_body_ast is {} for '{}'",
if fn_body_clone.is_some() {
"SOME"
} else {
"NONE"
},
func_name
);
let ctx = if let Some(ref fn_body) = fn_body_clone { let ctx = if let Some(ref fn_body) = fn_body_clone {
eprintln!("[routing] Creating ctx with fn_body ({} nodes)", fn_body.len()); eprintln!(
"[routing] Creating ctx with fn_body ({} nodes)",
fn_body.len()
);
LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body) LoopPatternContext::with_fn_body(condition, body, &func_name, debug, fn_body)
} else { } else {
LoopPatternContext::new(condition, body, &func_name, debug) LoopPatternContext::new(condition, body, &func_name, debug)
@ -137,7 +153,11 @@ impl MirBuilder {
} }
// Phase 187-2: Pattern router failed, try legacy whitelist // Phase 187-2: Pattern router failed, try legacy whitelist
trace::trace().routing("router", func_name, "Pattern router found no match, trying legacy whitelist"); trace::trace().routing(
"router",
func_name,
"Pattern router found no match, trying legacy whitelist",
);
// Delegate to legacy binding path (routing_legacy_binding.rs) // Delegate to legacy binding path (routing_legacy_binding.rs)
self.cf_loop_joinir_legacy_binding(condition, body, func_name, debug) self.cf_loop_joinir_legacy_binding(condition, body, func_name, debug)

View File

@ -7,10 +7,10 @@
//! Phase 194+ uses the pattern-based router instead. This legacy path is //! Phase 194+ uses the pattern-based router instead. This legacy path is
//! kept for backward compatibility with existing whitelist entries. //! kept for backward compatibility with existing whitelist entries.
use super::trace;
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder; use crate::mir::builder::MirBuilder;
use crate::mir::ValueId; use crate::mir::ValueId;
use super::trace;
impl MirBuilder { impl MirBuilder {
/// Phase 49-3: Legacy JoinIR Frontend integration via LoopFrontendBinding /// Phase 49-3: Legacy JoinIR Frontend integration via LoopFrontendBinding
@ -21,7 +21,7 @@ impl MirBuilder {
/// # Pipeline /// # Pipeline
/// 1. Build Loop AST → JSON v0 format (with "defs" array) /// 1. Build Loop AST → JSON v0 format (with "defs" array)
/// 2. AstToJoinIrLowerer::lower_program_json() → JoinModule /// 2. AstToJoinIrLowerer::lower_program_json() → JoinModule
/// 3. convert_join_module_to_mir_with_meta() → MirModule /// 3. bridge_joinir_to_mir_with_meta() → MirModule
/// 4. Merge MIR blocks into current_function /// 4. Merge MIR blocks into current_function
pub(in crate::mir::builder) fn cf_loop_joinir_legacy_binding( pub(in crate::mir::builder) fn cf_loop_joinir_legacy_binding(
&mut self, &mut self,
@ -32,7 +32,7 @@ impl MirBuilder {
) -> Result<Option<ValueId>, String> { ) -> Result<Option<ValueId>, String> {
use super::super::super::loop_frontend_binding::LoopFrontendBinding; use super::super::super::loop_frontend_binding::LoopFrontendBinding;
use crate::mir::join_ir::frontend::{AstToJoinIrLowerer, JoinFuncMetaMap}; use crate::mir::join_ir::frontend::{AstToJoinIrLowerer, JoinFuncMetaMap};
use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta; use crate::mir::join_ir_vm_bridge::bridge_joinir_to_mir_with_meta;
use crate::mir::types::ConstValue; use crate::mir::types::ConstValue;
use crate::mir::MirInstruction; use crate::mir::MirInstruction;
use crate::r#macro::ast_json::ast_to_json; use crate::r#macro::ast_json::ast_to_json;
@ -42,7 +42,11 @@ impl MirBuilder {
"JsonTokenizer.print_tokens/0" => LoopFrontendBinding::for_print_tokens(), "JsonTokenizer.print_tokens/0" => LoopFrontendBinding::for_print_tokens(),
"ArrayExtBox.filter/2" => LoopFrontendBinding::for_array_filter(), "ArrayExtBox.filter/2" => LoopFrontendBinding::for_array_filter(),
_ => { _ => {
trace::trace().routing("router", func_name, "No legacy binding defined, falling back"); trace::trace().routing(
"router",
func_name,
"No legacy binding defined, falling back",
);
return Ok(None); return Ok(None);
} }
}; };
@ -79,7 +83,10 @@ impl MirBuilder {
if ext_ref == "me" || ext_ref.starts_with("me.") { if ext_ref == "me" || ext_ref.starts_with("me.") {
continue; continue;
} }
trace::trace().debug("router", &format!("Adding '{}' to params (external_ref)", ext_ref)); trace::trace().debug(
"router",
&format!("Adding '{}' to params (external_ref)", ext_ref),
);
params.push(serde_json::json!(ext_ref)); params.push(serde_json::json!(ext_ref));
} }
@ -157,15 +164,11 @@ impl MirBuilder {
trace::trace().joinir_stats( trace::trace().joinir_stats(
"router", "router",
join_module.functions.len(), join_module.functions.len(),
join_module join_module.functions.values().map(|f| f.body.len()).sum(),
.functions
.values()
.map(|f| f.body.len())
.sum(),
); );
// Step 4: Convert JoinModule to MIR // Step 4: Convert JoinModule to MIR
let mir_module = convert_join_module_to_mir_with_meta(&join_module, &join_meta) let mir_module = bridge_joinir_to_mir_with_meta(&join_module, &join_meta)
.map_err(|e| format!("JoinIR→MIR conversion failed: {}", e.message))?; .map_err(|e| format!("JoinIR→MIR conversion failed: {}", e.message))?;
// Debug MIR module if trace enabled // Debug MIR module if trace enabled
@ -187,7 +190,11 @@ impl MirBuilder {
for (block_id, block) in &func.blocks { for (block_id, block) in &func.blocks {
trace::trace().blocks( trace::trace().blocks(
"router", "router",
&format!("Block {:?}: {} instructions", block_id, block.instructions.len()), &format!(
"Block {:?}: {} instructions",
block_id,
block.instructions.len()
),
); );
for (i, inst) in block.instructions.iter().enumerate() { for (i, inst) in block.instructions.iter().enumerate() {
trace::trace().instructions("router", &format!("[{}] {:?}", i, inst)); trace::trace().instructions("router", &format!("[{}] {:?}", i, inst));

View File

@ -41,8 +41,8 @@
//! NYASH_TRACE_VARMAP=1 NYASH_JOINIR_DEBUG=1 ./target/release/hakorune test.hako //! NYASH_TRACE_VARMAP=1 NYASH_JOINIR_DEBUG=1 ./target/release/hakorune test.hako
//! ``` //! ```
use std::collections::BTreeMap;
use crate::mir::ValueId; use crate::mir::ValueId;
use std::collections::BTreeMap;
/// Unified tracer for JoinIR loop operations. /// Unified tracer for JoinIR loop operations.
/// ///
@ -206,7 +206,10 @@ impl JoinLoopTrace {
/// - `msg`: Human-readable message about the routing decision /// - `msg`: Human-readable message about the routing decision
pub fn routing(&self, tag: &str, func_name: &str, msg: &str) { pub fn routing(&self, tag: &str, func_name: &str, msg: &str) {
if self.joinir_enabled || self.mainline_enabled { if self.joinir_enabled || self.mainline_enabled {
eprintln!("[trace:routing] {}: function '{}' - {}", tag, func_name, msg); eprintln!(
"[trace:routing] {}: function '{}' - {}",
tag, func_name, msg
);
} }
} }

View File

@ -26,15 +26,14 @@ pub(in crate::mir::builder) fn extract_loop_variable_from_condition(
condition: &ASTNode, condition: &ASTNode,
) -> Result<String, String> { ) -> Result<String, String> {
match condition { match condition {
ASTNode::BinaryOp { ASTNode::BinaryOp { operator, left, .. }
operator, left, .. if matches!(
} if matches!( operator,
operator, BinaryOperator::Less
BinaryOperator::Less | BinaryOperator::Greater
| BinaryOperator::Greater | BinaryOperator::LessEqual
| BinaryOperator::LessEqual | BinaryOperator::GreaterEqual
| BinaryOperator::GreaterEqual ) =>
) =>
{ {
// Binary comparison: extract variable from left side // Binary comparison: extract variable from left side
match &**left { match &**left {

View File

@ -117,7 +117,10 @@ impl super::MirBuilder {
} }
} }
// Phase 200-C: Store fn_body_ast for inline main() lowering // Phase 200-C: Store fn_body_ast for inline main() lowering
eprintln!("[build_static_main_box] Storing fn_body_ast with {} nodes for inline main()", body.len()); eprintln!(
"[build_static_main_box] Storing fn_body_ast with {} nodes for inline main()",
body.len()
);
self.fn_body_ast = Some(body.clone()); self.fn_body_ast = Some(body.clone());
// Lower statements in order to preserve def→use // Lower statements in order to preserve def→use

View File

@ -235,7 +235,8 @@ impl MirBuilder {
crate::mir::join_ir::lowering::should_panic_on_joinir_failure(func_name, false); crate::mir::join_ir::lowering::should_panic_on_joinir_failure(func_name, false);
// Core ON + 本線対象の場合は環境変数に関わらず試行 // Core ON + 本線対象の場合は環境変数に関わらず試行
let should_try_joinir = core_mainline || (joinir_enabled && is_target && (joinir_toplevel || joinir_dryrun)); let should_try_joinir =
core_mainline || (joinir_enabled && is_target && (joinir_toplevel || joinir_dryrun));
if should_try_joinir { if should_try_joinir {
if let Some(ref func) = self.current_function { if let Some(ref func) = self.current_function {

View File

@ -5,8 +5,8 @@
//! - JoinIR fragment → host MIR への ID変換 //! - JoinIR fragment → host MIR への ID変換
//! - 決定性を重視した実装 //! - 決定性を重視した実装
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, ValueId}; use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, ValueId};
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
/// JoinIR ID space を host MIR ID space に変換する /// JoinIR ID space を host MIR ID space に変換する
pub struct JoinIrIdRemapper { pub struct JoinIrIdRemapper {
@ -29,7 +29,9 @@ impl JoinIrIdRemapper {
/// Block ID mapping を取得 /// Block ID mapping を取得
pub fn get_block(&self, func_name: &str, old_id: BasicBlockId) -> Option<BasicBlockId> { pub fn get_block(&self, func_name: &str, old_id: BasicBlockId) -> Option<BasicBlockId> {
self.block_map.get(&(func_name.to_string(), old_id)).copied() self.block_map
.get(&(func_name.to_string(), old_id))
.copied()
} }
/// Value ID mapping を取得 /// Value ID mapping を取得
@ -70,7 +72,9 @@ impl JoinIrIdRemapper {
Compare { dst, lhs, rhs, .. } => vec![*dst, *lhs, *rhs], Compare { dst, lhs, rhs, .. } => vec![*dst, *lhs, *rhs],
Load { dst, ptr } => vec![*dst, *ptr], Load { dst, ptr } => vec![*dst, *ptr],
Store { value, ptr } => vec![*value, *ptr], Store { value, ptr } => vec![*value, *ptr],
Call { dst, func, args, .. } => { Call {
dst, func, args, ..
} => {
let mut vals = vec![*func]; let mut vals = vec![*func];
if let Some(d) = dst { if let Some(d) = dst {
vals.push(*d); vals.push(*d);
@ -78,7 +82,9 @@ impl JoinIrIdRemapper {
vals.extend(args.iter().copied()); vals.extend(args.iter().copied());
vals vals
} }
BoxCall { dst, box_val, args, .. } => { BoxCall {
dst, box_val, args, ..
} => {
let mut vals = vec![*box_val]; let mut vals = vec![*box_val];
if let Some(d) = dst { if let Some(d) = dst {
vals.push(*d); vals.push(*d);
@ -86,7 +92,9 @@ impl JoinIrIdRemapper {
vals.extend(args.iter().copied()); vals.extend(args.iter().copied());
vals vals
} }
PluginInvoke { dst, box_val, args, .. } => { PluginInvoke {
dst, box_val, args, ..
} => {
let mut vals = vec![*box_val]; let mut vals = vec![*box_val];
if let Some(d) = dst { if let Some(d) = dst {
vals.push(*d); vals.push(*d);
@ -107,7 +115,9 @@ impl JoinIrIdRemapper {
vals.extend(args.iter().copied()); vals.extend(args.iter().copied());
vals vals
} }
NewClosure { dst, captures, me, .. } => { NewClosure {
dst, captures, me, ..
} => {
let mut vals = vec![*dst]; let mut vals = vec![*dst];
vals.extend(captures.iter().map(|(_, v)| *v)); vals.extend(captures.iter().map(|(_, v)| *v));
if let Some(m) = me { if let Some(m) = me {
@ -119,10 +129,14 @@ impl JoinIrIdRemapper {
Debug { value, .. } => vec![*value], Debug { value, .. } => vec![*value],
DebugLog { values, .. } => values.clone(), DebugLog { values, .. } => values.clone(),
Throw { exception, .. } => vec![*exception], Throw { exception, .. } => vec![*exception],
Catch { exception_value, .. } => vec![*exception_value], Catch {
exception_value, ..
} => vec![*exception_value],
RefNew { dst, box_val } => vec![*dst, *box_val], RefNew { dst, box_val } => vec![*dst, *box_val],
RefGet { dst, reference, .. } => vec![*dst, *reference], RefGet { dst, reference, .. } => vec![*dst, *reference],
RefSet { reference, value, .. } => vec![*reference, *value], RefSet {
reference, value, ..
} => vec![*reference, *value],
WeakNew { dst, box_val } => vec![*dst, *box_val], WeakNew { dst, box_val } => vec![*dst, *box_val],
WeakLoad { dst, weak_ref } => vec![*dst, *weak_ref], WeakLoad { dst, weak_ref } => vec![*dst, *weak_ref],
WeakRef { dst, value, .. } => vec![*dst, *value], WeakRef { dst, value, .. } => vec![*dst, *value],
@ -136,7 +150,11 @@ impl JoinIrIdRemapper {
Cast { dst, value, .. } => vec![*dst, *value], Cast { dst, value, .. } => vec![*dst, *value],
TypeOp { dst, value, .. } => vec![*dst, *value], TypeOp { dst, value, .. } => vec![*dst, *value],
ArrayGet { dst, array, index } => vec![*dst, *array, *index], ArrayGet { dst, array, index } => vec![*dst, *array, *index],
ArraySet { array, index, value } => vec![*array, *index, *value], ArraySet {
array,
index,
value,
} => vec![*array, *index, *value],
Jump { .. } | Nop | Safepoint => vec![], Jump { .. } | Nop | Safepoint => vec![],
ExternCall { dst, args, .. } => { ExternCall { dst, args, .. } => {
let mut vals = Vec::new(); let mut vals = Vec::new();
@ -185,14 +203,27 @@ impl JoinIrIdRemapper {
value: remap(*value), value: remap(*value),
ptr: remap(*ptr), ptr: remap(*ptr),
}, },
Call { dst, func, callee, args, effects } => Call { Call {
dst,
func,
callee,
args,
effects,
} => Call {
dst: dst.map(remap), dst: dst.map(remap),
func: remap(*func), func: remap(*func),
callee: callee.clone(), callee: callee.clone(),
args: args.iter().map(|&a| remap(a)).collect(), args: args.iter().map(|&a| remap(a)).collect(),
effects: *effects, effects: *effects,
}, },
BoxCall { dst, box_val, method, method_id, args, effects } => BoxCall { BoxCall {
dst,
box_val,
method,
method_id,
args,
effects,
} => BoxCall {
dst: dst.map(remap), dst: dst.map(remap),
box_val: remap(*box_val), box_val: remap(*box_val),
method: method.clone(), method: method.clone(),
@ -200,7 +231,13 @@ impl JoinIrIdRemapper {
args: args.iter().map(|&a| remap(a)).collect(), args: args.iter().map(|&a| remap(a)).collect(),
effects: *effects, effects: *effects,
}, },
PluginInvoke { dst, box_val, method, args, effects } => PluginInvoke { PluginInvoke {
dst,
box_val,
method,
args,
effects,
} => PluginInvoke {
dst: dst.map(remap), dst: dst.map(remap),
box_val: remap(*box_val), box_val: remap(*box_val),
method: method.clone(), method: method.clone(),
@ -211,16 +248,29 @@ impl JoinIrIdRemapper {
dst: remap(*dst), dst: remap(*dst),
src: remap(*src), src: remap(*src),
}, },
NewBox { dst, box_type, args } => NewBox { NewBox {
dst,
box_type,
args,
} => NewBox {
dst: remap(*dst), dst: remap(*dst),
box_type: box_type.clone(), box_type: box_type.clone(),
args: args.iter().map(|&a| remap(a)).collect(), args: args.iter().map(|&a| remap(a)).collect(),
}, },
NewClosure { dst, params, body, captures, me } => NewClosure { NewClosure {
dst,
params,
body,
captures,
me,
} => NewClosure {
dst: remap(*dst), dst: remap(*dst),
params: params.clone(), params: params.clone(),
body: body.clone(), body: body.clone(),
captures: captures.iter().map(|(n, v)| (n.clone(), remap(*v))).collect(), captures: captures
.iter()
.map(|(n, v)| (n.clone(), remap(*v)))
.collect(),
me: me.map(remap), me: me.map(remap),
}, },
Print { value, effects } => Print { Print { value, effects } => Print {
@ -239,7 +289,11 @@ impl JoinIrIdRemapper {
exception: remap(*exception), exception: remap(*exception),
effects: *effects, effects: *effects,
}, },
Catch { exception_type, exception_value, handler_bb } => Catch { Catch {
exception_type,
exception_value,
handler_bb,
} => Catch {
exception_type: exception_type.clone(), exception_type: exception_type.clone(),
exception_value: remap(*exception_value), exception_value: remap(*exception_value),
handler_bb: *handler_bb, handler_bb: *handler_bb,
@ -248,12 +302,20 @@ impl JoinIrIdRemapper {
dst: remap(*dst), dst: remap(*dst),
box_val: remap(*box_val), box_val: remap(*box_val),
}, },
RefGet { dst, reference, field } => RefGet { RefGet {
dst,
reference,
field,
} => RefGet {
dst: remap(*dst), dst: remap(*dst),
reference: remap(*reference), reference: remap(*reference),
field: field.clone(), field: field.clone(),
}, },
RefSet { reference, field, value } => RefSet { RefSet {
reference,
field,
value,
} => RefSet {
reference: remap(*reference), reference: remap(*reference),
field: field.clone(), field: field.clone(),
value: remap(*value), value: remap(*value),
@ -271,12 +333,8 @@ impl JoinIrIdRemapper {
op: *op, op: *op,
value: remap(*value), value: remap(*value),
}, },
BarrierRead { ptr } => BarrierRead { BarrierRead { ptr } => BarrierRead { ptr: remap(*ptr) },
ptr: remap(*ptr), BarrierWrite { ptr } => BarrierWrite { ptr: remap(*ptr) },
},
BarrierWrite { ptr } => BarrierWrite {
ptr: remap(*ptr),
},
Barrier { op, ptr } => Barrier { Barrier { op, ptr } => Barrier {
op: *op, op: *op,
ptr: remap(*ptr), ptr: remap(*ptr),
@ -293,12 +351,20 @@ impl JoinIrIdRemapper {
dst: remap(*dst), dst: remap(*dst),
future: remap(*future), future: remap(*future),
}, },
TypeCheck { dst, value, expected_type } => TypeCheck { TypeCheck {
dst,
value,
expected_type,
} => TypeCheck {
dst: remap(*dst), dst: remap(*dst),
value: remap(*value), value: remap(*value),
expected_type: expected_type.clone(), expected_type: expected_type.clone(),
}, },
Cast { dst, value, target_type } => Cast { Cast {
dst,
value,
target_type,
} => Cast {
dst: remap(*dst), dst: remap(*dst),
value: remap(*value), value: remap(*value),
target_type: target_type.clone(), target_type: target_type.clone(),
@ -314,12 +380,22 @@ impl JoinIrIdRemapper {
array: remap(*array), array: remap(*array),
index: remap(*index), index: remap(*index),
}, },
ArraySet { array, index, value } => ArraySet { ArraySet {
array,
index,
value,
} => ArraySet {
array: remap(*array), array: remap(*array),
index: remap(*index), index: remap(*index),
value: remap(*value), value: remap(*value),
}, },
ExternCall { dst, iface_name, method_name, args, effects } => ExternCall { ExternCall {
dst,
iface_name,
method_name,
args,
effects,
} => ExternCall {
dst: dst.map(remap), dst: dst.map(remap),
iface_name: iface_name.clone(), iface_name: iface_name.clone(),
method_name: method_name.clone(), method_name: method_name.clone(),
@ -327,12 +403,13 @@ impl JoinIrIdRemapper {
effects: *effects, effects: *effects,
}, },
// Phase 189 FIX: Remap PHI dst and input values (BlockId remapping is done in control_flow.rs) // Phase 189 FIX: Remap PHI dst and input values (BlockId remapping is done in control_flow.rs)
Phi { dst, inputs, type_hint } => Phi { Phi {
dst,
inputs,
type_hint,
} => Phi {
dst: remap(*dst), dst: remap(*dst),
inputs: inputs inputs: inputs.iter().map(|(bb, val)| (*bb, remap(*val))).collect(),
.iter()
.map(|(bb, val)| (*bb, remap(*val)))
.collect(),
type_hint: type_hint.clone(), type_hint: type_hint.clone(),
}, },
// Pass through unchanged (Branch/Jump/Return handled separately) // Pass through unchanged (Branch/Jump/Return handled separately)

View File

@ -5,9 +5,9 @@
//! - Entry block への Copy instruction 挿入 //! - Entry block への Copy instruction 挿入
//! - SSA 値空間の接続 //! - SSA 値空間の接続
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, ValueId};
use std::collections::BTreeMap; // Phase 222.5-E: HashMap → BTreeMap for determinism
pub struct BoundaryInjector; pub struct BoundaryInjector;
@ -56,10 +56,11 @@ impl BoundaryInjector {
func: &mut MirFunction, func: &mut MirFunction,
entry_block_id: BasicBlockId, entry_block_id: BasicBlockId,
boundary: &JoinInlineBoundary, boundary: &JoinInlineBoundary,
value_map: &BTreeMap<ValueId, ValueId>, // Phase 222.5-E: HashMap → BTreeMap for determinism value_map: &BTreeMap<ValueId, ValueId>, // Phase 222.5-E: HashMap → BTreeMap for determinism
phi_dst_ids: &std::collections::HashSet<ValueId>, phi_dst_ids: &std::collections::HashSet<ValueId>,
debug: bool, debug: bool,
) -> Result<BTreeMap<ValueId, ValueId>, String> { // Phase 222.5-E: HashMap → BTreeMap for determinism ) -> Result<BTreeMap<ValueId, ValueId>, String> {
// Phase 222.5-E: HashMap → BTreeMap for determinism
// Phase 33-20: When loop_var_name is set, ALL join_inputs are handled by header PHIs // Phase 33-20: When loop_var_name is set, ALL join_inputs are handled by header PHIs
// This includes the loop variable AND all other carriers from exit_bindings. // This includes the loop variable AND all other carriers from exit_bindings.
// We skip ALL join_inputs Copy instructions, only condition_bindings remain. // We skip ALL join_inputs Copy instructions, only condition_bindings remain.
@ -67,7 +68,7 @@ impl BoundaryInjector {
// Phase 171-fix: Check both join_inputs and condition_bindings // Phase 171-fix: Check both join_inputs and condition_bindings
let effective_join_inputs = if skip_all_join_inputs { let effective_join_inputs = if skip_all_join_inputs {
0 // Phase 33-20: All join_inputs are handled by header PHIs 0 // Phase 33-20: All join_inputs are handled by header PHIs
} else { } else {
boundary.join_inputs.len() boundary.join_inputs.len()
}; };
@ -104,7 +105,10 @@ impl BoundaryInjector {
let mut reallocations = BTreeMap::new(); let mut reallocations = BTreeMap::new();
for binding in &boundary.condition_bindings { for binding in &boundary.condition_bindings {
let remapped_join = value_map.get(&binding.join_value).copied().unwrap_or(binding.join_value); let remapped_join = value_map
.get(&binding.join_value)
.copied()
.unwrap_or(binding.join_value);
if phi_dst_ids.contains(&remapped_join) { if phi_dst_ids.contains(&remapped_join) {
// Collision detected! Allocate a fresh ValueId // Collision detected! Allocate a fresh ValueId
@ -132,29 +136,27 @@ impl BoundaryInjector {
// Phase 171: Inject Copy instructions for join_inputs (loop parameters) // Phase 171: Inject Copy instructions for join_inputs (loop parameters)
// Phase 33-20: Skip ALL join_inputs when loop_var_name is set (header PHIs handle them) // Phase 33-20: Skip ALL join_inputs when loop_var_name is set (header PHIs handle them)
if !skip_all_join_inputs { if !skip_all_join_inputs {
for (join_input, host_input) in boundary for (join_input, host_input) in
.join_inputs boundary.join_inputs.iter().zip(boundary.host_inputs.iter())
.iter()
.zip(boundary.host_inputs.iter())
{ {
// リマップ後の ValueId を取得 // リマップ後の ValueId を取得
let remapped_join = value_map.get(join_input).copied().unwrap_or(*join_input); let remapped_join = value_map.get(join_input).copied().unwrap_or(*join_input);
let remapped_host = *host_input; // host_input is already in host space let remapped_host = *host_input; // host_input is already in host space
// Copy instruction: remapped_join = Copy remapped_host // Copy instruction: remapped_join = Copy remapped_host
let copy_inst = MirInstruction::Copy { let copy_inst = MirInstruction::Copy {
dst: remapped_join, dst: remapped_join,
src: remapped_host, src: remapped_host,
}; };
copy_instructions.push(copy_inst); copy_instructions.push(copy_inst);
if debug { if debug {
eprintln!( eprintln!(
"[BoundaryInjector] Join input: Copy {:?} = Copy {:?}", "[BoundaryInjector] Join input: Copy {:?} = Copy {:?}",
remapped_join, remapped_host remapped_join, remapped_host
); );
} }
} }
} }
@ -178,10 +180,16 @@ impl BoundaryInjector {
// Phase 177-3 Option B: Use pre-allocated reallocations for PHI collision cases // Phase 177-3 Option B: Use pre-allocated reallocations for PHI collision cases
for binding in &boundary.condition_bindings { for binding in &boundary.condition_bindings {
// Look up the remapped JoinIR ValueId from value_map // Look up the remapped JoinIR ValueId from value_map
let remapped_join = value_map.get(&binding.join_value).copied().unwrap_or(binding.join_value); let remapped_join = value_map
.get(&binding.join_value)
.copied()
.unwrap_or(binding.join_value);
// Phase 177-3 Option B: Check if this binding was reallocated (PHI collision case) // Phase 177-3 Option B: Check if this binding was reallocated (PHI collision case)
let final_dst = reallocations.get(&binding.join_value).copied().unwrap_or(remapped_join); let final_dst = reallocations
.get(&binding.join_value)
.copied()
.unwrap_or(remapped_join);
// Copy instruction: final_dst = Copy host_value // Copy instruction: final_dst = Copy host_value
let copy_inst = MirInstruction::Copy { let copy_inst = MirInstruction::Copy {
@ -203,7 +211,9 @@ impl BoundaryInjector {
// Entry block の先頭に Copy instructions を挿入 // Entry block の先頭に Copy instructions を挿入
// Reverse order to preserve original order when inserting at position 0 // Reverse order to preserve original order when inserting at position 0
// Phase 189 FIX: Also insert corresponding spans // Phase 189 FIX: Also insert corresponding spans
let default_span = entry_block.instruction_spans.first() let default_span = entry_block
.instruction_spans
.first()
.copied() .copied()
.unwrap_or_else(crate::ast::Span::unknown); .unwrap_or_else(crate::ast::Span::unknown);
for inst in copy_instructions.into_iter().rev() { for inst in copy_instructions.into_iter().rev() {

View File

@ -341,9 +341,11 @@ impl super::MirBuilder {
// P3-D は「既知メソッドの戻り値型」を直接推論する。 // P3-D は「既知メソッドの戻り値型」を直接推論する。
// BoxCall の method 名から TypeAnnotationBox と同じマッピングで型を取得。 // BoxCall の method 名から TypeAnnotationBox と同じマッピングで型を取得。
if hint.is_none() { if hint.is_none() {
if let Some(mt) = if let Some(mt) = MethodReturnHintBox::resolve_for_return(
MethodReturnHintBox::resolve_for_return(&function, *v, &self.value_types) &function,
{ *v,
&self.value_types,
) {
if std::env::var("NYASH_P3D_DEBUG").is_ok() { if std::env::var("NYASH_P3D_DEBUG").is_ok() {
eprintln!( eprintln!(
"[lifecycle/p3d] {} type inferred via MethodReturnHintBox: {:?}", "[lifecycle/p3d] {} type inferred via MethodReturnHintBox: {:?}",

View File

@ -158,9 +158,9 @@ impl LoopFrontendBinding {
Self { Self {
counter_var: "i".to_string(), counter_var: "i".to_string(),
counter_init: 0, counter_init: 0,
accumulator_var: None, // No accumulator accumulator_var: None, // No accumulator
bound_expr: BoundExpr::Constant(3), // Constant bound bound_expr: BoundExpr::Constant(3), // Constant bound
external_refs: vec![], // No external refs external_refs: vec![], // No external refs
pattern: LoopPattern::Simple { pattern: LoopPattern::Simple {
has_accumulator: false, has_accumulator: false,
}, },

View File

@ -16,18 +16,20 @@ fn enabled() -> bool {
/// Trace when a newbox/class origin is registered. /// Trace when a newbox/class origin is registered.
pub fn origin(event: &str, vid: ValueId, class: &str) { pub fn origin(event: &str, vid: ValueId, class: &str) {
if enabled() { if enabled() {
get_global_ring0() get_global_ring0().log.debug(&format!(
.log "[type-trace] origin:{} %{}{}",
.debug(&format!("[type-trace] origin:{} %{}{}", event, vid.0, class)); event, vid.0, class
));
} }
} }
/// Trace when a concrete MirType is recorded. /// Trace when a concrete MirType is recorded.
pub fn ty(event: &str, vid: ValueId, ty: &MirType) { pub fn ty(event: &str, vid: ValueId, ty: &MirType) {
if enabled() { if enabled() {
get_global_ring0() get_global_ring0().log.debug(&format!(
.log "[type-trace] type:{} %{}{:?}",
.debug(&format!("[type-trace] type:{} %{}{:?}", event, vid.0, ty)); event, vid.0, ty
));
} }
} }

View File

@ -81,7 +81,7 @@ impl TypeRegistry {
source: format!("param:{}:{:?}", param_name, ty), source: format!("param:{}:{:?}", param_name, ty),
timestamp: self.trace_log.len(), timestamp: self.trace_log.len(),
}; };
self.trace_log.push(entry.clone()); self.trace_log.push(entry.clone());
get_global_ring0() get_global_ring0()
.log .log
.debug(&format!("[type-registry] {} {:?}", entry.source, vid)); .debug(&format!("[type-registry] {} {:?}", entry.source, vid));
@ -217,9 +217,10 @@ impl TypeRegistry {
// 最終フォールバック: UnknownBox // 最終フォールバック: UnknownBox
if self.trace_enabled { if self.trace_enabled {
get_global_ring0() get_global_ring0().log.warn(&format!(
.log "[type-registry] WARNING: UnknownBox for %{}",
.warn(&format!("[type-registry] WARNING: UnknownBox for %{}", vid.0)); vid.0
));
} }
"UnknownBox".to_string() "UnknownBox".to_string()
} }

View File

@ -72,33 +72,23 @@ impl HintSink {
match cfg.sink { match cfg.sink {
HintSinkTarget::None => {} HintSinkTarget::None => {}
HintSinkTarget::Stderr => match hint { HintSinkTarget::Stderr => match hint {
HintKind::ScopeEnter(id) => { HintKind::ScopeEnter(id) => get_global_ring0()
get_global_ring0() .log
.log .debug(&format!("[mir][hint] ScopeEnter({})", id)),
.debug(&format!("[mir][hint] ScopeEnter({})", id)) HintKind::ScopeLeave(id) => get_global_ring0()
} .log
HintKind::ScopeLeave(id) => { .debug(&format!("[mir][hint] ScopeLeave({})", id)),
get_global_ring0()
.log
.debug(&format!("[mir][hint] ScopeLeave({})", id))
}
HintKind::Defer(calls) => get_global_ring0() HintKind::Defer(calls) => get_global_ring0()
.log .log
.debug(&format!("[mir][hint] Defer({})", calls.join(";"))), .debug(&format!("[mir][hint] Defer({})", calls.join(";"))),
HintKind::JoinResult(var) => get_global_ring0() HintKind::JoinResult(var) => get_global_ring0()
.log .log
.debug(&format!("[mir][hint] JoinResult({})", var)), .debug(&format!("[mir][hint] JoinResult({})", var)),
HintKind::LoopCarrier(vars) => { HintKind::LoopCarrier(vars) => get_global_ring0()
get_global_ring0() .log
.log .debug(&format!("[mir][hint] LoopCarrier({})", vars.join(","))),
.debug(&format!("[mir][hint] LoopCarrier({})", vars.join(","))) HintKind::LoopHeader => get_global_ring0().log.debug("[mir][hint] LoopHeader"),
} HintKind::LoopLatch => get_global_ring0().log.debug("[mir][hint] LoopLatch"),
HintKind::LoopHeader => {
get_global_ring0().log.debug("[mir][hint] LoopHeader")
}
HintKind::LoopLatch => {
get_global_ring0().log.debug("[mir][hint] LoopLatch")
}
HintKind::NoEmptyPhi => get_global_ring0().log.debug("[mir][hint] NoEmptyPhi"), HintKind::NoEmptyPhi => get_global_ring0().log.debug("[mir][hint] NoEmptyPhi"),
}, },
HintSinkTarget::Jsonl(ref path) => { HintSinkTarget::Jsonl(ref path) => {

View File

@ -150,12 +150,17 @@ impl MirInstruction {
// Handle Call instructions here (not in inst_meta) because CallLikeInst // Handle Call instructions here (not in inst_meta) because CallLikeInst
// doesn't have the callee field needed for Callee::Method receiver handling. // doesn't have the callee field needed for Callee::Method receiver handling.
// This is the single source of truth for Call's used values. // This is the single source of truth for Call's used values.
if let MirInstruction::Call { callee, func, args, .. } = self { if let MirInstruction::Call {
callee, func, args, ..
} = self
{
use crate::mir::definitions::call_unified::Callee; use crate::mir::definitions::call_unified::Callee;
let mut used: Vec<ValueId> = Vec::new(); let mut used: Vec<ValueId> = Vec::new();
match callee { match callee {
// Unified path: Callee::Method with receiver // Unified path: Callee::Method with receiver
Some(Callee::Method { receiver: Some(r), .. }) => { Some(Callee::Method {
receiver: Some(r), ..
}) => {
used.push(*r); used.push(*r);
} }
// Legacy path: func ValueId is the callable // Legacy path: func ValueId is the callable

View File

@ -20,6 +20,7 @@
//! - 単一関数: cond 評価 → Select(cond, then_val, else_val) → Ret //! - 単一関数: cond 評価 → Select(cond, then_val, else_val) → Ret
use super::{AstToJoinIrLowerer, BTreeMap, ExtractCtx, JoinFunction, JoinInst, JoinModule}; use super::{AstToJoinIrLowerer, BTreeMap, ExtractCtx, JoinFunction, JoinInst, JoinModule};
use crate::mir::join_ir::JoinIrPhase;
impl AstToJoinIrLowerer { impl AstToJoinIrLowerer {
/// If Return pattern の共通 lowering /// If Return pattern の共通 lowering
@ -152,6 +153,7 @@ impl AstToJoinIrLowerer {
JoinModule { JoinModule {
functions, functions,
entry: Some(func_id), entry: Some(func_id),
phase: JoinIrPhase::Structured,
} }
} }
} }

View File

@ -13,6 +13,7 @@
//! - `create_k_exit_function()`: k_exit 関数生成 //! - `create_k_exit_function()`: k_exit 関数生成
use super::{AstToJoinIrLowerer, JoinModule}; use super::{AstToJoinIrLowerer, JoinModule};
use crate::mir::join_ir::JoinIrPhase;
use crate::mir::join_ir::{JoinFuncId, JoinFunction, JoinInst}; use crate::mir::join_ir::{JoinFuncId, JoinFunction, JoinInst};
use crate::mir::ValueId; use crate::mir::ValueId;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -403,5 +404,6 @@ pub fn build_join_module(
JoinModule { JoinModule {
functions, functions,
entry: Some(entry_id), entry: Some(entry_id),
phase: JoinIrPhase::Structured,
} }
} }

View File

@ -1,5 +1,6 @@
use super::BTreeMap; use super::BTreeMap;
use super::{AstToJoinIrLowerer, ConstValue, ExtractCtx, JoinFunction, JoinInst, JoinModule}; use super::{AstToJoinIrLowerer, ConstValue, ExtractCtx, JoinFunction, JoinInst, JoinModule};
use crate::mir::join_ir::JoinIrPhase;
impl AstToJoinIrLowerer { impl AstToJoinIrLowerer {
/// Phase 34-8: Break/Continue 付きループの loweringパターン検出 /// Phase 34-8: Break/Continue 付きループの loweringパターン検出
@ -435,6 +436,7 @@ impl AstToJoinIrLowerer {
JoinModule { JoinModule {
functions, functions,
entry: Some(entry_id), entry: Some(entry_id),
phase: JoinIrPhase::Structured,
} }
} }
@ -642,6 +644,7 @@ impl AstToJoinIrLowerer {
JoinModule { JoinModule {
functions, functions,
entry: Some(entry_id), entry: Some(entry_id),
phase: JoinIrPhase::Structured,
} }
} }
@ -889,6 +892,7 @@ impl AstToJoinIrLowerer {
JoinModule { JoinModule {
functions, functions,
entry: Some(entry_id), entry: Some(entry_id),
phase: JoinIrPhase::Structured,
} }
} }
} }

View File

@ -43,6 +43,56 @@ mod tests;
pub(crate) use context::ExtractCtx; pub(crate) use context::ExtractCtx;
pub(crate) use stmt_handlers::StatementEffect; pub(crate) use stmt_handlers::StatementEffect;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum FunctionRoute {
IfReturn,
LoopFrontend,
NestedIf,
ReadQuoted,
}
fn resolve_function_route(func_name: &str) -> Result<FunctionRoute, String> {
const IF_RETURN_NAMES: &[&str] = &["test", "local", "_read_value_from_pair"];
const LOOP_NAMES: &[&str] = &["simple", "filter", "print_tokens", "map", "reduce", "fold", "jsonparser_skip_ws_mini"];
if IF_RETURN_NAMES.contains(&func_name) {
return Ok(FunctionRoute::IfReturn);
}
if LOOP_NAMES.contains(&func_name) {
return Ok(FunctionRoute::LoopFrontend);
}
if func_name == "parse_loop" {
if crate::config::env::joinir_dev_enabled()
&& std::env::var("HAKO_JOINIR_NESTED_IF").ok().as_deref() == Some("1")
{
return Ok(FunctionRoute::NestedIf);
}
return Err(
"[joinir/frontend] 'parse_loop' requires HAKO_JOINIR_NESTED_IF=1 (dev only)"
.to_string(),
);
}
if func_name == "read_quoted_from" {
if crate::config::env::joinir_dev_enabled()
&& std::env::var("HAKO_JOINIR_READ_QUOTED").ok().as_deref() == Some("1")
{
return Ok(FunctionRoute::ReadQuoted);
}
return Err(
"[joinir/frontend] 'read_quoted_from' requires HAKO_JOINIR_READ_QUOTED=1 (dev only)"
.to_string(),
);
}
Err(format!(
"[joinir/frontend] unsupported function '{}' (dev fixture not registered)",
func_name
))
}
/// AST/CFG → JoinIR 変換器 /// AST/CFG → JoinIR 変換器
/// ///
/// Phase 34-2: Program(JSON v0) から tiny IfSelect ケースを JoinIR に変換 /// Phase 34-2: Program(JSON v0) から tiny IfSelect ケースを JoinIR に変換
@ -85,51 +135,17 @@ impl AstToJoinIrLowerer {
.as_str() .as_str()
.expect("Function must have 'name'"); .expect("Function must have 'name'");
// 3. 関数名で分岐Phase P3: LoopFrontendBinding 層導入) let route = resolve_function_route(func_name)
// .unwrap_or_else(|msg| panic!("{msg}"));
// パターン分類:
// - If Return pattern: test/local/_read_value_from_pair match route {
// - Loop pattern: simple 等 → LoopFrontendBinding 経由 FunctionRoute::IfReturn => self.lower_if_return_pattern(program_json),
// - NestedIfMerge pattern: parse_loop (dev flag gated) FunctionRoute::LoopFrontend => loop_frontend_binding::lower_loop_by_function_name(
// - ReadQuoted pattern: read_quoted_from (dev flag gated) self,
match func_name { program_json,
"test" | "local" | "_read_value_from_pair" => { ),
self.lower_if_return_pattern(program_json) FunctionRoute::NestedIf => self.lower_nested_if_pattern(program_json),
} FunctionRoute::ReadQuoted => self.lower_read_quoted_pattern(program_json),
"simple" | "filter" | "print_tokens" | "map" | "reduce" | "fold" => {
// Phase P3: LoopFrontendBinding 層経由でディスパッチ
loop_frontend_binding::lower_loop_by_function_name(self, program_json)
}
"parse_loop" => {
// Phase 41-4: NestedIfMerge pattern (dev flag gated)
// Guard: JoinIR dev + HAKO_JOINIR_NESTED_IF=1 を要求
if crate::config::env::joinir_dev_enabled()
&& std::env::var("HAKO_JOINIR_NESTED_IF").ok().as_deref() == Some("1")
{
self.lower_nested_if_pattern(program_json)
} else {
// Dev flag が OFF の場合は panic旧ルートにフォールバックするため
panic!(
"parse_loop NestedIfMerge requires HAKO_JOINIR_NESTED_IF=1. \
Set env var to enable Phase 41-4 route."
);
}
}
"read_quoted_from" => {
// Phase 45: read_quoted_from pattern (dev flag gated)
// Guard if + Loop with break + accumulator
if crate::config::env::joinir_dev_enabled()
&& std::env::var("HAKO_JOINIR_READ_QUOTED").ok().as_deref() == Some("1")
{
self.lower_read_quoted_pattern(program_json)
} else {
panic!(
"read_quoted_from JoinIR requires HAKO_JOINIR_READ_QUOTED=1. \
Set env var to enable Phase 45 route."
);
}
}
_ => panic!("Unsupported function: {}", func_name),
} }
} }

View File

@ -21,6 +21,7 @@
use super::BTreeMap; use super::BTreeMap;
use super::{AstToJoinIrLowerer, ExtractCtx, JoinFunction, JoinInst, JoinModule}; use super::{AstToJoinIrLowerer, ExtractCtx, JoinFunction, JoinInst, JoinModule};
use crate::mir::join_ir::JoinIrPhase;
impl AstToJoinIrLowerer { impl AstToJoinIrLowerer {
/// Phase 41-4.2: ネスト if パターンの lowering /// Phase 41-4.2: ネスト if パターンの lowering
@ -193,6 +194,7 @@ impl AstToJoinIrLowerer {
JoinModule { JoinModule {
functions, functions,
entry: Some(func_id), entry: Some(func_id),
phase: JoinIrPhase::Structured,
} }
} }

View File

@ -29,6 +29,7 @@ use super::BTreeMap;
use super::{ use super::{
AstToJoinIrLowerer, ConstValue, ExtractCtx, JoinFunction, JoinInst, JoinModule, MergePair, AstToJoinIrLowerer, ConstValue, ExtractCtx, JoinFunction, JoinInst, JoinModule, MergePair,
}; };
use crate::mir::join_ir::JoinIrPhase;
impl AstToJoinIrLowerer { impl AstToJoinIrLowerer {
/// Phase 45: read_quoted_from パターンの lowering /// Phase 45: read_quoted_from パターンの lowering
@ -494,6 +495,7 @@ impl AstToJoinIrLowerer {
JoinModule { JoinModule {
functions, functions,
entry: Some(entry_id), entry: Some(entry_id),
phase: JoinIrPhase::Structured,
} }
} }
} }

View File

@ -110,13 +110,12 @@ impl<'a> BoolExprLowerer<'a> {
}; };
// Emit Compare instruction // Emit Compare instruction
self.builder self.builder.emit_instruction(MirInstruction::Compare {
.emit_instruction(MirInstruction::Compare { dst,
dst, op: cmp_op,
op: cmp_op, lhs,
lhs, rhs,
rhs, })?;
})?;
// Mark result type as Bool // Mark result type as Bool
self.builder.value_types.insert(dst, MirType::Bool); self.builder.value_types.insert(dst, MirType::Bool);
@ -130,13 +129,12 @@ impl<'a> BoolExprLowerer<'a> {
let dst = self.builder.next_value_id(); let dst = self.builder.next_value_id();
// Emit BinOp And instruction // Emit BinOp And instruction
self.builder self.builder.emit_instruction(MirInstruction::BinOp {
.emit_instruction(MirInstruction::BinOp { dst,
dst, op: BinaryOp::BitAnd, // Use BitAnd for logical AND
op: BinaryOp::BitAnd, // Use BitAnd for logical AND lhs,
lhs, rhs,
rhs, })?;
})?;
// Mark result type as Bool // Mark result type as Bool
self.builder.value_types.insert(dst, MirType::Bool); self.builder.value_types.insert(dst, MirType::Bool);
@ -150,13 +148,12 @@ impl<'a> BoolExprLowerer<'a> {
let dst = self.builder.next_value_id(); let dst = self.builder.next_value_id();
// Emit BinOp Or instruction // Emit BinOp Or instruction
self.builder self.builder.emit_instruction(MirInstruction::BinOp {
.emit_instruction(MirInstruction::BinOp { dst,
dst, op: BinaryOp::BitOr, // Use BitOr for logical OR
op: BinaryOp::BitOr, // Use BitOr for logical OR lhs,
lhs, rhs,
rhs, })?;
})?;
// Mark result type as Bool // Mark result type as Bool
self.builder.value_types.insert(dst, MirType::Bool); self.builder.value_types.insert(dst, MirType::Bool);
@ -179,12 +176,11 @@ impl<'a> BoolExprLowerer<'a> {
let dst = self.builder.next_value_id(); let dst = self.builder.next_value_id();
// Emit UnaryOp Not instruction // Emit UnaryOp Not instruction
self.builder self.builder.emit_instruction(MirInstruction::UnaryOp {
.emit_instruction(MirInstruction::UnaryOp { dst,
dst, op: crate::mir::UnaryOp::Not,
op: crate::mir::UnaryOp::Not, operand: operand_val,
operand: operand_val, })?;
})?;
// Mark result type as Bool // Mark result type as Bool
self.builder.value_types.insert(dst, MirType::Bool); self.builder.value_types.insert(dst, MirType::Bool);
@ -217,7 +213,7 @@ impl<'a> BoolExprLowerer<'a> {
// use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span}; // use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
// use crate::mir::builder::MirBuilder; // use crate::mir::builder::MirBuilder;
// use crate::mir::FunctionSignature; // use crate::mir::FunctionSignature;
// //
// /// Helper to create a test MirBuilder // /// Helper to create a test MirBuilder
// fn create_test_builder() -> MirBuilder { // fn create_test_builder() -> MirBuilder {
// let mut builder = MirBuilder::new(); // let mut builder = MirBuilder::new();
@ -232,13 +228,13 @@ impl<'a> BoolExprLowerer<'a> {
// builder.start_new_block(); // builder.start_new_block();
// builder // builder
// } // }
// //
// /// Test: Simple comparison (i < 10) // /// Test: Simple comparison (i < 10)
// #[test] // #[test]
// fn test_simple_comparison() { // fn test_simple_comparison() {
// let mut builder = create_test_builder(); // let mut builder = create_test_builder();
// let mut lowerer = BoolExprLowerer::new(&mut builder); // let mut lowerer = BoolExprLowerer::new(&mut builder);
// //
// // AST: i < 10 // // AST: i < 10
// let ast = ASTNode::BinaryOp { // let ast = ASTNode::BinaryOp {
// operator: BinaryOperator::Less, // operator: BinaryOperator::Less,
@ -252,17 +248,17 @@ impl<'a> BoolExprLowerer<'a> {
// }), // }),
// span: Span::unknown(), // span: Span::unknown(),
// }; // };
// //
// let result = lowerer.lower_condition(&ast); // let result = lowerer.lower_condition(&ast);
// assert!(result.is_ok(), "Simple comparison should succeed"); // assert!(result.is_ok(), "Simple comparison should succeed");
// } // }
// //
// /// Test: OR chain (ch == " " || ch == "\t") // /// Test: OR chain (ch == " " || ch == "\t")
// #[test] // #[test]
// fn test_or_chain() { // fn test_or_chain() {
// let mut builder = create_test_builder(); // let mut builder = create_test_builder();
// let mut lowerer = BoolExprLowerer::new(&mut builder); // let mut lowerer = BoolExprLowerer::new(&mut builder);
// //
// // AST: ch == " " || ch == "\t" // // AST: ch == " " || ch == "\t"
// let ast = ASTNode::BinaryOp { // let ast = ASTNode::BinaryOp {
// operator: BinaryOperator::Or, // operator: BinaryOperator::Or,
@ -292,17 +288,17 @@ impl<'a> BoolExprLowerer<'a> {
// }), // }),
// span: Span::unknown(), // span: Span::unknown(),
// }; // };
// //
// let result = lowerer.lower_condition(&ast); // let result = lowerer.lower_condition(&ast);
// assert!(result.is_ok(), "OR chain should succeed"); // assert!(result.is_ok(), "OR chain should succeed");
// } // }
// //
// /// Test: Complex mixed condition (i < len && (c == " " || c == "\t")) // /// Test: Complex mixed condition (i < len && (c == " " || c == "\t"))
// #[test] // #[test]
// fn test_complex_mixed_condition() { // fn test_complex_mixed_condition() {
// let mut builder = create_test_builder(); // let mut builder = create_test_builder();
// let mut lowerer = BoolExprLowerer::new(&mut builder); // let mut lowerer = BoolExprLowerer::new(&mut builder);
// //
// // AST: i < len && (c == " " || c == "\t") // // AST: i < len && (c == " " || c == "\t")
// let ast = ASTNode::BinaryOp { // let ast = ASTNode::BinaryOp {
// operator: BinaryOperator::And, // operator: BinaryOperator::And,
@ -348,17 +344,17 @@ impl<'a> BoolExprLowerer<'a> {
// }), // }),
// span: Span::unknown(), // span: Span::unknown(),
// }; // };
// //
// let result = lowerer.lower_condition(&ast); // let result = lowerer.lower_condition(&ast);
// assert!(result.is_ok(), "Complex mixed condition should succeed"); // assert!(result.is_ok(), "Complex mixed condition should succeed");
// } // }
// //
// /// Test: NOT operator (!condition) // /// Test: NOT operator (!condition)
// #[test] // #[test]
// fn test_not_operator() { // fn test_not_operator() {
// let mut builder = create_test_builder(); // let mut builder = create_test_builder();
// let mut lowerer = BoolExprLowerer::new(&mut builder); // let mut lowerer = BoolExprLowerer::new(&mut builder);
// //
// // AST: !(i < 10) // // AST: !(i < 10)
// let ast = ASTNode::UnaryOp { // let ast = ASTNode::UnaryOp {
// operator: crate::ast::UnaryOperator::Not, // operator: crate::ast::UnaryOperator::Not,

View File

@ -210,15 +210,12 @@ impl CarrierInfo {
variable_map: &BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism variable_map: &BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
) -> Result<Self, String> { ) -> Result<Self, String> {
// Find loop variable // Find loop variable
let loop_var_id = variable_map let loop_var_id = variable_map.get(&loop_var_name).copied().ok_or_else(|| {
.get(&loop_var_name) format!(
.copied() "Loop variable '{}' not found in variable_map",
.ok_or_else(|| { loop_var_name
format!( )
"Loop variable '{}' not found in variable_map", })?;
loop_var_name
)
})?;
// Collect all non-loop-var variables as carriers // Collect all non-loop-var variables as carriers
let mut carriers: Vec<CarrierVar> = variable_map let mut carriers: Vec<CarrierVar> = variable_map
@ -280,9 +277,10 @@ impl CarrierInfo {
let mut carriers = Vec::new(); let mut carriers = Vec::new();
for name in carrier_names { for name in carrier_names {
let host_id = variable_map.get(&name).copied().ok_or_else(|| { let host_id = variable_map
format!("Carrier variable '{}' not found in variable_map", name) .get(&name)
})?; .copied()
.ok_or_else(|| format!("Carrier variable '{}' not found in variable_map", name))?;
carriers.push(CarrierVar { carriers.push(CarrierVar {
name, name,
@ -408,7 +406,9 @@ impl CarrierInfo {
/// eprintln!("Whitespace chars: {:?}", helper.whitespace_chars); /// eprintln!("Whitespace chars: {:?}", helper.whitespace_chars);
/// } /// }
/// ``` /// ```
pub fn trim_helper(&self) -> Option<&crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper> { pub fn trim_helper(
&self,
) -> Option<&crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper> {
self.trim_helper.as_ref() self.trim_helper.as_ref()
} }
@ -430,7 +430,10 @@ impl CarrierInfo {
/// * `Some(ValueId)` - 対応する carrier の join_id が見つかった場合 /// * `Some(ValueId)` - 対応する carrier の join_id が見つかった場合
/// * `None` - promoted_loopbodylocals に含まれない、または join_id 未設定の場合 /// * `None` - promoted_loopbodylocals に含まれない、または join_id 未設定の場合
pub fn resolve_promoted_join_id(&self, original_name: &str) -> Option<ValueId> { pub fn resolve_promoted_join_id(&self, original_name: &str) -> Option<ValueId> {
if !self.promoted_loopbodylocals.contains(&original_name.to_string()) { if !self
.promoted_loopbodylocals
.contains(&original_name.to_string())
{
return None; return None;
} }
@ -646,29 +649,21 @@ mod tests {
CarrierVar { CarrierVar {
name: name.to_string(), name: name.to_string(),
host_id: ValueId(id), host_id: ValueId(id),
join_id: None, // Phase 177-STRUCT-1 join_id: None, // Phase 177-STRUCT-1
role: CarrierRole::LoopState, // Phase 227: Default to LoopState role: CarrierRole::LoopState, // Phase 227: Default to LoopState
init: CarrierInit::FromHost, // Phase 228: Default to FromHost init: CarrierInit::FromHost, // Phase 228: Default to FromHost
} }
} }
// Helper: Create a CarrierInfo for testing // Helper: Create a CarrierInfo for testing
fn test_carrier_info(loop_var: &str, loop_id: u32, carriers: Vec<CarrierVar>) -> CarrierInfo { fn test_carrier_info(loop_var: &str, loop_id: u32, carriers: Vec<CarrierVar>) -> CarrierInfo {
CarrierInfo::with_carriers( CarrierInfo::with_carriers(loop_var.to_string(), ValueId(loop_id), carriers)
loop_var.to_string(),
ValueId(loop_id),
carriers,
)
} }
#[test] #[test]
fn test_merge_from_empty() { fn test_merge_from_empty() {
// Merge empty CarrierInfo should not change anything // Merge empty CarrierInfo should not change anything
let mut carrier_info = test_carrier_info( let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
"i",
5,
vec![test_carrier("sum", 10)],
);
let other = test_carrier_info("j", 20, vec![]); let other = test_carrier_info("j", 20, vec![]);
@ -681,17 +676,9 @@ mod tests {
#[test] #[test]
fn test_merge_from_new_carrier() { fn test_merge_from_new_carrier() {
// Merge a new carrier that doesn't exist yet // Merge a new carrier that doesn't exist yet
let mut carrier_info = test_carrier_info( let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
"i",
5,
vec![test_carrier("sum", 10)],
);
let other = test_carrier_info( let other = test_carrier_info("j", 20, vec![test_carrier("count", 15)]);
"j",
20,
vec![test_carrier("count", 15)],
);
carrier_info.merge_from(&other); carrier_info.merge_from(&other);
@ -704,11 +691,7 @@ mod tests {
#[test] #[test]
fn test_merge_from_duplicate_carrier() { fn test_merge_from_duplicate_carrier() {
// Merge a carrier with the same name should NOT duplicate // Merge a carrier with the same name should NOT duplicate
let mut carrier_info = test_carrier_info( let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
"i",
5,
vec![test_carrier("sum", 10)],
);
let other = test_carrier_info( let other = test_carrier_info(
"j", "j",
@ -728,19 +711,12 @@ mod tests {
#[test] #[test]
fn test_merge_from_multiple_carriers() { fn test_merge_from_multiple_carriers() {
// Merge multiple carriers // Merge multiple carriers
let mut carrier_info = test_carrier_info( let mut carrier_info = test_carrier_info("i", 5, vec![test_carrier("sum", 10)]);
"i",
5,
vec![test_carrier("sum", 10)],
);
let other = test_carrier_info( let other = test_carrier_info(
"j", "j",
20, 20,
vec![ vec![test_carrier("count", 15), test_carrier("product", 18)],
test_carrier("count", 15),
test_carrier("product", 18),
],
); );
carrier_info.merge_from(&other); carrier_info.merge_from(&other);
@ -758,19 +734,13 @@ mod tests {
let mut carrier_info = test_carrier_info( let mut carrier_info = test_carrier_info(
"i", "i",
5, 5,
vec![ vec![test_carrier("zebra", 30), test_carrier("alpha", 10)],
test_carrier("zebra", 30),
test_carrier("alpha", 10),
],
); );
let other = test_carrier_info( let other = test_carrier_info(
"j", "j",
20, 20,
vec![ vec![test_carrier("beta", 15), test_carrier("gamma", 18)],
test_carrier("beta", 15),
test_carrier("gamma", 18),
],
); );
carrier_info.merge_from(&other); carrier_info.merge_from(&other);

View File

@ -67,12 +67,7 @@ pub fn emit_carrier_update_with_env(
// Get carrier parameter ValueId from env // Get carrier parameter ValueId from env
let carrier_param = env let carrier_param = env
.resolve(&carrier.name) .resolve(&carrier.name)
.ok_or_else(|| { .ok_or_else(|| format!("Carrier '{}' not found in UpdateEnv", carrier.name))?;
format!(
"Carrier '{}' not found in UpdateEnv",
carrier.name
)
})?;
// Allocate result ValueId // Allocate result ValueId
let result = alloc_value(); let result = alloc_value();
@ -99,12 +94,7 @@ pub fn emit_carrier_update_with_env(
// Get carrier parameter ValueId from env // Get carrier parameter ValueId from env
let carrier_param = env let carrier_param = env
.resolve(&carrier.name) .resolve(&carrier.name)
.ok_or_else(|| { .ok_or_else(|| format!("Carrier '{}' not found in UpdateEnv", carrier.name))?;
format!(
"Carrier '{}' not found in UpdateEnv",
carrier.name
)
})?;
// Resolve RHS (Phase 184: Now supports body-local variables!) // Resolve RHS (Phase 184: Now supports body-local variables!)
let rhs_id = match rhs { let rhs_id = match rhs {
@ -255,12 +245,7 @@ pub fn emit_carrier_update(
// Get carrier parameter ValueId from env // Get carrier parameter ValueId from env
let carrier_param = env let carrier_param = env
.get(&carrier.name) .get(&carrier.name)
.ok_or_else(|| { .ok_or_else(|| format!("Carrier '{}' not found in ConditionEnv", carrier.name))?;
format!(
"Carrier '{}' not found in ConditionEnv",
carrier.name
)
})?;
// Allocate result ValueId // Allocate result ValueId
let result = alloc_value(); let result = alloc_value();
@ -287,12 +272,7 @@ pub fn emit_carrier_update(
// Get carrier parameter ValueId from env // Get carrier parameter ValueId from env
let carrier_param = env let carrier_param = env
.get(&carrier.name) .get(&carrier.name)
.ok_or_else(|| { .ok_or_else(|| format!("Carrier '{}' not found in ConditionEnv", carrier.name))?;
format!(
"Carrier '{}' not found in ConditionEnv",
carrier.name
)
})?;
// Resolve RHS // Resolve RHS
let rhs_id = match rhs { let rhs_id = match rhs {
@ -304,14 +284,12 @@ pub fn emit_carrier_update(
})); }));
const_id const_id
} }
UpdateRhs::Variable(var_name) => { UpdateRhs::Variable(var_name) => env.get(var_name).ok_or_else(|| {
env.get(var_name).ok_or_else(|| { format!(
format!( "Update RHS variable '{}' not found in ConditionEnv",
"Update RHS variable '{}' not found in ConditionEnv", var_name
var_name )
) })?,
})?
}
// Phase 188: String updates now emit JoinIR BinOp // Phase 188: String updates now emit JoinIR BinOp
// StringAppendLiteral: s = s + "literal" // StringAppendLiteral: s = s + "literal"
UpdateRhs::StringLiteral(s) => { UpdateRhs::StringLiteral(s) => {
@ -441,13 +419,8 @@ mod tests {
}; };
let mut instructions = Vec::new(); let mut instructions = Vec::new();
let result = emit_carrier_update( let result =
&carrier, emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
&update,
&mut alloc_value,
&env,
&mut instructions,
);
assert!(result.is_ok()); assert!(result.is_ok());
let result_id = result.unwrap(); let result_id = result.unwrap();
@ -497,13 +470,8 @@ mod tests {
}; };
let mut instructions = Vec::new(); let mut instructions = Vec::new();
let result = emit_carrier_update( let result =
&carrier, emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
&update,
&mut alloc_value,
&env,
&mut instructions,
);
assert!(result.is_ok()); assert!(result.is_ok());
let result_id = result.unwrap(); let result_id = result.unwrap();
@ -553,13 +521,8 @@ mod tests {
}; };
let mut instructions = Vec::new(); let mut instructions = Vec::new();
let result = emit_carrier_update( let result =
&carrier, emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
&update,
&mut alloc_value,
&env,
&mut instructions,
);
assert!(result.is_ok()); assert!(result.is_ok());
let result_id = result.unwrap(); let result_id = result.unwrap();
@ -596,13 +559,8 @@ mod tests {
}; };
let mut instructions = Vec::new(); let mut instructions = Vec::new();
let result = emit_carrier_update( let result =
&carrier, emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
&update,
&mut alloc_value,
&env,
&mut instructions,
);
assert!(result.is_err()); assert!(result.is_err());
assert!(result.unwrap_err().contains("Carrier 'unknown' not found")); assert!(result.unwrap_err().contains("Carrier 'unknown' not found"));
@ -627,13 +585,8 @@ mod tests {
}; };
let mut instructions = Vec::new(); let mut instructions = Vec::new();
let result = emit_carrier_update( let result =
&carrier, emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
&update,
&mut alloc_value,
&env,
&mut instructions,
);
assert!(result.is_err()); assert!(result.is_err());
assert!(result.unwrap_err().contains("doesn't match carrier")); assert!(result.unwrap_err().contains("doesn't match carrier"));
@ -658,16 +611,13 @@ mod tests {
}; };
let mut instructions = Vec::new(); let mut instructions = Vec::new();
let result = emit_carrier_update( let result =
&carrier, emit_carrier_update(&carrier, &update, &mut alloc_value, &env, &mut instructions);
&update,
&mut alloc_value,
&env,
&mut instructions,
);
assert!(result.is_err()); assert!(result.is_err());
assert!(result.unwrap_err().contains("Update RHS variable 'unknown_var' not found")); assert!(result
.unwrap_err()
.contains("Update RHS variable 'unknown_var' not found"));
} }
// ============================================================================ // ============================================================================
@ -765,7 +715,12 @@ mod tests {
// Should use x=100 (condition env), not x=200 (body-local env) // Should use x=100 (condition env), not x=200 (body-local env)
match &instructions[0] { match &instructions[0] {
JoinInst::Compute(MirLikeInst::BinOp { dst: _, op: _, lhs: _, rhs }) => { JoinInst::Compute(MirLikeInst::BinOp {
dst: _,
op: _,
lhs: _,
rhs,
}) => {
assert_eq!(*rhs, ValueId(100)); // Condition env wins assert_eq!(*rhs, ValueId(100)); // Condition env wins
} }
_ => panic!("Expected BinOp instruction"), _ => panic!("Expected BinOp instruction"),

View File

@ -107,7 +107,8 @@ impl ComplexAddendNormalizer {
left, left,
right, right,
.. ..
} | ASTNode::BinaryOp { }
| ASTNode::BinaryOp {
operator: BinaryOperator::Subtract, operator: BinaryOperator::Subtract,
left, left,
right, right,

View File

@ -61,7 +61,7 @@ impl ConditionEnv {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
name_to_join: BTreeMap::new(), // Phase 222.5-D: HashMap → BTreeMap for determinism name_to_join: BTreeMap::new(), // Phase 222.5-D: HashMap → BTreeMap for determinism
captured: BTreeMap::new(), // Phase 222.5-D: HashMap → BTreeMap for determinism captured: BTreeMap::new(), // Phase 222.5-D: HashMap → BTreeMap for determinism
} }
} }
@ -82,7 +82,9 @@ impl ConditionEnv {
/// Returns `Some(ValueId)` if the variable exists in the environment, /// Returns `Some(ValueId)` if the variable exists in the environment,
/// `None` otherwise. /// `None` otherwise.
pub fn get(&self, name: &str) -> Option<ValueId> { pub fn get(&self, name: &str) -> Option<ValueId> {
self.name_to_join.get(name).copied() self.name_to_join
.get(name)
.copied()
.or_else(|| self.captured.get(name).copied()) .or_else(|| self.captured.get(name).copied())
} }
@ -126,7 +128,9 @@ impl ConditionEnv {
/// ///
/// Phase 200-B: Includes both name_to_join and captured variables. /// Phase 200-B: Includes both name_to_join and captured variables.
pub fn names(&self) -> Vec<String> { pub fn names(&self) -> Vec<String> {
let mut names: Vec<_> = self.name_to_join.keys() let mut names: Vec<_> = self
.name_to_join
.keys()
.chain(self.captured.keys()) .chain(self.captured.keys())
.cloned() .cloned()
.collect(); .collect();
@ -235,8 +239,8 @@ mod tests {
fn test_condition_binding() { fn test_condition_binding() {
let binding = ConditionBinding::new( let binding = ConditionBinding::new(
"start".to_string(), "start".to_string(),
ValueId(33), // HOST ValueId(33), // HOST
ValueId(1), // JoinIR ValueId(1), // JoinIR
); );
assert_eq!(binding.name, "start"); assert_eq!(binding.name, "start");

View File

@ -100,12 +100,8 @@ where
| BinaryOperator::Greater => { | BinaryOperator::Greater => {
lower_comparison(operator, left, right, alloc_value, env, instructions) lower_comparison(operator, left, right, alloc_value, env, instructions)
} }
BinaryOperator::And => { BinaryOperator::And => lower_logical_and(left, right, alloc_value, env, instructions),
lower_logical_and(left, right, alloc_value, env, instructions) BinaryOperator::Or => lower_logical_or(left, right, alloc_value, env, instructions),
}
BinaryOperator::Or => {
lower_logical_or(left, right, alloc_value, env, instructions)
}
_ => Err(format!( _ => Err(format!(
"Unsupported binary operator in condition: {:?}", "Unsupported binary operator in condition: {:?}",
operator operator
@ -264,7 +260,10 @@ where
return Err("Float literals not supported in JoinIR conditions yet".to_string()); return Err("Float literals not supported in JoinIR conditions yet".to_string());
} }
_ => { _ => {
return Err(format!("Unsupported literal type in condition: {:?}", value)); return Err(format!(
"Unsupported literal type in condition: {:?}",
value
));
} }
}; };
@ -317,7 +316,14 @@ where
let recv_val = lower_value_expression(object, alloc_value, env, instructions)?; let recv_val = lower_value_expression(object, alloc_value, env, instructions)?;
// 2. Lower method call using MethodCallLowerer (will lower arguments internally) // 2. Lower method call using MethodCallLowerer (will lower arguments internally)
MethodCallLowerer::lower_for_condition(recv_val, method, arguments, alloc_value, env, instructions) MethodCallLowerer::lower_for_condition(
recv_val,
method,
arguments,
alloc_value,
env,
instructions,
)
} }
_ => Err(format!( _ => Err(format!(

View File

@ -15,9 +15,9 @@
//! **Fail-Safe**: Implementations return explicit errors for unsupported patterns, //! **Fail-Safe**: Implementations return explicit errors for unsupported patterns,
//! allowing callers to fall back to alternative paths. //! allowing callers to fall back to alternative paths.
use super::scope_manager::ScopeManager;
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::ValueId; use crate::mir::ValueId;
use super::scope_manager::ScopeManager;
/// Phase 244: Context for condition lowering /// Phase 244: Context for condition lowering
/// ///
@ -158,12 +158,12 @@ pub trait ConditionLoweringBox<S: ScopeManager> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::ast::{Span, LiteralValue, BinaryOperator}; use crate::ast::{BinaryOperator, LiteralValue, Span};
use crate::mir::builder::MirBuilder; use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo; use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
use crate::mir::join_ir::lowering::expr_lowerer::{ExprLowerer, ExprContext}; use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
use crate::mir::join_ir::lowering::expr_lowerer::{ExprContext, ExprLowerer};
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
fn span() -> Span { fn span() -> Span {
Span::unknown() Span::unknown()

View File

@ -79,7 +79,12 @@ pub enum ConditionPattern {
pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern { pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern {
match cond { match cond {
// Comparison operators: ==, !=, <, >, <=, >= // Comparison operators: ==, !=, <, >, <=, >=
ASTNode::BinaryOp { operator, left, right, .. } => { ASTNode::BinaryOp {
operator,
left,
right,
..
} => {
// Check if operator is a comparison // Check if operator is a comparison
let is_comparison = matches!( let is_comparison = matches!(
operator, operator,
@ -225,12 +230,12 @@ pub struct NormalizedCondition {
/// ``` /// ```
fn flip_compare_op(op: CompareOp) -> CompareOp { fn flip_compare_op(op: CompareOp) -> CompareOp {
match op { match op {
CompareOp::Lt => CompareOp::Gt, // < → > CompareOp::Lt => CompareOp::Gt, // < → >
CompareOp::Gt => CompareOp::Lt, // > → < CompareOp::Gt => CompareOp::Lt, // > → <
CompareOp::Le => CompareOp::Ge, // <= → >= CompareOp::Le => CompareOp::Ge, // <= → >=
CompareOp::Ge => CompareOp::Le, // >= → <= CompareOp::Ge => CompareOp::Le, // >= → <=
CompareOp::Eq => CompareOp::Eq, // == → == (不変) CompareOp::Eq => CompareOp::Eq, // == → == (不変)
CompareOp::Ne => CompareOp::Ne, // != → != (不変) CompareOp::Ne => CompareOp::Ne, // != → != (不変)
} }
} }
@ -278,12 +283,24 @@ fn binary_op_to_compare_op(op: &BinaryOperator) -> Option<CompareOp> {
/// ``` /// ```
pub fn normalize_comparison(cond: &ASTNode) -> Option<NormalizedCondition> { pub fn normalize_comparison(cond: &ASTNode) -> Option<NormalizedCondition> {
match cond { match cond {
ASTNode::BinaryOp { operator, left, right, .. } => { ASTNode::BinaryOp {
operator,
left,
right,
..
} => {
// Comparison operator のみ受理 // Comparison operator のみ受理
let compare_op = binary_op_to_compare_op(operator)?; let compare_op = binary_op_to_compare_op(operator)?;
// Case 1: var CmpOp literal (e.g., i > 0) // Case 1: var CmpOp literal (e.g., i > 0)
if let (ASTNode::Variable { name: left_var, .. }, ASTNode::Literal { value: LiteralValue::Integer(right_val), .. }) = (left.as_ref(), right.as_ref()) { if let (
ASTNode::Variable { name: left_var, .. },
ASTNode::Literal {
value: LiteralValue::Integer(right_val),
..
},
) = (left.as_ref(), right.as_ref())
{
return Some(NormalizedCondition { return Some(NormalizedCondition {
left_var: left_var.clone(), left_var: left_var.clone(),
op: compare_op, op: compare_op,
@ -292,7 +309,16 @@ pub fn normalize_comparison(cond: &ASTNode) -> Option<NormalizedCondition> {
} }
// Case 2: literal CmpOp var (e.g., 0 < i) → 左右反転 // Case 2: literal CmpOp var (e.g., 0 < i) → 左右反転
if let (ASTNode::Literal { value: LiteralValue::Integer(left_val), .. }, ASTNode::Variable { name: right_var, .. }) = (left.as_ref(), right.as_ref()) { if let (
ASTNode::Literal {
value: LiteralValue::Integer(left_val),
..
},
ASTNode::Variable {
name: right_var, ..
},
) = (left.as_ref(), right.as_ref())
{
return Some(NormalizedCondition { return Some(NormalizedCondition {
left_var: right_var.clone(), left_var: right_var.clone(),
op: flip_compare_op(compare_op), // 演算子を反転 op: flip_compare_op(compare_op), // 演算子を反転
@ -301,7 +327,13 @@ pub fn normalize_comparison(cond: &ASTNode) -> Option<NormalizedCondition> {
} }
// Case 3: var CmpOp var (e.g., i > j) // Case 3: var CmpOp var (e.g., i > j)
if let (ASTNode::Variable { name: left_var, .. }, ASTNode::Variable { name: right_var, .. }) = (left.as_ref(), right.as_ref()) { if let (
ASTNode::Variable { name: left_var, .. },
ASTNode::Variable {
name: right_var, ..
},
) = (left.as_ref(), right.as_ref())
{
return Some(NormalizedCondition { return Some(NormalizedCondition {
left_var: left_var.clone(), left_var: left_var.clone(),
op: compare_op, op: compare_op,
@ -351,7 +383,10 @@ mod tests {
fn test_simple_comparison_greater() { fn test_simple_comparison_greater() {
// i > 0 // i > 0
let cond = binop(BinaryOperator::Greater, var("i"), int_lit(0)); let cond = binop(BinaryOperator::Greater, var("i"), int_lit(0));
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert_eq!(
analyze_condition_pattern(&cond),
ConditionPattern::SimpleComparison
);
assert!(is_simple_comparison(&cond)); assert!(is_simple_comparison(&cond));
} }
@ -359,7 +394,10 @@ mod tests {
fn test_simple_comparison_less() { fn test_simple_comparison_less() {
// i < 10 // i < 10
let cond = binop(BinaryOperator::Less, var("i"), int_lit(10)); let cond = binop(BinaryOperator::Less, var("i"), int_lit(10));
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert_eq!(
analyze_condition_pattern(&cond),
ConditionPattern::SimpleComparison
);
assert!(is_simple_comparison(&cond)); assert!(is_simple_comparison(&cond));
} }
@ -367,7 +405,10 @@ mod tests {
fn test_simple_comparison_equal() { fn test_simple_comparison_equal() {
// i == 5 // i == 5
let cond = binop(BinaryOperator::Equal, var("i"), int_lit(5)); let cond = binop(BinaryOperator::Equal, var("i"), int_lit(5));
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert_eq!(
analyze_condition_pattern(&cond),
ConditionPattern::SimpleComparison
);
assert!(is_simple_comparison(&cond)); assert!(is_simple_comparison(&cond));
} }
@ -375,7 +416,10 @@ mod tests {
fn test_simple_comparison_not_equal() { fn test_simple_comparison_not_equal() {
// i != 0 // i != 0
let cond = binop(BinaryOperator::NotEqual, var("i"), int_lit(0)); let cond = binop(BinaryOperator::NotEqual, var("i"), int_lit(0));
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert_eq!(
analyze_condition_pattern(&cond),
ConditionPattern::SimpleComparison
);
assert!(is_simple_comparison(&cond)); assert!(is_simple_comparison(&cond));
} }
@ -384,7 +428,10 @@ mod tests {
// Phase 242-EX-A: i % 2 == 1 (BinaryOp in LHS) is now SimpleComparison // Phase 242-EX-A: i % 2 == 1 (BinaryOp in LHS) is now SimpleComparison
let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2)); let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2));
let cond = binop(BinaryOperator::Equal, lhs, int_lit(1)); let cond = binop(BinaryOperator::Equal, lhs, int_lit(1));
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert_eq!(
analyze_condition_pattern(&cond),
ConditionPattern::SimpleComparison
);
assert!(is_simple_comparison(&cond)); assert!(is_simple_comparison(&cond));
} }
@ -393,7 +440,10 @@ mod tests {
// Phase 242-EX-A: i == a + b (BinaryOp in RHS) is now SimpleComparison // Phase 242-EX-A: i == a + b (BinaryOp in RHS) is now SimpleComparison
let rhs = binop(BinaryOperator::Add, var("a"), var("b")); let rhs = binop(BinaryOperator::Add, var("a"), var("b"));
let cond = binop(BinaryOperator::Equal, var("i"), rhs); let cond = binop(BinaryOperator::Equal, var("i"), rhs);
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert_eq!(
analyze_condition_pattern(&cond),
ConditionPattern::SimpleComparison
);
assert!(is_simple_comparison(&cond)); assert!(is_simple_comparison(&cond));
} }
@ -402,7 +452,10 @@ mod tests {
// Pattern3/4 で使う典型的なフィルタ条件: i % 2 == 1 // Pattern3/4 で使う典型的なフィルタ条件: i % 2 == 1
let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2)); let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2));
let cond = binop(BinaryOperator::Equal, lhs, int_lit(1)); let cond = binop(BinaryOperator::Equal, lhs, int_lit(1));
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert_eq!(
analyze_condition_pattern(&cond),
ConditionPattern::SimpleComparison
);
assert!(is_simple_comparison(&cond)); assert!(is_simple_comparison(&cond));
} }
@ -535,7 +588,10 @@ mod tests {
fn test_analyze_pattern_literal_cmp_var() { fn test_analyze_pattern_literal_cmp_var() {
// Phase 222: 0 < i → SimpleComparison // Phase 222: 0 < i → SimpleComparison
let cond = binop(BinaryOperator::Less, int_lit(0), var("i")); let cond = binop(BinaryOperator::Less, int_lit(0), var("i"));
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert_eq!(
analyze_condition_pattern(&cond),
ConditionPattern::SimpleComparison
);
assert!(is_simple_comparison(&cond)); assert!(is_simple_comparison(&cond));
} }
@ -543,7 +599,10 @@ mod tests {
fn test_analyze_pattern_var_cmp_var() { fn test_analyze_pattern_var_cmp_var() {
// Phase 222: i > j → SimpleComparison // Phase 222: i > j → SimpleComparison
let cond = binop(BinaryOperator::Greater, var("i"), var("j")); let cond = binop(BinaryOperator::Greater, var("i"), var("j"));
assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); assert_eq!(
analyze_condition_pattern(&cond),
ConditionPattern::SimpleComparison
);
assert!(is_simple_comparison(&cond)); assert!(is_simple_comparison(&cond));
} }
} }

View File

@ -40,10 +40,7 @@ use std::collections::BTreeSet;
/// ); /// );
/// // Result: ["end", "len", "start"] (sorted, 'i' excluded) /// // Result: ["end", "len", "start"] (sorted, 'i' excluded)
/// ``` /// ```
pub fn extract_condition_variables( pub fn extract_condition_variables(cond_ast: &ASTNode, exclude_vars: &[String]) -> Vec<String> {
cond_ast: &ASTNode,
exclude_vars: &[String],
) -> Vec<String> {
let mut all_vars = BTreeSet::new(); let mut all_vars = BTreeSet::new();
collect_variables_recursive(cond_ast, &mut all_vars); collect_variables_recursive(cond_ast, &mut all_vars);

View File

@ -152,11 +152,7 @@ mod tests {
#[test] #[test]
fn test_normalize_digit_pos_lt_zero() { fn test_normalize_digit_pos_lt_zero() {
// Pattern: digit_pos < 0 // Pattern: digit_pos < 0
let cond = binary_op( let cond = binary_op(var_node("digit_pos"), BinaryOperator::Less, int_literal(0));
var_node("digit_pos"),
BinaryOperator::Less,
int_literal(0),
);
let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos"); let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos");
@ -166,14 +162,12 @@ mod tests {
operator: UnaryOperator::Not, operator: UnaryOperator::Not,
operand, operand,
.. ..
} => { } => match operand.as_ref() {
match operand.as_ref() { ASTNode::Variable { name, .. } => {
ASTNode::Variable { name, .. } => { assert_eq!(name, "is_digit_pos");
assert_eq!(name, "is_digit_pos");
}
_ => panic!("Expected Variable node"),
} }
} _ => panic!("Expected Variable node"),
},
_ => panic!("Expected UnaryOp Not node"), _ => panic!("Expected UnaryOp Not node"),
} }
} }
@ -201,21 +195,13 @@ mod tests {
#[test] #[test]
fn test_no_normalize_wrong_variable() { fn test_no_normalize_wrong_variable() {
// Pattern: other_var < 0 (different variable name) // Pattern: other_var < 0 (different variable name)
let cond = binary_op( let cond = binary_op(var_node("other_var"), BinaryOperator::Less, int_literal(0));
var_node("other_var"),
BinaryOperator::Less,
int_literal(0),
);
let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos"); let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos");
// Should NOT transform (return original) // Should NOT transform (return original)
match normalized { match normalized {
ASTNode::BinaryOp { ASTNode::BinaryOp { operator, left, .. } => {
operator,
left,
..
} => {
assert_eq!(operator, BinaryOperator::Less); assert_eq!(operator, BinaryOperator::Less);
match left.as_ref() { match left.as_ref() {
ASTNode::Variable { name, .. } => { ASTNode::Variable { name, .. } => {
@ -231,20 +217,14 @@ mod tests {
#[test] #[test]
fn test_no_normalize_wrong_constant() { fn test_no_normalize_wrong_constant() {
// Pattern: digit_pos < 10 (different constant, not 0) // Pattern: digit_pos < 10 (different constant, not 0)
let cond = binary_op( let cond = binary_op(var_node("digit_pos"), BinaryOperator::Less, int_literal(10));
var_node("digit_pos"),
BinaryOperator::Less,
int_literal(10),
);
let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos"); let normalized = DigitPosConditionNormalizer::normalize(&cond, "digit_pos", "is_digit_pos");
// Should NOT transform (return original) // Should NOT transform (return original)
match normalized { match normalized {
ASTNode::BinaryOp { ASTNode::BinaryOp {
operator, operator, right, ..
right,
..
} => { } => {
assert_eq!(operator, BinaryOperator::Less); assert_eq!(operator, BinaryOperator::Less);
match right.as_ref() { match right.as_ref() {

View File

@ -15,12 +15,12 @@
//! **Fail-Safe**: Unsupported AST nodes return explicit errors, allowing callers //! **Fail-Safe**: Unsupported AST nodes return explicit errors, allowing callers
//! to fall back to legacy paths. //! to fall back to legacy paths.
use crate::ast::ASTNode;
use crate::mir::ValueId;
use crate::mir::join_ir::JoinInst;
use crate::mir::builder::MirBuilder;
use super::scope_manager::ScopeManager;
use super::condition_lowerer::lower_condition_to_joinir; use super::condition_lowerer::lower_condition_to_joinir;
use super::scope_manager::ScopeManager;
use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::JoinInst;
use crate::mir::ValueId;
mod ast_support; mod ast_support;
mod scope_resolution; mod scope_resolution;
@ -165,11 +165,9 @@ impl<'env, 'builder, S: ScopeManager> ExprLowerer<'env, 'builder, S> {
pub fn lower(&mut self, ast: &ASTNode) -> Result<ValueId, ExprLoweringError> { pub fn lower(&mut self, ast: &ASTNode) -> Result<ValueId, ExprLoweringError> {
match self.context { match self.context {
ExprContext::Condition => self.lower_condition(ast), ExprContext::Condition => self.lower_condition(ast),
ExprContext::General => { ExprContext::General => Err(ExprLoweringError::UnsupportedNode(
Err(ExprLoweringError::UnsupportedNode( "General expression context not yet implemented (Phase 231)".to_string(),
"General expression context not yet implemented (Phase 231)".to_string() )),
))
}
} }
} }
@ -185,9 +183,10 @@ impl<'env, 'builder, S: ScopeManager> ExprLowerer<'env, 'builder, S> {
fn lower_condition(&mut self, ast: &ASTNode) -> Result<ValueId, ExprLoweringError> { fn lower_condition(&mut self, ast: &ASTNode) -> Result<ValueId, ExprLoweringError> {
// 1. Check if AST is supported in condition context // 1. Check if AST is supported in condition context
if !ast_support::is_supported_condition(ast) { if !ast_support::is_supported_condition(ast) {
return Err(ExprLoweringError::UnsupportedNode( return Err(ExprLoweringError::UnsupportedNode(format!(
format!("Unsupported condition node: {:?}", ast) "Unsupported condition node: {:?}",
)); ast
)));
} }
// 2. Build ConditionEnv from ScopeManager // 2. Build ConditionEnv from ScopeManager
@ -204,17 +203,18 @@ impl<'env, 'builder, S: ScopeManager> ExprLowerer<'env, 'builder, S> {
id id
}; };
let (result_value, instructions) = lower_condition_to_joinir( let (result_value, instructions) =
ast, lower_condition_to_joinir(ast, &mut alloc_value, &condition_env)
&mut alloc_value, .map_err(|e| ExprLoweringError::LoweringError(e))?;
&condition_env,
).map_err(|e| ExprLoweringError::LoweringError(e))?;
// Phase 235: 保存しておき、テストから観察できるようにする // Phase 235: 保存しておき、テストから観察できるようにする
self.last_instructions = instructions; self.last_instructions = instructions;
if self.debug { if self.debug {
eprintln!("[expr_lowerer/phase231] Lowered condition → ValueId({:?})", result_value); eprintln!(
"[expr_lowerer/phase231] Lowered condition → ValueId({:?})",
result_value
);
} }
Ok(result_value) Ok(result_value)
@ -230,7 +230,7 @@ impl<'env, 'builder, S: ScopeManager> ExprLowerer<'env, 'builder, S> {
// Phase 244: ConditionLoweringBox trait implementation // Phase 244: ConditionLoweringBox trait implementation
// ============================================================================ // ============================================================================
use super::condition_lowering_box::{ConditionLoweringBox, ConditionContext}; use super::condition_lowering_box::{ConditionContext, ConditionLoweringBox};
impl<'env, 'builder, S: ScopeManager> ConditionLoweringBox<S> for ExprLowerer<'env, 'builder, S> { impl<'env, 'builder, S: ScopeManager> ConditionLoweringBox<S> for ExprLowerer<'env, 'builder, S> {
/// Phase 244: Implement ConditionLoweringBox trait for ExprLowerer /// Phase 244: Implement ConditionLoweringBox trait for ExprLowerer
@ -280,13 +280,13 @@ impl<'env, 'builder, S: ScopeManager> ConditionLoweringBox<S> for ExprLowerer<'e
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::test_helpers::{bin, lit_i, span, var};
use super::*; use super::*;
use crate::ast::{BinaryOperator, LiteralValue, Span, UnaryOperator}; use crate::ast::{BinaryOperator, LiteralValue, Span, UnaryOperator};
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo; use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
use crate::mir::join_ir::lowering::condition_env::ConditionEnv; use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager; use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
use crate::mir::join_ir::{BinOpKind, MirLikeInst, UnaryOp as JoinUnaryOp}; use crate::mir::join_ir::{BinOpKind, MirLikeInst, UnaryOp as JoinUnaryOp};
use super::test_helpers::{bin, lit_i, span, var};
// Helper to create a test MirBuilder (Phase 231: minimal stub) // Helper to create a test MirBuilder (Phase 231: minimal stub)
fn create_test_builder() -> MirBuilder { fn create_test_builder() -> MirBuilder {
@ -340,7 +340,10 @@ mod tests {
let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder); let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder);
let result = expr_lowerer.lower(&ast); let result = expr_lowerer.lower(&ast);
assert!(result.is_ok(), "Should lower simple comparison successfully"); assert!(
result.is_ok(),
"Should lower simple comparison successfully"
);
} }
#[test] #[test]
@ -381,7 +384,10 @@ mod tests {
let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder); let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder);
let result = expr_lowerer.lower(&ast); let result = expr_lowerer.lower(&ast);
assert!(matches!(result, Err(ExprLoweringError::VariableNotFound(_)))); assert!(matches!(
result,
Err(ExprLoweringError::VariableNotFound(_))
));
} }
#[test] #[test]
@ -406,7 +412,9 @@ mod tests {
let mut builder = create_test_builder(); let mut builder = create_test_builder();
// AST: Break (unsupported in condition context) // AST: Break (unsupported in condition context)
let ast = ASTNode::Break { span: Span::unknown() }; let ast = ASTNode::Break {
span: Span::unknown(),
};
let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder); let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder);
let result = expr_lowerer.lower(&ast); let result = expr_lowerer.lower(&ast);
@ -429,7 +437,9 @@ mod tests {
}), }),
span: Span::unknown(), span: Span::unknown(),
}; };
assert!(ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(&ast)); assert!(ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(
&ast
));
// Supported: MethodCall // Supported: MethodCall
let ast = ASTNode::MethodCall { let ast = ASTNode::MethodCall {
@ -441,10 +451,14 @@ mod tests {
arguments: vec![], arguments: vec![],
span: Span::unknown(), span: Span::unknown(),
}; };
assert!(ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(&ast)); assert!(ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(
&ast
));
// Unsupported: Break node // Unsupported: Break node
let ast = ASTNode::Break { span: Span::unknown() }; let ast = ASTNode::Break {
span: Span::unknown(),
};
assert!(!ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(&ast)); assert!(!ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(&ast));
} }
@ -507,10 +521,9 @@ mod tests {
fn assert_has_compare(instructions: &[JoinInst]) { fn assert_has_compare(instructions: &[JoinInst]) {
assert!( assert!(
instructions.iter().any(|inst| matches!( instructions
inst, .iter()
JoinInst::Compute(MirLikeInst::Compare { .. } .any(|inst| matches!(inst, JoinInst::Compute(MirLikeInst::Compare { .. }))),
))),
"Expected at least one Compare instruction, got {:?}", "Expected at least one Compare instruction, got {:?}",
instructions instructions
); );
@ -532,8 +545,11 @@ mod tests {
assert!( assert!(
instructions.iter().any(|inst| matches!( instructions.iter().any(|inst| matches!(
inst, inst,
JoinInst::Compute(MirLikeInst::UnaryOp { op: JoinUnaryOp::Not, .. } JoinInst::Compute(MirLikeInst::UnaryOp {
))), op: JoinUnaryOp::Not,
..
})
)),
"Expected at least one UnaryOp::Not, got {:?}", "Expected at least one UnaryOp::Not, got {:?}",
instructions instructions
); );

View File

@ -5,14 +5,19 @@ pub(crate) fn is_supported_condition(ast: &ASTNode) -> bool {
match ast { match ast {
Literal { .. } => true, Literal { .. } => true,
Variable { .. } => true, Variable { .. } => true,
BinaryOp { operator, left, right, .. } => { BinaryOp {
operator,
left,
right,
..
} => {
is_supported_binary_op(operator) is_supported_binary_op(operator)
&& is_supported_condition(left) && is_supported_condition(left)
&& is_supported_condition(right) && is_supported_condition(right)
} }
UnaryOp { operator, operand, .. } => { UnaryOp {
matches!(operator, UnaryOperator::Not) && is_supported_condition(operand) operator, operand, ..
} } => matches!(operator, UnaryOperator::Not) && is_supported_condition(operand),
MethodCall { .. } => is_supported_method_call(ast), MethodCall { .. } => is_supported_method_call(ast),
_ => false, _ => false,
} }
@ -20,7 +25,10 @@ pub(crate) fn is_supported_condition(ast: &ASTNode) -> bool {
fn is_supported_binary_op(op: &BinaryOperator) -> bool { fn is_supported_binary_op(op: &BinaryOperator) -> bool {
use BinaryOperator::*; use BinaryOperator::*;
matches!(op, Less | LessEqual | Greater | GreaterEqual | Equal | NotEqual | And | Or) matches!(
op,
Less | LessEqual | Greater | GreaterEqual | Equal | NotEqual | And | Or
)
} }
fn is_supported_method_call(ast: &ASTNode) -> bool { fn is_supported_method_call(ast: &ASTNode) -> bool {

View File

@ -1,6 +1,6 @@
use super::{ExprLoweringError, ScopeManager};
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::join_ir::lowering::condition_env::ConditionEnv; use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
use super::{ExprLoweringError, ScopeManager};
pub(crate) fn build_condition_env_from_scope<S: ScopeManager>( pub(crate) fn build_condition_env_from_scope<S: ScopeManager>(
scope: &S, scope: &S,
@ -30,7 +30,9 @@ fn collect_vars(ast: &ASTNode, vars: &mut Vec<String>) {
collect_vars(right, vars); collect_vars(right, vars);
} }
ASTNode::UnaryOp { operand, .. } => collect_vars(operand, vars), ASTNode::UnaryOp { operand, .. } => collect_vars(operand, vars),
ASTNode::MethodCall { object, arguments, .. } => { ASTNode::MethodCall {
object, arguments, ..
} => {
collect_vars(object, vars); collect_vars(object, vars);
for arg in arguments { for arg in arguments {
collect_vars(arg, vars); collect_vars(arg, vars);

View File

@ -30,7 +30,6 @@
//! - `value_id_ranges::funcscanner_append_defs` - ValueId allocation strategy //! - `value_id_ranges::funcscanner_append_defs` - ValueId allocation strategy
//! - `loop_scope_shape::CaseAContext` - Context extraction //! - `loop_scope_shape::CaseAContext` - Context extraction
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext; use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
use crate::mir::join_ir::lowering::value_id_ranges; use crate::mir::join_ir::lowering::value_id_ranges;
use crate::mir::join_ir::{ use crate::mir::join_ir::{

View File

@ -4,8 +4,8 @@
//! - ValueId/BTreeMap の初期化ボイラープレート統一化 //! - ValueId/BTreeMap の初期化ボイラープレート統一化
//! - Pinned/Carrier 変数の一元管理 //! - Pinned/Carrier 変数の一元管理
use std::collections::BTreeMap;
use crate::mir::ValueId; use crate::mir::ValueId;
use std::collections::BTreeMap;
/// Entry関数構築用の統一ビルダー /// Entry関数構築用の統一ビルダー
/// ///

View File

@ -84,19 +84,19 @@
//! - `loop_to_join` - Main loop lowering coordinator //! - `loop_to_join` - Main loop lowering coordinator
// Pattern-specific lowering modules // Pattern-specific lowering modules
pub mod skip_ws;
pub mod trim;
pub mod append_defs; pub mod append_defs;
pub mod skip_ws;
pub mod stage1_using_resolver; pub mod stage1_using_resolver;
pub mod trim;
// Helper modules // Helper modules
pub mod entry_builder; pub mod entry_builder;
pub mod whitespace_check; pub mod whitespace_check;
// Re-export public lowering functions // Re-export public lowering functions
pub(crate) use skip_ws::lower_case_a_skip_ws_with_scope;
pub(crate) use trim::lower_case_a_trim_with_scope;
pub(crate) use append_defs::lower_case_a_append_defs_with_scope; pub(crate) use append_defs::lower_case_a_append_defs_with_scope;
pub(crate) use skip_ws::lower_case_a_skip_ws_with_scope;
pub(crate) use stage1_using_resolver::lower_case_a_stage1_usingresolver_with_scope; pub(crate) use stage1_using_resolver::lower_case_a_stage1_usingresolver_with_scope;
pub(crate) use trim::lower_case_a_trim_with_scope;
// Re-export helper utilities // Re-export helper utilities

View File

@ -31,7 +31,6 @@
//! - `value_id_ranges::skip_ws` - ValueId allocation strategy //! - `value_id_ranges::skip_ws` - ValueId allocation strategy
//! - `loop_scope_shape::CaseAContext` - Context extraction //! - `loop_scope_shape::CaseAContext` - Context extraction
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext; use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
use crate::mir::join_ir::lowering::value_id_ranges; use crate::mir::join_ir::lowering::value_id_ranges;
use crate::mir::join_ir::lowering::value_id_ranges::skip_ws as vid; use crate::mir::join_ir::lowering::value_id_ranges::skip_ws as vid;

View File

@ -30,7 +30,6 @@
//! - `value_id_ranges::stage1_using_resolver` - ValueId allocation strategy //! - `value_id_ranges::stage1_using_resolver` - ValueId allocation strategy
//! - `loop_scope_shape::CaseAContext` - Context extraction //! - `loop_scope_shape::CaseAContext` - Context extraction
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext; use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
use crate::mir::join_ir::lowering::value_id_ranges; use crate::mir::join_ir::lowering::value_id_ranges;
use crate::mir::join_ir::lowering::value_id_ranges::stage1_using_resolver as stage1_vid; use crate::mir::join_ir::lowering::value_id_ranges::stage1_using_resolver as stage1_vid;

View File

@ -44,7 +44,6 @@
//! - `value_id_ranges::funcscanner_trim` - ValueId allocation strategy //! - `value_id_ranges::funcscanner_trim` - ValueId allocation strategy
//! - `whitespace_check` - Whitespace detection helper (shared with skip_ws) //! - `whitespace_check` - Whitespace detection helper (shared with skip_ws)
use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext; use crate::mir::join_ir::lowering::loop_scope_shape::CaseAContext;
use crate::mir::join_ir::lowering::value_id_ranges; use crate::mir::join_ir::lowering::value_id_ranges;
use crate::mir::join_ir::{ use crate::mir::join_ir::{

View File

@ -59,10 +59,7 @@ impl WhitespaceDetector {
/// 具体的な JoinInst 生成は呼び出し側で行う。 /// 具体的な JoinInst 生成は呼び出し側で行う。
/// ここは判定ロジック(どの文字を空白と判定するか)を記録する。 /// ここは判定ロジック(どの文字を空白と判定するか)を記録する。
#[allow(dead_code)] #[allow(dead_code)]
pub fn build_whitespace_check_expr( pub fn build_whitespace_check_expr(ch_value: ValueId, _debug: bool) -> Option<ValueId> {
ch_value: ValueId,
_debug: bool,
) -> Option<ValueId> {
// NOTE: JoinInst を生成する実装は呼び出し側で行う // NOTE: JoinInst を生成する実装は呼び出し側で行う
// ここは判定ロジック(どの文字を空白と判定するか)を記録 // ここは判定ロジック(どの文字を空白と判定するか)を記録

View File

@ -133,7 +133,10 @@ pub fn try_lower_if_to_joinir(
// IfMerge が成功すればそれを返す、失敗したら Select を試行 // IfMerge が成功すればそれを返す、失敗したら Select を試行
// Phase 61-1: context がある場合は with_context() を使用 // Phase 61-1: context がある場合は with_context() を使用
let if_merge_lowerer = if let Some(ctx) = context { let if_merge_lowerer = if let Some(ctx) = context {
crate::mir::join_ir::lowering::if_merge::IfMergeLowerer::with_context(debug_level, ctx.clone()) crate::mir::join_ir::lowering::if_merge::IfMergeLowerer::with_context(
debug_level,
ctx.clone(),
)
} else { } else {
crate::mir::join_ir::lowering::if_merge::IfMergeLowerer::new(debug_level) crate::mir::join_ir::lowering::if_merge::IfMergeLowerer::new(debug_level)
}; };
@ -153,7 +156,10 @@ pub fn try_lower_if_to_joinir(
// 4. IfMerge が失敗したら Select を試行(単一変数パターン) // 4. IfMerge が失敗したら Select を試行(単一変数パターン)
// Phase 61-1: context がある場合は with_context() を使用 // Phase 61-1: context がある場合は with_context() を使用
let if_select_lowerer = if let Some(ctx) = context { let if_select_lowerer = if let Some(ctx) = context {
crate::mir::join_ir::lowering::if_select::IfSelectLowerer::with_context(debug_level, ctx.clone()) crate::mir::join_ir::lowering::if_select::IfSelectLowerer::with_context(
debug_level,
ctx.clone(),
)
} else { } else {
crate::mir::join_ir::lowering::if_select::IfSelectLowerer::new(debug_level) crate::mir::join_ir::lowering::if_select::IfSelectLowerer::new(debug_level)
}; };

View File

@ -42,8 +42,8 @@
//! ... //! ...
//! ``` //! ```
use crate::mir::ValueId;
use super::carrier_info::CarrierRole; use super::carrier_info::CarrierRole;
use crate::mir::ValueId;
/// Explicit binding between JoinIR exit value and host variable /// Explicit binding between JoinIR exit value and host variable
/// ///
@ -278,11 +278,11 @@ impl JoinInlineBoundary {
host_outputs: vec![], host_outputs: vec![],
exit_bindings: vec![], exit_bindings: vec![],
#[allow(deprecated)] #[allow(deprecated)]
condition_inputs: vec![], // Phase 171: Default to empty (deprecated) condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
condition_bindings: vec![], // Phase 171-fix: Default to empty condition_bindings: vec![], // Phase 171-fix: Default to empty
expr_result: None, // Phase 33-14: Default to carrier-only pattern expr_result: None, // Phase 33-14: Default to carrier-only pattern
loop_var_name: None, // Phase 33-16 loop_var_name: None, // Phase 33-16
carrier_info: None, // Phase 228: Default to None carrier_info: None, // Phase 228: Default to None
} }
} }
@ -321,11 +321,11 @@ impl JoinInlineBoundary {
host_outputs, host_outputs,
exit_bindings: vec![], exit_bindings: vec![],
#[allow(deprecated)] #[allow(deprecated)]
condition_inputs: vec![], // Phase 171: Default to empty (deprecated) condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
condition_bindings: vec![], // Phase 171-fix: Default to empty condition_bindings: vec![], // Phase 171-fix: Default to empty
expr_result: None, // Phase 33-14 expr_result: None, // Phase 33-14
loop_var_name: None, // Phase 33-16 loop_var_name: None, // Phase 33-16
carrier_info: None, // Phase 228 carrier_info: None, // Phase 228
} }
} }
@ -381,11 +381,11 @@ impl JoinInlineBoundary {
host_outputs: vec![], host_outputs: vec![],
exit_bindings, exit_bindings,
#[allow(deprecated)] #[allow(deprecated)]
condition_inputs: vec![], // Phase 171: Default to empty (deprecated) condition_inputs: vec![], // Phase 171: Default to empty (deprecated)
condition_bindings: vec![], // Phase 171-fix: Default to empty condition_bindings: vec![], // Phase 171-fix: Default to empty
expr_result: None, // Phase 33-14 expr_result: None, // Phase 33-14
loop_var_name: None, // Phase 33-16 loop_var_name: None, // Phase 33-16
carrier_info: None, // Phase 228 carrier_info: None, // Phase 228
} }
} }
@ -429,9 +429,9 @@ impl JoinInlineBoundary {
#[allow(deprecated)] #[allow(deprecated)]
condition_inputs, condition_inputs,
condition_bindings: vec![], // Phase 171-fix: Will be populated by new constructor condition_bindings: vec![], // Phase 171-fix: Will be populated by new constructor
expr_result: None, // Phase 33-14 expr_result: None, // Phase 33-14
loop_var_name: None, // Phase 33-16 loop_var_name: None, // Phase 33-16
carrier_info: None, // Phase 228 carrier_info: None, // Phase 228
} }
} }
@ -479,9 +479,9 @@ impl JoinInlineBoundary {
#[allow(deprecated)] #[allow(deprecated)]
condition_inputs, condition_inputs,
condition_bindings: vec![], // Phase 171-fix: Will be populated by new constructor condition_bindings: vec![], // Phase 171-fix: Will be populated by new constructor
expr_result: None, // Phase 33-14 expr_result: None, // Phase 33-14
loop_var_name: None, // Phase 33-16 loop_var_name: None, // Phase 33-16
carrier_info: None, // Phase 228 carrier_info: None, // Phase 228
} }
} }
@ -534,11 +534,11 @@ impl JoinInlineBoundary {
host_outputs: vec![], host_outputs: vec![],
exit_bindings: vec![], exit_bindings: vec![],
#[allow(deprecated)] #[allow(deprecated)]
condition_inputs: vec![], // Deprecated, use condition_bindings instead condition_inputs: vec![], // Deprecated, use condition_bindings instead
condition_bindings, condition_bindings,
expr_result: None, // Phase 33-14 expr_result: None, // Phase 33-14
loop_var_name: None, // Phase 33-16 loop_var_name: None, // Phase 33-16
carrier_info: None, // Phase 228 carrier_info: None, // Phase 228
} }
} }
} }
@ -550,8 +550,8 @@ mod tests {
#[test] #[test]
fn test_boundary_inputs_only() { fn test_boundary_inputs_only() {
let boundary = JoinInlineBoundary::new_inputs_only( let boundary = JoinInlineBoundary::new_inputs_only(
vec![ValueId(0)], // JoinIR uses ValueId(0) for loop var vec![ValueId(0)], // JoinIR uses ValueId(0) for loop var
vec![ValueId(4)], // Host has loop var at ValueId(4) vec![ValueId(4)], // Host has loop var at ValueId(4)
); );
assert_eq!(boundary.join_inputs.len(), 1); assert_eq!(boundary.join_inputs.len(), 1);
@ -560,17 +560,14 @@ mod tests {
#[allow(deprecated)] #[allow(deprecated)]
{ {
assert_eq!(boundary.host_outputs.len(), 0); assert_eq!(boundary.host_outputs.len(), 0);
assert_eq!(boundary.condition_inputs.len(), 0); // Phase 171: Deprecated field assert_eq!(boundary.condition_inputs.len(), 0); // Phase 171: Deprecated field
} }
assert_eq!(boundary.condition_bindings.len(), 0); // Phase 171-fix: New field assert_eq!(boundary.condition_bindings.len(), 0); // Phase 171-fix: New field
} }
#[test] #[test]
#[should_panic(expected = "join_inputs and host_inputs must have same length")] #[should_panic(expected = "join_inputs and host_inputs must have same length")]
fn test_boundary_mismatched_inputs() { fn test_boundary_mismatched_inputs() {
JoinInlineBoundary::new_inputs_only( JoinInlineBoundary::new_inputs_only(vec![ValueId(0), ValueId(1)], vec![ValueId(4)]);
vec![ValueId(0), ValueId(1)],
vec![ValueId(4)],
);
} }
} }

View File

@ -24,9 +24,9 @@
//! .build(); //! .build();
//! ``` //! ```
use crate::mir::ValueId;
use super::condition_to_joinir::ConditionBinding; use super::condition_to_joinir::ConditionBinding;
use super::inline_boundary::{JoinInlineBoundary, LoopExitBinding}; use super::inline_boundary::{JoinInlineBoundary, LoopExitBinding};
use crate::mir::ValueId;
/// Role of a parameter in JoinIR lowering (Phase 200-A) /// Role of a parameter in JoinIR lowering (Phase 200-A)
/// ///
@ -96,7 +96,7 @@ impl JoinInlineBoundaryBuilder {
expr_result: None, expr_result: None,
loop_var_name: None, loop_var_name: None,
carrier_info: None, // Phase 228: Initialize as None carrier_info: None, // Phase 228: Initialize as None
} },
} }
} }
@ -224,7 +224,8 @@ impl JoinInlineBoundaryBuilder {
// Phase 200-B: Add to condition_bindings without PHI // Phase 200-B: Add to condition_bindings without PHI
// 1. Allocate JoinIR-local ValueId // 1. Allocate JoinIR-local ValueId
let join_id = ValueId( let join_id = ValueId(
(self.boundary.join_inputs.len() + self.boundary.condition_bindings.len()) as u32 (self.boundary.join_inputs.len() + self.boundary.condition_bindings.len())
as u32,
); );
// 2. Create ConditionBinding // 2. Create ConditionBinding
@ -257,7 +258,9 @@ impl JoinInlineBoundaryBuilder {
/// ///
/// `Some(ValueId)` if the variable exists in condition_bindings, `None` otherwise. /// `Some(ValueId)` if the variable exists in condition_bindings, `None` otherwise.
pub fn get_condition_binding(&self, name: &str) -> Option<ValueId> { pub fn get_condition_binding(&self, name: &str) -> Option<ValueId> {
self.boundary.condition_bindings.iter() self.boundary
.condition_bindings
.iter()
.find(|b| b.name == name) .find(|b| b.name == name)
.map(|b| b.join_value) .map(|b| b.join_value)
} }
@ -363,18 +366,18 @@ mod tests {
#[test] #[test]
fn test_builder_pattern3_style() { fn test_builder_pattern3_style() {
// Pattern3 style: Two carriers (i + sum), exit_bindings, loop_var_name // Pattern3 style: Two carriers (i + sum), exit_bindings, loop_var_name
let boundary = JoinInlineBoundaryBuilder::new() let boundary = JoinInlineBoundaryBuilder::new()
.with_inputs(vec![ValueId(0), ValueId(1)], vec![ValueId(100), ValueId(101)]) .with_inputs(
.with_exit_bindings(vec![ vec![ValueId(0), ValueId(1)],
LoopExitBinding { vec![ValueId(100), ValueId(101)],
carrier_name: "sum".to_string(), )
join_exit_value: ValueId(18), .with_exit_bindings(vec![LoopExitBinding {
host_slot: ValueId(101), carrier_name: "sum".to_string(),
role: CarrierRole::LoopState, join_exit_value: ValueId(18),
} host_slot: ValueId(101),
]) role: CarrierRole::LoopState,
}])
.with_loop_var_name(Some("i".to_string())) .with_loop_var_name(Some("i".to_string()))
.build(); .build();
@ -391,8 +394,8 @@ mod tests {
// Pattern4 style: Dynamic carrier count, continue support // Pattern4 style: Dynamic carrier count, continue support
let boundary = JoinInlineBoundaryBuilder::new() let boundary = JoinInlineBoundaryBuilder::new()
.with_inputs( .with_inputs(
vec![ValueId(0), ValueId(1), ValueId(2)], // i + 2 carriers vec![ValueId(0), ValueId(1), ValueId(2)], // i + 2 carriers
vec![ValueId(100), ValueId(101), ValueId(102)] vec![ValueId(100), ValueId(101), ValueId(102)],
) )
.with_exit_bindings(vec![ .with_exit_bindings(vec![
LoopExitBinding { LoopExitBinding {
@ -406,7 +409,7 @@ mod tests {
join_exit_value: ValueId(20), join_exit_value: ValueId(20),
host_slot: ValueId(101), host_slot: ValueId(101),
role: CarrierRole::LoopState, role: CarrierRole::LoopState,
} },
]) ])
.with_loop_var_name(Some("i".to_string())) .with_loop_var_name(Some("i".to_string()))
.build(); .build();

View File

@ -224,7 +224,11 @@ impl<'a> LoopBodyLocalInitLowerer<'a> {
/// ///
/// * `Ok(ValueId)` - JoinIR ValueId of computed result /// * `Ok(ValueId)` - JoinIR ValueId of computed result
/// * `Err(msg)` - Unsupported expression (Fail-Fast) /// * `Err(msg)` - Unsupported expression (Fail-Fast)
fn lower_init_expr(&mut self, expr: &ASTNode, env: &LoopBodyLocalEnv) -> Result<ValueId, String> { fn lower_init_expr(
&mut self,
expr: &ASTNode,
env: &LoopBodyLocalEnv,
) -> Result<ValueId, String> {
match expr { match expr {
// Constant literal: 42, 0, 1, "string" (use Literal with value) // Constant literal: 42, 0, 1, "string" (use Literal with value)
ASTNode::Literal { value, .. } => { ASTNode::Literal { value, .. } => {
@ -488,7 +492,6 @@ impl<'a> LoopBodyLocalInitLowerer<'a> {
Ok(result_id) Ok(result_id)
} }
} }
#[cfg(test)] #[cfg(test)]

View File

@ -133,25 +133,33 @@ pub fn try_lower_loop_pattern_to_joinir(
// Step 3: Route to appropriate lowerer based on pattern // Step 3: Route to appropriate lowerer based on pattern
match pattern { match pattern {
LoopPatternKind::Pattern4Continue => { LoopPatternKind::Pattern4Continue => {
if let Some(inst) = super::loop_patterns::lower_loop_with_continue_to_joinir(loop_form, lowerer) { if let Some(inst) =
super::loop_patterns::lower_loop_with_continue_to_joinir(loop_form, lowerer)
{
eprintln!("[try_lower_loop_pattern] ✅ Pattern 4 (Continue) matched"); eprintln!("[try_lower_loop_pattern] ✅ Pattern 4 (Continue) matched");
return Some(inst); return Some(inst);
} }
} }
LoopPatternKind::Pattern3IfPhi => { LoopPatternKind::Pattern3IfPhi => {
if let Some(inst) = super::loop_patterns::lower_loop_with_conditional_phi_to_joinir(loop_form, lowerer) { if let Some(inst) =
super::loop_patterns::lower_loop_with_conditional_phi_to_joinir(loop_form, lowerer)
{
eprintln!("[try_lower_loop_pattern] ✅ Pattern 3 (If-Else PHI) matched"); eprintln!("[try_lower_loop_pattern] ✅ Pattern 3 (If-Else PHI) matched");
return Some(inst); return Some(inst);
} }
} }
LoopPatternKind::Pattern2Break => { LoopPatternKind::Pattern2Break => {
if let Some(inst) = super::loop_patterns::lower_loop_with_break_to_joinir(loop_form, lowerer) { if let Some(inst) =
super::loop_patterns::lower_loop_with_break_to_joinir(loop_form, lowerer)
{
eprintln!("[try_lower_loop_pattern] ✅ Pattern 2 (Break) matched"); eprintln!("[try_lower_loop_pattern] ✅ Pattern 2 (Break) matched");
return Some(inst); return Some(inst);
} }
} }
LoopPatternKind::Pattern1SimpleWhile => { LoopPatternKind::Pattern1SimpleWhile => {
if let Some(inst) = super::loop_patterns::lower_simple_while_to_joinir(loop_form, lowerer) { if let Some(inst) =
super::loop_patterns::lower_simple_while_to_joinir(loop_form, lowerer)
{
eprintln!("[try_lower_loop_pattern] ✅ Pattern 1 (Simple While) matched"); eprintln!("[try_lower_loop_pattern] ✅ Pattern 1 (Simple While) matched");
return Some(inst); return Some(inst);
} }

View File

@ -48,13 +48,13 @@
pub mod simple_while; pub mod simple_while;
pub mod with_break; pub mod with_break;
pub mod with_if_phi;
pub mod with_continue; pub mod with_continue;
pub mod with_if_phi;
pub use simple_while::lower_simple_while_to_joinir; pub use simple_while::lower_simple_while_to_joinir;
pub use with_break::lower_loop_with_break_to_joinir; pub use with_break::lower_loop_with_break_to_joinir;
pub use with_if_phi::lower_loop_with_conditional_phi_to_joinir;
pub use with_continue::lower_loop_with_continue_to_joinir; pub use with_continue::lower_loop_with_continue_to_joinir;
pub use with_if_phi::lower_loop_with_conditional_phi_to_joinir;
// ============================================================================ // ============================================================================
// Helper Functions (Shared Utilities) // Helper Functions (Shared Utilities)
@ -76,7 +76,6 @@ pub use with_continue::lower_loop_with_continue_to_joinir;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
// ======================================================================== // ========================================================================
// Pattern 1: Simple While Loop Tests // Pattern 1: Simple While Loop Tests

View File

@ -236,15 +236,17 @@ impl CaseALoweringShape {
// Phase 170-C-2b: Use UpdateSummary if available // Phase 170-C-2b: Use UpdateSummary if available
if let Some(ref summary) = features.update_summary { if let Some(ref summary) = features.update_summary {
// Single carrier with CounterLike update → StringExamination // Single carrier with CounterLike update → StringExamination
if summary.carriers.len() == 1 if summary.carriers.len() == 1 && summary.carriers[0].kind == UpdateKind::CounterLike {
&& summary.carriers[0].kind == UpdateKind::CounterLike
{
return CaseALoweringShape::StringExamination; return CaseALoweringShape::StringExamination;
} }
// Any AccumulationLike carrier → ArrayAccumulation (for single carrier) // Any AccumulationLike carrier → ArrayAccumulation (for single carrier)
// or IterationWithAccumulation (for multiple carriers) // or IterationWithAccumulation (for multiple carriers)
if summary.carriers.iter().any(|c| c.kind == UpdateKind::AccumulationLike) { if summary
.carriers
.iter()
.any(|c| c.kind == UpdateKind::AccumulationLike)
{
if carrier_count == 1 { if carrier_count == 1 {
return CaseALoweringShape::ArrayAccumulation; return CaseALoweringShape::ArrayAccumulation;
} else { } else {
@ -299,12 +301,14 @@ impl CaseALoweringShape {
// Note: carriers is BTreeSet<String>, so each item is already a String // Note: carriers is BTreeSet<String>, so each item is already a String
let carrier_names: Vec<String> = scope.carriers.iter().cloned().collect(); let carrier_names: Vec<String> = scope.carriers.iter().cloned().collect();
let update_summary = let update_summary =
crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_by_name(&carrier_names); crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_by_name(
&carrier_names,
);
// Create stub features (Phase 170-B will use real LoopFeatures) // Create stub features (Phase 170-B will use real LoopFeatures)
let stub_features = crate::mir::loop_pattern_detection::LoopFeatures { let stub_features = crate::mir::loop_pattern_detection::LoopFeatures {
has_break: false, // Unknown from LoopScopeShape alone has_break: false, // Unknown from LoopScopeShape alone
has_continue: false, // Unknown from LoopScopeShape alone has_continue: false, // Unknown from LoopScopeShape alone
has_if: false, has_if: false,
has_if_else_phi: false, has_if_else_phi: false,
carrier_count, carrier_count,
@ -319,7 +323,10 @@ impl CaseALoweringShape {
/// Is this a recognized lowering shape? /// Is this a recognized lowering shape?
#[allow(dead_code)] #[allow(dead_code)]
pub fn is_recognized(&self) -> bool { pub fn is_recognized(&self) -> bool {
!matches!(self, CaseALoweringShape::NotCaseA | CaseALoweringShape::Generic) !matches!(
self,
CaseALoweringShape::NotCaseA | CaseALoweringShape::Generic
)
} }
/// Get human-readable name for tracing/debugging /// Get human-readable name for tracing/debugging
@ -340,9 +347,18 @@ mod tests {
#[test] #[test]
fn test_shape_display() { fn test_shape_display() {
assert_eq!(CaseALoweringShape::StringExamination.name(), "StringExamination"); assert_eq!(
assert_eq!(CaseALoweringShape::ArrayAccumulation.name(), "ArrayAccumulation"); CaseALoweringShape::StringExamination.name(),
assert_eq!(CaseALoweringShape::IterationWithAccumulation.name(), "IterationWithAccumulation"); "StringExamination"
);
assert_eq!(
CaseALoweringShape::ArrayAccumulation.name(),
"ArrayAccumulation"
);
assert_eq!(
CaseALoweringShape::IterationWithAccumulation.name(),
"IterationWithAccumulation"
);
assert_eq!(CaseALoweringShape::Generic.name(), "Generic"); assert_eq!(CaseALoweringShape::Generic.name(), "Generic");
assert_eq!(CaseALoweringShape::NotCaseA.name(), "NotCaseA"); assert_eq!(CaseALoweringShape::NotCaseA.name(), "NotCaseA");
} }

View File

@ -147,7 +147,10 @@ impl LoopToJoinLowerer {
} }
// Phase 33-23: Validator箱に検証を委譲 // Phase 33-23: Validator箱に検証を委譲
if !self.validator.is_supported_case_a(func, &region, &exit_edges, &scope) { if !self
.validator
.is_supported_case_a(func, &region, &exit_edges, &scope)
{
if self.debug { if self.debug {
eprintln!( eprintln!(
"[LoopToJoinLowerer] rejected by validator: {:?}", "[LoopToJoinLowerer] rejected by validator: {:?}",

View File

@ -81,7 +81,8 @@ impl LoopUpdateAnalyzer {
pub fn analyze_carrier_updates( pub fn analyze_carrier_updates(
body: &[ASTNode], body: &[ASTNode],
carriers: &[CarrierVar], carriers: &[CarrierVar],
) -> BTreeMap<String, UpdateExpr> { // Phase 222.5-D: HashMap → BTreeMap for determinism ) -> BTreeMap<String, UpdateExpr> {
// Phase 222.5-D: HashMap → BTreeMap for determinism
let mut updates = BTreeMap::new(); let mut updates = BTreeMap::new();
// Extract carrier names for quick lookup // Extract carrier names for quick lookup
@ -109,7 +110,9 @@ impl LoopUpdateAnalyzer {
if let Some(target_name) = Self::extract_variable_name(target) { if let Some(target_name) = Self::extract_variable_name(target) {
if carrier_names.contains(&target_name.as_str()) { if carrier_names.contains(&target_name.as_str()) {
// This is a carrier update, analyze the RHS // This is a carrier update, analyze the RHS
if let Some(update_expr) = Self::analyze_update_value(&target_name, value) { if let Some(update_expr) =
Self::analyze_update_value(&target_name, value)
{
updates.insert(target_name, update_expr); updates.insert(target_name, update_expr);
} }
} }
@ -233,9 +236,7 @@ impl LoopUpdateAnalyzer {
} => Some(UpdateRhs::StringLiteral(s.clone())), } => Some(UpdateRhs::StringLiteral(s.clone())),
// Variable: sum + i (also handles: result + ch) // Variable: sum + i (also handles: result + ch)
ASTNode::Variable { name, .. } => { ASTNode::Variable { name, .. } => Some(UpdateRhs::Variable(name.clone())),
Some(UpdateRhs::Variable(name.clone()))
}
// Phase 190: Number accumulation pattern detection // Phase 190: Number accumulation pattern detection
// This is called from analyze_update_value, so we're analyzing the full RHS of an assignment // This is called from analyze_update_value, so we're analyzing the full RHS of an assignment
@ -245,9 +246,9 @@ impl LoopUpdateAnalyzer {
// Phase 178: Method call or other complex expression // Phase 178: Method call or other complex expression
// e.g., result + s.substring(pos, end) // e.g., result + s.substring(pos, end)
ASTNode::Call { .. } ASTNode::Call { .. } | ASTNode::MethodCall { .. } | ASTNode::UnaryOp { .. } => {
| ASTNode::MethodCall { .. } Some(UpdateRhs::Other)
| ASTNode::UnaryOp { .. } => Some(UpdateRhs::Other), }
_ => None, _ => None,
} }
@ -617,7 +618,11 @@ mod tests {
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers); let updates = LoopUpdateAnalyzer::analyze_carrier_updates(&body, &carriers);
// Verify both carriers have updates // Verify both carriers have updates
assert_eq!(updates.len(), 2, "Should detect updates for both i and result"); assert_eq!(
updates.len(),
2,
"Should detect updates for both i and result"
);
// Verify i = i + 1 (Const increment) // Verify i = i + 1 (Const increment)
if let Some(UpdateExpr::BinOp { lhs, op, rhs }) = updates.get("i") { if let Some(UpdateExpr::BinOp { lhs, op, rhs }) = updates.get("i") {
@ -640,10 +645,16 @@ mod tests {
assert_eq!(*base, 10, "NumberAccumulation should use base 10"); assert_eq!(*base, 10, "NumberAccumulation should use base 10");
assert_eq!(digit_var, "digit_pos", "Should use digit_pos variable"); assert_eq!(digit_var, "digit_pos", "Should use digit_pos variable");
} else { } else {
panic!("Expected NumberAccumulation for result update, got {:?}", rhs); panic!(
"Expected NumberAccumulation for result update, got {:?}",
rhs
);
} }
} else { } else {
panic!("Expected BinOp for result update, got {:?}", updates.get("result")); panic!(
"Expected BinOp for result update, got {:?}",
updates.get("result")
);
} }
} }
} }

View File

@ -155,7 +155,9 @@ impl LoopUpdateSummary {
/// ///
/// Returns a set of variable names that are assigned (LHS) in the loop body. /// Returns a set of variable names that are assigned (LHS) in the loop body.
/// This prevents phantom carriers from non-assigned variables. /// This prevents phantom carriers from non-assigned variables.
fn extract_assigned_variables(loop_body: &[crate::ast::ASTNode]) -> std::collections::HashSet<String> { fn extract_assigned_variables(
loop_body: &[crate::ast::ASTNode],
) -> std::collections::HashSet<String> {
use crate::ast::ASTNode; use crate::ast::ASTNode;
let mut assigned = std::collections::HashSet::new(); let mut assigned = std::collections::HashSet::new();
@ -170,7 +172,11 @@ fn extract_assigned_variables(loop_body: &[crate::ast::ASTNode]) -> std::collect
visit_node(value, assigned); visit_node(value, assigned);
} }
// If statement: recurse into then/else branches // If statement: recurse into then/else branches
ASTNode::If { then_body, else_body, .. } => { ASTNode::If {
then_body,
else_body,
..
} => {
for stmt in then_body { for stmt in then_body {
visit_node(stmt, assigned); visit_node(stmt, assigned);
} }
@ -207,7 +213,12 @@ fn classify_update_kind_from_rhs(rhs: &crate::ast::ASTNode) -> UpdateKind {
match rhs { match rhs {
// x = x + 1 → CounterLike // x = x + 1 → CounterLike
// x = x + n → AccumulationLike (where n is not 1) // x = x + n → AccumulationLike (where n is not 1)
ASTNode::BinaryOp { operator, left, right, .. } => { ASTNode::BinaryOp {
operator,
left,
right,
..
} => {
if matches!(operator, BinaryOperator::Add) { if matches!(operator, BinaryOperator::Add) {
// Check if left is self-reference (will be validated by caller) // Check if left is self-reference (will be validated by caller)
if matches!(left.as_ref(), ASTNode::Variable { .. }) { if matches!(left.as_ref(), ASTNode::Variable { .. }) {
@ -215,9 +226,9 @@ fn classify_update_kind_from_rhs(rhs: &crate::ast::ASTNode) -> UpdateKind {
if let ASTNode::Literal { value, .. } = right.as_ref() { if let ASTNode::Literal { value, .. } = right.as_ref() {
if let LiteralValue::Integer(n) = value { if let LiteralValue::Integer(n) = value {
if *n == 1 { if *n == 1 {
return UpdateKind::CounterLike; // x = x + 1 return UpdateKind::CounterLike; // x = x + 1
} else { } else {
return UpdateKind::AccumulationLike; // x = x + n return UpdateKind::AccumulationLike; // x = x + n
} }
} }
} else { } else {
@ -251,7 +262,10 @@ fn classify_update_kind_from_rhs(rhs: &crate::ast::ASTNode) -> UpdateKind {
/// Phase 219: Extract assignment RHS for a given variable /// Phase 219: Extract assignment RHS for a given variable
/// ///
/// Returns the RHS expression of the first assignment to `var_name` in loop body. /// Returns the RHS expression of the first assignment to `var_name` in loop body.
fn find_assignment_rhs<'a>(var_name: &str, loop_body: &'a [crate::ast::ASTNode]) -> Option<&'a crate::ast::ASTNode> { fn find_assignment_rhs<'a>(
var_name: &str,
loop_body: &'a [crate::ast::ASTNode],
) -> Option<&'a crate::ast::ASTNode> {
use crate::ast::ASTNode; use crate::ast::ASTNode;
fn visit_node<'a>(var_name: &str, node: &'a ASTNode) -> Option<&'a ASTNode> { fn visit_node<'a>(var_name: &str, node: &'a ASTNode) -> Option<&'a ASTNode> {
@ -265,7 +279,11 @@ fn find_assignment_rhs<'a>(var_name: &str, loop_body: &'a [crate::ast::ASTNode])
// Recurse into value // Recurse into value
visit_node(var_name, value) visit_node(var_name, value)
} }
ASTNode::If { then_body, else_body, .. } => { ASTNode::If {
then_body,
else_body,
..
} => {
for stmt in then_body { for stmt in then_body {
if let Some(rhs) = visit_node(var_name, stmt) { if let Some(rhs) = visit_node(var_name, stmt) {
return Some(rhs); return Some(rhs);
@ -354,7 +372,7 @@ pub fn analyze_loop_updates_by_name(carrier_names: &[String]) -> LoopUpdateSumma
.iter() .iter()
.map(|name| CarrierUpdateInfo { .map(|name| CarrierUpdateInfo {
name: name.clone(), name: name.clone(),
kind: UpdateKind::AccumulationLike, // Default to accumulation kind: UpdateKind::AccumulationLike, // Default to accumulation
then_expr: None, then_expr: None,
else_expr: None, else_expr: None,
}) })

View File

@ -61,11 +61,7 @@ impl LoopViewBuilder {
/// ///
/// - `Some(JoinModule)`: Lowering成功 /// - `Some(JoinModule)`: Lowering成功
/// - `None`: 未サポートパターン(フォールバック経路へ) /// - `None`: 未サポートパターン(フォールバック経路へ)
pub fn build( pub fn build(&self, scope: LoopScopeShape, func_name: Option<&str>) -> Option<JoinModule> {
&self,
scope: LoopScopeShape,
func_name: Option<&str>,
) -> Option<JoinModule> {
let name = func_name.unwrap_or(""); let name = func_name.unwrap_or("");
// Phase 188-Impl-1: Pattern 1 (Simple While Loop) detection // Phase 188-Impl-1: Pattern 1 (Simple While Loop) detection
@ -139,7 +135,10 @@ impl LoopViewBuilder {
&mut join_value_space, &mut join_value_space,
) { ) {
if self.debug { if self.debug {
eprintln!("[LoopViewBuilder] Pattern 1 lowering succeeded for {:?}", name); eprintln!(
"[LoopViewBuilder] Pattern 1 lowering succeeded for {:?}",
name
);
} }
return Some(result); return Some(result);
} }
@ -181,7 +180,9 @@ impl LoopViewBuilder {
} }
CaseALoweringShape::IterationWithAccumulation => { CaseALoweringShape::IterationWithAccumulation => {
if self.debug { if self.debug {
eprintln!("[LoopViewBuilder] Shape: IterationWithAccumulation → stage1 lowerer"); eprintln!(
"[LoopViewBuilder] Shape: IterationWithAccumulation → stage1 lowerer"
);
} }
generic_case_a::lower_case_a_stage1_usingresolver_with_scope(scope) generic_case_a::lower_case_a_stage1_usingresolver_with_scope(scope)
} }

View File

@ -9,6 +9,24 @@ pub(crate) fn build_fragment_meta(
i_exit: ValueId, i_exit: ValueId,
carrier_exit_ids: &[ValueId], carrier_exit_ids: &[ValueId],
) -> JoinFragmentMeta { ) -> JoinFragmentMeta {
let dev_on = env::joinir_dev_enabled();
if carrier_exit_ids.len() != carrier_info.carriers.len() {
let msg = format!(
"[joinir/boundary] exit value length mismatch: carriers={} exit_ids={}",
carrier_info.carriers.len(),
carrier_exit_ids.len()
);
debug_assert!(
carrier_exit_ids.len() == carrier_info.carriers.len(),
"{}",
msg
);
if dev_on {
panic!("{}", msg);
}
}
let mut exit_values = Vec::new(); let mut exit_values = Vec::new();
exit_values.push((loop_var_name.to_string(), i_exit)); exit_values.push((loop_var_name.to_string(), i_exit));
@ -16,9 +34,9 @@ pub(crate) fn build_fragment_meta(
exit_values.push((carrier.name.clone(), carrier_exit_ids[idx])); exit_values.push((carrier.name.clone(), carrier_exit_ids[idx]));
} }
if env::joinir_debug_level() > 0 { if env::joinir_debug_level() > 0 || dev_on {
eprintln!( eprintln!(
"[joinir/boundary_builder] Exit values (loop_var='{}', carriers={}): {:?}", "[joinir/boundary] Exit values (loop_var='{}', carriers={}): {:?}",
loop_var_name, loop_var_name,
carrier_info.carriers.len(), carrier_info.carriers.len(),
exit_values exit_values

View File

@ -1,15 +1,15 @@
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
use crate::mir::join_ir::lowering::condition_lowering_box::ConditionContext; use crate::mir::join_ir::lowering::condition_lowering_box::ConditionContext;
use crate::mir::join_ir::lowering::condition_to_joinir::lower_condition_to_joinir; use crate::mir::join_ir::lowering::condition_to_joinir::lower_condition_to_joinir;
use crate::mir::join_ir::lowering::expr_lowerer::{ExprContext, ExprLowerer}; use crate::mir::join_ir::lowering::expr_lowerer::{ExprContext, ExprLowerer};
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv; use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv; use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
use crate::mir::join_ir::JoinInst; use crate::mir::join_ir::JoinInst;
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
use crate::mir::ValueId; use crate::mir::ValueId;
use crate::mir::builder::MirBuilder;
/// Build a Pattern2ScopeManager for ExprLowerer paths. /// Build a Pattern2ScopeManager for ExprLowerer paths.
fn make_scope_manager<'a>( fn make_scope_manager<'a>(

View File

@ -62,18 +62,18 @@ use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta}; use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
use crate::mir::join_ir::lowering::condition_to_joinir::{lower_condition_to_joinir, ConditionEnv}; use crate::mir::join_ir::lowering::condition_to_joinir::{lower_condition_to_joinir, ConditionEnv};
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace; use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv; // Phase 244
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
use crate::mir::join_ir::lowering::loop_update_analyzer::{UpdateExpr, UpdateRhs}; use crate::mir::join_ir::lowering::loop_update_analyzer::{UpdateExpr, UpdateRhs};
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv; // Phase 244
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv; // Phase 244
use crate::mir::join_ir::{ use crate::mir::join_ir::{
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst,
MirLikeInst, UnaryOp, UnaryOp,
}; };
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
use crate::mir::loop_pattern_detection::error_messages::{ use crate::mir::loop_pattern_detection::error_messages::{
format_unsupported_condition_error, extract_body_local_names, extract_body_local_names, format_unsupported_condition_error,
}; };
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv; // Phase 244
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
use crate::mir::ValueId; use crate::mir::ValueId;
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
@ -130,15 +130,15 @@ pub(crate) fn lower_loop_with_continue_minimal(
// Phase 170-D-impl-3: Validate that loop condition only uses supported variable scopes // Phase 170-D-impl-3: Validate that loop condition only uses supported variable scopes
// LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables // LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables
let loop_var_name = carrier_info.loop_var_name.clone(); let loop_var_name = carrier_info.loop_var_name.clone();
let loop_cond_scope = LoopConditionScopeBox::analyze( let loop_cond_scope =
&loop_var_name, LoopConditionScopeBox::analyze(&loop_var_name, &[condition], Some(&_scope));
&[condition],
Some(&_scope),
);
if loop_cond_scope.has_loop_body_local() { if loop_cond_scope.has_loop_body_local() {
let body_local_names = extract_body_local_names(&loop_cond_scope.vars); let body_local_names = extract_body_local_names(&loop_cond_scope.vars);
return Err(format_unsupported_condition_error("pattern4", &body_local_names)); return Err(format_unsupported_condition_error(
"pattern4",
&body_local_names,
));
} }
eprintln!( eprintln!(
@ -152,7 +152,11 @@ pub(crate) fn lower_loop_with_continue_minimal(
eprintln!( eprintln!(
"[joinir/pattern4] Phase 202-C: Generating JoinIR for {} carriers: {:?}", "[joinir/pattern4] Phase 202-C: Generating JoinIR for {} carriers: {:?}",
carrier_count, carrier_count,
carrier_info.carriers.iter().map(|c| &c.name).collect::<Vec<_>>() carrier_info
.carriers
.iter()
.map(|c| &c.name)
.collect::<Vec<_>>()
); );
// ================================================================== // ==================================================================
@ -171,7 +175,7 @@ pub(crate) fn lower_loop_with_continue_minimal(
for _ in 0..carrier_count { for _ in 0..carrier_count {
carrier_init_ids.push(join_value_space.alloc_local()); carrier_init_ids.push(join_value_space.alloc_local());
} }
let loop_result = join_value_space.alloc_local(); // result from loop_step let loop_result = join_value_space.alloc_local(); // result from loop_step
// loop_step() parameters: [i_param, carrier1_param, carrier2_param, ...] // loop_step() parameters: [i_param, carrier1_param, carrier2_param, ...]
let i_param = join_value_space.alloc_local(); let i_param = join_value_space.alloc_local();
@ -202,10 +206,12 @@ pub(crate) fn lower_loop_with_continue_minimal(
// Phase 169 / Phase 171-fix / Phase 244: Lower condition using ConditionLoweringBox trait // Phase 169 / Phase 171-fix / Phase 244: Lower condition using ConditionLoweringBox trait
let (cond_value, mut cond_instructions) = { let (cond_value, mut cond_instructions) = {
use crate::mir::join_ir::lowering::expr_lowerer::{ExprContext, ExprLowerer};
use crate::mir::join_ir::lowering::condition_lowering_box::{ConditionLoweringBox, ConditionContext};
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
use crate::mir::builder::MirBuilder; use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::condition_lowering_box::{
ConditionContext, ConditionLoweringBox,
};
use crate::mir::join_ir::lowering::expr_lowerer::{ExprContext, ExprLowerer};
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
// Build minimal ScopeManager for header condition // Build minimal ScopeManager for header condition
let empty_body_env = LoopBodyLocalEnv::new(); let empty_body_env = LoopBodyLocalEnv::new();
@ -221,7 +227,8 @@ pub(crate) fn lower_loop_with_continue_minimal(
if ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(condition) { if ExprLowerer::<Pattern2ScopeManager>::is_supported_condition(condition) {
// Phase 244: ExprLowerer via ConditionLoweringBox trait // Phase 244: ExprLowerer via ConditionLoweringBox trait
let mut dummy_builder = MirBuilder::new(); let mut dummy_builder = MirBuilder::new();
let mut expr_lowerer = ExprLowerer::new(&scope_manager, ExprContext::Condition, &mut dummy_builder); let mut expr_lowerer =
ExprLowerer::new(&scope_manager, ExprContext::Condition, &mut dummy_builder);
let context = ConditionContext { let context = ConditionContext {
loop_var_name: loop_var_name.clone(), loop_var_name: loop_var_name.clone(),
@ -263,7 +270,7 @@ pub(crate) fn lower_loop_with_continue_minimal(
let mut carrier_next_ids: Vec<ValueId> = Vec::new(); let mut carrier_next_ids: Vec<ValueId> = Vec::new();
let mut carrier_merged_ids: Vec<ValueId> = Vec::new(); let mut carrier_merged_ids: Vec<ValueId> = Vec::new();
for _ in 0..carrier_count { for _ in 0..carrier_count {
carrier_next_ids.push(alloc_value()); // carrier_next = carrier + ... carrier_next_ids.push(alloc_value()); // carrier_next = carrier + ...
carrier_merged_ids.push(alloc_value()); // carrier_merged = Select(...) carrier_merged_ids.push(alloc_value()); // carrier_merged = Select(...)
} }
@ -303,11 +310,8 @@ pub(crate) fn lower_loop_with_continue_minimal(
let mut loop_step_params = vec![i_param]; let mut loop_step_params = vec![i_param];
loop_step_params.extend(carrier_param_ids.iter().copied()); loop_step_params.extend(carrier_param_ids.iter().copied());
let mut loop_step_func = JoinFunction::new( let mut loop_step_func =
loop_step_id, JoinFunction::new(loop_step_id, "loop_step".to_string(), loop_step_params);
"loop_step".to_string(),
loop_step_params,
);
// ------------------------------------------------------------------ // ------------------------------------------------------------------
// Natural Exit Condition Check (Phase 169: from AST) // Natural Exit Condition Check (Phase 169: from AST)
@ -408,9 +412,15 @@ pub(crate) fn lower_loop_with_continue_minimal(
let carrier_name = &carrier_info.carriers[idx].name; let carrier_name = &carrier_info.carriers[idx].name;
// Phase 197: Extract RHS from update expression metadata // Phase 197: Extract RHS from update expression metadata
eprintln!("[loop_with_continue_minimal] Processing carrier '{}' (idx={})", carrier_name, idx); eprintln!(
"[loop_with_continue_minimal] Processing carrier '{}' (idx={})",
carrier_name, idx
);
let rhs = if let Some(update_expr) = carrier_updates.get(carrier_name) { let rhs = if let Some(update_expr) = carrier_updates.get(carrier_name) {
eprintln!("[loop_with_continue_minimal] Found update expr: {:?}", update_expr); eprintln!(
"[loop_with_continue_minimal] Found update expr: {:?}",
update_expr
);
match update_expr { match update_expr {
UpdateExpr::BinOp { op, rhs, .. } => { UpdateExpr::BinOp { op, rhs, .. } => {
// Verify operator is Add (only supported for now) // Verify operator is Add (only supported for now)
@ -451,12 +461,12 @@ pub(crate) fn lower_loop_with_continue_minimal(
// This is effectively a passthrough (no JoinIR update) // This is effectively a passthrough (no JoinIR update)
loop_step_func.body.push(JoinInst::Select { loop_step_func.body.push(JoinInst::Select {
dst: carrier_merged, dst: carrier_merged,
cond: continue_cond, // Condition doesn't matter when both values are same cond: continue_cond, // Condition doesn't matter when both values are same
then_val: carrier_param, then_val: carrier_param,
else_val: carrier_param, else_val: carrier_param,
type_hint: None, type_hint: None,
}); });
continue; // Skip the BinOp and normal Select below continue; // Skip the BinOp and normal Select below
} }
// Phase 178: String updates detected but not lowered to JoinIR yet // Phase 178: String updates detected but not lowered to JoinIR yet
// Skip JoinIR update - use Select passthrough to keep carrier_merged defined // Skip JoinIR update - use Select passthrough to keep carrier_merged defined
@ -469,12 +479,12 @@ pub(crate) fn lower_loop_with_continue_minimal(
// This is effectively a passthrough (no JoinIR update) // This is effectively a passthrough (no JoinIR update)
loop_step_func.body.push(JoinInst::Select { loop_step_func.body.push(JoinInst::Select {
dst: carrier_merged, dst: carrier_merged,
cond: continue_cond, // Condition doesn't matter when both values are same cond: continue_cond, // Condition doesn't matter when both values are same
then_val: carrier_param, then_val: carrier_param,
else_val: carrier_param, else_val: carrier_param,
type_hint: None, type_hint: None,
}); });
continue; // Skip the BinOp and normal Select below continue; // Skip the BinOp and normal Select below
} }
} }
} }
@ -495,8 +505,10 @@ pub(crate) fn lower_loop_with_continue_minimal(
}; };
// carrier_next = carrier_param + rhs // carrier_next = carrier_param + rhs
eprintln!("[loop_with_continue_minimal] Generating: ValueId({}) = ValueId({}) + ValueId({})", eprintln!(
carrier_next.0, carrier_param.0, rhs.0); "[loop_with_continue_minimal] Generating: ValueId({}) = ValueId({}) + ValueId({})",
carrier_next.0, carrier_param.0, rhs.0
);
loop_step_func loop_step_func
.body .body
.push(JoinInst::Compute(MirLikeInst::BinOp { .push(JoinInst::Compute(MirLikeInst::BinOp {
@ -510,8 +522,8 @@ pub(crate) fn lower_loop_with_continue_minimal(
loop_step_func.body.push(JoinInst::Select { loop_step_func.body.push(JoinInst::Select {
dst: carrier_merged, dst: carrier_merged,
cond: continue_cond, cond: continue_cond,
then_val: carrier_param, // Continue: no update then_val: carrier_param, // Continue: no update
else_val: carrier_next, // Normal: update else_val: carrier_next, // Normal: update
type_hint: None, type_hint: None,
}); });
} }
@ -525,7 +537,7 @@ pub(crate) fn lower_loop_with_continue_minimal(
loop_step_func.body.push(JoinInst::Call { loop_step_func.body.push(JoinInst::Call {
func: loop_step_id, func: loop_step_id,
args: tail_call_args, args: tail_call_args,
k_next: None, // CRITICAL: None for tail call k_next: None, // CRITICAL: None for tail call
dst: None, dst: None,
}); });
@ -534,11 +546,8 @@ pub(crate) fn lower_loop_with_continue_minimal(
// ================================================================== // ==================================================================
// k_exit(carrier1_exit, carrier2_exit, ...) function // k_exit(carrier1_exit, carrier2_exit, ...) function
// ================================================================== // ==================================================================
let mut k_exit_func = JoinFunction::new( let mut k_exit_func =
k_exit_id, JoinFunction::new(k_exit_id, "k_exit".to_string(), carrier_exit_ids.clone());
"k_exit".to_string(),
carrier_exit_ids.clone(),
);
// For now, return the first carrier's exit value (or void if no carriers) // For now, return the first carrier's exit value (or void if no carriers)
// TODO: Consider returning a tuple or using a different mechanism // TODO: Consider returning a tuple or using a different mechanism
@ -562,7 +571,11 @@ pub(crate) fn lower_loop_with_continue_minimal(
eprintln!( eprintln!(
"[joinir/pattern4] Carriers: {} ({:?})", "[joinir/pattern4] Carriers: {} ({:?})",
carrier_count, carrier_count,
carrier_info.carriers.iter().map(|c| c.name.as_str()).collect::<Vec<_>>() carrier_info
.carriers
.iter()
.map(|c| c.name.as_str())
.collect::<Vec<_>>()
); );
// ================================================================== // ==================================================================

View File

@ -32,14 +32,16 @@ use crate::ast::ASTNode;
use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta}; use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta};
use crate::mir::join_ir::lowering::condition_env::ConditionEnv; use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
use crate::mir::join_ir::lowering::condition_lowerer::lower_value_expression; use crate::mir::join_ir::lowering::condition_lowerer::lower_value_expression;
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use crate::mir::join_ir::lowering::condition_pattern::{analyze_condition_pattern, ConditionPattern}; use crate::mir::join_ir::lowering::condition_pattern::{
use crate::mir::ValueId; analyze_condition_pattern, ConditionPattern,
use crate::mir::join_ir::{
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
MirLikeInst, UnaryOp,
}; };
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
use crate::mir::join_ir::{
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst,
UnaryOp,
};
use crate::mir::ValueId;
/// Phase 213: Lower if-sum pattern to JoinIR using AST /// Phase 213: Lower if-sum pattern to JoinIR using AST
/// ///
@ -83,22 +85,34 @@ pub fn lower_if_sum_pattern(
// Uses cond_env for variable resolution (e.g., `len` in `i < len`) // Uses cond_env for variable resolution (e.g., `len` in `i < len`)
let (loop_var, loop_op, loop_lhs_val, loop_limit_val, loop_limit_insts) = let (loop_var, loop_op, loop_lhs_val, loop_limit_val, loop_limit_insts) =
extract_loop_condition(loop_condition, &mut alloc_value, cond_env)?; extract_loop_condition(loop_condition, &mut alloc_value, cond_env)?;
eprintln!("[joinir/pattern3/if-sum] Loop condition: {} {:?} ValueId({})", loop_var, loop_op, loop_limit_val.0); eprintln!(
"[joinir/pattern3/if-sum] Loop condition: {} {:?} ValueId({})",
loop_var, loop_op, loop_limit_val.0
);
// Step 2: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=ValueId) // Step 2: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=ValueId)
// Phase 220-D: Now returns ValueId and instructions // Phase 220-D: Now returns ValueId and instructions
// Phase 242-EX-A: Now supports complex LHS // Phase 242-EX-A: Now supports complex LHS
let (if_var, if_op, if_lhs_val, if_value_val, if_value_insts) = let (if_var, if_op, if_lhs_val, if_value_val, if_value_insts) =
extract_if_condition(if_stmt, &mut alloc_value, cond_env)?; extract_if_condition(if_stmt, &mut alloc_value, cond_env)?;
eprintln!("[joinir/pattern3/if-sum] If condition: {} {:?} ValueId({})", if_var, if_op, if_value_val.0); eprintln!(
"[joinir/pattern3/if-sum] If condition: {} {:?} ValueId({})",
if_var, if_op, if_value_val.0
);
// Step 3: Extract then-branch update (e.g., sum = sum + 1 → var="sum", addend=1) // Step 3: Extract then-branch update (e.g., sum = sum + 1 → var="sum", addend=1)
let (update_var, update_addend) = extract_then_update(if_stmt)?; let (update_var, update_addend) = extract_then_update(if_stmt)?;
eprintln!("[joinir/pattern3/if-sum] Then update: {} += {}", update_var, update_addend); eprintln!(
"[joinir/pattern3/if-sum] Then update: {} += {}",
update_var, update_addend
);
// Step 4: Extract counter update (e.g., i = i + 1 → var="i", step=1) // Step 4: Extract counter update (e.g., i = i + 1 → var="i", step=1)
let (counter_var, counter_step) = extract_counter_update(body, &loop_var)?; let (counter_var, counter_step) = extract_counter_update(body, &loop_var)?;
eprintln!("[joinir/pattern3/if-sum] Counter update: {} += {}", counter_var, counter_step); eprintln!(
"[joinir/pattern3/if-sum] Counter update: {} += {}",
counter_var, counter_step
);
// Step 5: Generate JoinIR // Step 5: Generate JoinIR
let mut alloc_value = || join_value_space.alloc_local(); let mut alloc_value = || join_value_space.alloc_local();
@ -111,10 +125,10 @@ pub fn lower_if_sum_pattern(
// === ValueId allocation === // === ValueId allocation ===
// main() locals // main() locals
let i_init_val = alloc_value(); // i = 0 let i_init_val = alloc_value(); // i = 0
let sum_init_val = alloc_value(); // sum = 0 let sum_init_val = alloc_value(); // sum = 0
let count_init_val = alloc_value(); // count = 0 (optional) let count_init_val = alloc_value(); // count = 0 (optional)
let loop_result = alloc_value(); // result from loop_step let loop_result = alloc_value(); // result from loop_step
// loop_step params // loop_step params
let i_param = alloc_value(); let i_param = alloc_value();
@ -124,19 +138,19 @@ pub fn lower_if_sum_pattern(
// loop_step locals // loop_step locals
// Phase 220-D: loop_limit_val and if_value_val are already allocated by extract_*_condition() // Phase 220-D: loop_limit_val and if_value_val are already allocated by extract_*_condition()
// and will be used directly from their return values // and will be used directly from their return values
let cmp_loop = alloc_value(); // loop condition comparison let cmp_loop = alloc_value(); // loop condition comparison
let exit_cond = alloc_value(); // negated loop condition let exit_cond = alloc_value(); // negated loop condition
let if_cmp = alloc_value(); // if condition comparison let if_cmp = alloc_value(); // if condition comparison
let sum_then = alloc_value(); // sum + update_addend let sum_then = alloc_value(); // sum + update_addend
let count_const = alloc_value(); // count increment (1) let count_const = alloc_value(); // count increment (1)
let count_then = alloc_value(); // count + 1 let count_then = alloc_value(); // count + 1
let const_0 = alloc_value(); // 0 for else branch let const_0 = alloc_value(); // 0 for else branch
let sum_else = alloc_value(); // sum + 0 (identity) let sum_else = alloc_value(); // sum + 0 (identity)
let count_else = alloc_value(); // count + 0 (identity) let count_else = alloc_value(); // count + 0 (identity)
let sum_new = alloc_value(); // Select result for sum let sum_new = alloc_value(); // Select result for sum
let count_new = alloc_value(); // Select result for count let count_new = alloc_value(); // Select result for count
let step_const = alloc_value(); // counter step let step_const = alloc_value(); // counter step
let i_next = alloc_value(); // i + step let i_next = alloc_value(); // i + step
// k_exit params // k_exit params
let sum_final = alloc_value(); let sum_final = alloc_value();
@ -148,7 +162,7 @@ pub fn lower_if_sum_pattern(
// i_init = 0 (initial value from ctx) // i_init = 0 (initial value from ctx)
main_func.body.push(JoinInst::Compute(MirLikeInst::Const { main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
dst: i_init_val, dst: i_init_val,
value: ConstValue::Integer(0), // TODO: Get from AST value: ConstValue::Integer(0), // TODO: Get from AST
})); }));
// sum_init = 0 // sum_init = 0
@ -195,19 +209,23 @@ pub fn lower_if_sum_pattern(
// Compare: i < limit (or other op from AST) // Compare: i < limit (or other op from AST)
// Phase 242-EX-A: Use computed LHS if available, otherwise use loop parameter // Phase 242-EX-A: Use computed LHS if available, otherwise use loop parameter
let loop_lhs = loop_lhs_val.unwrap_or(i_param); let loop_lhs = loop_lhs_val.unwrap_or(i_param);
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare { loop_step_func
dst: cmp_loop, .body
op: loop_op, .push(JoinInst::Compute(MirLikeInst::Compare {
lhs: loop_lhs, dst: cmp_loop,
rhs: loop_limit_val, op: loop_op,
})); lhs: loop_lhs,
rhs: loop_limit_val,
}));
// exit_cond = !cmp_loop // exit_cond = !cmp_loop
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::UnaryOp { loop_step_func
dst: exit_cond, .body
op: UnaryOp::Not, .push(JoinInst::Compute(MirLikeInst::UnaryOp {
operand: cmp_loop, dst: exit_cond,
})); op: UnaryOp::Not,
operand: cmp_loop,
}));
// Jump to exit if condition is false // Jump to exit if condition is false
loop_step_func.body.push(JoinInst::Jump { loop_step_func.body.push(JoinInst::Jump {
@ -226,86 +244,110 @@ pub fn lower_if_sum_pattern(
// Compare: if_var <op> if_value // Compare: if_var <op> if_value
// Phase 242-EX-A: Use computed LHS if available, otherwise use loop parameter // Phase 242-EX-A: Use computed LHS if available, otherwise use loop parameter
let if_lhs = if_lhs_val.unwrap_or(i_param); let if_lhs = if_lhs_val.unwrap_or(i_param);
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare { loop_step_func
dst: if_cmp, .body
op: if_op, .push(JoinInst::Compute(MirLikeInst::Compare {
lhs: if_lhs, dst: if_cmp,
rhs: if_value_val, op: if_op,
})); lhs: if_lhs,
rhs: if_value_val,
}));
// --- Then Branch --- // --- Then Branch ---
// sum_then = sum + update_addend // sum_then = sum + update_addend
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { loop_step_func
dst: const_0, .body
value: ConstValue::Integer(update_addend), .push(JoinInst::Compute(MirLikeInst::Const {
})); dst: const_0,
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { value: ConstValue::Integer(update_addend),
dst: sum_then, }));
op: BinOpKind::Add, loop_step_func
lhs: sum_param, .body
rhs: const_0, .push(JoinInst::Compute(MirLikeInst::BinOp {
})); dst: sum_then,
op: BinOpKind::Add,
lhs: sum_param,
rhs: const_0,
}));
// count_then = count + 1 // count_then = count + 1
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { loop_step_func
dst: count_const, .body
value: ConstValue::Integer(1), .push(JoinInst::Compute(MirLikeInst::Const {
})); dst: count_const,
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { value: ConstValue::Integer(1),
dst: count_then, }));
op: BinOpKind::Add, loop_step_func
lhs: count_param, .body
rhs: count_const, .push(JoinInst::Compute(MirLikeInst::BinOp {
})); dst: count_then,
op: BinOpKind::Add,
lhs: count_param,
rhs: count_const,
}));
// --- Else Branch --- // --- Else Branch ---
// sum_else = sum + 0 (identity) // sum_else = sum + 0 (identity)
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { loop_step_func
dst: step_const, // reuse for 0 .body
value: ConstValue::Integer(0), .push(JoinInst::Compute(MirLikeInst::Const {
})); dst: step_const, // reuse for 0
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { value: ConstValue::Integer(0),
dst: sum_else, }));
op: BinOpKind::Add, loop_step_func
lhs: sum_param, .body
rhs: step_const, .push(JoinInst::Compute(MirLikeInst::BinOp {
})); dst: sum_else,
op: BinOpKind::Add,
lhs: sum_param,
rhs: step_const,
}));
// count_else = count + 0 (identity) // count_else = count + 0 (identity)
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { loop_step_func
dst: count_else, .body
op: BinOpKind::Add, .push(JoinInst::Compute(MirLikeInst::BinOp {
lhs: count_param, dst: count_else,
rhs: step_const, // 0 op: BinOpKind::Add,
})); lhs: count_param,
rhs: step_const, // 0
}));
// --- Select --- // --- Select ---
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Select { loop_step_func
dst: sum_new, .body
cond: if_cmp, .push(JoinInst::Compute(MirLikeInst::Select {
then_val: sum_then, dst: sum_new,
else_val: sum_else, cond: if_cmp,
})); then_val: sum_then,
else_val: sum_else,
}));
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Select { loop_step_func
dst: count_new, .body
cond: if_cmp, .push(JoinInst::Compute(MirLikeInst::Select {
then_val: count_then, dst: count_new,
else_val: count_else, cond: if_cmp,
})); then_val: count_then,
else_val: count_else,
}));
// --- Counter Update --- // --- Counter Update ---
let step_const2 = alloc_value(); let step_const2 = alloc_value();
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { loop_step_func
dst: step_const2, .body
value: ConstValue::Integer(counter_step), .push(JoinInst::Compute(MirLikeInst::Const {
})); dst: step_const2,
loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { value: ConstValue::Integer(counter_step),
dst: i_next, }));
op: BinOpKind::Add, loop_step_func
lhs: i_param, .body
rhs: step_const2, .push(JoinInst::Compute(MirLikeInst::BinOp {
})); dst: i_next,
op: BinOpKind::Add,
lhs: i_param,
rhs: step_const2,
}));
// --- Tail Recursion --- // --- Tail Recursion ---
loop_step_func.body.push(JoinInst::Call { loop_step_func.body.push(JoinInst::Call {
@ -343,9 +385,18 @@ pub fn lower_if_sum_pattern(
let fragment_meta = JoinFragmentMeta::with_expr_result(sum_final, exit_meta); let fragment_meta = JoinFragmentMeta::with_expr_result(sum_final, exit_meta);
eprintln!("[joinir/pattern3/if-sum] Generated AST-based JoinIR"); eprintln!("[joinir/pattern3/if-sum] Generated AST-based JoinIR");
eprintln!("[joinir/pattern3/if-sum] Loop: {} {:?} ValueId({})", loop_var, loop_op, loop_limit_val.0); eprintln!(
eprintln!("[joinir/pattern3/if-sum] If: {} {:?} ValueId({})", if_var, if_op, if_value_val.0); "[joinir/pattern3/if-sum] Loop: {} {:?} ValueId({})",
eprintln!("[joinir/pattern3/if-sum] Phase 215-2: expr_result={:?}", sum_final); loop_var, loop_op, loop_limit_val.0
);
eprintln!(
"[joinir/pattern3/if-sum] If: {} {:?} ValueId({})",
if_var, if_op, if_value_val.0
);
eprintln!(
"[joinir/pattern3/if-sum] Phase 215-2: expr_result={:?}",
sum_final
);
Ok((join_module, fragment_meta)) Ok((join_module, fragment_meta))
} }
@ -406,7 +457,12 @@ where
ConditionValue::Variable(var_name) => { ConditionValue::Variable(var_name) => {
let var_node = ASTNode::Variable { let var_node = ASTNode::Variable {
name: var_name, name: var_name,
span: crate::ast::Span { start: 0, end: 0, line: 1, column: 1 }, span: crate::ast::Span {
start: 0,
end: 0,
line: 1,
column: 1,
},
}; };
lower_value_expression(&var_node, alloc_value, cond_env, &mut limit_instructions)? lower_value_expression(&var_node, alloc_value, cond_env, &mut limit_instructions)?
} }
@ -418,7 +474,12 @@ where
// Phase 242-EX-A: Normalization failed → handle complex conditions dynamically // Phase 242-EX-A: Normalization failed → handle complex conditions dynamically
// Support: `expr CmpOp expr` (e.g., `i % 2 == 1`, `a + b > c`) // Support: `expr CmpOp expr` (e.g., `i % 2 == 1`, `a + b > c`)
match cond { match cond {
ASTNode::BinaryOp { operator, left, right, .. } => { ASTNode::BinaryOp {
operator,
left,
right,
..
} => {
use crate::ast::BinaryOperator; use crate::ast::BinaryOperator;
// Convert operator to CompareOp // Convert operator to CompareOp
@ -429,7 +490,12 @@ where
BinaryOperator::GreaterEqual => CompareOp::Ge, BinaryOperator::GreaterEqual => CompareOp::Ge,
BinaryOperator::Equal => CompareOp::Eq, BinaryOperator::Equal => CompareOp::Eq,
BinaryOperator::NotEqual => CompareOp::Ne, BinaryOperator::NotEqual => CompareOp::Ne,
_ => return Err(format!("[if-sum] Unsupported operator in condition: {:?}", operator)), _ => {
return Err(format!(
"[if-sum] Unsupported operator in condition: {:?}",
operator
))
}
}; };
// Lower left-hand side (complex expression) // Lower left-hand side (complex expression)
@ -473,7 +539,7 @@ where
{ {
match if_stmt { match if_stmt {
ASTNode::If { condition, .. } => { ASTNode::If { condition, .. } => {
extract_loop_condition(condition, alloc_value, cond_env) // Same format extract_loop_condition(condition, alloc_value, cond_env) // Same format
} }
_ => Err("[if-sum] Expected If statement".to_string()), _ => Err("[if-sum] Expected If statement".to_string()),
} }
@ -490,7 +556,13 @@ fn extract_then_update(if_stmt: &ASTNode) -> Result<(String, i64), String> {
if let ASTNode::Assignment { target, value, .. } = stmt { if let ASTNode::Assignment { target, value, .. } = stmt {
let target_name = extract_variable_name(&**target)?; let target_name = extract_variable_name(&**target)?;
// Check if value is var + lit // Check if value is var + lit
if let ASTNode::BinaryOp { operator: crate::ast::BinaryOperator::Add, left, right, .. } = value.as_ref() { if let ASTNode::BinaryOp {
operator: crate::ast::BinaryOperator::Add,
left,
right,
..
} = value.as_ref()
{
let lhs_name = extract_variable_name(left)?; let lhs_name = extract_variable_name(left)?;
if lhs_name == target_name { if lhs_name == target_name {
let addend = extract_integer_literal(right)?; let addend = extract_integer_literal(right)?;
@ -513,7 +585,13 @@ fn extract_counter_update(body: &[ASTNode], loop_var: &str) -> Result<(String, i
if let ASTNode::Assignment { target, value, .. } = stmt { if let ASTNode::Assignment { target, value, .. } = stmt {
if let Ok(target_name) = extract_variable_name(&**target) { if let Ok(target_name) = extract_variable_name(&**target) {
if target_name == loop_var { if target_name == loop_var {
if let ASTNode::BinaryOp { operator: crate::ast::BinaryOperator::Add, left, right, .. } = value.as_ref() { if let ASTNode::BinaryOp {
operator: crate::ast::BinaryOperator::Add,
left,
right,
..
} = value.as_ref()
{
let lhs_name = extract_variable_name(left)?; let lhs_name = extract_variable_name(left)?;
if lhs_name == target_name { if lhs_name == target_name {
let step = extract_integer_literal(right)?; let step = extract_integer_literal(right)?;
@ -524,7 +602,10 @@ fn extract_counter_update(body: &[ASTNode], loop_var: &str) -> Result<(String, i
} }
} }
} }
Err(format!("[if-sum] No counter update found for '{}'", loop_var)) Err(format!(
"[if-sum] No counter update found for '{}'",
loop_var
))
} }
/// Extract variable name from AST node /// Extract variable name from AST node
@ -541,7 +622,10 @@ fn extract_variable_name(node: &ASTNode) -> Result<String, String> {
/// For condition values, use `lower_value_expression()` which supports variables. /// For condition values, use `lower_value_expression()` which supports variables.
fn extract_integer_literal(node: &ASTNode) -> Result<i64, String> { fn extract_integer_literal(node: &ASTNode) -> Result<i64, String> {
match node { match node {
ASTNode::Literal { value: crate::ast::LiteralValue::Integer(n), .. } => Ok(*n), ASTNode::Literal {
value: crate::ast::LiteralValue::Integer(n),
..
} => Ok(*n),
_ => Err(format!("[if-sum] Expected integer literal, got {:?}", node)), _ => Err(format!("[if-sum] Expected integer literal, got {:?}", node)),
} }
} }
@ -602,14 +686,8 @@ mod tests {
int_lit(1), int_lit(1),
); );
let sum_update = assignment( let sum_update = assignment(var("sum"), bin(BinaryOperator::Add, var("sum"), int_lit(1)));
var("sum"), let counter_update = assignment(var("i"), bin(BinaryOperator::Add, var("i"), int_lit(1)));
bin(BinaryOperator::Add, var("sum"), int_lit(1)),
);
let counter_update = assignment(
var("i"),
bin(BinaryOperator::Add, var("i"), int_lit(1)),
);
let if_stmt = ASTNode::If { let if_stmt = ASTNode::If {
condition: Box::new(if_condition), condition: Box::new(if_condition),
@ -634,7 +712,9 @@ mod tests {
for func in module.functions.values() { for func in module.functions.values() {
for inst in &func.body { for inst in &func.body {
match inst { match inst {
JoinInst::Compute(MirLikeInst::BinOp { op: BinOpKind::Mod, .. }) => { JoinInst::Compute(MirLikeInst::BinOp {
op: BinOpKind::Mod, ..
}) => {
has_mod = true; has_mod = true;
} }
JoinInst::Compute(MirLikeInst::Compare { .. }) => { JoinInst::Compute(MirLikeInst::Compare { .. }) => {

View File

@ -116,7 +116,10 @@ impl MethodCallLowerer {
if args.len() != expected_arity { if args.len() != expected_arity {
return Err(format!( return Err(format!(
"Arity mismatch: {}.{}() expects {} args, got {}", "Arity mismatch: {}.{}() expects {} args, got {}",
recv_val.0, method_name, expected_arity, args.len() recv_val.0,
method_name,
expected_arity,
args.len()
)); ));
} }
@ -127,7 +130,7 @@ impl MethodCallLowerer {
arg_ast, arg_ast,
alloc_value, alloc_value,
env, env,
instructions instructions,
)?; )?;
lowered_args.push(arg_val); lowered_args.push(arg_val);
} }
@ -201,7 +204,10 @@ impl MethodCallLowerer {
if args.len() != expected_arity { if args.len() != expected_arity {
return Err(format!( return Err(format!(
"Arity mismatch: {}.{}() expects {} args, got {}", "Arity mismatch: {}.{}() expects {} args, got {}",
recv_val.0, method_name, expected_arity, args.len() recv_val.0,
method_name,
expected_arity,
args.len()
)); ));
} }
@ -214,7 +220,7 @@ impl MethodCallLowerer {
alloc_value, alloc_value,
cond_env, cond_env,
body_local_env, body_local_env,
instructions instructions,
)?; )?;
lowered_args.push(arg_val); lowered_args.push(arg_val);
} }
@ -311,8 +317,8 @@ impl MethodCallLowerer {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::mir::join_ir::JoinInst;
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv; use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
use crate::mir::join_ir::JoinInst;
#[test] #[test]
fn test_resolve_string_length() { fn test_resolve_string_length() {
@ -391,7 +397,9 @@ mod tests {
); );
assert!(result.is_err()); assert!(result.is_err());
assert!(result.unwrap_err().contains("not allowed in loop condition")); assert!(result
.unwrap_err()
.contains("not allowed in loop condition"));
} }
#[test] #[test]
@ -461,7 +469,9 @@ mod tests {
&mut instructions, &mut instructions,
); );
assert!(cond_result.is_err()); assert!(cond_result.is_err());
assert!(cond_result.unwrap_err().contains("not allowed in loop condition")); assert!(cond_result
.unwrap_err()
.contains("not allowed in loop condition"));
// But IS allowed in init context // But IS allowed in init context
// Phase 226: Create empty LoopBodyLocalEnv // Phase 226: Create empty LoopBodyLocalEnv

View File

@ -24,26 +24,20 @@ pub mod carrier_info; // Phase 196: Carrier metadata for loop lowering
pub(crate) mod carrier_update_emitter; // Phase 179: Carrier update instruction emission pub(crate) mod carrier_update_emitter; // Phase 179: Carrier update instruction emission
pub(crate) mod common; // Internal lowering utilities pub(crate) mod common; // Internal lowering utilities
pub mod complex_addend_normalizer; // Phase 192: Complex addend normalization (AST preprocessing) pub mod complex_addend_normalizer; // Phase 192: Complex addend normalization (AST preprocessing)
pub mod digitpos_condition_normalizer; // Phase 224-E: DigitPos condition normalizer (digit_pos < 0 → !is_digit_pos)
pub mod condition_env; // Phase 171-fix: Condition expression environment pub mod condition_env; // Phase 171-fix: Condition expression environment
pub(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic
pub mod condition_lowering_box; // Phase 244: Unified condition lowering interface (trait-based) pub mod condition_lowering_box; // Phase 244: Unified condition lowering interface (trait-based)
pub mod condition_pattern; // Phase 219-fix: If condition pattern detection (simple vs complex) pub mod condition_pattern; // Phase 219-fix: If condition pattern detection (simple vs complex)
pub mod loop_body_local_env; // Phase 184: Body-local variable environment
pub mod loop_body_local_init; // Phase 186: Body-local init expression lowering
pub(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic
pub mod condition_to_joinir; // Phase 169: JoinIR condition lowering orchestrator (refactored) pub mod condition_to_joinir; // Phase 169: JoinIR condition lowering orchestrator (refactored)
pub mod method_call_lowerer; // Phase 224-B: MethodCall lowering (metadata-driven)
pub(crate) mod condition_var_extractor; // Phase 171-fix: Variable extraction from condition AST pub(crate) mod condition_var_extractor; // Phase 171-fix: Variable extraction from condition AST
pub mod continue_branch_normalizer; // Phase 33-19: Continue branch normalization for Pattern B pub mod continue_branch_normalizer; // Phase 33-19: Continue branch normalization for Pattern B
pub mod expr_lowerer; // Phase 231: Unified expression lowering with scope management pub mod digitpos_condition_normalizer; // Phase 224-E: DigitPos condition normalizer (digit_pos < 0 → !is_digit_pos)
pub mod loop_update_analyzer; // Phase 197: Update expression analyzer for carrier semantics
pub mod loop_update_summary; // Phase 170-C-2: Update pattern summary for shape detection
pub(crate) mod exit_args_resolver; // Internal exit argument resolution pub(crate) mod exit_args_resolver; // Internal exit argument resolution
pub mod expr_lowerer; // Phase 231: Unified expression lowering with scope management
pub mod funcscanner_append_defs; pub mod funcscanner_append_defs;
pub mod funcscanner_trim; pub mod funcscanner_trim;
pub(crate) mod generic_case_a; // Phase 192: Modularized Case A lowering pub(crate) mod generic_case_a; // Phase 192: Modularized Case A lowering
pub mod generic_type_resolver; // Phase 66: P3-C ジェネリック型推論箱 pub mod generic_type_resolver; // Phase 66: P3-C ジェネリック型推論箱
pub mod method_return_hint; // Phase 83: P3-D 既知メソッド戻り値型推論箱
pub mod if_dry_runner; // Phase 33-10.0 pub mod if_dry_runner; // Phase 33-10.0
pub(crate) mod if_lowering_router; // Phase 33-12: If-expression routing (re-exported) pub(crate) mod if_lowering_router; // Phase 33-12: If-expression routing (re-exported)
pub mod if_merge; // Phase 33-7 pub mod if_merge; // Phase 33-7
@ -53,20 +47,26 @@ pub(crate) mod if_select; // Phase 33: Internal If/Select lowering
pub mod inline_boundary; // Phase 188-Impl-3: JoinIR→Host boundary pub mod inline_boundary; // Phase 188-Impl-3: JoinIR→Host boundary
pub mod inline_boundary_builder; // Phase 200-2: Builder pattern for JoinInlineBoundary pub mod inline_boundary_builder; // Phase 200-2: Builder pattern for JoinInlineBoundary
pub mod join_value_space; // Phase 201: Unified JoinIR ValueId allocation pub mod join_value_space; // Phase 201: Unified JoinIR ValueId allocation
pub mod loop_body_local_env; // Phase 184: Body-local variable environment
pub mod loop_body_local_init; // Phase 186: Body-local init expression lowering
pub(crate) mod loop_form_intake; // Internal loop form intake pub(crate) mod loop_form_intake; // Internal loop form intake
pub mod scope_manager; // Phase 231: Unified variable scope management
pub(crate) mod loop_pattern_router; // Phase 33-12: Loop pattern routing (re-exported) pub(crate) mod loop_pattern_router; // Phase 33-12: Loop pattern routing (re-exported)
pub(crate) mod loop_pattern_validator; // Phase 33-23: Loop structure validation pub(crate) mod loop_pattern_validator; // Phase 33-23: Loop structure validation
pub(crate) mod loop_patterns; // Phase 188: Pattern-based loop lowering (3 patterns) pub(crate) mod loop_patterns; // Phase 188: Pattern-based loop lowering (3 patterns)
pub mod loop_scope_shape; pub mod loop_scope_shape;
pub mod loop_to_join; pub mod loop_to_join;
pub mod loop_update_analyzer; // Phase 197: Update expression analyzer for carrier semantics
pub mod loop_update_summary; // Phase 170-C-2: Update pattern summary for shape detection
pub(crate) mod loop_view_builder; // Phase 33-23: Loop lowering dispatch pub(crate) mod loop_view_builder; // Phase 33-23: Loop lowering dispatch
pub mod loop_with_break_minimal; // Phase 188-Impl-2: Pattern 2 minimal lowerer pub mod loop_with_break_minimal; // Phase 188-Impl-2: Pattern 2 minimal lowerer
pub mod loop_with_continue_minimal; // Phase 195: Pattern 4 minimal lowerer pub mod loop_with_continue_minimal;
// Phase 242-EX-A: loop_with_if_phi_minimal removed - replaced by loop_with_if_phi_if_sum pub mod method_call_lowerer; // Phase 224-B: MethodCall lowering (metadata-driven)
pub mod method_return_hint; // Phase 83: P3-D 既知メソッド戻り値型推論箱
pub mod scope_manager; // Phase 231: Unified variable scope management // Phase 195: Pattern 4 minimal lowerer
// Phase 242-EX-A: loop_with_if_phi_minimal removed - replaced by loop_with_if_phi_if_sum
pub mod loop_with_if_phi_if_sum; // Phase 213: Pattern 3 AST-based if-sum lowerer (Phase 242-EX-A: supports complex conditions) pub mod loop_with_if_phi_if_sum; // Phase 213: Pattern 3 AST-based if-sum lowerer (Phase 242-EX-A: supports complex conditions)
pub mod simple_while_minimal; // Phase 188-Impl-1: Pattern 1 minimal lowerer
pub mod min_loop; pub mod min_loop;
pub mod simple_while_minimal; // Phase 188-Impl-1: Pattern 1 minimal lowerer
pub mod skip_ws; pub mod skip_ws;
pub mod stage1_using_resolver; pub mod stage1_using_resolver;
pub mod stageb_body; pub mod stageb_body;

View File

@ -19,11 +19,11 @@
//! Phase 231 starts with Pattern2-specific implementation to validate the design. //! Phase 231 starts with Pattern2-specific implementation to validate the design.
//! Future phases will generalize to Pattern1, Pattern3, etc. //! Future phases will generalize to Pattern1, Pattern3, etc.
use crate::mir::ValueId; use super::carrier_info::CarrierInfo;
use super::condition_env::ConditionEnv; use super::condition_env::ConditionEnv;
use super::loop_body_local_env::LoopBodyLocalEnv; use super::loop_body_local_env::LoopBodyLocalEnv;
use super::carrier_info::CarrierInfo;
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv; use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
use crate::mir::ValueId;
/// Phase 231: Scope kind for variables /// Phase 231: Scope kind for variables
/// ///
@ -146,8 +146,7 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
} }
// 4. Promoted LoopBodyLocal → Carrier lookup命名規約は CarrierInfo 側に集約) // 4. Promoted LoopBodyLocal → Carrier lookup命名規約は CarrierInfo 側に集約)
self.carrier_info self.carrier_info.resolve_promoted_join_id(name)
.resolve_promoted_join_id(name)
} }
fn scope_of(&self, name: &str) -> Option<VarScopeKind> { fn scope_of(&self, name: &str) -> Option<VarScopeKind> {
@ -182,7 +181,7 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::mir::join_ir::lowering::carrier_info::{CarrierVar, CarrierRole, CarrierInit}; use crate::mir::join_ir::lowering::carrier_info::{CarrierInit, CarrierRole, CarrierVar};
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedVar; use crate::mir::loop_pattern_detection::function_scope_capture::CapturedVar;
#[test] #[test]
@ -218,15 +217,13 @@ mod tests {
let carrier_info = CarrierInfo { let carrier_info = CarrierInfo {
loop_var_name: "i".to_string(), loop_var_name: "i".to_string(),
loop_var_id: ValueId(1), loop_var_id: ValueId(1),
carriers: vec![ carriers: vec![CarrierVar {
CarrierVar { name: "sum".to_string(),
name: "sum".to_string(), host_id: ValueId(2),
host_id: ValueId(2), join_id: Some(ValueId(101)),
join_id: Some(ValueId(101)), role: CarrierRole::LoopState,
role: CarrierRole::LoopState, init: CarrierInit::FromHost,
init: CarrierInit::FromHost, }],
},
],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec![], promoted_loopbodylocals: vec![],
}; };
@ -250,15 +247,13 @@ mod tests {
let carrier_info = CarrierInfo { let carrier_info = CarrierInfo {
loop_var_name: "i".to_string(), loop_var_name: "i".to_string(),
loop_var_id: ValueId(1), loop_var_id: ValueId(1),
carriers: vec![ carriers: vec![CarrierVar {
CarrierVar { name: "is_digit_pos".to_string(),
name: "is_digit_pos".to_string(), host_id: ValueId(2),
host_id: ValueId(2), join_id: Some(ValueId(102)),
join_id: Some(ValueId(102)), role: CarrierRole::ConditionOnly,
role: CarrierRole::ConditionOnly, init: CarrierInit::BoolConst(false),
init: CarrierInit::BoolConst(false), }],
},
],
trim_helper: None, trim_helper: None,
promoted_loopbodylocals: vec!["digit_pos".to_string()], promoted_loopbodylocals: vec!["digit_pos".to_string()],
}; };
@ -307,7 +302,8 @@ mod tests {
condition_env.insert("i".to_string(), ValueId(100)); condition_env.insert("i".to_string(), ValueId(100));
condition_env.insert("len".to_string(), ValueId(201)); condition_env.insert("len".to_string(), ValueId(201));
let mut captured_env = crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv::new(); let mut captured_env =
crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv::new();
captured_env.add_var(CapturedVar { captured_env.add_var(CapturedVar {
name: "len".to_string(), name: "len".to_string(),
host_id: ValueId(42), host_id: ValueId(42),

View File

@ -45,11 +45,10 @@
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace; use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
use crate::mir::join_ir::{ use crate::mir::join_ir::{
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst,
MirLikeInst, UnaryOp, UnaryOp,
}; };
/// Lower Pattern 1 (Simple While Loop) to JoinIR /// Lower Pattern 1 (Simple While Loop) to JoinIR
/// ///
/// # Phase 188-Impl-3: Pure JoinIR Fragment Generation /// # Phase 188-Impl-3: Pure JoinIR Fragment Generation
@ -106,17 +105,17 @@ pub(crate) fn lower_simple_while_minimal(
// ValueId allocation (Phase 188-Impl-3: Sequential local IDs) // ValueId allocation (Phase 188-Impl-3: Sequential local IDs)
// ================================================================== // ==================================================================
// main() locals // main() locals
let i_init = alloc_value(); // ValueId(0) - loop init value let i_init = alloc_value(); // ValueId(0) - loop init value
let loop_result = alloc_value(); // ValueId(1) - result from loop_step let loop_result = alloc_value(); // ValueId(1) - result from loop_step
let const_0_main = alloc_value(); // ValueId(2) - return value let const_0_main = alloc_value(); // ValueId(2) - return value
// loop_step locals // loop_step locals
let i_param = alloc_value(); // ValueId(3) - parameter let i_param = alloc_value(); // ValueId(3) - parameter
let const_3 = alloc_value(); // ValueId(4) - comparison constant let const_3 = alloc_value(); // ValueId(4) - comparison constant
let cmp_lt = alloc_value(); // ValueId(5) - i < 3 let cmp_lt = alloc_value(); // ValueId(5) - i < 3
let exit_cond = alloc_value(); // ValueId(6) - !(i < 3) let exit_cond = alloc_value(); // ValueId(6) - !(i < 3)
let const_1 = alloc_value(); // ValueId(7) - increment constant let const_1 = alloc_value(); // ValueId(7) - increment constant
let i_next = alloc_value(); // ValueId(8) - i + 1 let i_next = alloc_value(); // ValueId(8) - i + 1
// k_exit locals // k_exit locals
let const_0_exit = alloc_value(); // ValueId(9) - exit return value let const_0_exit = alloc_value(); // ValueId(9) - exit return value
@ -151,11 +150,8 @@ pub(crate) fn lower_simple_while_minimal(
// ================================================================== // ==================================================================
// loop_step(i) function // loop_step(i) function
// ================================================================== // ==================================================================
let mut loop_step_func = JoinFunction::new( let mut loop_step_func =
loop_step_id, JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![i_param]);
"loop_step".to_string(),
vec![i_param],
);
// exit_cond = !(i < 3) // exit_cond = !(i < 3)
// Step 1: const 3 // Step 1: const 3
@ -196,9 +192,7 @@ pub(crate) fn lower_simple_while_minimal(
// Phase 188-Impl-1-E: Use Print instruction // Phase 188-Impl-1-E: Use Print instruction
loop_step_func loop_step_func
.body .body
.push(JoinInst::Compute(MirLikeInst::Print { .push(JoinInst::Compute(MirLikeInst::Print { value: i_param }));
value: i_param,
}));
// i_next = i + 1 // i_next = i + 1
// Step 1: const 1 // Step 1: const 1
@ -223,7 +217,7 @@ pub(crate) fn lower_simple_while_minimal(
loop_step_func.body.push(JoinInst::Call { loop_step_func.body.push(JoinInst::Call {
func: loop_step_id, func: loop_step_id,
args: vec![i_next], args: vec![i_next],
k_next: None, // CRITICAL: None for tail call k_next: None, // CRITICAL: None for tail call
dst: None, dst: None,
}); });

View File

@ -167,7 +167,7 @@ impl<'a> UpdateEnv<'a> {
// Phase 247-EX: Naming convention - "digit_pos" → "digit_value" (not "digit_pos_value") // Phase 247-EX: Naming convention - "digit_pos" → "digit_value" (not "digit_pos_value")
// Extract base name: "digit_pos" → "digit", "pos" → "pos" // Extract base name: "digit_pos" → "digit", "pos" → "pos"
let base_name = if promoted_name.ends_with("_pos") { let base_name = if promoted_name.ends_with("_pos") {
&promoted_name[..promoted_name.len() - 4] // Remove "_pos" suffix &promoted_name[..promoted_name.len() - 4] // Remove "_pos" suffix
} else { } else {
promoted_name.as_str() promoted_name.as_str()
}; };
@ -359,8 +359,8 @@ mod tests {
let mut cond_env = ConditionEnv::new(); let mut cond_env = ConditionEnv::new();
// Register both carriers in ConditionEnv // Register both carriers in ConditionEnv
cond_env.insert("is_digit_pos".to_string(), ValueId(100)); // Boolean carrier cond_env.insert("is_digit_pos".to_string(), ValueId(100)); // Boolean carrier
cond_env.insert("digit_value".to_string(), ValueId(200)); // Integer carrier (digit_pos → digit) cond_env.insert("digit_value".to_string(), ValueId(200)); // Integer carrier (digit_pos → digit)
let body_env = LoopBodyLocalEnv::new(); let body_env = LoopBodyLocalEnv::new();
let promoted: Vec<String> = vec!["digit_pos".to_string()]; let promoted: Vec<String> = vec!["digit_pos".to_string()];

View File

@ -35,6 +35,9 @@ pub mod verify;
// Phase 30.x: JSON serialization (jsonir v0) // Phase 30.x: JSON serialization (jsonir v0)
pub mod json; pub mod json;
// Phase 26-H.B: Normalized JoinIR (テスト専用ミニ)
pub mod normalized;
// Phase 34-1: Frontend (AST→JoinIR) — skeleton only // Phase 34-1: Frontend (AST→JoinIR) — skeleton only
pub mod frontend; pub mod frontend;
@ -44,6 +47,12 @@ pub use lowering::{
}; };
// Re-export verification functions // Re-export verification functions
pub use normalized::{
normalize_pattern1_minimal, normalize_pattern2_minimal, normalized_pattern1_to_structured,
normalized_pattern2_to_structured, NormalizedModule,
};
#[cfg(feature = "normalized_dev")]
pub use normalized::fixtures;
pub use verify::verify_progress_for_skip_ws; pub use verify::verify_progress_for_skip_ws;
// Phase 200-3: Contract verification functions are in merge/mod.rs (private module access) // Phase 200-3: Contract verification functions are in merge/mod.rs (private module access)
@ -194,6 +203,15 @@ impl LoopExitShape {
} }
} }
/// JoinIR フェーズメタデータ。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JoinIrPhase {
/// Lowering 直後の構造化 JoinIRPattern15 / CarrierInfo / Boundary/ExitLine
Structured,
/// 将来導入予定の正規化済み JoinIR関数継続Env、TailCall-only
Normalized,
}
/// JoinIR 関数 /// JoinIR 関数
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct JoinFunction { pub struct JoinFunction {
@ -471,9 +489,7 @@ pub enum MirLikeInst {
/// Phase 188: Print 文(コンソール出力) /// Phase 188: Print 文(コンソール出力)
/// print(value) の構造を JoinIR で表現 /// print(value) の構造を JoinIR で表現
/// MIR 変換時に Print 命令に変換 /// MIR 変換時に Print 命令に変換
Print { Print { value: VarId },
value: VarId,
},
/// Phase 188-Impl-3: 条件付き値選択(三項演算子) /// Phase 188-Impl-3: 条件付き値選択(三項演算子)
/// cond が true なら then_val を、false なら else_val を dst に代入 /// cond が true なら then_val を、false なら else_val を dst に代入
@ -535,6 +551,9 @@ pub struct JoinModule {
/// エントリーポイント関数ID /// エントリーポイント関数ID
pub entry: Option<JoinFuncId>, pub entry: Option<JoinFuncId>,
/// JoinIR のフェーズ(構造化 / 正規化)
pub phase: JoinIrPhase,
} }
impl JoinModule { impl JoinModule {
@ -542,12 +561,25 @@ impl JoinModule {
Self { Self {
functions: BTreeMap::new(), functions: BTreeMap::new(),
entry: None, entry: None,
phase: JoinIrPhase::Structured,
} }
} }
pub fn add_function(&mut self, func: JoinFunction) { pub fn add_function(&mut self, func: JoinFunction) {
self.functions.insert(func.id, func); self.functions.insert(func.id, func);
} }
pub fn is_structured(&self) -> bool {
self.phase == JoinIrPhase::Structured
}
pub fn is_normalized(&self) -> bool {
self.phase == JoinIrPhase::Normalized
}
pub fn mark_normalized(&mut self) {
self.phase = JoinIrPhase::Normalized;
}
} }
impl Default for JoinModule { impl Default for JoinModule {
@ -584,6 +616,16 @@ mod tests {
assert_eq!(module.functions.len(), 1); assert_eq!(module.functions.len(), 1);
assert!(module.functions.contains_key(&JoinFuncId::new(0))); assert!(module.functions.contains_key(&JoinFuncId::new(0)));
assert_eq!(module.phase, JoinIrPhase::Structured);
assert!(module.is_structured());
}
#[test]
fn test_mark_normalized() {
let mut module = JoinModule::new();
assert!(module.is_structured());
module.mark_normalized();
assert!(module.is_normalized());
} }
#[test] #[test]

Some files were not shown because too many files have changed in this diff Show More