diff --git a/src/mir/join_ir/lowering/carrier_info.rs b/src/mir/join_ir/lowering/carrier_info.rs index 72db6b44..6740df4b 100644 --- a/src/mir/join_ir/lowering/carrier_info.rs +++ b/src/mir/join_ir/lowering/carrier_info.rs @@ -3,8 +3,11 @@ //! This module defines metadata structures for tracking carrier variables //! in loop lowering. This enables dynamic generation of exit bindings //! without hardcoded variable names or ValueIds. +//! +//! Phase 193-2: Enhanced builder methods for flexible construction use crate::mir::ValueId; +use std::collections::HashMap; /// Information about a single carrier variable #[derive(Debug, Clone)] @@ -26,6 +29,167 @@ pub struct CarrierInfo { pub carriers: Vec, } +impl CarrierInfo { + /// Phase 193-2: Create CarrierInfo from a variable_map + /// + /// Automatically extracts all non-loop-control variables from the host's + /// variable_map. This eliminates manual carrier listing for simple cases. + /// + /// # Arguments + /// + /// * `loop_var_name` - Name of the loop control variable (e.g., "i") + /// * `variable_map` - Host function's variable_map (String → ValueId) + /// + /// # Returns + /// + /// CarrierInfo with loop_var and all other variables as carriers + /// + /// # Example + /// + /// ```ignore + /// let carrier_info = CarrierInfo::from_variable_map( + /// "i".to_string(), + /// &variable_map // {"i": ValueId(5), "sum": ValueId(10), "count": ValueId(11)} + /// )?; + /// // Result: CarrierInfo with loop_var="i", carriers=[sum, count] + /// ``` + pub fn from_variable_map( + loop_var_name: String, + variable_map: &HashMap, + ) -> Result { + // Find loop variable + let loop_var_id = variable_map + .get(&loop_var_name) + .copied() + .ok_or_else(|| { + format!( + "Loop variable '{}' not found in variable_map", + loop_var_name + ) + })?; + + // Collect all non-loop-var variables as carriers + let mut carriers: Vec = variable_map + .iter() + .filter(|(name, _)| *name != &loop_var_name) + .map(|(name, &id)| CarrierVar { + name: name.clone(), + host_id: id, + }) + .collect(); + + // Sort for determinism + carriers.sort_by(|a, b| a.name.cmp(&b.name)); + + Ok(CarrierInfo { + loop_var_name, + loop_var_id, + carriers, + }) + } + + /// Phase 193-2: Create CarrierInfo with explicit carrier list + /// + /// Useful when you have specific carriers in mind and want explicit control + /// over which variables are treated as carriers. + /// + /// # Arguments + /// + /// * `loop_var_name` - Name of the loop control variable + /// * `loop_var_id` - ValueId of the loop variable + /// * `carrier_names` - Names of carrier variables (will look up in variable_map) + /// * `variable_map` - Host function's variable_map for lookups + /// + /// # Returns + /// + /// CarrierInfo with only the specified carriers + /// + /// # Example + /// + /// ```ignore + /// let carrier_info = CarrierInfo::with_explicit_carriers( + /// "i".to_string(), + /// ValueId(5), + /// vec!["sum".to_string(), "count".to_string()], + /// &variable_map + /// )?; + /// ``` + pub fn with_explicit_carriers( + loop_var_name: String, + loop_var_id: ValueId, + carrier_names: Vec, + variable_map: &HashMap, + ) -> Result { + let mut carriers = Vec::new(); + + for name in carrier_names { + let host_id = variable_map.get(&name).copied().ok_or_else(|| { + format!("Carrier variable '{}' not found in variable_map", name) + })?; + + carriers.push(CarrierVar { + name, + host_id, + }); + } + + // Sort for determinism + carriers.sort_by(|a, b| a.name.cmp(&b.name)); + + Ok(CarrierInfo { + loop_var_name, + loop_var_id, + carriers, + }) + } + + /// Phase 193-2: Create CarrierInfo with manual CarrierVar list + /// + /// Most explicit construction method - you provide everything directly. + /// Useful when you already have CarrierVar structs built elsewhere. + /// + /// # Arguments + /// + /// * `loop_var_name` - Name of the loop control variable + /// * `loop_var_id` - ValueId of the loop variable + /// * `carriers` - Vec of already-constructed CarrierVar structs + pub fn with_carriers( + loop_var_name: String, + loop_var_id: ValueId, + mut carriers: Vec, + ) -> Self { + // Sort for determinism + carriers.sort_by(|a, b| a.name.cmp(&b.name)); + + Self { + loop_var_name, + loop_var_id, + carriers, + } + } + + /// Phase 193-2: Get carrier count + /// + /// Convenience method for checking how many carriers this info has. + pub fn carrier_count(&self) -> usize { + self.carriers.len() + } + + /// Phase 193-2: Check if this has multiple carriers + /// + /// Useful for pattern matching: "is this a multi-carrier loop?" + pub fn is_multi_carrier(&self) -> bool { + self.carriers.len() > 1 + } + + /// Phase 193-2: Find a carrier by name + /// + /// Lookup a specific carrier variable by name. + pub fn find_carrier(&self, name: &str) -> Option<&CarrierVar> { + self.carriers.iter().find(|c| c.name == name) + } +} + /// Exit metadata returned by lowerers /// /// This structure captures the mapping from JoinIR exit values to @@ -61,4 +225,34 @@ impl ExitMeta { pub fn multiple(exit_values: Vec<(String, ValueId)>) -> Self { Self { exit_values } } + + /// Phase 193-2: Get the count of exit bindings + /// + /// Useful for checking if this ExitMeta has any exit values. + pub fn binding_count(&self) -> usize { + self.exit_values.len() + } + + /// Phase 193-2: Check if this has any exit values + pub fn is_empty(&self) -> bool { + self.exit_values.is_empty() + } + + /// Phase 193-2: Find a binding by carrier name + /// + /// Lookup a specific exit value by carrier name. + pub fn find_binding(&self, carrier_name: &str) -> Option { + self.exit_values + .iter() + .find(|(name, _)| name == carrier_name) + .map(|(_, value_id)| *value_id) + } + + /// Phase 193-2: Add a binding to ExitMeta + /// + /// Convenient way to build ExitMeta incrementally. + pub fn with_binding(mut self, carrier_name: String, join_value: ValueId) -> Self { + self.exit_values.push((carrier_name, join_value)); + self + } }