feat(control_tree): Phase 126 AvailableInputsCollectorBox

- Collect available_inputs from function params + CapturedEnv (SSOT)
- BTreeMap for deterministic order
- Box-first modularization with unit tests (5 tests PASS)
- Source priority: params > CapturedEnv
- No AST inference (only pre-computed sources)
This commit is contained in:
nyash-codex
2025-12-18 06:43:27 +09:00
parent b7a16aacd0
commit 89c2915fa0
2 changed files with 143 additions and 0 deletions

View File

@ -0,0 +1,142 @@
//! Phase 126: available_inputs collector (SSOT)
//!
//! ## Responsibility
//!
//! - Collect available_inputs from function params + CapturedEnv
//! - Returns BTreeMap<String, ValueId> 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<String, ValueId> (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<String, ValueId> {
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()]);
}
}

View File

@ -32,6 +32,7 @@
pub mod builder; pub mod builder;
pub mod contracts; pub mod contracts;
pub mod parity; pub mod parity;
pub mod available_inputs_collector; // Phase 126: available_inputs SSOT
pub use builder::StepTreeNormalizedShadowLowererBox; pub use builder::StepTreeNormalizedShadowLowererBox;
pub use contracts::{CapabilityCheckResult, UnsupportedCapability}; pub use contracts::{CapabilityCheckResult, UnsupportedCapability};