183 lines
5.3 KiB
Rust
183 lines
5.3 KiB
Rust
|
|
//! Phase 171-fix: Condition Expression Environment
|
||
|
|
//!
|
||
|
|
//! This module provides the environment for lowering condition expressions to JoinIR.
|
||
|
|
//! It maps variable names to JoinIR-local ValueIds, ensuring proper separation between
|
||
|
|
//! HOST and JoinIR value spaces.
|
||
|
|
//!
|
||
|
|
//! ## Design Philosophy
|
||
|
|
//!
|
||
|
|
//! **Single Responsibility**: This module ONLY handles variable name → ValueId mapping
|
||
|
|
//! for condition expressions. It does NOT:
|
||
|
|
//! - Perform AST lowering (that's condition_lowerer.rs)
|
||
|
|
//! - Extract variables from AST (that's condition_var_extractor.rs)
|
||
|
|
//! - Manage HOST ↔ JoinIR bindings (that's inline_boundary.rs)
|
||
|
|
|
||
|
|
use crate::mir::ValueId;
|
||
|
|
use std::collections::HashMap;
|
||
|
|
|
||
|
|
/// Environment for condition expression lowering
|
||
|
|
///
|
||
|
|
/// Maps variable names to JoinIR-local ValueIds. Used when lowering
|
||
|
|
/// condition AST nodes to JoinIR instructions.
|
||
|
|
///
|
||
|
|
/// # Example
|
||
|
|
///
|
||
|
|
/// ```ignore
|
||
|
|
/// let mut env = ConditionEnv::new();
|
||
|
|
/// env.insert("i".to_string(), ValueId(0)); // Loop parameter
|
||
|
|
/// env.insert("end".to_string(), ValueId(1)); // Condition-only var
|
||
|
|
///
|
||
|
|
/// // Later during lowering:
|
||
|
|
/// if let Some(value_id) = env.get("i") {
|
||
|
|
/// // Use value_id in JoinIR instruction
|
||
|
|
/// }
|
||
|
|
/// ```
|
||
|
|
#[derive(Debug, Clone, Default)]
|
||
|
|
pub struct ConditionEnv {
|
||
|
|
name_to_join: HashMap<String, ValueId>,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl ConditionEnv {
|
||
|
|
/// Create a new empty environment
|
||
|
|
pub fn new() -> Self {
|
||
|
|
Self {
|
||
|
|
name_to_join: HashMap::new(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Insert a variable binding
|
||
|
|
///
|
||
|
|
/// # Arguments
|
||
|
|
///
|
||
|
|
/// * `name` - Variable name (e.g., "i", "end")
|
||
|
|
/// * `join_id` - JoinIR-local ValueId for this variable
|
||
|
|
pub fn insert(&mut self, name: String, join_id: ValueId) {
|
||
|
|
self.name_to_join.insert(name, join_id);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Look up a variable by name
|
||
|
|
///
|
||
|
|
/// Returns `Some(ValueId)` if the variable exists in the environment,
|
||
|
|
/// `None` otherwise.
|
||
|
|
pub fn get(&self, name: &str) -> Option<ValueId> {
|
||
|
|
self.name_to_join.get(name).copied()
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Check if a variable exists in the environment
|
||
|
|
pub fn contains(&self, name: &str) -> bool {
|
||
|
|
self.name_to_join.contains_key(name)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get the number of variables in the environment
|
||
|
|
pub fn len(&self) -> usize {
|
||
|
|
self.name_to_join.len()
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Check if the environment is empty
|
||
|
|
pub fn is_empty(&self) -> bool {
|
||
|
|
self.name_to_join.is_empty()
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get an iterator over all (name, ValueId) pairs
|
||
|
|
pub fn iter(&self) -> impl Iterator<Item = (&String, &ValueId)> {
|
||
|
|
self.name_to_join.iter()
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Get all variable names (sorted)
|
||
|
|
pub fn names(&self) -> Vec<String> {
|
||
|
|
let mut names: Vec<_> = self.name_to_join.keys().cloned().collect();
|
||
|
|
names.sort();
|
||
|
|
names
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Binding between HOST and JoinIR ValueIds for condition variables
|
||
|
|
///
|
||
|
|
/// This structure explicitly connects a variable name to both its HOST ValueId
|
||
|
|
/// (from the host function's variable_map) and its JoinIR ValueId (allocated
|
||
|
|
/// locally within the JoinIR fragment).
|
||
|
|
///
|
||
|
|
/// # Example
|
||
|
|
///
|
||
|
|
/// For condition variable "start" in `loop(start < end)`:
|
||
|
|
///
|
||
|
|
/// ```ignore
|
||
|
|
/// ConditionBinding {
|
||
|
|
/// name: "start".to_string(),
|
||
|
|
/// host_value: ValueId(33), // HOST function's ValueId for "start"
|
||
|
|
/// join_value: ValueId(1), // JoinIR-local ValueId for "start"
|
||
|
|
/// }
|
||
|
|
/// ```
|
||
|
|
#[derive(Debug, Clone)]
|
||
|
|
pub struct ConditionBinding {
|
||
|
|
/// Variable name (e.g., "start", "end")
|
||
|
|
pub name: String,
|
||
|
|
|
||
|
|
/// HOST function's ValueId for this variable
|
||
|
|
///
|
||
|
|
/// This comes from `builder.variable_map` in the host function.
|
||
|
|
pub host_value: ValueId,
|
||
|
|
|
||
|
|
/// JoinIR-local ValueId for this variable
|
||
|
|
///
|
||
|
|
/// This is allocated within the JoinIR fragment and must be remapped
|
||
|
|
/// when merging into the host function.
|
||
|
|
pub join_value: ValueId,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl ConditionBinding {
|
||
|
|
/// Create a new condition binding
|
||
|
|
pub fn new(name: String, host_value: ValueId, join_value: ValueId) -> Self {
|
||
|
|
Self {
|
||
|
|
name,
|
||
|
|
host_value,
|
||
|
|
join_value,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[cfg(test)]
|
||
|
|
mod tests {
|
||
|
|
use super::*;
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_condition_env_basic() {
|
||
|
|
let mut env = ConditionEnv::new();
|
||
|
|
assert!(env.is_empty());
|
||
|
|
assert_eq!(env.len(), 0);
|
||
|
|
|
||
|
|
env.insert("i".to_string(), ValueId(0));
|
||
|
|
assert!(!env.is_empty());
|
||
|
|
assert_eq!(env.len(), 1);
|
||
|
|
assert!(env.contains("i"));
|
||
|
|
assert_eq!(env.get("i"), Some(ValueId(0)));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_condition_env_multiple_vars() {
|
||
|
|
let mut env = ConditionEnv::new();
|
||
|
|
env.insert("i".to_string(), ValueId(0));
|
||
|
|
env.insert("start".to_string(), ValueId(1));
|
||
|
|
env.insert("end".to_string(), ValueId(2));
|
||
|
|
|
||
|
|
assert_eq!(env.len(), 3);
|
||
|
|
assert_eq!(env.get("i"), Some(ValueId(0)));
|
||
|
|
assert_eq!(env.get("start"), Some(ValueId(1)));
|
||
|
|
assert_eq!(env.get("end"), Some(ValueId(2)));
|
||
|
|
assert_eq!(env.get("nonexistent"), None);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
fn test_condition_binding() {
|
||
|
|
let binding = ConditionBinding::new(
|
||
|
|
"start".to_string(),
|
||
|
|
ValueId(33), // HOST
|
||
|
|
ValueId(1), // JoinIR
|
||
|
|
);
|
||
|
|
|
||
|
|
assert_eq!(binding.name, "start");
|
||
|
|
assert_eq!(binding.host_value, ValueId(33));
|
||
|
|
assert_eq!(binding.join_value, ValueId(1));
|
||
|
|
}
|
||
|
|
}
|