diff --git a/docs/development/current/main/joinir-architecture-overview.md b/docs/development/current/main/joinir-architecture-overview.md index ee8ae6f9..fc65afd9 100644 --- a/docs/development/current/main/joinir-architecture-overview.md +++ b/docs/development/current/main/joinir-architecture-overview.md @@ -70,12 +70,6 @@ JoinIR ラインで守るべきルールを先に書いておくよ: - LoopBuilder は物理削除済み。JoinIR を OFF にする経路やフォールバックは存在しない。 - `NYASH_JOINIR_CORE` は deprecated(0 を指定しても警告して無視)。JoinIR の OFF トグルは提供しない。 -11. **Parser の直後に JoinIR 正規化ゲートを置く** - - `.hako → AST` のあと、必ず JoinIR Frontend(Pattern 検出+ lowering)を通してから MIR/LLVM に進むのを基本線とする。 - - dev/ci/strict 系プロファイルでは、この JoinIR 正規化がビルドゲートになる(ここで `[joinir/freeze]` が出たらビルド失敗)。 - - relaxed / 実験用プロファイルでは、一時的に JoinIR 正規化をスキップすることはあっても、最終的な「言語としての安全域」は - JoinIR Frontend が受理できる構造で定義する。 - --- ## 1.9 ValueId Space Management (Phase 201) @@ -124,33 +118,6 @@ Local Region (1000+): | Pattern 2 lowerer | Local (1000+) | `alloc_local()` | 中間値(Const, BinOp, etc.) | | Pattern 3 lowerer | Local (1000+) | `alloc_local()` | 中間値(PHI, Select, etc.) | | Pattern 4 lowerer | Local (1000+) | `alloc_local()` | 中間値(Select, BinOp, etc.) | - ---- - -## 7. 将来の拡張(例外 / async / try-catch) - -現状の JoinIR は「同期・正常系」のループ/if/更新を扱う芯としてはほぼ完成しており、 -LoopBuilder 完全削除後も P1–P5 + 各種 UpdateKind で JsonParser/selfhost の代表ケースを安全に lowering できている。 - -一方で、言語としては将来的に `throw/try/catch` や async/await 相当の表現も必要になる。その扱いについては、 -以下の方針をここに固定しておく(まだ実装フェーズには入っていない): - -1. **ErrorCarrier(Result 風キャリア)の導入(将来フェーズ)** - - 関数/ループのキャリアに `err_flag` / `err_value` を追加し、「エラーが発生したら err キャリアを立てて継続を上に抜けていく」 - という形で throw 相当を表現する。 - - catch 側は「ある関数境界で err キャリアを inspect して処理する」箱として実装し、構文の try/catch はこの箱への sugar として扱う。 - - これにより core JoinIR(ループ骨格・PHI・ExitLine)は変更せず、ErrorCarrier を 1 本足すだけで例外伝播を表現できる。 - -2. **async/await は state machine 箱として後置する** - - async 関数は `.hako AST → state machine JoinIR` へ変換する専用の箱で表現し、その state machine 自体は - 既存の JoinIR(ループ+if+キャリア)で実装する。 - - これにより、JoinIR のコア設計を同期用に保ったまま、外側に async 用の変換レイヤーを追加する形にする。 - -3. **導入タイミング** - - まずは JsonParser/selfhost の同期ループ/if が JoinIR Pattern 群でほぼ全部通る状態を優先して作る。 - - ErrorCarrier や try/catch/async の実装は、その後の「第2章」として Phase 23x 以降に扱う(現在は設計メモのみ)。 - -この方針により、「例外や async のために JoinIR の芯を崩さず、外側に箱を足す」拡張戦略を維持する。 | LoopHeaderPhiBuilder | PHI Reserved (0-99) | `reserve_phi()` | PHI dst ID 保護(上書き防止) | ### 1.9.4 設計原則 diff --git a/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs b/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs index a3eaf053..0bc325bb 100644 --- a/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs +++ b/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs @@ -282,27 +282,9 @@ pub(super) fn merge_and_rewrite( // In the header block, carriers are defined by PHIs, not Copies. // JoinIR function parameters get copied to local variables, but after // inlining with header PHIs, those Copies would overwrite the PHI results. - if let MirInstruction::Copy { dst, src } = inst { + if let MirInstruction::Copy { dst, src: _ } = inst { // Check if this Copy's dst (after remapping) matches any header PHI dst let remapped_dst = remapper.get_value(*dst).unwrap_or(*dst); - let remapped_src = remapper.get_value(*src).unwrap_or(*src); - - eprintln!( - "[DEBUG-220C] Copy instruction: {:?} = {:?} → remapped: {:?} = {:?}", - dst, src, remapped_dst, remapped_src - ); - - // Phase 220-C: Skip self-copies (src == dst after remapping) - // These occur when condition variables are remapped to HOST values, - // and the JoinIR Copy becomes a pointless self-copy. - if remapped_src == remapped_dst { - eprintln!( - "[cf_loop/joinir] Phase 220-C: ✅ Skipping self-copy {:?} = {:?} (remapped to {:?})", - dst, src, remapped_dst - ); - continue; // Skip - pointless self-copy - } - let is_header_phi_dst = loop_header_phi_info.carrier_phis .values() .any(|entry| entry.phi_dst == remapped_dst); @@ -421,41 +403,23 @@ pub(super) fn merge_and_rewrite( } } else { // Insert Copy instructions for parameter binding - eprintln!("[DEBUG-220C-PARAM] Processing {} param bindings", args.len()); for (i, arg_val_remapped) in args.iter().enumerate() { if i < target_params.len() { let param_val_original = target_params[i]; - eprintln!( - "[DEBUG-220C-PARAM] Param[{}]: original={:?}, remapped lookup...", - i, param_val_original - ); if let Some(param_val_remapped) = remapper.get_value(param_val_original) { - eprintln!( - "[DEBUG-220C-PARAM] Param[{}]: {:?} → {:?}, arg={:?}", - i, param_val_original, param_val_remapped, arg_val_remapped - ); - - // Phase 220-C: Skip self-copies (arg == param after remapping) - // This occurs when condition variables are remapped to HOST values - if param_val_remapped == *arg_val_remapped { - eprintln!( - "[cf_loop/joinir] Phase 220-C: ✅ Skip self-copy param binding {:?} = {:?}", - param_val_remapped, arg_val_remapped - ); - continue; // Skip self-copy - } - new_block.instructions.push(MirInstruction::Copy { dst: param_val_remapped, src: *arg_val_remapped, }); - eprintln!( - "[DEBUG-220C-PARAM] Added Copy: {:?} = {:?}", - param_val_remapped, arg_val_remapped - ); + if debug { + eprintln!( + "[cf_loop/joinir] Param binding: arg {:?} → param {:?}", + arg_val_remapped, param_val_remapped + ); + } } } } diff --git a/src/mir/builder/control_flow/joinir/merge/mod.rs b/src/mir/builder/control_flow/joinir/merge/mod.rs index 9ec28b02..24fa902a 100644 --- a/src/mir/builder/control_flow/joinir/merge/mod.rs +++ b/src/mir/builder/control_flow/joinir/merge/mod.rs @@ -87,12 +87,19 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks( let (mut used_values, value_to_func_name, function_params) = value_collector::collect_values(mir_module, &remapper, debug)?; - // Phase 220-C: Condition bindings should NOT be added to used_values - // They will be remapped directly to HOST values in remap_values() - // (Removed Phase 171-fix logic) - - // Phase 172-3: Add exit_bindings' join_exit_values to used_values for remapping + // Phase 171-fix: Add condition_bindings' join_values to used_values for remapping if let Some(boundary) = boundary { + for binding in &boundary.condition_bindings { + if debug { + eprintln!( + "[cf_loop/joinir] Phase 171-fix: Adding condition binding '{}' JoinIR {:?} to used_values", + binding.name, binding.join_value + ); + } + used_values.insert(binding.join_value); + } + + // Phase 172-3: Add exit_bindings' join_exit_values to used_values for remapping for binding in &boundary.exit_bindings { if debug { eprintln!( @@ -190,8 +197,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks( } // Phase 3: Remap ValueIds (with reserved PHI dsts protection) - // Phase 220-C: Pre-populate condition_bindings BEFORE remap_values - remap_values(builder, &used_values, &mut remapper, &reserved_phi_dsts, boundary, debug)?; + remap_values(builder, &used_values, &mut remapper, &reserved_phi_dsts, debug)?; // Phase 177-3 DEBUG: Verify remapper state after Phase 3 eprintln!("[DEBUG-177] === Remapper state after Phase 3 ==="); @@ -604,6 +610,68 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks( builder.reserved_value_ids.clear(); } + // Phase 221: Return expr_result if present (for expr-position loops) + // The expr_result from boundary contains the JoinIR-local ValueId that should + // be returned. We need to map it to the HOST ValueId space. + // + // IMPORTANT: If expr_result corresponds to a carrier variable, we should return + // the carrier PHI dst (from loop header), NOT the remapped JoinIR value. + // This is because carriers use header PHIs which dominate the exit block. + if let Some(boundary) = boundary { + eprintln!( + "[cf_loop/joinir] Phase 221: Boundary present, expr_result={:?}, exit_bindings={:?}", + boundary.expr_result, + boundary.exit_bindings.iter().map(|b| (b.carrier_name.as_str(), b.join_exit_value)).collect::>() + ); + if let Some(expr_result_id) = boundary.expr_result { + // Check if expr_result corresponds to a carrier exit binding + // If so, use the carrier PHI dst instead of remapped value + for binding in &boundary.exit_bindings { + if binding.join_exit_value == expr_result_id { + // expr_result is a carrier! Use the carrier PHI dst + if let Some(&carrier_phi_dst) = carrier_phis.get(&binding.carrier_name) { + eprintln!( + "[cf_loop/joinir] Phase 221: expr_result {:?} is carrier '{}', returning PHI dst {:?}", + expr_result_id, binding.carrier_name, carrier_phi_dst + ); + return Ok(Some(carrier_phi_dst)); + } else { + return Err(format!( + "[cf_loop/joinir] Phase 221: Carrier '{}' not found in carrier_phis", + binding.carrier_name + )); + } + } + } + + // expr_result is NOT a carrier - use remapped value + if let Some(remapped_expr) = remapper.get_value(expr_result_id) { + eprintln!( + "[cf_loop/joinir] Phase 221: Returning non-carrier expr_result: JoinIR {:?} → Host {:?}", + expr_result_id, remapped_expr + ); + return Ok(Some(remapped_expr)); + } else { + // expr_result was not remapped - this is an error + return Err(format!( + "[cf_loop/joinir] Phase 221: expr_result {:?} was not found in remapper", + expr_result_id + )); + } + } else { + eprintln!("[cf_loop/joinir] Phase 221: expr_result is None, using fallback"); + } + } else { + eprintln!("[cf_loop/joinir] Phase 221: No boundary, using fallback"); + } + + // Fallback: return exit_phi_result_id (for legacy patterns or carrier-only loops) + if debug && exit_phi_result_id.is_some() { + eprintln!( + "[cf_loop/joinir] Phase 221: Returning exit_phi_result_id (fallback): {:?}", + exit_phi_result_id + ); + } Ok(exit_phi_result_id) } @@ -612,15 +680,11 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks( /// Phase 201-A: Accept reserved ValueIds that must not be reused. /// These are PHI dst ValueIds that will be created by LoopHeaderPhiBuilder. /// We must skip these IDs to prevent carrier value corruption. -/// -/// Phase 220-C: Pre-populate condition_bindings to use HOST ValueIds directly. -/// Condition variables should NOT get new allocations - they use HOST values. fn remap_values( builder: &mut crate::mir::builder::MirBuilder, used_values: &std::collections::BTreeSet, remapper: &mut crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper, reserved_ids: &std::collections::HashSet, - boundary: Option<&JoinInlineBoundary>, debug: bool, ) -> Result<(), String> { if debug { @@ -628,32 +692,7 @@ fn remap_values( used_values.len(), reserved_ids.len()); } - // Phase 220-C: Pre-populate condition_bindings BEFORE normal remapping - // Condition variables use HOST ValueIds directly (no new allocation) - if let Some(boundary) = boundary { - for binding in &boundary.condition_bindings { - remapper.set_value(binding.join_value, binding.host_value); - if debug { - eprintln!( - "[cf_loop/joinir] Phase 220-C: Condition '{}': JoinIR {:?} → HOST {:?} (no allocation)", - binding.name, binding.join_value, binding.host_value - ); - } - } - } - for old_value in used_values { - // Phase 220-C: Skip if already mapped (e.g., condition_bindings) - if remapper.get_value(*old_value).is_some() { - if debug { - eprintln!( - "[cf_loop/joinir] Phase 220-C: Skipping {:?} (already mapped)", - old_value - ); - } - continue; - } - // Phase 201-A: Allocate new ValueId, skipping reserved PHI dsts let new_value = loop { let candidate = builder.next_value_id(); diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs b/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs index effba9e3..ddaa7ff8 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs @@ -112,15 +112,12 @@ impl MirBuilder { "[cf_loop/pattern3] if-sum pattern detected but no if statement found".to_string() })?; - // Phase 220-B: Call AST-based if-sum lowerer with ConditionEnv support - let (join_module, fragment_meta, cond_bindings) = lower_if_sum_pattern( + // Call AST-based if-sum lowerer + let (join_module, fragment_meta) = lower_if_sum_pattern( condition, if_stmt, body, &mut join_value_space, - &self.variable_map, // Phase 220-B: Pass variable_map for ConditionEnv - &ctx.loop_var_name, // Phase 220-B: Pass loop variable name - ctx.loop_var_id, // Phase 220-B: Pass loop variable ValueId )?; let exit_meta = &fragment_meta.exit_meta; @@ -176,17 +173,11 @@ impl MirBuilder { ) ); - // Phase 220-B: Wire condition_bindings to boundary builder - trace::trace().debug( - "pattern3/if-sum", - &format!("Wiring {} condition bindings to boundary", cond_bindings.len()) - ); - + // Phase 215-2: Pass expr_result to boundary let mut boundary_builder = JoinInlineBoundaryBuilder::new() .with_inputs(join_inputs, host_inputs) .with_exit_bindings(exit_bindings) - .with_loop_var_name(Some(ctx.loop_var_name.clone())) - .with_condition_bindings(cond_bindings); // Phase 220-B: Add condition bindings + .with_loop_var_name(Some(ctx.loop_var_name.clone())); // Add expr_result if present if let Some(expr_id) = fragment_meta.expr_result { diff --git a/src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs b/src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs index 16a3c235..890a5151 100644 --- a/src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs +++ b/src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs @@ -30,17 +30,13 @@ use crate::ast::ASTNode; use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta}; -use crate::mir::join_ir::lowering::condition_env::{ConditionBinding, ConditionEnv}; -use crate::mir::join_ir::lowering::condition_to_joinir::extract_condition_variables; 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; -use std::collections::BTreeMap; -/// Phase 220-B: Lower if-sum pattern to JoinIR using AST (with ConditionEnv support) +/// Phase 213: Lower if-sum pattern to JoinIR using AST /// /// # Arguments /// @@ -48,95 +44,26 @@ use std::collections::BTreeMap; /// * `if_stmt` - If statement AST from loop body /// * `body` - Full loop body AST (for finding counter update) /// * `join_value_space` - Unified ValueId allocator -/// * `variable_map` - HOST function's variable_map for ConditionEnv construction -/// * `loop_var_name` - Loop variable name (e.g., "i") -/// * `loop_var_id` - HOST ValueId for loop variable /// /// # Returns /// -/// * `Ok((JoinModule, JoinFragmentMeta, Vec))` - JoinIR module, exit metadata, and condition bindings +/// * `Ok((JoinModule, JoinFragmentMeta))` - JoinIR module with exit metadata /// * `Err(String)` - Pattern not supported or extraction failed pub fn lower_if_sum_pattern( loop_condition: &ASTNode, if_stmt: &ASTNode, body: &[ASTNode], join_value_space: &mut JoinValueSpace, - variable_map: &BTreeMap, - loop_var_name: &str, - loop_var_id: ValueId, -) -> Result<(JoinModule, JoinFragmentMeta, Vec), String> { +) -> Result<(JoinModule, JoinFragmentMeta), String> { eprintln!("[joinir/pattern3/if-sum] Starting AST-based if-sum lowering"); - // Phase 220-B Step 1: Build ConditionEnv (following Pattern 2 style) - let cond_vars = extract_condition_variables( - loop_condition, - &[loop_var_name.to_string()], // Exclude loop variable itself - ); + // Step 1: Extract loop condition info (e.g., i < len → var="i", op=Lt, limit=len) + let (loop_var, loop_op, loop_limit) = extract_loop_condition(loop_condition)?; + eprintln!("[joinir/pattern3/if-sum] Loop condition: {} {:?} {}", loop_var, loop_op, loop_limit); - eprintln!( - "[joinir/pattern3/if-sum] Extracted {} condition variables: {:?}", - cond_vars.len(), - cond_vars - ); - - // Phase 220-B Step 2: Build ConditionEnv using ConditionEnvBuilder pattern - let mut cond_env = ConditionEnv::new(); - let mut cond_bindings = Vec::new(); - - // Add loop variable to environment - let loop_var_join_id = join_value_space.alloc_param(); - cond_env.insert(loop_var_name.to_string(), loop_var_join_id); - - eprintln!( - "[joinir/pattern3/if-sum] Loop variable '{}': host={:?}, join={:?}", - loop_var_name, loop_var_id, loop_var_join_id - ); - - // Add condition-only variables - for var_name in &cond_vars { - let host_id = variable_map - .get(var_name) - .copied() - .ok_or_else(|| { - format!( - "[if-sum] Condition variable '{}' not found in variable_map. \ - Available: {:?}", - var_name, - variable_map.keys().collect::>() - ) - })?; - - let join_id = join_value_space.alloc_param(); - cond_env.insert(var_name.clone(), join_id); - cond_bindings.push(ConditionBinding { - name: var_name.clone(), - host_value: host_id, - join_value: join_id, - }); - - eprintln!( - "[joinir/pattern3/if-sum] Condition variable '{}': host={:?}, join={:?}", - var_name, host_id, join_id - ); - } - - eprintln!( - "[joinir/pattern3/if-sum] ConditionEnv built: {} variables, {} bindings", - cond_env.len(), - cond_bindings.len() - ); - - // Step 3: Extract loop condition info (e.g., i < len → var="i", op=Lt, limit=len/ValueId) - let (loop_var, loop_op, loop_limit) = extract_loop_condition(loop_condition, &cond_env)?; - eprintln!("[joinir/pattern3/if-sum] Loop condition: {} {:?} {:?}", loop_var, loop_op, loop_limit); - - // Step 4: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=0) - let (if_var, if_op, if_value) = extract_if_condition(if_stmt, &cond_env)?; - let if_value_desc = match &if_value { - ValueOrLiteral::Literal(n) => format!("literal {}", n), - ValueOrLiteral::Variable(name, id) => format!("variable '{}' ({:?})", name, id), - }; - eprintln!("[joinir/pattern3/if-sum] If condition: {} {:?} {}", if_var, if_op, if_value_desc); + // Step 2: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=0) + let (if_var, if_op, if_value) = extract_if_condition(if_stmt)?; + eprintln!("[joinir/pattern3/if-sum] If condition: {} {:?} {}", if_var, if_op, if_value); // 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)?; @@ -210,16 +137,10 @@ pub fn lower_if_sum_pattern( value: ConstValue::Integer(0), })); - // Phase 220-B: Build call args including condition-only variables - // result = loop_step(i_init, sum_init, count_init, ...condition_vars) - let mut loop_call_args = vec![i_init_val, sum_init_val, count_init_val]; - for binding in &cond_bindings { - loop_call_args.push(binding.join_value); // Include condition vars - } - + // result = loop_step(i_init, sum_init, count_init) main_func.body.push(JoinInst::Call { func: loop_step_id, - args: loop_call_args, + args: vec![i_init_val, sum_init_val, count_init_val], k_next: None, dst: Some(loop_result), }); @@ -230,53 +151,26 @@ pub fn lower_if_sum_pattern( join_module.add_function(main_func); - // === loop_step function === - // Phase 220-B: Include condition-only variables as parameters - let mut loop_step_params = vec![loop_var_join_id, sum_param, count_param]; - - // Add condition-only variables as parameters - for binding in &cond_bindings { - loop_step_params.push(binding.join_value); - } - - eprintln!( - "[if-sum/joinir] loop_step params: {} total ({} carriers + {} condition vars)", - loop_step_params.len(), - 3, // i, sum, count - cond_bindings.len() - ); - + // === loop_step(i, sum, count) function === let mut loop_step_func = JoinFunction::new( loop_step_id, "loop_step".to_string(), - loop_step_params, + vec![i_param, sum_param, count_param], ); // --- Exit Condition Check --- - // Phase 220-B: Handle both literal and variable loop limits - let loop_limit_value_id = match loop_limit { - ValueOrLiteral::Literal(n) => { - // Literal: Generate Const instruction - loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { - dst: loop_limit_val, - value: ConstValue::Integer(n), - })); - loop_limit_val - } - ValueOrLiteral::Variable(ref var_name, value_id) => { - // Variable: Use ValueId from ConditionEnv (already a parameter) - eprintln!("[if-sum/joinir] Loop limit is variable '{}' = {:?}", var_name, value_id); - value_id - } - }; + // Load loop limit from AST + loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { + dst: loop_limit_val, + value: ConstValue::Integer(loop_limit), + })); // Compare: i < limit (or other op from AST) - // Phase 220-B: Use loop_var_join_id (from ConditionEnv) instead of i_param loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_loop, op: loop_op, - lhs: loop_var_join_id, // Phase 220-B: Use ConditionEnv ValueId - rhs: loop_limit_value_id, + lhs: i_param, + rhs: loop_limit_val, })); // exit_cond = !cmp_loop @@ -294,30 +188,18 @@ pub fn lower_if_sum_pattern( }); // --- If Condition (AST-based) --- - // Phase 220-B: Handle both literal and variable if conditions - let if_value_id = match if_value { - ValueOrLiteral::Literal(n) => { - // Literal: Generate Const instruction - loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { - dst: if_const, - value: ConstValue::Integer(n), - })); - if_const - } - ValueOrLiteral::Variable(ref var_name, value_id) => { - // Variable: Use ValueId from ConditionEnv (already a parameter) - eprintln!("[if-sum/joinir] If condition value is variable '{}' = {:?}", var_name, value_id); - value_id - } - }; + // Load if constant + loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { + dst: if_const, + value: ConstValue::Integer(if_value), + })); // Compare: if_var if_value - // Phase 220-B: Use loop_var_join_id (from ConditionEnv) loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst: if_cmp, op: if_op, - lhs: loop_var_join_id, // Phase 220-B: Use ConditionEnv ValueId - rhs: if_value_id, + lhs: i_param, // Assuming if_var == loop_var (common case) + rhs: if_const, })); // --- Then Branch --- @@ -387,24 +269,17 @@ pub fn lower_if_sum_pattern( dst: step_const2, value: ConstValue::Integer(counter_step), })); - // Phase 220-B: Use loop_var_join_id for counter increment loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { dst: i_next, op: BinOpKind::Add, - lhs: loop_var_join_id, // Phase 220-B: Use ConditionEnv ValueId + lhs: i_param, rhs: step_const2, })); // --- Tail Recursion --- - // Phase 220-B: Include condition-only variables in recursive call - let mut recursive_args = vec![i_next, sum_new, count_new]; - for binding in &cond_bindings { - recursive_args.push(binding.join_value); // Pass condition vars through - } - loop_step_func.body.push(JoinInst::Call { func: loop_step_id, - args: recursive_args, + args: vec![i_next, sum_new, count_new], k_next: None, dst: None, }); @@ -437,29 +312,21 @@ pub fn lower_if_sum_pattern( let fragment_meta = JoinFragmentMeta::with_expr_result(sum_final, exit_meta); eprintln!("[joinir/pattern3/if-sum] Generated AST-based JoinIR"); - let loop_limit_desc = match &loop_limit { - ValueOrLiteral::Literal(n) => format!("literal {}", n), - ValueOrLiteral::Variable(name, id) => format!("variable '{}' ({:?})", name, id), - }; - eprintln!("[joinir/pattern3/if-sum] Loop: {} {:?} {}", loop_var, loop_op, loop_limit_desc); - eprintln!("[joinir/pattern3/if-sum] If: {} {:?} {}", if_var, if_op, if_value_desc); + eprintln!("[joinir/pattern3/if-sum] Loop: {} {:?} {}", loop_var, loop_op, loop_limit); + eprintln!("[joinir/pattern3/if-sum] If: {} {:?} {}", if_var, if_op, if_value); eprintln!("[joinir/pattern3/if-sum] Phase 215-2: expr_result={:?}", sum_final); - eprintln!("[joinir/pattern3/if-sum] Phase 220-B: Returning {} condition bindings", cond_bindings.len()); - Ok((join_module, fragment_meta, cond_bindings)) + Ok((join_module, fragment_meta)) } -/// Phase 220-B: Extract loop condition with variable support +/// Extract loop condition: variable, operator, and limit /// -/// Supports: `var < lit/var`, `var <= lit/var`, `var > lit/var`, `var >= lit/var` -fn extract_loop_condition( - cond: &ASTNode, - cond_env: &ConditionEnv, -) -> Result<(String, CompareOp, ValueOrLiteral), String> { +/// Supports: `var < lit`, `var <= lit`, `var > lit`, `var >= lit` +fn extract_loop_condition(cond: &ASTNode) -> Result<(String, CompareOp, i64), String> { match cond { ASTNode::BinaryOp { operator, left, right, .. } => { let var_name = extract_variable_name(left)?; - let limit = extract_value_or_variable(right, cond_env)?; + let limit = extract_integer_literal(right)?; let op = match operator { crate::ast::BinaryOperator::Less => CompareOp::Lt, crate::ast::BinaryOperator::LessEqual => CompareOp::Le, @@ -473,14 +340,11 @@ fn extract_loop_condition( } } -/// Phase 220-B: Extract if condition with variable support -fn extract_if_condition( - if_stmt: &ASTNode, - cond_env: &ConditionEnv, -) -> Result<(String, CompareOp, ValueOrLiteral), String> { +/// Extract if condition: variable, operator, and value +fn extract_if_condition(if_stmt: &ASTNode) -> Result<(String, CompareOp, i64), String> { match if_stmt { ASTNode::If { condition, .. } => { - extract_loop_condition(condition, cond_env) // Same format + extract_loop_condition(condition) // Same format } _ => Err("[if-sum] Expected If statement".to_string()), } @@ -542,54 +406,15 @@ fn extract_variable_name(node: &ASTNode) -> Result { } } -/// Phase 220-B: Value or literal enum for condition expressions -#[derive(Debug)] -enum ValueOrLiteral { - Literal(i64), - Variable(String, ValueId), -} - -/// Helper: Extract integer literal (for non-condition contexts like arithmetic) +/// Extract integer literal from AST node fn extract_integer_literal(node: &ASTNode) -> Result { match node { ASTNode::Literal { value: crate::ast::LiteralValue::Integer(n), .. } => Ok(*n), ASTNode::Variable { name, .. } => { - Err(format!("[if-sum] Variable '{}' in arithmetic expression not supported yet", name)) + // Handle variable reference (e.g., `len`) + // For Phase 213, we only support literals. Variables need Phase 214+ + Err(format!("[if-sum] Variable '{}' in condition not supported yet (Phase 214+)", name)) } _ => Err(format!("[if-sum] Expected integer literal, got {:?}", node)), } } - -/// Phase 220-B: Extract value or variable from AST node (with ConditionEnv support) -fn extract_value_or_variable( - node: &ASTNode, - cond_env: &ConditionEnv, -) -> Result { - match node { - // Literal: Return as immediate value - ASTNode::Literal { value: crate::ast::LiteralValue::Integer(n), .. } => { - Ok(ValueOrLiteral::Literal(*n)) - } - - // Variable: Lookup in ConditionEnv - ASTNode::Variable { name, .. } => { - if let Some(value_id) = cond_env.get(name) { - eprintln!("[if-sum/extract] Variable '{}' resolved to {:?}", name, value_id); - Ok(ValueOrLiteral::Variable(name.clone(), value_id)) - } else { - // Fail-Fast: Variable not in ConditionEnv - Err(format!( - "[if-sum] Variable '{}' not found in ConditionEnv (available: {:?})", - name, cond_env.names() - )) - } - } - - // Method call: Not supported yet (defer to Phase 221+) - ASTNode::MethodCall { .. } => { - Err("[if-sum] Method calls in conditions not supported yet (Phase 221+)".to_string()) - } - - _ => Err(format!("[if-sum] Expected integer literal or variable, got {:?}", node)), - } -}