diff --git a/src/mir/control_tree/normalized_shadow/available_inputs_collector.rs b/src/mir/control_tree/normalized_shadow/available_inputs_collector.rs new file mode 100644 index 00000000..c1328a77 --- /dev/null +++ b/src/mir/control_tree/normalized_shadow/available_inputs_collector.rs @@ -0,0 +1,142 @@ +//! Phase 126: available_inputs collector (SSOT) +//! +//! ## Responsibility +//! +//! - Collect available_inputs from function params + CapturedEnv +//! - Returns BTreeMap with deterministic order +//! - No AST inference (SSOT sources only) +//! +//! ## Design +//! +//! - Input sources (priority order): +//! 1. Function params (from ScopeContext + VariableContext) +//! 2. CapturedEnv (pinned/captured variables from outer scope) +//! - Forbidden: AST-based capture inference (Phase 100 CapturedEnv is SSOT) + +// Phase 126: Import contexts from MirBuilder (pub(in crate::mir) visibility) +// Since we're in crate::mir::control_tree::normalized_shadow, we can access these +use crate::mir::builder::MirBuilder; +use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv; +use crate::mir::ValueId; +use std::collections::BTreeMap; + +/// Phase 126: Box-First available_inputs collector +pub struct AvailableInputsCollectorBox; + +impl AvailableInputsCollectorBox { + /// Collect available_inputs from SSOT sources (via MirBuilder) + /// + /// ## Contract + /// + /// - Sources (priority order): + /// 1. Function params: builder.scope_ctx.function_param_names + builder.variable_ctx.variable_map + /// 2. CapturedEnv: captured_env.vars (pinned/captured from outer scope) + /// - Output: BTreeMap (deterministic order) + /// - No AST inference: only use pre-computed SSOT sources + /// + /// ## Implementation + /// + /// - Collect function params first (higher priority) + /// - Collect CapturedEnv vars (lower priority, don't override params) + /// - Use BTreeMap for deterministic iteration + pub fn collect( + builder: &MirBuilder, + captured_env: Option<&CapturedEnv>, + ) -> BTreeMap { + let mut available_inputs = BTreeMap::new(); + + // 1. Function params (SSOT: scope_ctx + variable_ctx) + for param_name in &builder.scope_ctx.function_param_names { + if let Some(value_id) = builder.variable_ctx.lookup(param_name) { + available_inputs.insert(param_name.clone(), value_id); + } + } + + // 2. CapturedEnv (SSOT: pinned/captured vars) + if let Some(env) = captured_env { + for var in &env.vars { + // Don't override function params (params have higher priority) + if !available_inputs.contains_key(&var.name) { + available_inputs.insert(var.name.clone(), var.host_id); + } + } + } + + available_inputs + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mir::loop_pattern_detection::function_scope_capture::CapturedVar; + + #[test] + fn test_collect_empty() { + let builder = MirBuilder::new(); + let result = AvailableInputsCollectorBox::collect(&builder, None); + assert_eq!(result.len(), 0); + } + + #[test] + fn test_collect_function_params() { + let mut builder = MirBuilder::new(); + + // Simulate function param: x -> ValueId(1) + builder.scope_ctx.function_param_names.insert("x".to_string()); + builder.variable_ctx.insert("x".to_string(), ValueId(1)); + + let result = AvailableInputsCollectorBox::collect(&builder, None); + assert_eq!(result.len(), 1); + assert_eq!(result.get("x"), Some(&ValueId(1))); + } + + #[test] + fn test_collect_captured_env() { + let builder = MirBuilder::new(); + + let mut captured = CapturedEnv::new(); + captured.insert("outer_x".to_string(), ValueId(42)); + + let result = AvailableInputsCollectorBox::collect(&builder, Some(&captured)); + assert_eq!(result.len(), 1); + assert_eq!(result.get("outer_x"), Some(&ValueId(42))); + } + + #[test] + fn test_collect_params_override_captured() { + let mut builder = MirBuilder::new(); + + // Function param: x -> ValueId(1) + builder.scope_ctx.function_param_names.insert("x".to_string()); + builder.variable_ctx.insert("x".to_string(), ValueId(1)); + + // Captured: x -> ValueId(42) (should be ignored) + let mut captured = CapturedEnv::new(); + captured.insert("x".to_string(), ValueId(42)); + + let result = AvailableInputsCollectorBox::collect(&builder, Some(&captured)); + assert_eq!(result.len(), 1); + // Function param (ValueId(1)) should win over captured (ValueId(42)) + assert_eq!(result.get("x"), Some(&ValueId(1))); + } + + #[test] + fn test_collect_deterministic_order() { + let mut builder = MirBuilder::new(); + + // Add params in non-alphabetical order + builder.scope_ctx.function_param_names.insert("z".to_string()); + builder.scope_ctx.function_param_names.insert("a".to_string()); + builder.scope_ctx.function_param_names.insert("m".to_string()); + builder.variable_ctx.insert("z".to_string(), ValueId(3)); + builder.variable_ctx.insert("a".to_string(), ValueId(1)); + builder.variable_ctx.insert("m".to_string(), ValueId(2)); + + let result = AvailableInputsCollectorBox::collect(&builder, None); + let keys: Vec<_> = result.keys().collect(); + + // BTreeMap ensures alphabetical order + assert_eq!(keys, vec![&"a".to_string(), &"m".to_string(), &"z".to_string()]); + } +} diff --git a/src/mir/control_tree/normalized_shadow/mod.rs b/src/mir/control_tree/normalized_shadow/mod.rs index d31320d5..585333a7 100644 --- a/src/mir/control_tree/normalized_shadow/mod.rs +++ b/src/mir/control_tree/normalized_shadow/mod.rs @@ -32,6 +32,7 @@ pub mod builder; pub mod contracts; pub mod parity; +pub mod available_inputs_collector; // Phase 126: available_inputs SSOT pub use builder::StepTreeNormalizedShadowLowererBox; pub use contracts::{CapabilityCheckResult, UnsupportedCapability};