diff --git a/src/mir/builder/control_flow/joinir/merge/mod.rs b/src/mir/builder/control_flow/joinir/merge/mod.rs index 142389b2..85b3f4b1 100644 --- a/src/mir/builder/control_flow/joinir/merge/mod.rs +++ b/src/mir/builder/control_flow/joinir/merge/mod.rs @@ -88,6 +88,17 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks( } 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!( + "[cf_loop/joinir] Phase 172-3: Adding exit binding '{}' JoinIR {:?} to used_values", + binding.carrier_name, binding.join_exit_value + ); + } + used_values.insert(binding.join_exit_value); + } } // Phase 3: Remap ValueIds diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs b/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs index 9158a414..d2ef6e1f 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs @@ -141,9 +141,10 @@ impl MirBuilder { variable_definitions: BTreeMap::new(), }; - // Phase 169 / Phase 171-fix: Call Pattern 2 lowerer with ConditionEnv - let join_module = match lower_loop_with_break_minimal(scope, condition, &env) { - Ok(module) => module, + // Phase 169 / Phase 171-fix / Phase 172-3: Call Pattern 2 lowerer with ConditionEnv + // Now returns (JoinModule, ExitMeta) for proper exit_bindings construction + let (join_module, exit_meta) = match lower_loop_with_break_minimal(scope, condition, &env, &loop_var_name) { + Ok((module, meta)) => (module, meta), Err(e) => { // Phase 195: Use unified trace trace::trace().debug("pattern2", &format!("Pattern 2 lowerer failed: {}", e)); @@ -174,20 +175,43 @@ impl MirBuilder { ); // Merge JoinIR blocks into current function - // Phase 171-fix: Create boundary with ConditionBindings - let boundary = if condition_bindings.is_empty() { - crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_inputs_only( - vec![ValueId(0)], // JoinIR's main() parameter (loop variable init) - vec![loop_var_id], // Host's loop variable - ) - } else { - crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_with_condition_bindings( - vec![ValueId(0)], // JoinIR's main() parameter (loop variable init) - vec![loop_var_id], // Host's loop variable - condition_bindings, // Phase 171-fix: ConditionBindings with JoinIR ValueIds - ) - }; - // Phase 189: Discard exit PHI result (Pattern 2 returns void) + // Phase 172-3/4: Create boundary with exit_bindings from ExitMeta + // + // The loop variable's exit value needs to be reflected back to variable_map. + // ExitMeta provides the correct JoinIR-local ValueId for k_exit parameter. + use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding; + + // Phase 172-3: Build exit bindings from ExitMeta (provides actual k_exit parameter ValueId) + let exit_bindings: Vec = exit_meta.exit_values + .iter() + .filter_map(|(carrier_name, join_exit_value)| { + // Look up host slot from variable_map + let host_slot = self.variable_map.get(carrier_name).copied()?; + Some(LoopExitBinding { + carrier_name: carrier_name.clone(), + join_exit_value: *join_exit_value, + host_slot, + }) + }) + .collect(); + + // Phase 172-3: Build boundary with both condition_bindings and exit_bindings + let mut boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_inputs_only( + vec![ValueId(0)], // JoinIR's main() parameter (loop variable init) + vec![loop_var_id], // Host's loop variable + ); + boundary.condition_bindings = condition_bindings; + boundary.exit_bindings = exit_bindings.clone(); + + // Phase 172-3 Debug: Log exit bindings from ExitMeta + for binding in &exit_bindings { + eprintln!( + "[cf_loop/pattern2] Phase 172-3: exit_binding '{}' JoinIR {:?} → HOST {:?}", + binding.carrier_name, binding.join_exit_value, binding.host_slot + ); + } + + // Phase 189: Capture exit PHI result (now used for reconnect) let _ = self.merge_joinir_mir_blocks(&mir_module, Some(&boundary), debug)?; // Phase 188-Impl-2: Return Void (loops don't produce values) 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 eb21c044..2e008913 100644 --- a/src/mir/join_ir/lowering/loop_with_break_minimal.rs +++ b/src/mir/join_ir/lowering/loop_with_break_minimal.rs @@ -56,6 +56,7 @@ //! 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::ExitMeta; 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::{ @@ -106,11 +107,21 @@ use crate::mir::ValueId; /// /// The caller must build a ConditionEnv that maps variable names to JoinIR-local ValueIds. /// This ensures JoinIR never accesses HOST ValueIds directly. +/// +/// # Phase 172-3: ExitMeta Return +/// +/// Returns `(JoinModule, ExitMeta)` where ExitMeta contains the JoinIR-local ValueId +/// of the k_exit parameter. This allows the caller to build proper exit_bindings. +/// +/// # Arguments +/// +/// * `loop_var_name` - Name of the loop variable (for ExitMeta construction) pub fn lower_loop_with_break_minimal( _scope: LoopScopeShape, condition: &ASTNode, env: &ConditionEnv, -) -> Result { + loop_var_name: &str, +) -> Result<(JoinModule, ExitMeta), String> { // Phase 188-Impl-2: Use local ValueId allocator (sequential from 0) // JoinIR has NO knowledge of host ValueIds - boundary handled separately let mut value_counter = 0u32; @@ -294,5 +305,9 @@ pub fn lower_loop_with_break_minimal( eprintln!("[joinir/pattern2] Condition from AST (not hardcoded)"); eprintln!("[joinir/pattern2] Exit PHI: k_exit receives i from both natural exit and break"); - Ok(join_module) + // Phase 172-3: Build ExitMeta with k_exit parameter's ValueId + let exit_meta = ExitMeta::single(loop_var_name.to_string(), i_exit); + eprintln!("[joinir/pattern2] Phase 172-3: ExitMeta {{ {} → {:?} }}", loop_var_name, i_exit); + + Ok((join_module, exit_meta)) }