diff --git a/src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs b/src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs new file mode 100644 index 00000000..7a9dca9e --- /dev/null +++ b/src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs @@ -0,0 +1,120 @@ +//! Phase 33-10-Refactor-P1: ExitMetaCollector Box +//! +//! Modularizes the exit_bindings collection logic from Pattern lowerers +//! into a focused, testable Box. +//! +//! **Responsibility**: Construct exit_bindings from ExitMeta + variable_map lookup + +use crate::mir::builder::MirBuilder; +use crate::mir::join_ir::lowering::carrier_info::ExitMeta; +use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding; + +/// ExitMetaCollector: A Box that builds exit_bindings from ExitMeta +/// +/// # Responsibility (Single: ExitMeta → exit_bindings construction) +/// +/// Takes ExitMeta (from JoinIR lowerer) and the host's variable_map, +/// then constructs LoopExitBinding entries mapping carriers to their exit values. +/// +/// # Box Contract +/// +/// **Input**: +/// - ExitMeta with exit_values (carrier_name → join_exit_value mappings) +/// - MirBuilder with variable_map for host ValueId lookup +/// +/// **Effect**: +/// - Creates LoopExitBinding vector (pure function, no side effects) +/// +/// **Output**: +/// - Vec: exit_bindings ready for JoinInlineBoundary +pub struct ExitMetaCollector; + +impl ExitMetaCollector { + /// Build exit_bindings from ExitMeta and variable_map + /// + /// # Algorithm + /// + /// For each entry in exit_meta.exit_values: + /// 1. Look up the carrier's host ValueId from builder.variable_map + /// 2. Create LoopExitBinding with carrier_name, join_exit_value, host_slot + /// 3. Collect into Vec + /// + /// # Skipped carriers + /// + /// Carriers not found in variable_map are silently skipped (filter_map behavior). + /// This is intentional: some carriers may not be relevant to the current pattern. + /// + /// # Logging + /// + /// If debug enabled, logs each binding created for validation. + pub fn collect( + builder: &MirBuilder, + exit_meta: &ExitMeta, + debug: bool, + ) -> Vec { + let mut bindings = Vec::new(); + + if debug { + eprintln!( + "[cf_loop/exit_line] ExitMetaCollector: Collecting {} exit values", + exit_meta.exit_values.len() + ); + } + + // Iterate over ExitMeta entries and build bindings + for (carrier_name, join_exit_value) in &exit_meta.exit_values { + // Look up host slot from variable_map + if let Some(&host_slot) = builder.variable_map.get(carrier_name) { + let binding = LoopExitBinding { + carrier_name: carrier_name.clone(), + join_exit_value: *join_exit_value, + host_slot, + }; + + if debug { + eprintln!( + "[cf_loop/exit_line] ExitMetaCollector: Collected '{}' JoinIR {:?} → HOST {:?}", + carrier_name, join_exit_value, host_slot + ); + } + + bindings.push(binding); + } else if debug { + eprintln!( + "[cf_loop/exit_line] ExitMetaCollector DEBUG: Carrier '{}' not in variable_map (skip)", + carrier_name + ); + } + } + + if debug { + eprintln!( + "[cf_loop/exit_line] ExitMetaCollector: Collected {} bindings", + bindings.len() + ); + } + + bindings + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_empty_exit_meta() { + // This test would require full MirBuilder setup + // Placeholder for future detailed testing + // When exit_meta is empty, should return empty vec + assert!(true); + } + + #[test] + fn test_missing_carrier_in_variable_map() { + // This test would require full MirBuilder setup + // Placeholder for future detailed testing + // When carrier not in variable_map, should be silently skipped + assert!(true); + } +} diff --git a/src/mir/builder/control_flow/joinir/merge/exit_line/mod.rs b/src/mir/builder/control_flow/joinir/merge/exit_line/mod.rs index 26a2e30d..de60e68a 100644 --- a/src/mir/builder/control_flow/joinir/merge/exit_line/mod.rs +++ b/src/mir/builder/control_flow/joinir/merge/exit_line/mod.rs @@ -10,8 +10,10 @@ //! ``` pub mod reconnector; +pub mod meta_collector; pub use reconnector::ExitLineReconnector; +pub use meta_collector::ExitMetaCollector; /// Phase 33-10-Refactor-P2: ExitLineOrchestrator facade /// diff --git a/src/mir/builder/control_flow/joinir/merge/mod.rs b/src/mir/builder/control_flow/joinir/merge/mod.rs index 67d9bdad..7df3352c 100644 --- a/src/mir/builder/control_flow/joinir/merge/mod.rs +++ b/src/mir/builder/control_flow/joinir/merge/mod.rs @@ -16,7 +16,7 @@ mod block_allocator; mod value_collector; mod instruction_rewriter; mod exit_phi_builder; -mod exit_line; +pub mod exit_line; use crate::mir::{MirModule, ValueId}; use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; 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 d2ef6e1f..d1f27479 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 @@ -179,21 +179,11 @@ impl MirBuilder { // // 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 33-10-Refactor-P1: Use ExitMetaCollector Box to build exit_bindings + use crate::mir::builder::control_flow::joinir::merge::exit_line::ExitMetaCollector; - // 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 33-10: Collect exit bindings from ExitMeta using Box + let exit_bindings = ExitMetaCollector::collect(self, &exit_meta, debug); // 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( @@ -203,14 +193,6 @@ impl MirBuilder { 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)?;