diff --git a/src/mir/join_ir/lowering/carrier_update_emitter.rs b/src/mir/join_ir/lowering/carrier_update_emitter.rs new file mode 100644 index 00000000..45a11a8d --- /dev/null +++ b/src/mir/join_ir/lowering/carrier_update_emitter.rs @@ -0,0 +1,416 @@ +//! Phase 176-2 / Phase 179: Carrier Update Emission +//! +//! Converts UpdateExpr (from LoopUpdateAnalyzer) into JoinIR instructions +//! that compute the updated carrier value. +//! +//! This module is extracted from loop_with_break_minimal.rs to improve +//! modularity and single responsibility. + +use crate::mir::join_ir::lowering::carrier_info::CarrierVar; +use crate::mir::join_ir::lowering::condition_to_joinir::ConditionEnv; +use crate::mir::join_ir::lowering::loop_update_analyzer::{UpdateExpr, UpdateRhs}; +use crate::mir::join_ir::{BinOpKind, ConstValue, JoinInst, MirLikeInst}; +use crate::mir::ValueId; + +/// Emit JoinIR instructions for a single carrier update +/// +/// Converts UpdateExpr (from LoopUpdateAnalyzer) into JoinIR instructions +/// that compute the updated carrier value. +/// +/// # Arguments +/// +/// * `carrier` - Carrier variable information (name, ValueId) +/// * `update` - Update expression (e.g., CounterLike, AccumulationLike) +/// * `alloc_value` - ValueId allocator closure +/// * `env` - ConditionEnv for variable resolution +/// * `instructions` - Output vector to append instructions to +/// +/// # Returns +/// +/// ValueId of the computed update result +/// +/// # Example +/// +/// ```ignore +/// // For "count = count + 1": +/// let count_next = emit_carrier_update( +/// &count_carrier, +/// &UpdateExpr::BinOp { lhs: "count", op: Add, rhs: Const(1) }, +/// &mut alloc_value, +/// &env, +/// &mut instructions, +/// )?; +/// // Generates: +/// // const_1 = Const(1) +/// // count_next = BinOp(Add, count_param, const_1) +/// ``` +pub fn emit_carrier_update( + carrier: &CarrierVar, + update: &UpdateExpr, + alloc_value: &mut dyn FnMut() -> ValueId, + env: &ConditionEnv, + instructions: &mut Vec, +) -> Result { + match update { + UpdateExpr::Const(step) => { + // CounterLike: carrier = carrier + step + // Allocate const ValueId + let const_id = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::Const { + dst: const_id, + value: ConstValue::Integer(*step), + })); + + // Get carrier parameter ValueId from env + let carrier_param = env + .get(&carrier.name) + .ok_or_else(|| { + format!( + "Carrier '{}' not found in ConditionEnv", + carrier.name + ) + })?; + + // Allocate result ValueId + let result = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::BinOp { + dst: result, + op: BinOpKind::Add, + lhs: carrier_param, + rhs: const_id, + })); + + Ok(result) + } + + UpdateExpr::BinOp { lhs, op, rhs } => { + // General binary operation: carrier = carrier op rhs + // Verify lhs matches carrier name + if lhs != &carrier.name { + return Err(format!( + "Update expression LHS '{}' doesn't match carrier '{}'", + lhs, carrier.name + )); + } + + // Get carrier parameter ValueId from env + let carrier_param = env + .get(&carrier.name) + .ok_or_else(|| { + format!( + "Carrier '{}' not found in ConditionEnv", + carrier.name + ) + })?; + + // Resolve RHS + let rhs_id = match rhs { + UpdateRhs::Const(n) => { + let const_id = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::Const { + dst: const_id, + value: ConstValue::Integer(*n), + })); + const_id + } + UpdateRhs::Variable(var_name) => { + env.get(var_name).ok_or_else(|| { + format!( + "Update RHS variable '{}' not found in ConditionEnv", + var_name + ) + })? + } + // Phase 178: String updates detected but not lowered to JoinIR yet + // The Rust MIR path handles string concatenation + // For JoinIR: just pass through the carrier param (no JoinIR update) + UpdateRhs::StringLiteral(_) | UpdateRhs::Other => { + eprintln!( + "[joinir/pattern2] Phase 178: Carrier '{}' has string/complex update - skipping JoinIR emit, using param passthrough", + carrier.name + ); + return Ok(carrier_param); // Pass-through: no JoinIR update + } + }; + + // Allocate result ValueId + let result = alloc_value(); + instructions.push(JoinInst::Compute(MirLikeInst::BinOp { + dst: result, + op: *op, + lhs: carrier_param, + rhs: rhs_id, + })); + + Ok(result) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mir::join_ir::lowering::carrier_info::CarrierVar; + + // Helper: Create a test ConditionEnv + fn test_env() -> ConditionEnv { + let mut env = ConditionEnv::new(); + env.insert("count".to_string(), ValueId(10)); + env.insert("sum".to_string(), ValueId(20)); + env.insert("i".to_string(), ValueId(30)); + env + } + + // Helper: Create a test CarrierVar + fn test_carrier(name: &str, host_id: u32) -> CarrierVar { + CarrierVar { + name: name.to_string(), + host_id: ValueId(host_id), + join_id: None, // Phase 177-STRUCT-1 + } + } + + #[test] + fn test_emit_const_update() { + // Test: count = count + 1 (UpdateExpr::Const) + let carrier = test_carrier("count", 100); + let update = UpdateExpr::Const(1); + let env = test_env(); + + let mut value_counter = 50u32; + let mut alloc_value = || { + let id = ValueId(value_counter); + value_counter += 1; + id + }; + + let mut instructions = Vec::new(); + let result = emit_carrier_update( + &carrier, + &update, + &mut alloc_value, + &env, + &mut instructions, + ); + + assert!(result.is_ok()); + let result_id = result.unwrap(); + + // Should generate 2 instructions: Const(1) + BinOp(Add) + assert_eq!(instructions.len(), 2); + + // Instruction 1: Const(1) + match &instructions[0] { + JoinInst::Compute(MirLikeInst::Const { dst, value }) => { + assert_eq!(*dst, ValueId(50)); // First allocated + assert!(matches!(value, ConstValue::Integer(1))); + } + _ => panic!("Expected Const instruction"), + } + + // Instruction 2: BinOp(Add, count, const_1) + match &instructions[1] { + JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => { + assert_eq!(*dst, ValueId(51)); // Second allocated + assert_eq!(*op, BinOpKind::Add); + assert_eq!(*lhs, ValueId(10)); // count from env + assert_eq!(*rhs, ValueId(50)); // const_1 + } + _ => panic!("Expected BinOp instruction"), + } + + assert_eq!(result_id, ValueId(51)); + } + + #[test] + fn test_emit_binop_update_with_const() { + // Test: sum = sum + 5 (UpdateExpr::BinOp with Const RHS) + let carrier = test_carrier("sum", 200); + let update = UpdateExpr::BinOp { + lhs: "sum".to_string(), + op: BinOpKind::Add, + rhs: UpdateRhs::Const(5), + }; + let env = test_env(); + + let mut value_counter = 60u32; + let mut alloc_value = || { + let id = ValueId(value_counter); + value_counter += 1; + id + }; + + let mut instructions = Vec::new(); + let result = emit_carrier_update( + &carrier, + &update, + &mut alloc_value, + &env, + &mut instructions, + ); + + assert!(result.is_ok()); + let result_id = result.unwrap(); + + // Should generate 2 instructions: Const(5) + BinOp(Add) + assert_eq!(instructions.len(), 2); + + // Instruction 1: Const(5) + match &instructions[0] { + JoinInst::Compute(MirLikeInst::Const { dst, value }) => { + assert_eq!(*dst, ValueId(60)); + assert!(matches!(value, ConstValue::Integer(5))); + } + _ => panic!("Expected Const instruction"), + } + + // Instruction 2: BinOp(Add, sum, const_5) + match &instructions[1] { + JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => { + assert_eq!(*dst, ValueId(61)); + assert_eq!(*op, BinOpKind::Add); + assert_eq!(*lhs, ValueId(20)); // sum from env + assert_eq!(*rhs, ValueId(60)); // const_5 + } + _ => panic!("Expected BinOp instruction"), + } + + assert_eq!(result_id, ValueId(61)); + } + + #[test] + fn test_emit_binop_update_with_variable() { + // Test: sum = sum + i (UpdateExpr::BinOp with Variable RHS) + let carrier = test_carrier("sum", 200); + let update = UpdateExpr::BinOp { + lhs: "sum".to_string(), + op: BinOpKind::Add, + rhs: UpdateRhs::Variable("i".to_string()), + }; + let env = test_env(); + + let mut value_counter = 70u32; + let mut alloc_value = || { + let id = ValueId(value_counter); + value_counter += 1; + id + }; + + let mut instructions = Vec::new(); + let result = emit_carrier_update( + &carrier, + &update, + &mut alloc_value, + &env, + &mut instructions, + ); + + assert!(result.is_ok()); + let result_id = result.unwrap(); + + // Should generate 1 instruction: BinOp(Add, sum, i) + assert_eq!(instructions.len(), 1); + + // Instruction: BinOp(Add, sum, i) + match &instructions[0] { + JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => { + assert_eq!(*dst, ValueId(70)); + assert_eq!(*op, BinOpKind::Add); + assert_eq!(*lhs, ValueId(20)); // sum from env + assert_eq!(*rhs, ValueId(30)); // i from env + } + _ => panic!("Expected BinOp instruction"), + } + + assert_eq!(result_id, ValueId(70)); + } + + #[test] + fn test_emit_update_carrier_not_in_env() { + // Test error case: carrier not found in env + let carrier = test_carrier("unknown", 300); + let update = UpdateExpr::Const(1); + let env = test_env(); // doesn't have "unknown" + + let mut value_counter = 80u32; + let mut alloc_value = || { + let id = ValueId(value_counter); + value_counter += 1; + id + }; + + let mut instructions = Vec::new(); + let result = emit_carrier_update( + &carrier, + &update, + &mut alloc_value, + &env, + &mut instructions, + ); + + assert!(result.is_err()); + assert!(result.unwrap_err().contains("Carrier 'unknown' not found")); + } + + #[test] + fn test_emit_update_lhs_mismatch() { + // Test error case: LHS doesn't match carrier name + let carrier = test_carrier("count", 100); + let update = UpdateExpr::BinOp { + lhs: "sum".to_string(), // Wrong! Should be "count" + op: BinOpKind::Add, + rhs: UpdateRhs::Const(1), + }; + let env = test_env(); + + let mut value_counter = 90u32; + let mut alloc_value = || { + let id = ValueId(value_counter); + value_counter += 1; + id + }; + + let mut instructions = Vec::new(); + let result = emit_carrier_update( + &carrier, + &update, + &mut alloc_value, + &env, + &mut instructions, + ); + + assert!(result.is_err()); + assert!(result.unwrap_err().contains("doesn't match carrier")); + } + + #[test] + fn test_emit_update_rhs_variable_not_found() { + // Test error case: RHS variable not in env + let carrier = test_carrier("sum", 200); + let update = UpdateExpr::BinOp { + lhs: "sum".to_string(), + op: BinOpKind::Add, + rhs: UpdateRhs::Variable("unknown_var".to_string()), + }; + let env = test_env(); + + let mut value_counter = 100u32; + let mut alloc_value = || { + let id = ValueId(value_counter); + value_counter += 1; + id + }; + + let mut instructions = Vec::new(); + let result = emit_carrier_update( + &carrier, + &update, + &mut alloc_value, + &env, + &mut instructions, + ); + + assert!(result.is_err()); + assert!(result.unwrap_err().contains("Update RHS variable 'unknown_var' not found")); + } +} diff --git a/src/mir/join_ir/lowering/loop_with_break_minimal.rs b/src/mir/join_ir/lowering/loop_with_break_minimal.rs index 01471a46..416d0bd6 100644 --- a/src/mir/join_ir/lowering/loop_with_break_minimal.rs +++ b/src/mir/join_ir/lowering/loop_with_break_minimal.rs @@ -56,10 +56,11 @@ //! Following the "80/20 rule" from CLAUDE.md - get it working first, generalize later. use crate::ast::ASTNode; -use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, CarrierVar, ExitMeta, JoinFragmentMeta}; +use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta, JoinFragmentMeta}; +use crate::mir::join_ir::lowering::carrier_update_emitter::emit_carrier_update; use crate::mir::join_ir::lowering::condition_to_joinir::{lower_condition_to_joinir, ConditionEnv}; use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; -use crate::mir::join_ir::lowering::loop_update_analyzer::{UpdateExpr, UpdateRhs}; +use crate::mir::join_ir::lowering::loop_update_analyzer::UpdateExpr; use crate::mir::join_ir::{ BinOpKind, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, MirLikeInst, UnaryOp, @@ -71,141 +72,6 @@ use crate::mir::loop_pattern_detection::error_messages::{ use crate::mir::ValueId; use std::collections::HashMap; -/// Phase 176-2: Emit JoinIR instructions for a single carrier update -/// -/// Converts UpdateExpr (from LoopUpdateAnalyzer) into JoinIR instructions -/// that compute the updated carrier value. -/// -/// # Arguments -/// -/// * `carrier` - Carrier variable information (name, ValueId) -/// * `update` - Update expression (e.g., CounterLike, AccumulationLike) -/// * `alloc_value` - ValueId allocator closure -/// * `env` - ConditionEnv for variable resolution -/// * `instructions` - Output vector to append instructions to -/// -/// # Returns -/// -/// ValueId of the computed update result -/// -/// # Example -/// -/// ```ignore -/// // For "count = count + 1": -/// let count_next = emit_carrier_update( -/// &count_carrier, -/// &UpdateExpr::BinOp { lhs: "count", op: Add, rhs: Const(1) }, -/// &mut alloc_value, -/// &env, -/// &mut instructions, -/// )?; -/// // Generates: -/// // const_1 = Const(1) -/// // count_next = BinOp(Add, count_param, const_1) -/// ``` -fn emit_carrier_update( - carrier: &CarrierVar, - update: &UpdateExpr, - alloc_value: &mut dyn FnMut() -> ValueId, - env: &ConditionEnv, - instructions: &mut Vec, -) -> Result { - match update { - UpdateExpr::Const(step) => { - // CounterLike: carrier = carrier + step - // Allocate const ValueId - let const_id = alloc_value(); - instructions.push(JoinInst::Compute(MirLikeInst::Const { - dst: const_id, - value: ConstValue::Integer(*step), - })); - - // Get carrier parameter ValueId from env - let carrier_param = env - .get(&carrier.name) - .ok_or_else(|| { - format!( - "Carrier '{}' not found in ConditionEnv", - carrier.name - ) - })?; - - // Allocate result ValueId - let result = alloc_value(); - instructions.push(JoinInst::Compute(MirLikeInst::BinOp { - dst: result, - op: BinOpKind::Add, - lhs: carrier_param, - rhs: const_id, - })); - - Ok(result) - } - - UpdateExpr::BinOp { lhs, op, rhs } => { - // General binary operation: carrier = carrier op rhs - // Verify lhs matches carrier name - if lhs != &carrier.name { - return Err(format!( - "Update expression LHS '{}' doesn't match carrier '{}'", - lhs, carrier.name - )); - } - - // Get carrier parameter ValueId from env - let carrier_param = env - .get(&carrier.name) - .ok_or_else(|| { - format!( - "Carrier '{}' not found in ConditionEnv", - carrier.name - ) - })?; - - // Resolve RHS - let rhs_id = match rhs { - UpdateRhs::Const(n) => { - let const_id = alloc_value(); - instructions.push(JoinInst::Compute(MirLikeInst::Const { - dst: const_id, - value: ConstValue::Integer(*n), - })); - const_id - } - UpdateRhs::Variable(var_name) => { - env.get(var_name).ok_or_else(|| { - format!( - "Update RHS variable '{}' not found in ConditionEnv", - var_name - ) - })? - } - // Phase 178: String updates detected but not lowered to JoinIR yet - // The Rust MIR path handles string concatenation - // For JoinIR: just pass through the carrier param (no JoinIR update) - UpdateRhs::StringLiteral(_) | UpdateRhs::Other => { - eprintln!( - "[joinir/pattern2] Phase 178: Carrier '{}' has string/complex update - skipping JoinIR emit, using param passthrough", - carrier.name - ); - return Ok(carrier_param); // Pass-through: no JoinIR update - } - }; - - // Allocate result ValueId - let result = alloc_value(); - instructions.push(JoinInst::Compute(MirLikeInst::BinOp { - dst: result, - op: *op, - lhs: carrier_param, - rhs: rhs_id, - })); - - Ok(result) - } - } -} - /// Lower Pattern 2 (Loop with Conditional Break) to JoinIR /// /// # Phase 188-Impl-2: Pure JoinIR Fragment Generation @@ -728,271 +594,4 @@ mod tests { assert!(vars.contains("ch")); // The problematic body-local variable } - // Phase 176-2: Tests for emit_carrier_update helper - mod carrier_update_tests { - use super::*; - use crate::mir::join_ir::lowering::loop_update_analyzer::{UpdateExpr, UpdateRhs}; - - // Helper: Create a test ConditionEnv - fn test_env() -> ConditionEnv { - let mut env = ConditionEnv::new(); - env.insert("count".to_string(), ValueId(10)); - env.insert("sum".to_string(), ValueId(20)); - env.insert("i".to_string(), ValueId(30)); - env - } - - // Helper: Create a test CarrierVar - fn test_carrier(name: &str, host_id: u32) -> CarrierVar { - CarrierVar { - name: name.to_string(), - host_id: ValueId(host_id), - join_id: None, // Phase 177-STRUCT-1 - } - } - - #[test] - fn test_emit_const_update() { - // Test: count = count + 1 (UpdateExpr::Const) - let carrier = test_carrier("count", 100); - let update = UpdateExpr::Const(1); - let env = test_env(); - - let mut value_counter = 50u32; - let mut alloc_value = || { - let id = ValueId(value_counter); - value_counter += 1; - id - }; - - let mut instructions = Vec::new(); - let result = emit_carrier_update( - &carrier, - &update, - &mut alloc_value, - &env, - &mut instructions, - ); - - assert!(result.is_ok()); - let result_id = result.unwrap(); - - // Should generate 2 instructions: Const(1) + BinOp(Add) - assert_eq!(instructions.len(), 2); - - // Instruction 1: Const(1) - match &instructions[0] { - JoinInst::Compute(MirLikeInst::Const { dst, value }) => { - assert_eq!(*dst, ValueId(50)); // First allocated - assert!(matches!(value, ConstValue::Integer(1))); - } - _ => panic!("Expected Const instruction"), - } - - // Instruction 2: BinOp(Add, count, const_1) - match &instructions[1] { - JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => { - assert_eq!(*dst, ValueId(51)); // Second allocated - assert_eq!(*op, BinOpKind::Add); - assert_eq!(*lhs, ValueId(10)); // count from env - assert_eq!(*rhs, ValueId(50)); // const_1 - } - _ => panic!("Expected BinOp instruction"), - } - - assert_eq!(result_id, ValueId(51)); - } - - #[test] - fn test_emit_binop_update_with_const() { - // Test: sum = sum + 5 (UpdateExpr::BinOp with Const RHS) - let carrier = test_carrier("sum", 200); - let update = UpdateExpr::BinOp { - lhs: "sum".to_string(), - op: BinOpKind::Add, - rhs: UpdateRhs::Const(5), - }; - let env = test_env(); - - let mut value_counter = 60u32; - let mut alloc_value = || { - let id = ValueId(value_counter); - value_counter += 1; - id - }; - - let mut instructions = Vec::new(); - let result = emit_carrier_update( - &carrier, - &update, - &mut alloc_value, - &env, - &mut instructions, - ); - - assert!(result.is_ok()); - let result_id = result.unwrap(); - - // Should generate 2 instructions: Const(5) + BinOp(Add) - assert_eq!(instructions.len(), 2); - - // Instruction 1: Const(5) - match &instructions[0] { - JoinInst::Compute(MirLikeInst::Const { dst, value }) => { - assert_eq!(*dst, ValueId(60)); - assert!(matches!(value, ConstValue::Integer(5))); - } - _ => panic!("Expected Const instruction"), - } - - // Instruction 2: BinOp(Add, sum, const_5) - match &instructions[1] { - JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => { - assert_eq!(*dst, ValueId(61)); - assert_eq!(*op, BinOpKind::Add); - assert_eq!(*lhs, ValueId(20)); // sum from env - assert_eq!(*rhs, ValueId(60)); // const_5 - } - _ => panic!("Expected BinOp instruction"), - } - - assert_eq!(result_id, ValueId(61)); - } - - #[test] - fn test_emit_binop_update_with_variable() { - // Test: sum = sum + i (UpdateExpr::BinOp with Variable RHS) - let carrier = test_carrier("sum", 200); - let update = UpdateExpr::BinOp { - lhs: "sum".to_string(), - op: BinOpKind::Add, - rhs: UpdateRhs::Variable("i".to_string()), - }; - let env = test_env(); - - let mut value_counter = 70u32; - let mut alloc_value = || { - let id = ValueId(value_counter); - value_counter += 1; - id - }; - - let mut instructions = Vec::new(); - let result = emit_carrier_update( - &carrier, - &update, - &mut alloc_value, - &env, - &mut instructions, - ); - - assert!(result.is_ok()); - let result_id = result.unwrap(); - - // Should generate 1 instruction: BinOp(Add, sum, i) - assert_eq!(instructions.len(), 1); - - // Instruction: BinOp(Add, sum, i) - match &instructions[0] { - JoinInst::Compute(MirLikeInst::BinOp { dst, op, lhs, rhs }) => { - assert_eq!(*dst, ValueId(70)); - assert_eq!(*op, BinOpKind::Add); - assert_eq!(*lhs, ValueId(20)); // sum from env - assert_eq!(*rhs, ValueId(30)); // i from env - } - _ => panic!("Expected BinOp instruction"), - } - - assert_eq!(result_id, ValueId(70)); - } - - #[test] - fn test_emit_update_carrier_not_in_env() { - // Test error case: carrier not found in env - let carrier = test_carrier("unknown", 300); - let update = UpdateExpr::Const(1); - let env = test_env(); // doesn't have "unknown" - - let mut value_counter = 80u32; - let mut alloc_value = || { - let id = ValueId(value_counter); - value_counter += 1; - id - }; - - let mut instructions = Vec::new(); - let result = emit_carrier_update( - &carrier, - &update, - &mut alloc_value, - &env, - &mut instructions, - ); - - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Carrier 'unknown' not found")); - } - - #[test] - fn test_emit_update_lhs_mismatch() { - // Test error case: LHS doesn't match carrier name - let carrier = test_carrier("count", 100); - let update = UpdateExpr::BinOp { - lhs: "sum".to_string(), // Wrong! Should be "count" - op: BinOpKind::Add, - rhs: UpdateRhs::Const(1), - }; - let env = test_env(); - - let mut value_counter = 90u32; - let mut alloc_value = || { - let id = ValueId(value_counter); - value_counter += 1; - id - }; - - let mut instructions = Vec::new(); - let result = emit_carrier_update( - &carrier, - &update, - &mut alloc_value, - &env, - &mut instructions, - ); - - assert!(result.is_err()); - assert!(result.unwrap_err().contains("doesn't match carrier")); - } - - #[test] - fn test_emit_update_rhs_variable_not_found() { - // Test error case: RHS variable not in env - let carrier = test_carrier("sum", 200); - let update = UpdateExpr::BinOp { - lhs: "sum".to_string(), - op: BinOpKind::Add, - rhs: UpdateRhs::Variable("unknown_var".to_string()), - }; - let env = test_env(); - - let mut value_counter = 100u32; - let mut alloc_value = || { - let id = ValueId(value_counter); - value_counter += 1; - id - }; - - let mut instructions = Vec::new(); - let result = emit_carrier_update( - &carrier, - &update, - &mut alloc_value, - &env, - &mut instructions, - ); - - assert!(result.is_err()); - assert!(result.unwrap_err().contains("Update RHS variable 'unknown_var' not found")); - } - } } diff --git a/src/mir/join_ir/lowering/mod.rs b/src/mir/join_ir/lowering/mod.rs index ccebc370..4ff41188 100644 --- a/src/mir/join_ir/lowering/mod.rs +++ b/src/mir/join_ir/lowering/mod.rs @@ -21,6 +21,7 @@ pub mod bool_expr_lowerer; // Phase 168: Boolean expression lowering for complex conditions pub mod carrier_info; // Phase 196: Carrier metadata for loop lowering +pub mod carrier_update_emitter; // Phase 179: Carrier update instruction emission pub mod common; pub mod condition_env; // Phase 171-fix: Condition expression environment pub mod condition_lowerer; // Phase 171-fix: Core condition lowering logic