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 9634f10c..ec4cb6c8 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 @@ -7,6 +7,23 @@ use super::super::trace; /// Phase 179-A: Expected ValueId for k_exit parameter (sum_final) in Pattern 3 /// This corresponds to the exit PHI input in the JoinIR lowering for loop_with_if_phi_minimal +/// +/// # TODO (Phase 179 Task 2): Convert to ExitMeta-based exit binding generation +/// +/// **Current State**: Hardcoded ValueId(18) - fragile and non-reusable +/// +/// **Why it's hardcoded**: +/// - Pattern 3's lowerer (`lower_loop_with_if_phi_pattern`) returns `Option` +/// - Unlike Pattern 4 which returns `(JoinModule, JoinFragmentMeta)` +/// - No ExitMeta available to dynamically look up exit PHI ValueIds +/// +/// **Migration Path** (when Pattern 3 lowerer is updated): +/// 1. Change `lower_loop_with_if_phi_pattern` to return `(JoinModule, JoinFragmentMeta)` +/// 2. Remove this constant +/// 3. Use ExitMeta loop (like Pattern 4 lines 350-378) to generate exit_bindings dynamically +/// 4. See: pattern4_with_continue.rs lines 350-378 for reference implementation +/// +/// **Impact**: Low priority - Pattern 3 is test-only and works correctly with hardcoded value const PATTERN3_K_EXIT_SUM_FINAL_ID: ValueId = ValueId(18); /// Phase 194: Detection function for Pattern 3 diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs b/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs index f48db74d..dac5bf7d 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs @@ -347,34 +347,24 @@ impl MirBuilder { ); } - // Phase 196: Dynamically generate exit_bindings from ExitMeta and CarrierInfo - let mut exit_bindings = Vec::new(); - for (carrier_name, join_exit_value) in &exit_meta.exit_values { - // Find the host_slot for this carrier - let host_slot = carrier_info - .carriers - .iter() - .find(|c| &c.name == carrier_name) - .map(|c| c.host_id) - .ok_or_else(|| { - format!( - "[cf_loop/pattern4] Carrier '{}' not found in CarrierInfo", - carrier_name - ) - })?; + // Phase 179 Task 2: Use ExitMetaCollector for unified exit binding generation + // Replaces manual loop (Phase 196 implementation) with modular Box approach + use super::super::merge::exit_line::meta_collector::ExitMetaCollector; + let exit_bindings = ExitMetaCollector::collect( + self, + &exit_meta, + debug, + ); - exit_bindings.push( - crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding { - carrier_name: carrier_name.clone(), - join_exit_value: *join_exit_value, - host_slot, - }, - ); - - trace::trace().debug( - "pattern4", - &format!("Generated binding: {} → JoinIR={} Host={}", carrier_name, join_exit_value.0, host_slot.0) - ); + // Validate that all expected carriers have bindings + // (ExitMetaCollector silently skips missing carriers, but Pattern 4 expects all) + for carrier in &carrier_info.carriers { + if !exit_bindings.iter().any(|b| b.carrier_name == carrier.name) { + return Err(format!( + "[cf_loop/pattern4] Carrier '{}' not found in variable_map - exit binding missing", + carrier.name + )); + } } // Phase 196: Build host_inputs dynamically