joinir: clean pattern visibility and refactor pattern2 pipeline

This commit is contained in:
nyash-codex
2025-12-11 19:11:26 +09:00
parent 463a6240fb
commit 59a985b7fa
16 changed files with 1141 additions and 812 deletions

View File

@ -1032,3 +1032,110 @@ JoinIR は Rust 側だけでなく、将来的に .hako selfhost コンパイラ
- 根本原因: carrier 検出ロジックの name heuristic が脆弱 - 根本原因: carrier 検出ロジックの name heuristic が脆弱
- **次フェーズ**: Carrier 検出修正Phase 219 - **次フェーズ**: Carrier 検出修正Phase 219
- 詳細: phase218-jsonparser-if-sum-min.md - 詳細: phase218-jsonparser-if-sum-min.md
---
## 3. JoinIR → JoinIR 正規化レイヤ(構想 / Phase 26-H ライン)
### 3.1 Structured JoinIR と Normalized JoinIR概念レベル
JoinIR ラインは、今後は「同じ JoinIR をフェーズごとに正規化していく」二段構成で扱う想定だよ:
- **Structured JoinIR現行層**
- Pattern15 / CarrierInfo / ConditionEnv / UpdateEnv / Boundary / ExitLine までを終えた状態。
- while/if/break/continue/return がまだ「構造」として残っていてよい層。
- いまの JoinIR loweringPattern lowerer 群)が出力しているものはここ。
- **Normalized JoinIRJoinIR / CPS 風層)**
- 制御構造をすべて「関数+継続+ Env」の形に正規化した状態。
- ループは `loop_step(env, k_exit)` と exit 継続のペア、if は `if_branch(env, k_then, k_else)` join 継続で表現。
- while/break/continue/return は **TailCallFn/TailCallKont + If** だけに還元され、goto/生の branch は現れない。
- Env は「ループキャリア + DigitPos/num_str などの Derived + captured 変数」を 1 つの struct としてまとめる。
パイプラインのイメージ:
```text
AST
JoinIR(Structured) // Pattern15, CarrierInfo, ConditionEnv/UpdateEnv, Boundary/ExitLine
↓ JoinIR パス A: 正規化JoinIR → JoinIR
JoinIR(Normalized) // 関数 + 継続 + Env のみTailCall-only
↓ JoinIR→MIR bridge
MIR
```
型レベルでは、次の二案のどちらかで扱う想定だよ(詳細は今後の Phase で選択):
- **案① 型を分ける**
- `JoinModuleRaw`Structured、`JoinModuleCps`Normalizedを別型にする。
- BridgeJoinIR→MIRは `JoinModuleCps` だけを受け取る。
- **案② 型は 1 つ+フェーズフラグ**
- `JoinModule { phase: JoinIrPhase, ... }` として、`phase = Structured / Normalized` をメタデータで持つ。
- JoinIR パスはすべて `fn run(&mut JoinModule)`= joinir→joinirで構成し、
Verifier が「phase=Normalized なら while/if/break/continue/return 禁止」などの不変条件をチェックする。
どちらの案でも本質は同じで、「JoinIR の下に JoinIR= 正規形フェーズ)を 1 段挟む」という設計、というのがポイントだよ。
### 3.2 Normalized JoinIR の基本モデル(ラフスケッチ)
Normalized JoinIR では、制御構造を次の 3 要素だけで表現する想定だよ:
- **Env環境**
- そのループ/if に必要な情報を 1 つにまとめた struct。
- フィールド種別の例:
- `Carrier`LoopState キャリア: i, sum, result, num_state 等)
- `Derived`DigitPos 二重値, NumberAccumulation 等)
- `Captured`(外側のローカルや関数パラメータ: s, len 等)
- **Fn通常の関数**
- 典型例: `loop_step(env, k_exit)` / `loop_body(env, k_exit)`。
- 末尾は常に `TailCallFn` または `TailCallKont`。
- **Kont継続**
- 典型例: ループ exit 後の処理、if 後の join、関数 return など。
- 末尾は別の Fn/Kont への tail-call。
制御の不変条件Normalized フェーズ):
1. 制御フローは `TailCallFn` / `TailCallKont` / `If(cond, then_k, else_k, env)` のみで表現する。
2. 各ループは「`loop_step(env, k_exit)` 関数 + `k_exit(env)` 継続」のペアとして現れるbreak は必ず k_exit へ TailCall
3. continue は `loop_step` への TailCall として現れる(ヘッダ PHI 相当は Env の書き込み順で表現)。
4. return は専用の `return_kont(env)` への TailCall で表現される。
5. EnvLayoutVerifier検証箱が、常に正しい EnvLayout が使われていることをチェックする。
これにより、現在 JoinIR 層で苦労している「PHI 配線」「exit_bindings/jump_args 整合性」「評価順のねじれ」は、
Normalized JoinIR 側では「Env フィールドの更新順」と「どの継続を呼ぶか」に還元される想定だよ。
### 3.3 Phase 26-H のフェーズ分割(案)
この正規化レイヤを一度に入れるのは重いので、Phase 26-H ラインとして小さく段階分けして進める方針だよ。
(番号は仮で、実際の Phase 番号は current の進行に合わせて調整する想定)
1. **Phase 26-H.A モデル定義と Doc 固定(コード変更最小)**
- JoinIR Architecture Overviewこのファイルに Structured / Normalized の二層構造を明文化(本節)。
- `JoinIrPhase` などのメタ情報だけを追加し、既存 lowering / Bridge の挙動は変えない。
- Normalized JoinIR の不変条件TailCall-only / EnvLayout 整合性)を Verifier の設計として書き出す。
2. **Phase 26-H.B 最小サブセットでの JoinIR→JoinIR パス導入**
- Pattern1単純 while, break/continue なし)だけを対象にした Normalized パスを実装。
- パイプライン:
- 既存: `JoinIR(Structured) → MIR`
- 新規: `JoinIR(Structured) → JoinIR(Normalized, small subset) → MIR`
- 新規パスは dev フラグの裏側で比較用にのみ使い、E2E で結果が一致することを確認(既定経路は従来のまま)。
3. **Phase 26-H.C Pattern24 / DigitPos / JsonParser への拡張**
- Pattern2/3/4 と DigitPos / NumberAccumulation / Trim を Normalized パスの対象に広げる。
- JsonParser `_parse_number` / `_atoi` を優先対象にし、JoinIR(Normalized) 経由での実行を dev フラグ付きで有効化。
- JoinIR(Structured) → JoinIR(Normalized) の各ステップに構造テストを追加し、「Env の更新」「継続の呼び出し順」が期待どおりかを固定。
4. **Phase 26-H.D Normalized JoinIR を canonical route に昇格**
- JoinIR→MIR Bridge が「Normalized フェーズの JoinIR だけ」を受け取るように変更。
- Structured JoinIR → MIR の直接パスは「比較テスト専用」として残すか、段階的に削除。
- selfhost / JsonParser / hako_check の代表ループはすべて Normalized JoinIR 経由で通ることを確認。
各サブフェーズでは「既存意味論を変えない」「Fail-Fast 原則を維持する」「新旧経路の比較テストを先に用意する」をガードとして運用するよ。
詳細な API/型設計は、Phase 26-H.A/B の中で `EnvLayoutBox` / `LoopStepSynthesizer` / `JpVerifier` 等の箱として段階的に固めていく想定。
### 3.4 Phase 27-CLEAN Pattern24 の軽量整理
- Pattern2〜4/loop_with_break_minimal まわりで可視性・ログ・補助関数を整理し、joinir_dev フラグ配下のデバッグログに寄せる。意味論は変えずに「読みやすさ」「追いやすさ」を優先するクリーンアップフェーズだよ。

View File

@ -20,8 +20,8 @@
//! - Delegates to specialized analyzers for break/continue logic //! - Delegates to specialized analyzers for break/continue logic
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::loop_pattern_detection::LoopFeatures;
use crate::mir::loop_pattern_detection::break_condition_analyzer::BreakConditionAnalyzer; use crate::mir::loop_pattern_detection::break_condition_analyzer::BreakConditionAnalyzer;
use crate::mir::loop_pattern_detection::LoopFeatures;
/// Detect if a loop body contains continue statements /// Detect if a loop body contains continue statements
/// ///
@ -37,7 +37,7 @@ use crate::mir::loop_pattern_detection::break_condition_analyzer::BreakCondition
/// ///
/// This is a simple recursive scan that doesn't handle nested loops perfectly, /// This is a simple recursive scan that doesn't handle nested loops perfectly,
/// but is sufficient for initial pattern detection. /// but is sufficient for initial pattern detection.
pub fn detect_continue_in_body(body: &[ASTNode]) -> bool { pub(crate) fn detect_continue_in_body(body: &[ASTNode]) -> bool {
for stmt in body { for stmt in body {
if has_continue_node(stmt) { if has_continue_node(stmt) {
return true; return true;
@ -55,7 +55,7 @@ pub fn detect_continue_in_body(body: &[ASTNode]) -> bool {
/// # Returns /// # Returns
/// ///
/// `true` if at least one break statement is found in the body or nested structures /// `true` if at least one break statement is found in the body or nested structures
pub fn detect_break_in_body(body: &[ASTNode]) -> bool { pub(crate) fn detect_break_in_body(body: &[ASTNode]) -> bool {
for stmt in body { for stmt in body {
if has_break_node(stmt) { if has_break_node(stmt) {
return true; return true;
@ -78,11 +78,7 @@ pub fn detect_break_in_body(body: &[ASTNode]) -> bool {
/// # Returns /// # Returns
/// ///
/// A LoopFeatures struct containing all detected structural characteristics /// A LoopFeatures struct containing all detected structural characteristics
pub fn extract_features( pub(crate) fn extract_features(body: &[ASTNode], has_continue: bool, has_break: bool) -> LoopFeatures {
body: &[ASTNode],
has_continue: bool,
has_break: bool,
) -> LoopFeatures {
// Phase 212.5: Detect ANY if statement in loop body (structural detection) // Phase 212.5: Detect ANY if statement in loop body (structural detection)
let has_if = detect_if_in_body(body); let has_if = detect_if_in_body(body);
@ -147,8 +143,12 @@ fn detect_if_else_phi_in_body(body: &[ASTNode]) -> bool {
} = node } = node
{ {
// Check if both branches have assignments // Check if both branches have assignments
let then_has_assign = then_body.iter().any(|n| matches!(n, ASTNode::Assignment { .. })); let then_has_assign = then_body
let else_has_assign = else_body.iter().any(|n| matches!(n, ASTNode::Assignment { .. })); .iter()
.any(|n| matches!(n, ASTNode::Assignment { .. }));
let else_has_assign = else_body
.iter()
.any(|n| matches!(n, ASTNode::Assignment { .. }));
if then_has_assign && else_has_assign { if then_has_assign && else_has_assign {
return true; return true;
} }
@ -193,7 +193,11 @@ fn count_carriers_in_body(body: &[ASTNode]) -> usize {
} }
} }
// Return at least 1 if we have assignments, otherwise 0 // Return at least 1 if we have assignments, otherwise 0
if count > 0 { 1 } else { 0 } if count > 0 {
1
} else {
0
}
} }
/// Recursive helper to check if AST node contains continue /// Recursive helper to check if AST node contains continue
@ -248,7 +252,7 @@ fn has_break_node(node: &ASTNode) -> bool {
/// # Returns /// # Returns
/// ///
/// `true` if an `if ... else { break }` pattern is found /// `true` if an `if ... else { break }` pattern is found
pub fn has_break_in_else_clause(body: &[ASTNode]) -> bool { pub(crate) fn has_break_in_else_clause(body: &[ASTNode]) -> bool {
BreakConditionAnalyzer::has_break_in_else_clause(body) BreakConditionAnalyzer::has_break_in_else_clause(body)
} }
@ -286,7 +290,7 @@ pub fn has_break_in_else_clause(body: &[ASTNode]) -> bool {
/// // <- Returns the "!(ch == " ")" condition (negated) /// // <- Returns the "!(ch == " ")" condition (negated)
/// } /// }
/// ``` /// ```
pub fn extract_break_condition(body: &[ASTNode]) -> Option<&ASTNode> { pub(crate) fn extract_break_condition(body: &[ASTNode]) -> Option<&ASTNode> {
BreakConditionAnalyzer::extract_break_condition(body).ok() BreakConditionAnalyzer::extract_break_condition(body).ok()
} }
@ -296,13 +300,17 @@ mod tests {
#[test] #[test]
fn test_detect_continue_simple() { fn test_detect_continue_simple() {
let continue_node = ASTNode::Continue { span: crate::ast::Span::unknown() }; let continue_node = ASTNode::Continue {
span: crate::ast::Span::unknown(),
};
assert!(has_continue_node(&continue_node)); assert!(has_continue_node(&continue_node));
} }
#[test] #[test]
fn test_detect_break_simple() { fn test_detect_break_simple() {
let break_node = ASTNode::Break { span: crate::ast::Span::unknown() }; let break_node = ASTNode::Break {
span: crate::ast::Span::unknown(),
};
assert!(has_break_node(&break_node)); assert!(has_break_node(&break_node));
} }

View File

@ -32,13 +32,13 @@
//! This module is now a thin wrapper around `CarrierInfo::from_variable_map()`. //! This module is now a thin wrapper around `CarrierInfo::from_variable_map()`.
//! The primary logic lives in `carrier_info.rs` for consistency across MIR and JoinIR contexts. //! The primary logic lives in `carrier_info.rs` for consistency across MIR and JoinIR contexts.
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
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::carrier_info::CarrierInfo;
use crate::mir::ValueId;
use std::collections::BTreeMap; use std::collections::BTreeMap;
pub struct CommonPatternInitializer; pub(crate) struct CommonPatternInitializer;
impl CommonPatternInitializer { impl CommonPatternInitializer {
/// Initialize pattern context: extract loop var, build CarrierInfo /// Initialize pattern context: extract loop var, build CarrierInfo
@ -84,10 +84,7 @@ impl CommonPatternInitializer {
) -> Result<(String, ValueId, CarrierInfo), String> { ) -> Result<(String, ValueId, CarrierInfo), String> {
// Step 1: Extract loop variable from condition // Step 1: Extract loop variable from condition
let loop_var_name = builder.extract_loop_variable_from_condition(condition)?; let loop_var_name = builder.extract_loop_variable_from_condition(condition)?;
let loop_var_id = variable_map let loop_var_id = variable_map.get(&loop_var_name).copied().ok_or_else(|| {
.get(&loop_var_name)
.copied()
.ok_or_else(|| {
format!( format!(
"[common_init] Loop variable '{}' not found in variable_map", "[common_init] Loop variable '{}' not found in variable_map",
loop_var_name loop_var_name
@ -98,14 +95,13 @@ impl CommonPatternInitializer {
// Phase 222.5-D: Direct BTreeMap usage (no conversion needed) // Phase 222.5-D: Direct BTreeMap usage (no conversion needed)
// Step 2: Use CarrierInfo::from_variable_map as primary initialization method // Step 2: Use CarrierInfo::from_variable_map as primary initialization method
let mut carrier_info = CarrierInfo::from_variable_map( let mut carrier_info = CarrierInfo::from_variable_map(loop_var_name.clone(), variable_map)?;
loop_var_name.clone(),
variable_map,
)?;
// Step 3: Apply exclusions if provided (Pattern 2 specific) // Step 3: Apply exclusions if provided (Pattern 2 specific)
if let Some(excluded) = exclude_carriers { if let Some(excluded) = exclude_carriers {
carrier_info.carriers.retain(|c| !excluded.contains(&c.name.as_str())); carrier_info
.carriers
.retain(|c| !excluded.contains(&c.name.as_str()));
} }
Ok((loop_var_name, loop_var_id, carrier_info)) Ok((loop_var_name, loop_var_id, carrier_info))
@ -150,11 +146,15 @@ impl CommonPatternInitializer {
_loop_var_name: &str, _loop_var_name: &str,
_variable_map: &BTreeMap<String, ValueId>, _variable_map: &BTreeMap<String, ValueId>,
) -> bool { ) -> bool {
use crate::mir::join_ir::lowering::loop_update_analyzer::{LoopUpdateAnalyzer, UpdateExpr, UpdateRhs};
use crate::mir::join_ir::lowering::carrier_info::CarrierVar; use crate::mir::join_ir::lowering::carrier_info::CarrierVar;
use crate::mir::join_ir::lowering::loop_update_analyzer::{
LoopUpdateAnalyzer, UpdateExpr, UpdateRhs,
};
// Create dummy carriers from body assignment targets for analysis // Create dummy carriers from body assignment targets for analysis
let dummy_carriers: Vec<CarrierVar> = body.iter().filter_map(|node| { let dummy_carriers: Vec<CarrierVar> = body
.iter()
.filter_map(|node| {
match node { match node {
ASTNode::Assignment { target, .. } => { ASTNode::Assignment { target, .. } => {
if let ASTNode::Variable { name, .. } = target.as_ref() { if let ASTNode::Variable { name, .. } = target.as_ref() {
@ -171,7 +171,8 @@ impl CommonPatternInitializer {
} }
_ => None, _ => None,
} }
}).collect(); })
.collect();
let updates = LoopUpdateAnalyzer::analyze_carrier_updates(body, &dummy_carriers); let updates = LoopUpdateAnalyzer::analyze_carrier_updates(body, &dummy_carriers);

View File

@ -35,7 +35,7 @@ use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
use crate::mir::ValueId; use crate::mir::ValueId;
use std::collections::BTreeMap; use std::collections::BTreeMap;
pub struct ConditionEnvBuilder; pub(crate) struct ConditionEnvBuilder;
impl ConditionEnvBuilder { impl ConditionEnvBuilder {
/// Phase 201: Build ConditionEnv using JoinValueSpace (disjoint ValueId regions) /// Phase 201: Build ConditionEnv using JoinValueSpace (disjoint ValueId regions)
@ -65,10 +65,8 @@ impl ConditionEnvBuilder {
space: &mut JoinValueSpace, space: &mut JoinValueSpace,
) -> Result<(ConditionEnv, Vec<ConditionBinding>, ValueId), String> { ) -> Result<(ConditionEnv, Vec<ConditionBinding>, ValueId), String> {
// Extract all variables used in the condition (excluding loop parameter) // Extract all variables used in the condition (excluding loop parameter)
let condition_var_names = extract_condition_variables( let condition_var_names =
break_condition, extract_condition_variables(break_condition, &[loop_var_name.to_string()]);
&[loop_var_name.to_string()],
);
let mut env = ConditionEnv::new(); let mut env = ConditionEnv::new();
let mut bindings = Vec::new(); let mut bindings = Vec::new();
@ -79,10 +77,7 @@ impl ConditionEnvBuilder {
// For each condition variable, allocate JoinIR-local ValueId and build binding // For each condition variable, allocate JoinIR-local ValueId and build binding
for var_name in &condition_var_names { for var_name in &condition_var_names {
let host_id = variable_map let host_id = variable_map.get(var_name).copied().ok_or_else(|| {
.get(var_name)
.copied()
.ok_or_else(|| {
format!( format!(
"Condition variable '{}' not found in variable_map. \ "Condition variable '{}' not found in variable_map. \
Loop condition references undefined variable.", Loop condition references undefined variable.",
@ -108,7 +103,10 @@ impl ConditionEnvBuilder {
/// ///
/// Uses JoinValueSpace to allocate the loop parameter ValueId. /// Uses JoinValueSpace to allocate the loop parameter ValueId.
#[allow(dead_code)] #[allow(dead_code)]
pub fn build_loop_param_only_v2(loop_var_name: &str, space: &mut JoinValueSpace) -> (ConditionEnv, ValueId) { pub fn build_loop_param_only_v2(
loop_var_name: &str,
space: &mut JoinValueSpace,
) -> (ConditionEnv, ValueId) {
let mut env = ConditionEnv::new(); let mut env = ConditionEnv::new();
let loop_var_join_id = space.alloc_param(); let loop_var_join_id = space.alloc_param();
env.insert(loop_var_name.to_string(), loop_var_join_id); env.insert(loop_var_name.to_string(), loop_var_join_id);
@ -177,7 +175,10 @@ impl ConditionEnvBuilder {
let debug = env::var("NYASH_CAPTURE_DEBUG").is_ok(); let debug = env::var("NYASH_CAPTURE_DEBUG").is_ok();
if debug { if debug {
eprintln!("[capture/env_builder] Building ConditionEnv with {} captured vars", captured.vars.len()); eprintln!(
"[capture/env_builder] Building ConditionEnv with {} captured vars",
captured.vars.len()
);
} }
// Step 1: Build base ConditionEnv with loop params using v2 API (Phase 222.5-B) // Step 1: Build base ConditionEnv with loop params using v2 API (Phase 222.5-B)
@ -197,17 +198,25 @@ impl ConditionEnvBuilder {
}; };
// 2b: Add to boundary with Condition role // 2b: Add to boundary with Condition role
boundary.add_param_with_role(&var.name, host_id, crate::mir::join_ir::lowering::inline_boundary_builder::ParamRole::Condition); boundary.add_param_with_role(
&var.name,
host_id,
crate::mir::join_ir::lowering::inline_boundary_builder::ParamRole::Condition,
);
// 2c: Get JoinIR ValueId from boundary // 2c: Get JoinIR ValueId from boundary
let join_id = boundary.get_condition_binding(&var.name) let join_id = boundary
.get_condition_binding(&var.name)
.expect("captured var should be in boundary after add_param_with_role"); .expect("captured var should be in boundary after add_param_with_role");
// 2d: Add to ConditionEnv.captured map // 2d: Add to ConditionEnv.captured map
env.captured.insert(var.name.clone(), join_id); env.captured.insert(var.name.clone(), join_id);
if debug { if debug {
eprintln!("[capture/env_builder] Added captured var '{}': host={:?}, join={:?}", var.name, host_id, join_id); eprintln!(
"[capture/env_builder] Added captured var '{}': host={:?}, join={:?}",
var.name, host_id, join_id
);
} }
} }
@ -224,7 +233,11 @@ impl ConditionEnvBuilder {
if debug { if debug {
let param_count = env.iter().count(); let param_count = env.iter().count();
eprintln!("[capture/env_builder] Final ConditionEnv: {} params, {} captured", param_count, env.captured.len()); eprintln!(
"[capture/env_builder] Final ConditionEnv: {} params, {} captured",
param_count,
env.captured.len()
);
} }
(env, loop_var_join_id) (env, loop_var_join_id)
@ -351,9 +364,7 @@ mod tests {
// Should return error // Should return error
assert!(result.is_err()); assert!(result.is_err());
assert!(result assert!(result.unwrap_err().contains("undefined_var"));
.unwrap_err()
.contains("undefined_var"));
} }
/// Phase 201: Test that v2 API uses JoinValueSpace correctly /// Phase 201: Test that v2 API uses JoinValueSpace correctly
@ -410,7 +421,8 @@ mod tests {
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace; use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
let mut space = JoinValueSpace::new(); let mut space = JoinValueSpace::new();
let (env, loop_var_join_id) = ConditionEnvBuilder::build_loop_param_only_v2("i", &mut space); let (env, loop_var_join_id) =
ConditionEnvBuilder::build_loop_param_only_v2("i", &mut space);
// Phase 201: Should use Param region // Phase 201: Should use Param region
assert_eq!(loop_var_join_id, ValueId(100)); assert_eq!(loop_var_join_id, ValueId(100));

View File

@ -3,11 +3,9 @@
//! Applies exit bindings to JoinInlineBoundary. //! Applies exit bindings to JoinInlineBoundary.
//! Single-responsibility box for boundary application logic. //! Single-responsibility box for boundary application logic.
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
/// Apply bindings to JoinInlineBoundary /// Apply bindings to JoinInlineBoundary
@ -27,7 +25,7 @@ use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for deter
/// # Returns /// # Returns
/// ///
/// Success or error if boundary cannot be updated /// Success or error if boundary cannot be updated
pub fn apply_exit_bindings_to_boundary( pub(crate) fn apply_exit_bindings_to_boundary(
carrier_info: &CarrierInfo, carrier_info: &CarrierInfo,
exit_meta: &ExitMeta, exit_meta: &ExitMeta,
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
@ -40,13 +38,14 @@ pub fn apply_exit_bindings_to_boundary(
let mut join_outputs = vec![carrier_info.loop_var_id]; // legacy field for compatibility let mut join_outputs = vec![carrier_info.loop_var_id]; // legacy field for compatibility
for carrier in &carrier_info.carriers { for carrier in &carrier_info.carriers {
let post_loop_id = variable_map.get(&carrier.name).copied().ok_or_else(|| { let post_loop_id = variable_map
format!("Post-loop ValueId not found for carrier '{}'", carrier.name) .get(&carrier.name)
})?; .copied()
.ok_or_else(|| format!("Post-loop ValueId not found for carrier '{}'", carrier.name))?;
let join_exit_id = exit_meta.find_binding(&carrier.name).ok_or_else(|| { let join_exit_id = exit_meta
format!("Exit value not found for carrier '{}'", carrier.name) .find_binding(&carrier.name)
})?; .ok_or_else(|| format!("Exit value not found for carrier '{}'", carrier.name))?;
bindings.push(LoopExitBinding { bindings.push(LoopExitBinding {
carrier_name: carrier.name.clone(), carrier_name: carrier.name.clone(),
@ -83,7 +82,7 @@ pub fn apply_exit_bindings_to_boundary(
/// # Returns /// # Returns
/// ///
/// LoopExitBinding for the loop variable /// LoopExitBinding for the loop variable
pub fn create_loop_var_exit_binding(carrier_info: &CarrierInfo) -> LoopExitBinding { pub(crate) fn create_loop_var_exit_binding(carrier_info: &CarrierInfo) -> LoopExitBinding {
use crate::mir::join_ir::lowering::carrier_info::CarrierRole; use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
LoopExitBinding { LoopExitBinding {
carrier_name: carrier_info.loop_var_name.clone(), carrier_name: carrier_info.loop_var_name.clone(),
@ -155,11 +154,7 @@ mod tests {
#[test] #[test]
fn test_loop_var_exit_binding() { fn test_loop_var_exit_binding() {
let carrier_info = CarrierInfo::with_carriers( let carrier_info = CarrierInfo::with_carriers("i".to_string(), ValueId(5), vec![]);
"i".to_string(),
ValueId(5),
vec![],
);
let binding = create_loop_var_exit_binding(&carrier_info); let binding = create_loop_var_exit_binding(&carrier_info);
assert_eq!(binding.carrier_name, "i"); assert_eq!(binding.carrier_name, "i");

View File

@ -3,9 +3,9 @@
//! Constructs loop exit bindings and allocates post-loop ValueIds. //! Constructs loop exit bindings and allocates post-loop ValueIds.
//! Single-responsibility box for binding construction logic. //! Single-responsibility box for binding construction logic.
use crate::mir::ValueId;
use crate::mir::join_ir::lowering::inline_boundary::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::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
/// Generate loop exit bindings /// Generate loop exit bindings
@ -24,7 +24,7 @@ use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for deter
/// # Returns /// # Returns
/// ///
/// Vec of LoopExitBinding, one per carrier, sorted by carrier name /// Vec of LoopExitBinding, one per carrier, sorted by carrier name
pub fn build_loop_exit_bindings( pub(crate) fn build_loop_exit_bindings(
carrier_info: &CarrierInfo, carrier_info: &CarrierInfo,
exit_meta: &ExitMeta, exit_meta: &ExitMeta,
variable_map: &mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism variable_map: &mut BTreeMap<String, ValueId>, // Phase 222.5-D: HashMap → BTreeMap for determinism
@ -33,7 +33,8 @@ pub fn build_loop_exit_bindings(
// Process each carrier in sorted order // Process each carrier in sorted order
for carrier in &carrier_info.carriers { for carrier in &carrier_info.carriers {
let join_exit_id = exit_meta.find_binding(&carrier.name) let join_exit_id = exit_meta
.find_binding(&carrier.name)
.ok_or_else(|| format!("Carrier '{}' missing in ExitMeta", carrier.name))?; .ok_or_else(|| format!("Carrier '{}' missing in ExitMeta", carrier.name))?;
bindings.push(LoopExitBinding { bindings.push(LoopExitBinding {
@ -64,12 +65,10 @@ pub fn build_loop_exit_bindings(
/// # Returns /// # Returns
/// ///
/// Newly allocated ValueId /// Newly allocated ValueId
pub fn allocate_new_value_id(variable_map: &BTreeMap<String, ValueId>) -> ValueId { // Phase 222.5-D: HashMap → BTreeMap for determinism pub(crate) fn allocate_new_value_id(variable_map: &BTreeMap<String, ValueId>) -> ValueId {
// Phase 222.5-D: HashMap → BTreeMap for determinism
// Find the maximum ValueId in current variable_map // Find the maximum ValueId in current variable_map
let max_id = variable_map.values() let max_id = variable_map.values().map(|v| v.0).max().unwrap_or(0);
.map(|v| v.0)
.max()
.unwrap_or(0);
// Allocate next sequential ID // Allocate next sequential ID
// Note: This is a temporary strategy and should be replaced with // Note: This is a temporary strategy and should be replaced with

View File

@ -20,7 +20,7 @@ use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};
/// # Returns /// # Returns
/// ///
/// Ok(()) if validation passes, Err with descriptive message if validation fails /// Ok(()) if validation passes, Err with descriptive message if validation fails
pub fn validate_exit_binding( pub(crate) fn validate_exit_binding(
carrier_info: &CarrierInfo, carrier_info: &CarrierInfo,
exit_meta: &ExitMeta, exit_meta: &ExitMeta,
) -> Result<(), String> { ) -> Result<(), String> {
@ -44,10 +44,7 @@ pub fn validate_exit_binding(
// Validate that all carriers in CarrierInfo have exit values // Validate that all carriers in CarrierInfo have exit values
for carrier in &carrier_info.carriers { for carrier in &carrier_info.carriers {
if exit_meta.find_binding(&carrier.name).is_none() { if exit_meta.find_binding(&carrier.name).is_none() {
return Err(format!( return Err(format!("Carrier '{}' missing in ExitMeta", carrier.name));
"Carrier '{}' missing in ExitMeta",
carrier.name
));
} }
} }

View File

@ -1,9 +1,9 @@
//! Pattern 1: Simple While Loop minimal lowerer //! Pattern 1: Simple While Loop minimal lowerer
use super::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::super::trace;
/// Phase 194: Detection function for Pattern 1 /// Phase 194: Detection function for Pattern 1
/// ///
@ -11,7 +11,7 @@ use super::super::trace;
/// ///
/// Pattern 1 matches: /// Pattern 1 matches:
/// - Pattern kind is Pattern1SimpleWhile (no break, no continue, no if-else PHI) /// - Pattern kind is Pattern1SimpleWhile (no break, no continue, no if-else PHI)
pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool { pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
use crate::mir::loop_pattern_detection::LoopPatternKind; use crate::mir::loop_pattern_detection::LoopPatternKind;
ctx.pattern_kind == LoopPatternKind::Pattern1SimpleWhile ctx.pattern_kind == LoopPatternKind::Pattern1SimpleWhile
} }
@ -19,7 +19,7 @@ pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
/// Phase 194: Lowering function for Pattern 1 /// Phase 194: Lowering function for Pattern 1
/// ///
/// Wrapper around cf_loop_pattern1_minimal to match router signature /// Wrapper around cf_loop_pattern1_minimal to match router signature
pub fn lower( pub(crate) fn lower(
builder: &mut MirBuilder, builder: &mut MirBuilder,
ctx: &super::router::LoopPatternContext, ctx: &super::router::LoopPatternContext,
) -> Result<Option<ValueId>, String> { ) -> Result<Option<ValueId>, String> {
@ -51,12 +51,7 @@ impl MirBuilder {
// Phase 179-B: Use PatternPipelineContext for unified preprocessing // Phase 179-B: Use PatternPipelineContext for unified preprocessing
use super::pattern_pipeline::{build_pattern_context, PatternVariant}; use super::pattern_pipeline::{build_pattern_context, PatternVariant};
let ctx = build_pattern_context( let ctx = build_pattern_context(self, condition, body, PatternVariant::Pattern1)?;
self,
condition,
body,
PatternVariant::Pattern1,
)?;
// Phase 195: Use unified trace // Phase 195: Use unified trace
trace::trace().varmap("pattern1_start", &self.variable_map); trace::trace().varmap("pattern1_start", &self.variable_map);
@ -116,7 +111,10 @@ impl MirBuilder {
let void_val = crate::mir::builder::emission::constant::emit_void(self); let void_val = crate::mir::builder::emission::constant::emit_void(self);
// Phase 195: Use unified trace // Phase 195: Use unified trace
trace::trace().debug("pattern1", &format!("Loop complete, returning Void {:?}", void_val)); trace::trace().debug(
"pattern1",
&format!("Loop complete, returning Void {:?}", void_val),
);
Ok(Some(void_val)) Ok(Some(void_val))
} }

View File

@ -15,11 +15,11 @@
//! - Hardcoded loop condition (i <= 5), if condition (i % 2 == 1) //! - Hardcoded loop condition (i <= 5), if condition (i % 2 == 1)
//! - Kept for backward compatibility with existing tests //! - Kept for backward compatibility with existing tests
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
use super::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::super::merge::exit_line::meta_collector::ExitMetaCollector;
use super::super::trace;
// Phase 213: Hardcoded ValueIds removed - now using ExitMeta-based exit binding generation // Phase 213: Hardcoded ValueIds removed - now using ExitMeta-based exit binding generation
// See: ExitMetaCollector usage below (lines 115-135) // See: ExitMetaCollector usage below (lines 115-135)
@ -32,7 +32,7 @@ use super::super::trace;
/// - Pattern kind is Pattern3IfPhi (has if-else with PHI, no break/continue) /// - Pattern kind is Pattern3IfPhi (has if-else with PHI, no break/continue)
/// ///
/// NOTE: Priority is now handled by pattern classification, not router order /// NOTE: Priority is now handled by pattern classification, not router order
pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool { pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
use crate::mir::loop_pattern_detection::LoopPatternKind; use crate::mir::loop_pattern_detection::LoopPatternKind;
ctx.pattern_kind == LoopPatternKind::Pattern3IfPhi ctx.pattern_kind == LoopPatternKind::Pattern3IfPhi
} }
@ -40,7 +40,7 @@ pub fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
/// Phase 194: Lowering function for Pattern 3 /// Phase 194: Lowering function for Pattern 3
/// ///
/// Wrapper around cf_loop_pattern3_with_if_phi to match router signature /// Wrapper around cf_loop_pattern3_with_if_phi to match router signature
pub fn lower( pub(crate) fn lower(
builder: &mut MirBuilder, builder: &mut MirBuilder,
ctx: &super::router::LoopPatternContext, ctx: &super::router::LoopPatternContext,
) -> Result<Option<ValueId>, String> { ) -> Result<Option<ValueId>, String> {
@ -72,22 +72,23 @@ impl MirBuilder {
) -> Result<Option<ValueId>, String> { ) -> Result<Option<ValueId>, String> {
// Phase 179-B: Use PatternPipelineContext for unified preprocessing // Phase 179-B: Use PatternPipelineContext for unified preprocessing
use super::pattern_pipeline::{build_pattern_context, PatternVariant}; use super::pattern_pipeline::{build_pattern_context, PatternVariant};
let ctx = build_pattern_context( let ctx = build_pattern_context(self, condition, body, PatternVariant::Pattern3)?;
self,
condition,
body,
PatternVariant::Pattern3,
)?;
// Phase 213: AST-based if-sum pattern detection // Phase 213: AST-based if-sum pattern detection
// Phase 242-EX-A: Legacy mode removed - all if-sum patterns now handled dynamically // Phase 242-EX-A: Legacy mode removed - all if-sum patterns now handled dynamically
if !ctx.is_if_sum_pattern() { if !ctx.is_if_sum_pattern() {
// Not an if-sum pattern → let router try other patterns or fall back // Not an if-sum pattern → let router try other patterns or fall back
trace::trace().debug("pattern3", "Not an if-sum pattern, returning None to try other patterns"); trace::trace().debug(
"pattern3",
"Not an if-sum pattern, returning None to try other patterns",
);
return Ok(None); return Ok(None);
} }
trace::trace().debug("pattern3", "Detected if-sum pattern, using AST-based lowerer"); trace::trace().debug(
"pattern3",
"Detected if-sum pattern, using AST-based lowerer",
);
self.lower_pattern3_if_sum(&ctx, condition, body, debug) self.lower_pattern3_if_sum(&ctx, condition, body, debug)
} }
@ -134,35 +135,34 @@ impl MirBuilder {
for binding in &condition_bindings { for binding in &condition_bindings {
trace::trace().debug( trace::trace().debug(
"pattern3/if-sum", "pattern3/if-sum",
&format!(" '{}': HOST {:?} → JoinIR {:?}", binding.name, binding.host_value, binding.join_value), &format!(
" '{}': HOST {:?} → JoinIR {:?}",
binding.name, binding.host_value, binding.join_value
),
); );
} }
// Call AST-based if-sum lowerer with ConditionEnv // Call AST-based if-sum lowerer with ConditionEnv
let (join_module, fragment_meta) = lower_if_sum_pattern( let (join_module, fragment_meta) =
condition, lower_if_sum_pattern(condition, if_stmt, body, &cond_env, &mut join_value_space)?;
if_stmt,
body,
&cond_env,
&mut join_value_space,
)?;
let exit_meta = &fragment_meta.exit_meta; let exit_meta = &fragment_meta.exit_meta;
trace::trace().debug( trace::trace().debug(
"pattern3/if-sum", "pattern3/if-sum",
&format!("ExitMeta: {} exit values", exit_meta.exit_values.len()) &format!("ExitMeta: {} exit values", exit_meta.exit_values.len()),
); );
for (carrier_name, join_value) in &exit_meta.exit_values { for (carrier_name, join_value) in &exit_meta.exit_values {
trace::trace().debug( trace::trace().debug(
"pattern3/if-sum", "pattern3/if-sum",
&format!(" {} → ValueId({})", carrier_name, join_value.0) &format!(" {} → ValueId({})", carrier_name, join_value.0),
); );
} }
// Build exit bindings using ExitMetaCollector // Build exit bindings using ExitMetaCollector
// Phase 228-8: Pass carrier_info to include ConditionOnly carriers // Phase 228-8: Pass carrier_info to include ConditionOnly carriers
let exit_bindings = ExitMetaCollector::collect(self, exit_meta, Some(&ctx.carrier_info), debug); let exit_bindings =
ExitMetaCollector::collect(self, exit_meta, Some(&ctx.carrier_info), debug);
// Build boundary with carrier inputs // Build boundary with carrier inputs
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder; use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
@ -174,9 +174,7 @@ impl MirBuilder {
// Allocate join_inputs dynamically from JoinValueSpace // Allocate join_inputs dynamically from JoinValueSpace
// These ValueIds (0, 1, 2, ...) represent JoinIR function parameters // These ValueIds (0, 1, 2, ...) represent JoinIR function parameters
let join_inputs: Vec<ValueId> = (0..total_inputs) let join_inputs: Vec<ValueId> = (0..total_inputs).map(|i| ValueId(i as u32)).collect();
.map(|i| ValueId(i as u32))
.collect();
// Build host_inputs: loop_var + exit_bindings (in order) // Build host_inputs: loop_var + exit_bindings (in order)
let mut host_inputs = vec![ctx.loop_var_id]; let mut host_inputs = vec![ctx.loop_var_id];
@ -197,8 +195,9 @@ impl MirBuilder {
"pattern3/if-sum", "pattern3/if-sum",
&format!( &format!(
"Boundary inputs: {} total (loop_var + {} exit bindings)", "Boundary inputs: {} total (loop_var + {} exit bindings)",
total_inputs, exit_bindings.len() total_inputs,
) exit_bindings.len()
),
); );
// Phase 215-2: Pass expr_result to boundary // Phase 215-2: Pass expr_result to boundary
@ -213,7 +212,7 @@ impl MirBuilder {
if let Some(expr_id) = fragment_meta.expr_result { if let Some(expr_id) = fragment_meta.expr_result {
trace::trace().debug( trace::trace().debug(
"pattern3/if-sum", "pattern3/if-sum",
&format!("Passing expr_result={:?} to boundary", expr_id) &format!("Passing expr_result={:?} to boundary", expr_id),
); );
boundary_builder = boundary_builder.with_expr_result(Some(expr_id)); boundary_builder = boundary_builder.with_expr_result(Some(expr_id));
} }
@ -234,7 +233,7 @@ impl MirBuilder {
if let Some(expr_val) = merge_result { if let Some(expr_val) = merge_result {
trace::trace().debug( trace::trace().debug(
"pattern3/if-sum", "pattern3/if-sum",
&format!("Loop complete, returning expr_result {:?}", expr_val) &format!("Loop complete, returning expr_result {:?}", expr_val),
); );
Ok(Some(expr_val)) Ok(Some(expr_val))
} else { } else {
@ -243,7 +242,7 @@ impl MirBuilder {
let void_val = constant::emit_void(self); let void_val = constant::emit_void(self);
trace::trace().debug( trace::trace().debug(
"pattern3/if-sum", "pattern3/if-sum",
&format!("Loop complete, returning Void {:?}", void_val) &format!("Loop complete, returning Void {:?}", void_val),
); );
Ok(Some(void_val)) Ok(Some(void_val))
} }

View File

@ -19,7 +19,7 @@ use crate::mir::join_ir::lowering::continue_branch_normalizer::ContinueBranchNor
use crate::mir::join_ir::lowering::loop_update_analyzer::{LoopUpdateAnalyzer, UpdateExpr}; use crate::mir::join_ir::lowering::loop_update_analyzer::{LoopUpdateAnalyzer, UpdateExpr};
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism
pub struct Pattern4CarrierAnalyzer; pub(crate) struct Pattern4CarrierAnalyzer;
impl Pattern4CarrierAnalyzer { impl Pattern4CarrierAnalyzer {
/// Analyze and filter carriers for continue pattern /// Analyze and filter carriers for continue pattern
@ -54,10 +54,8 @@ impl Pattern4CarrierAnalyzer {
all_carriers: &CarrierInfo, all_carriers: &CarrierInfo,
) -> Result<CarrierInfo, String> { ) -> Result<CarrierInfo, String> {
// Identify which carriers are updated in loop body // Identify which carriers are updated in loop body
let carrier_updates = LoopUpdateAnalyzer::analyze_carrier_updates( let carrier_updates =
loop_body, LoopUpdateAnalyzer::analyze_carrier_updates(loop_body, &all_carriers.carriers);
&all_carriers.carriers,
);
// Filter carriers: only keep those that have update expressions // Filter carriers: only keep those that have update expressions
let updated_carriers: Vec<CarrierVar> = all_carriers let updated_carriers: Vec<CarrierVar> = all_carriers
@ -87,7 +85,8 @@ impl Pattern4CarrierAnalyzer {
pub fn analyze_carrier_updates( pub fn analyze_carrier_updates(
loop_body: &[ASTNode], loop_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
LoopUpdateAnalyzer::analyze_carrier_updates(loop_body, carriers) LoopUpdateAnalyzer::analyze_carrier_updates(loop_body, carriers)
} }
@ -155,9 +154,9 @@ impl Pattern4CarrierAnalyzer {
.. ..
} => { } => {
then_body.iter().any(|n| Self::has_continue(n)) then_body.iter().any(|n| Self::has_continue(n))
|| else_body.as_ref().map_or(false, |body| { || else_body
body.iter().any(|n| Self::has_continue(n)) .as_ref()
}) .map_or(false, |body| body.iter().any(|n| Self::has_continue(n)))
} }
ASTNode::Loop { body, .. } => body.iter().any(|n| Self::has_continue(n)), ASTNode::Loop { body, .. } => body.iter().any(|n| Self::has_continue(n)),
_ => false, _ => false,
@ -331,9 +330,7 @@ mod tests {
}), }),
span: span.clone(), span: span.clone(),
}], }],
else_body: Some(vec![ASTNode::Continue { else_body: Some(vec![ASTNode::Continue { span: span.clone() }]),
span: span.clone(),
}]),
span: span.clone(), span: span.clone(),
}]; }];

View File

@ -31,12 +31,12 @@
//! - **ExitMeta**: Maps final carrier values to host variable slots //! - **ExitMeta**: Maps final carrier values to host variable slots
//! - **Phase 33-21 fix**: Correct remapping of function parameters to header PHI dsts //! - **Phase 33-21 fix**: Correct remapping of function parameters to header PHI dsts
use super::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 super::super::trace;
use crate::mir::loop_pattern_detection::error_messages;
use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr; use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
use crate::mir::loop_pattern_detection::error_messages;
use crate::mir::ValueId;
use std::collections::BTreeMap; use std::collections::BTreeMap;
/// Phase 194+: Detection function for Pattern 4 /// Phase 194+: Detection function for Pattern 4
@ -62,9 +62,9 @@ use std::collections::BTreeMap;
/// 3. **Phase 178**: No string/complex carrier updates (JoinIR doesn't support string concat) /// 3. **Phase 178**: No string/complex carrier updates (JoinIR doesn't support string concat)
/// ///
/// If all conditions are met, Pattern 4 is detected. /// If all conditions are met, Pattern 4 is detected.
pub fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool { pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
use crate::mir::loop_pattern_detection::LoopPatternKind;
use super::common_init::CommonPatternInitializer; use super::common_init::CommonPatternInitializer;
use crate::mir::loop_pattern_detection::LoopPatternKind;
// Basic pattern check // Basic pattern check
if ctx.pattern_kind != LoopPatternKind::Pattern4Continue { if ctx.pattern_kind != LoopPatternKind::Pattern4Continue {
@ -97,17 +97,12 @@ pub fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
/// 4. Create JoinInlineBoundary for input/output mapping /// 4. Create JoinInlineBoundary for input/output mapping
/// 5. Merge MIR blocks into current_function /// 5. Merge MIR blocks into current_function
/// 6. Return loop result (first carrier value) /// 6. Return loop result (first carrier value)
pub fn lower( pub(crate) fn lower(
builder: &mut MirBuilder, builder: &mut MirBuilder,
ctx: &super::router::LoopPatternContext, ctx: &super::router::LoopPatternContext,
) -> Result<Option<ValueId>, String> { ) -> Result<Option<ValueId>, String> {
// Phase 33-19: Connect stub to actual implementation // Phase 33-19: Connect stub to actual implementation
builder.cf_loop_pattern4_with_continue( builder.cf_loop_pattern4_with_continue(ctx.condition, ctx.body, ctx.func_name, ctx.debug)
ctx.condition,
ctx.body,
ctx.func_name,
ctx.debug,
)
} }
impl MirBuilder { impl MirBuilder {
@ -170,10 +165,10 @@ fn prepare_pattern4_context(
) -> Result<Pattern4Prepared, String> { ) -> Result<Pattern4Prepared, String> {
use super::pattern4_carrier_analyzer::Pattern4CarrierAnalyzer; use super::pattern4_carrier_analyzer::Pattern4CarrierAnalyzer;
use super::pattern_pipeline::{build_pattern_context, PatternVariant}; use super::pattern_pipeline::{build_pattern_context, PatternVariant};
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
use crate::mir::loop_pattern_detection::loop_body_cond_promoter::{ use crate::mir::loop_pattern_detection::loop_body_cond_promoter::{
LoopBodyCondPromoter, ConditionPromotionRequest, ConditionPromotionResult, ConditionPromotionRequest, ConditionPromotionResult, LoopBodyCondPromoter,
}; };
use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScopeBox;
// Normalize continue branches for analysis/lowering // Normalize continue branches for analysis/lowering
let normalized_body = Pattern4CarrierAnalyzer::normalize_continue_branches(body); let normalized_body = Pattern4CarrierAnalyzer::normalize_continue_branches(body);
@ -195,28 +190,33 @@ fn prepare_pattern4_context(
&normalized_body, &normalized_body,
&carrier_info_prelim.carriers, &carrier_info_prelim.carriers,
); );
let mut carrier_info = Pattern4CarrierAnalyzer::analyze_carriers( let mut carrier_info =
&normalized_body, Pattern4CarrierAnalyzer::analyze_carriers(&normalized_body, &carrier_info_prelim)?;
&carrier_info_prelim,
)?;
trace::trace().debug( trace::trace().debug(
"pattern4", "pattern4",
&format!( &format!(
"CarrierInfo: loop_var={}, carriers={:?}", "CarrierInfo: loop_var={}, carriers={:?}",
carrier_info.loop_var_name, carrier_info.loop_var_name,
carrier_info.carriers.iter().map(|c| &c.name).collect::<Vec<_>>() carrier_info
) .carriers
.iter()
.map(|c| &c.name)
.collect::<Vec<_>>()
),
); );
trace::trace().debug( trace::trace().debug(
"pattern4", "pattern4",
&format!("Analyzed {} carrier update expressions", carrier_updates.len()) &format!(
"Analyzed {} carrier update expressions",
carrier_updates.len()
),
); );
for (carrier_name, update_expr) in &carrier_updates { for (carrier_name, update_expr) in &carrier_updates {
trace::trace().debug( trace::trace().debug(
"pattern4", "pattern4",
&format!(" {}{:?}", carrier_name, update_expr) &format!(" {}{:?}", carrier_name, update_expr),
); );
} }
@ -228,11 +228,8 @@ fn prepare_pattern4_context(
vec![condition] vec![condition]
}; };
let cond_scope = LoopConditionScopeBox::analyze( let cond_scope =
&loop_var_name, LoopConditionScopeBox::analyze(&loop_var_name, &conditions_to_analyze, Some(&loop_scope));
&conditions_to_analyze,
Some(&loop_scope),
);
if cond_scope.has_loop_body_local() { if cond_scope.has_loop_body_local() {
let promotion_req = ConditionPromotionRequest { let promotion_req = ConditionPromotionRequest {
@ -285,13 +282,15 @@ fn prepare_pattern4_context(
} else { } else {
return Err(error_messages::format_error_pattern4_trim_not_safe( return Err(error_messages::format_error_pattern4_trim_not_safe(
&helper.carrier_name, &helper.carrier_name,
helper.whitespace_count() helper.whitespace_count(),
)); ));
} }
} }
} }
ConditionPromotionResult::CannotPromote { reason, vars } => { ConditionPromotionResult::CannotPromote { reason, vars } => {
return Err(error_messages::format_error_pattern4_promotion_failed(&vars, &reason)); return Err(error_messages::format_error_pattern4_promotion_failed(
&vars, &reason,
));
} }
} }
} }
@ -311,11 +310,11 @@ fn lower_pattern4_joinir(
prepared: &Pattern4Prepared, prepared: &Pattern4Prepared,
debug: bool, debug: bool,
) -> Result<Option<ValueId>, String> { ) -> Result<Option<ValueId>, String> {
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
use super::conversion_pipeline::JoinIRConversionPipeline;
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_with_continue_minimal::lower_loop_with_continue_minimal; use crate::mir::join_ir::lowering::loop_with_continue_minimal::lower_loop_with_continue_minimal;
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder; use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
use super::conversion_pipeline::JoinIRConversionPipeline;
trace::trace().varmap("pattern4_start", &builder.variable_map); trace::trace().varmap("pattern4_start", &builder.variable_map);
@ -338,12 +337,12 @@ fn lower_pattern4_joinir(
trace::trace().debug( trace::trace().debug(
"pattern4", "pattern4",
&format!("ExitMeta: {} exit bindings", exit_meta.exit_values.len()) &format!("ExitMeta: {} exit bindings", exit_meta.exit_values.len()),
); );
for (carrier_name, join_value) in &exit_meta.exit_values { for (carrier_name, join_value) in &exit_meta.exit_values {
trace::trace().debug( trace::trace().debug(
"pattern4", "pattern4",
&format!(" {} → ValueId({})", carrier_name, join_value.0) &format!(" {} → ValueId({})", carrier_name, join_value.0),
); );
} }
@ -356,7 +355,9 @@ fn lower_pattern4_joinir(
for carrier in &prepared.carrier_info.carriers { for carrier in &prepared.carrier_info.carriers {
if !exit_bindings.iter().any(|b| b.carrier_name == carrier.name) { if !exit_bindings.iter().any(|b| b.carrier_name == carrier.name) {
return Err(error_messages::format_error_pattern4_carrier_not_found(&carrier.name)); return Err(error_messages::format_error_pattern4_carrier_not_found(
&carrier.name,
));
} }
} }
@ -368,7 +369,10 @@ fn lower_pattern4_joinir(
trace::trace().debug( trace::trace().debug(
"pattern4", "pattern4",
&format!("host_inputs: {:?}", host_inputs.iter().map(|v| v.0).collect::<Vec<_>>()) &format!(
"host_inputs: {:?}",
host_inputs.iter().map(|v| v.0).collect::<Vec<_>>()
),
); );
let mut join_inputs = vec![ValueId(0)]; // ValueId(0) = i_init in JoinIR let mut join_inputs = vec![ValueId(0)]; // ValueId(0) = i_init in JoinIR
@ -378,7 +382,10 @@ fn lower_pattern4_joinir(
trace::trace().debug( trace::trace().debug(
"pattern4", "pattern4",
&format!("join_inputs: {:?}", join_inputs.iter().map(|v| v.0).collect::<Vec<_>>()) &format!(
"join_inputs: {:?}",
join_inputs.iter().map(|v| v.0).collect::<Vec<_>>()
),
); );
let boundary = JoinInlineBoundaryBuilder::new() let boundary = JoinInlineBoundaryBuilder::new()
@ -397,7 +404,10 @@ fn lower_pattern4_joinir(
)?; )?;
let void_val = crate::mir::builder::emission::constant::emit_void(builder); let void_val = crate::mir::builder::emission::constant::emit_void(builder);
trace::trace().debug("pattern4", &format!("Loop complete, returning Void {:?}", void_val)); trace::trace().debug(
"pattern4",
&format!("Loop complete, returning Void {:?}", void_val),
);
Ok(Some(void_val)) Ok(Some(void_val))
} }

View File

@ -34,14 +34,14 @@
use crate::ast::ASTNode; use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder; use crate::mir::builder::MirBuilder;
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::ConditionBinding; use crate::mir::join_ir::lowering::condition_env::ConditionBinding;
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
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; use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr;
use crate::mir::join_ir::lowering::loop_update_summary::LoopUpdateSummary; // Phase 213 use crate::mir::join_ir::lowering::loop_update_summary::LoopUpdateSummary; // Phase 213
use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper; use crate::mir::loop_pattern_detection::trim_loop_helper::TrimLoopHelper;
use crate::mir::ValueId;
use crate::mir::BasicBlockId; use crate::mir::BasicBlockId;
use crate::mir::ValueId;
use std::collections::{BTreeMap, BTreeSet}; // Phase 222.5-D: HashMap → BTreeMap for determinism use std::collections::{BTreeMap, BTreeSet}; // Phase 222.5-D: HashMap → BTreeMap for determinism
use super::common_init::CommonPatternInitializer; use super::common_init::CommonPatternInitializer;
@ -72,9 +72,8 @@ use super::loop_scope_shape_builder::LoopScopeShapeBuilder;
/// let join_module = lower_simple_while_minimal(ctx.loop_scope)?; /// let join_module = lower_simple_while_minimal(ctx.loop_scope)?;
/// ``` /// ```
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PatternPipelineContext { pub(crate) struct PatternPipelineContext {
// === Common Data (All Patterns) === // === Common Data (All Patterns) ===
/// Loop variable name (e.g., "i") /// Loop variable name (e.g., "i")
pub loop_var_name: String, pub loop_var_name: String,
@ -88,7 +87,6 @@ pub struct PatternPipelineContext {
pub loop_scope: LoopScopeShape, pub loop_scope: LoopScopeShape,
// === Pattern 2/4: Break/Continue Condition === // === Pattern 2/4: Break/Continue Condition ===
/// Condition environment (variable → JoinIR ValueId mapping) /// Condition environment (variable → JoinIR ValueId mapping)
/// Used by Pattern 2 (break condition) and Pattern 4 (continue condition) /// Used by Pattern 2 (break condition) and Pattern 4 (continue condition)
#[allow(dead_code)] #[allow(dead_code)]
@ -105,21 +103,18 @@ pub struct PatternPipelineContext {
pub carrier_updates: Option<BTreeMap<String, UpdateExpr>>, // Phase 222.5-D: HashMap → BTreeMap for determinism pub carrier_updates: Option<BTreeMap<String, UpdateExpr>>, // Phase 222.5-D: HashMap → BTreeMap for determinism
// === Pattern 2/4: Trim Pattern Support === // === Pattern 2/4: Trim Pattern Support ===
/// Trim loop helper (if Trim pattern detected during promotion) /// Trim loop helper (if Trim pattern detected during promotion)
/// Used by Pattern 2 (string trim) - Pattern 4 support TBD /// Used by Pattern 2 (string trim) - Pattern 4 support TBD
#[allow(dead_code)] #[allow(dead_code)]
pub trim_helper: Option<TrimLoopHelper>, pub trim_helper: Option<TrimLoopHelper>,
// === Pattern 2: Break Condition === // === Pattern 2: Break Condition ===
/// Effective break condition (may be modified for Trim pattern) /// Effective break condition (may be modified for Trim pattern)
/// Used only by Pattern 2 /// Used only by Pattern 2
#[allow(dead_code)] #[allow(dead_code)]
pub break_condition: Option<ASTNode>, pub break_condition: Option<ASTNode>,
// === Pattern 3: If-Sum Generalization (Phase 213) === // === Pattern 3: If-Sum Generalization (Phase 213) ===
/// Loop condition AST node /// Loop condition AST node
/// Used by Pattern 3 for dynamic loop condition lowering /// Used by Pattern 3 for dynamic loop condition lowering
#[allow(dead_code)] #[allow(dead_code)]
@ -138,7 +133,7 @@ pub struct PatternPipelineContext {
/// Pattern variant selector /// Pattern variant selector
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PatternVariant { pub(crate) enum PatternVariant {
/// Pattern 1: Simple while loop (no break, no continue, no if-else PHI) /// Pattern 1: Simple while loop (no break, no continue, no if-else PHI)
Pattern1, Pattern1,
/// Pattern 2: Loop with break statement /// Pattern 2: Loop with break statement
@ -193,7 +188,7 @@ impl PatternPipelineContext {
// Complex conditions (e.g., i % 2 == 1) → fallback to legacy mode // Complex conditions (e.g., i % 2 == 1) → fallback to legacy mode
if let Some(ASTNode::If { condition, .. }) = if_stmt { if let Some(ASTNode::If { condition, .. }) = if_stmt {
use crate::mir::join_ir::lowering::condition_pattern::{ use crate::mir::join_ir::lowering::condition_pattern::{
analyze_condition_pattern, normalize_comparison, ConditionPattern analyze_condition_pattern, normalize_comparison, ConditionPattern,
}; };
// (a) Pattern check: must be SimpleComparison // (a) Pattern check: must be SimpleComparison
@ -213,7 +208,10 @@ impl PatternPipelineContext {
// Phase 219: Use assignment-based carrier detection // Phase 219: Use assignment-based carrier detection
// (1 counter like "i" + 1-2 accumulators like "sum", "count") // (1 counter like "i" + 1-2 accumulators like "sum", "count")
use crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_from_ast; use crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_from_ast;
let carrier_names: Vec<String> = self.carrier_info.carriers.iter() let carrier_names: Vec<String> = self
.carrier_info
.carriers
.iter()
.map(|c| c.name.clone()) .map(|c| c.name.clone())
.collect(); .collect();
@ -232,9 +230,9 @@ impl PatternPipelineContext {
/// ///
/// Returns the first if statement found in loop_body, if any. /// Returns the first if statement found in loop_body, if any.
pub fn extract_if_statement(&self) -> Option<&ASTNode> { pub fn extract_if_statement(&self) -> Option<&ASTNode> {
self.loop_body.as_ref().and_then(|body| { self.loop_body
body.iter().find(|stmt| matches!(stmt, ASTNode::If { .. })) .as_ref()
}) .and_then(|body| body.iter().find(|stmt| matches!(stmt, ASTNode::If { .. })))
} }
} }
@ -263,15 +261,14 @@ impl PatternPipelineContext {
/// - Loop variable not found in variable_map /// - Loop variable not found in variable_map
/// - Condition variable not found (Pattern 2/4) /// - Condition variable not found (Pattern 2/4)
/// - Trim pattern promotion fails (Pattern 2/4) /// - Trim pattern promotion fails (Pattern 2/4)
pub fn build_pattern_context( pub(crate) fn build_pattern_context(
builder: &mut MirBuilder, builder: &mut MirBuilder,
condition: &ASTNode, condition: &ASTNode,
body: &[ASTNode], body: &[ASTNode],
variant: PatternVariant, variant: PatternVariant,
) -> Result<PatternPipelineContext, String> { ) -> Result<PatternPipelineContext, String> {
// Step 1: Common initialization (all patterns) // Step 1: Common initialization (all patterns)
let (loop_var_name, loop_var_id, carrier_info) = let (loop_var_name, loop_var_id, carrier_info) = CommonPatternInitializer::initialize_pattern(
CommonPatternInitializer::initialize_pattern(
builder, builder,
condition, condition,
&builder.variable_map, &builder.variable_map,
@ -304,9 +301,16 @@ pub fn build_pattern_context(
}; };
// Step 3: Pattern-specific preprocessing // Step 3: Pattern-specific preprocessing
let (condition_env, condition_bindings, carrier_updates, trim_helper, break_condition, let (
loop_condition, loop_body, loop_update_summary) = condition_env,
match variant { condition_bindings,
carrier_updates,
trim_helper,
break_condition,
loop_condition,
loop_body,
loop_update_summary,
) = match variant {
PatternVariant::Pattern1 => { PatternVariant::Pattern1 => {
// Pattern 1: No additional preprocessing needed // Pattern 1: No additional preprocessing needed
(None, None, None, None, None, None, None, None) (None, None, None, None, None, None, None, None)

View File

@ -30,7 +30,7 @@ use crate::mir::loop_pattern_detection::{LoopFeatures, LoopPatternKind};
use super::ast_feature_extractor as ast_features; use super::ast_feature_extractor as ast_features;
/// Context passed to pattern detect/lower functions /// Context passed to pattern detect/lower functions
pub struct LoopPatternContext<'a> { pub(crate) struct LoopPatternContext<'a> {
/// Loop condition AST node /// Loop condition AST node
pub condition: &'a ASTNode, pub condition: &'a ASTNode,
@ -69,7 +69,7 @@ impl<'a> LoopPatternContext<'a> {
/// Phase 194+: Automatically detects continue/break statements in body /// Phase 194+: Automatically detects continue/break statements in body
/// Phase 192: Extract features and classify pattern from AST /// Phase 192: Extract features and classify pattern from AST
/// Phase 193: Feature extraction delegated to ast_feature_extractor module /// Phase 193: Feature extraction delegated to ast_feature_extractor module
pub fn new( pub(crate) fn new(
condition: &'a ASTNode, condition: &'a ASTNode,
body: &'a [ASTNode], body: &'a [ASTNode],
func_name: &'a str, func_name: &'a str,
@ -99,7 +99,7 @@ impl<'a> LoopPatternContext<'a> {
} }
/// Phase 200-C: Create context with fn_body for capture analysis /// Phase 200-C: Create context with fn_body for capture analysis
pub fn with_fn_body( pub(crate) fn with_fn_body(
condition: &'a ASTNode, condition: &'a ASTNode,
body: &'a [ASTNode], body: &'a [ASTNode],
func_name: &'a str, func_name: &'a str,
@ -117,19 +117,19 @@ impl<'a> LoopPatternContext<'a> {
/// Entry in the loop pattern router table. /// Entry in the loop pattern router table.
/// Each pattern registers a detect function and a lower function. /// Each pattern registers a detect function and a lower function.
pub struct LoopPatternEntry { pub(crate) struct LoopPatternEntry {
/// Human-readable pattern name for debugging /// Human-readable pattern name for debugging
pub name: &'static str, pub(crate) name: &'static str,
/// Priority (lower = tried first). Pattern1=10, Pattern2=20, Pattern3=30 /// Priority (lower = tried first). Pattern1=10, Pattern2=20, Pattern3=30
#[allow(dead_code)] #[allow(dead_code)]
pub priority: u8, pub(crate) priority: u8,
/// Detection function: returns true if this pattern matches /// Detection function: returns true if this pattern matches
pub detect: fn(&MirBuilder, &LoopPatternContext) -> bool, pub(crate) detect: fn(&MirBuilder, &LoopPatternContext) -> bool,
/// Lowering function: performs the actual JoinIR generation /// Lowering function: performs the actual JoinIR generation
pub lower: fn(&mut MirBuilder, &LoopPatternContext) -> Result<Option<ValueId>, String>, pub(crate) lower: fn(&mut MirBuilder, &LoopPatternContext) -> Result<Option<ValueId>, String>,
} }
/// Static table of all registered loop patterns. /// Static table of all registered loop patterns.
@ -156,7 +156,7 @@ pub struct LoopPatternEntry {
/// - Structure: has_break && !has_continue /// - Structure: has_break && !has_continue
/// ///
/// Note: func_name is now only used for debug logging, not pattern detection /// Note: func_name is now only used for debug logging, not pattern detection
pub static LOOP_PATTERNS: &[LoopPatternEntry] = &[ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
LoopPatternEntry { LoopPatternEntry {
name: "Pattern4_WithContinue", name: "Pattern4_WithContinue",
priority: 5, // Highest priority - continue is most specific priority: 5, // Highest priority - continue is most specific
@ -195,7 +195,7 @@ pub static LOOP_PATTERNS: &[LoopPatternEntry] = &[
/// - Pattern detection: `ctx.pattern_kind` (from `loop_pattern_detection::classify`) /// - Pattern detection: `ctx.pattern_kind` (from `loop_pattern_detection::classify`)
/// - No redundant pattern detection in detect functions /// - No redundant pattern detection in detect functions
/// - All patterns use structure-based classification /// - All patterns use structure-based classification
pub fn route_loop_pattern( pub(crate) fn route_loop_pattern(
builder: &mut MirBuilder, builder: &mut MirBuilder,
ctx: &LoopPatternContext, ctx: &LoopPatternContext,
) -> Result<Option<ValueId>, String> { ) -> Result<Option<ValueId>, String> {
@ -217,7 +217,13 @@ pub fn route_loop_pattern(
// No pattern matched - return None (caller will handle error) // No pattern matched - return None (caller will handle error)
// Phase 187-2: Legacy LoopBuilder removed, all loops must use JoinIR // Phase 187-2: Legacy LoopBuilder removed, all loops must use JoinIR
if ctx.debug { if ctx.debug {
trace::trace().debug("route", &format!("No pattern matched for function '{}' (pattern_kind={:?})", ctx.func_name, ctx.pattern_kind)); trace::trace().debug(
"route",
&format!(
"No pattern matched for function '{}' (pattern_kind={:?})",
ctx.func_name, ctx.pattern_kind
),
);
} }
Ok(None) Ok(None)
} }

View File

@ -50,7 +50,7 @@ use crate::mir::ValueId;
/// Trim pattern lowering orchestrator /// Trim pattern lowering orchestrator
/// ///
/// Phase 180: Single entry point for all Trim/P5 lowering operations. /// Phase 180: Single entry point for all Trim/P5 lowering operations.
pub struct TrimLoopLowerer; pub(crate) struct TrimLoopLowerer;
/// Result of successful Trim lowering preprocessing /// Result of successful Trim lowering preprocessing
/// ///
@ -59,7 +59,7 @@ pub struct TrimLoopLowerer;
/// - Updated carrier info with promoted carrier /// - Updated carrier info with promoted carrier
/// - Condition environment bindings /// - Condition environment bindings
/// - Trim helper for pattern-specific operations /// - Trim helper for pattern-specific operations
pub struct TrimLoweringResult { pub(crate) struct TrimLoweringResult {
/// Replaced break condition (e.g., `!is_carrier`) /// Replaced break condition (e.g., `!is_carrier`)
/// ///
/// Pattern2/4 will use this instead of the original break condition /// Pattern2/4 will use this instead of the original break condition
@ -98,12 +98,14 @@ impl TrimLoopLowerer {
Self::is_var_used_in_condition(var_name, left) Self::is_var_used_in_condition(var_name, left)
|| Self::is_var_used_in_condition(var_name, right) || Self::is_var_used_in_condition(var_name, right)
} }
ASTNode::UnaryOp { operand, .. } => { ASTNode::UnaryOp { operand, .. } => Self::is_var_used_in_condition(var_name, operand),
Self::is_var_used_in_condition(var_name, operand) ASTNode::MethodCall {
} object, arguments, ..
ASTNode::MethodCall { object, arguments, .. } => { } => {
Self::is_var_used_in_condition(var_name, object) Self::is_var_used_in_condition(var_name, object)
|| arguments.iter().any(|arg| Self::is_var_used_in_condition(var_name, arg)) || arguments
.iter()
.any(|arg| Self::is_var_used_in_condition(var_name, arg))
} }
// Add other node types as needed // Add other node types as needed
_ => false, _ => false,
@ -173,11 +175,8 @@ impl TrimLoopLowerer {
// TODO: Phase 180-3 will implement full logic from Pattern2 // TODO: Phase 180-3 will implement full logic from Pattern2
// Step 1: Check if condition references LoopBodyLocal variables // Step 1: Check if condition references LoopBodyLocal variables
let cond_scope = LoopConditionScopeBox::analyze( let cond_scope =
loop_var_name, LoopConditionScopeBox::analyze(loop_var_name, &[loop_cond, break_cond], Some(scope));
&[loop_cond, break_cond],
Some(scope),
);
eprintln!( eprintln!(
"[TrimLoopLowerer] Analyzing condition scope: {} variables", "[TrimLoopLowerer] Analyzing condition scope: {} variables",
@ -194,7 +193,9 @@ impl TrimLoopLowerer {
// Phase 183-2: Filter to only condition LoopBodyLocal (skip body-only) // Phase 183-2: Filter to only condition LoopBodyLocal (skip body-only)
use crate::mir::loop_pattern_detection::loop_condition_scope::CondVarScope; use crate::mir::loop_pattern_detection::loop_condition_scope::CondVarScope;
let condition_body_locals: Vec<_> = cond_scope.vars.iter() let condition_body_locals: Vec<_> = cond_scope
.vars
.iter()
.filter(|v| v.scope == CondVarScope::LoopBodyLocal) .filter(|v| v.scope == CondVarScope::LoopBodyLocal)
.filter(|v| { .filter(|v| {
// Check if variable is actually used in break condition // Check if variable is actually used in break condition
@ -213,7 +214,10 @@ impl TrimLoopLowerer {
eprintln!( eprintln!(
"[TrimLoopLowerer] Phase 183: Found {} condition LoopBodyLocal variables: {:?}", "[TrimLoopLowerer] Phase 183: Found {} condition LoopBodyLocal variables: {:?}",
condition_body_locals.len(), condition_body_locals.len(),
condition_body_locals.iter().map(|v| &v.name).collect::<Vec<_>>() condition_body_locals
.iter()
.map(|v| &v.name)
.collect::<Vec<_>>()
); );
// Step 2: Try promotion via LoopBodyCarrierPromoter // Step 2: Try promotion via LoopBodyCarrierPromoter
@ -242,9 +246,7 @@ impl TrimLoopLowerer {
); );
// Step 4: Safety check via TrimLoopHelper // Step 4: Safety check via TrimLoopHelper
let trim_helper = carrier_info let trim_helper = carrier_info.trim_helper().ok_or_else(|| {
.trim_helper()
.ok_or_else(|| {
format!( format!(
"[TrimLoopLowerer] Promoted but no TrimLoopHelper attached (carrier: '{}')", "[TrimLoopLowerer] Promoted but no TrimLoopHelper attached (carrier: '{}')",
trim_info.carrier_name trim_info.carrier_name
@ -262,15 +264,13 @@ impl TrimLoopLowerer {
eprintln!("[TrimLoopLowerer] Safe Trim pattern detected, implementing lowering"); eprintln!("[TrimLoopLowerer] Safe Trim pattern detected, implementing lowering");
eprintln!( eprintln!(
"[TrimLoopLowerer] Carrier: '{}', original var: '{}', whitespace chars: {:?}", "[TrimLoopLowerer] Carrier: '{}', original var: '{}', whitespace chars: {:?}",
trim_helper.carrier_name, trim_helper.original_var, trim_helper.whitespace_chars trim_helper.carrier_name,
trim_helper.original_var,
trim_helper.whitespace_chars
); );
// Step 5: Generate carrier initialization code // Step 5: Generate carrier initialization code
Self::generate_carrier_initialization( Self::generate_carrier_initialization(builder, body, trim_helper)?;
builder,
body,
trim_helper,
)?;
eprintln!( eprintln!(
"[TrimLoopLowerer] Registered carrier '{}' in variable_map", "[TrimLoopLowerer] Registered carrier '{}' in variable_map",
@ -286,11 +286,8 @@ impl TrimLoopLowerer {
); );
// Step 7: Setup ConditionEnv bindings // Step 7: Setup ConditionEnv bindings
let condition_bindings = Self::setup_condition_env_bindings( let condition_bindings =
builder, Self::setup_condition_env_bindings(builder, trim_helper, alloc_join_value)?;
trim_helper,
alloc_join_value,
)?;
eprintln!( eprintln!(
"[TrimLoopLowerer] Added {} condition bindings", "[TrimLoopLowerer] Added {} condition bindings",
@ -333,7 +330,8 @@ impl TrimLoopLowerer {
use crate::mir::builder::control_flow::joinir::patterns::trim_pattern_validator::TrimPatternValidator; use crate::mir::builder::control_flow::joinir::patterns::trim_pattern_validator::TrimPatternValidator;
// Extract substring pattern from body // Extract substring pattern from body
let (s_name, start_expr) = TrimPatternValidator::extract_substring_args(body, &trim_helper.original_var) let (s_name, start_expr) =
TrimPatternValidator::extract_substring_args(body, &trim_helper.original_var)
.ok_or_else(|| { .ok_or_else(|| {
format!( format!(
"[TrimLoopLowerer] Failed to extract substring pattern for Trim carrier '{}'", "[TrimLoopLowerer] Failed to extract substring pattern for Trim carrier '{}'",
@ -347,11 +345,10 @@ impl TrimLoopLowerer {
); );
// Get ValueIds for string and start // Get ValueIds for string and start
let s_id = builder let s_id =
.variable_map builder.variable_map.get(&s_name).copied().ok_or_else(|| {
.get(&s_name) format!("[TrimLoopLowerer] String variable '{}' not found", s_name)
.copied() })?;
.ok_or_else(|| format!("[TrimLoopLowerer] String variable '{}' not found", s_name))?;
// Compile start expression to get ValueId // Compile start expression to get ValueId
let start_id = builder.build_expression_impl(*start_expr)?; let start_id = builder.build_expression_impl(*start_expr)?;
@ -378,11 +375,17 @@ impl TrimLoopLowerer {
vec![start_id, start_plus_1], vec![start_id, start_plus_1],
)?; )?;
eprintln!("[TrimLoopLowerer] Generated initial substring call: ch0 = {:?}", ch0); eprintln!(
"[TrimLoopLowerer] Generated initial substring call: ch0 = {:?}",
ch0
);
// Generate: is_ch_match0 = (ch0 == " " || ch0 == "\t" || ...) // Generate: is_ch_match0 = (ch0 == " " || ch0 == "\t" || ...)
let is_ch_match0 = let is_ch_match0 = TrimPatternValidator::emit_whitespace_check(
TrimPatternValidator::emit_whitespace_check(builder, ch0, &trim_helper.whitespace_chars)?; builder,
ch0,
&trim_helper.whitespace_chars,
)?;
eprintln!( eprintln!(
"[TrimLoopLowerer] Generated initial whitespace check: is_ch_match0 = {:?}", "[TrimLoopLowerer] Generated initial whitespace check: is_ch_match0 = {:?}",
@ -486,7 +489,9 @@ mod tests {
}; };
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &var_node)); assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &var_node));
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &var_node)); assert!(!TrimLoopLowerer::is_var_used_in_condition(
"other", &var_node
));
} }
#[test] #[test]
@ -506,7 +511,9 @@ mod tests {
}; };
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &cond_node)); assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &cond_node));
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &cond_node)); assert!(!TrimLoopLowerer::is_var_used_in_condition(
"other", &cond_node
));
} }
#[test] #[test]
@ -525,8 +532,13 @@ mod tests {
span: Span::unknown(), span: Span::unknown(),
}; };
assert!(TrimLoopLowerer::is_var_used_in_condition("digit_pos", &cond_node)); assert!(TrimLoopLowerer::is_var_used_in_condition(
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &cond_node)); "digit_pos",
&cond_node
));
assert!(!TrimLoopLowerer::is_var_used_in_condition(
"other", &cond_node
));
} }
#[test] #[test]
@ -566,6 +578,8 @@ mod tests {
}; };
assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &or_node)); assert!(TrimLoopLowerer::is_var_used_in_condition("ch", &or_node));
assert!(!TrimLoopLowerer::is_var_used_in_condition("other", &or_node)); assert!(!TrimLoopLowerer::is_var_used_in_condition(
"other", &or_node
));
} }
} }

View File

@ -53,6 +53,8 @@ fn execute_function(
mut current_func: JoinFuncId, mut current_func: JoinFuncId,
mut current_args: Vec<JoinValue>, mut current_args: Vec<JoinValue>,
) -> Result<JoinValue, JoinRuntimeError> { ) -> Result<JoinValue, JoinRuntimeError> {
let verbose = crate::config::env::joinir_dev_enabled();
'exec: loop { 'exec: loop {
let func = module.functions.get(&current_func).ok_or_else(|| { let func = module.functions.get(&current_func).ok_or_else(|| {
JoinRuntimeError::new(format!("Function {:?} not found", current_func)) JoinRuntimeError::new(format!("Function {:?} not found", current_func))
@ -137,10 +139,12 @@ fn execute_function(
} => { } => {
// 1. Evaluate cond (Bool or Int) // 1. Evaluate cond (Bool or Int)
let cond_value = read_var(&locals, *cond)?; let cond_value = read_var(&locals, *cond)?;
if verbose {
eprintln!( eprintln!(
"[SELECT DEBUG] cond={:?}, cond_value={:?}", "[joinir/runner/select] cond={:?}, cond_value={:?}",
cond, cond_value cond, cond_value
); );
}
let cond_bool = match cond_value { let cond_bool = match cond_value {
JoinValue::Bool(b) => b, JoinValue::Bool(b) => b,
JoinValue::Int(i) => i != 0, // Int も許す0=false, それ以外=true JoinValue::Int(i) => i != 0, // Int も許す0=false, それ以外=true
@ -155,17 +159,21 @@ fn execute_function(
// 2. Select then_val or else_val // 2. Select then_val or else_val
let then_value = read_var(&locals, *then_val)?; let then_value = read_var(&locals, *then_val)?;
let else_value = read_var(&locals, *else_val)?; let else_value = read_var(&locals, *else_val)?;
if verbose {
eprintln!( eprintln!(
"[SELECT DEBUG] cond_bool={}, then_val={:?}={:?}, else_val={:?}={:?}", "[joinir/runner/select] cond_bool={}, then_val={:?}={:?}, else_val={:?}={:?}",
cond_bool, then_val, then_value, else_val, else_value cond_bool, then_val, then_value, else_val, else_value
); );
}
let selected_id = if cond_bool { *then_val } else { *else_val }; let selected_id = if cond_bool { *then_val } else { *else_val };
let selected_value = read_var(&locals, selected_id)?; let selected_value = read_var(&locals, selected_id)?;
if verbose {
eprintln!( eprintln!(
"[SELECT DEBUG] selected_id={:?}, selected_value={:?}", "[joinir/runner/select] selected_id={:?}, selected_value={:?}",
selected_id, selected_value selected_id, selected_value
); );
}
// 3. Write to dst // 3. Write to dst
locals.insert(*dst, selected_value); locals.insert(*dst, selected_value);