Phase 75 pilots BindingId-based variable lookup in the ScopeManager and ConditionEnv components. Demonstrates "BindingId priority → name fallback" strategy with comprehensive testing and zero production impact. Changes: - scope_manager.rs: Added lookup_with_binding() trait method - condition_env.rs: Implemented resolve_var_with_binding() with 3-tier fallback - expr_lowerer.rs: Integrated pilot lookup paths - condition_lowering_box.rs: Updated with BindingId support Tests: 3/3 new pilot tests PASS (priority/fallback/legacy) Tests: lib 958/958 PASS, normalized_dev 54/54 PASS (no regressions) Design: Feature-gated with normalized_dev, enables Phase 76 expansion. 🤖 Generated with Claude Code Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
298 lines
9.5 KiB
Rust
298 lines
9.5 KiB
Rust
//! Phase 244: Unified Condition Lowering Interface
|
|
//!
|
|
//! This module provides a trait-based abstraction for condition lowering,
|
|
//! allowing different implementations (ExprLowerer, legacy lowerers) to
|
|
//! be used interchangeably.
|
|
//!
|
|
//! ## Design Philosophy
|
|
//!
|
|
//! **Box-First**: ConditionLoweringBox is a "box" trait that encapsulates
|
|
//! all condition lowering logic with a single, unified interface.
|
|
//!
|
|
//! **Single Responsibility**: Implementations ONLY perform AST → ValueId lowering.
|
|
//! They do NOT manage scopes, extract variables, or handle PHI generation.
|
|
//!
|
|
//! **Fail-Safe**: Implementations return explicit errors for unsupported patterns,
|
|
//! allowing callers to fall back to alternative paths.
|
|
|
|
use super::scope_manager::ScopeManager;
|
|
use crate::ast::ASTNode;
|
|
use crate::mir::ValueId;
|
|
|
|
/// Phase 244: Context for condition lowering
|
|
///
|
|
/// This struct encapsulates all the necessary context for lowering a condition
|
|
/// expression to JoinIR, including loop variable information and scope access.
|
|
///
|
|
/// # Fields
|
|
///
|
|
/// * `loop_var_name` - Name of the loop variable (e.g., "i")
|
|
/// * `loop_var_id` - ValueId of the loop variable in JoinIR space
|
|
/// * `scope` - Reference to ScopeManager for variable lookup
|
|
/// * `alloc_value` - ValueId allocator function
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```ignore
|
|
/// let context = ConditionContext {
|
|
/// loop_var_name: "i".to_string(),
|
|
/// loop_var_id: ValueId(1),
|
|
/// scope: &scope_manager,
|
|
/// alloc_value: &mut alloc_fn,
|
|
/// };
|
|
///
|
|
/// let value_id = lowerer.lower_condition(&ast, &context)?;
|
|
/// ```
|
|
pub struct ConditionContext<'a, S: ScopeManager> {
|
|
/// Name of the loop variable (e.g., "i", "pos")
|
|
pub loop_var_name: String,
|
|
|
|
/// ValueId of the loop variable in JoinIR space
|
|
pub loop_var_id: ValueId,
|
|
|
|
/// Scope manager for variable resolution
|
|
pub scope: &'a S,
|
|
|
|
/// ValueId allocator function
|
|
pub alloc_value: &'a mut dyn FnMut() -> ValueId,
|
|
}
|
|
|
|
/// Phase 244: Unified condition lowering interface
|
|
///
|
|
/// This trait provides a common interface for all condition lowering implementations.
|
|
/// It allows Pattern 2/3/4 to use any lowering strategy (ExprLowerer, legacy, etc.)
|
|
/// without coupling to specific implementation details.
|
|
///
|
|
/// # Design Principles
|
|
///
|
|
/// 1. **Single Method**: `lower_condition()` is the only required method
|
|
/// 2. **Context-Based**: All necessary information passed via ConditionContext
|
|
/// 3. **Fail-Fast**: Errors returned immediately (no silent fallbacks)
|
|
/// 4. **Stateless**: Implementations should be reusable across multiple calls
|
|
///
|
|
/// # Example Implementation
|
|
///
|
|
/// ```ignore
|
|
/// impl<S: ScopeManager> ConditionLoweringBox<S> for ExprLowerer<'_, '_, S> {
|
|
/// fn lower_condition(
|
|
/// &mut self,
|
|
/// condition: &ASTNode,
|
|
/// context: &ConditionContext<S>,
|
|
/// ) -> Result<ValueId, String> {
|
|
/// // Delegate to existing ExprLowerer::lower() method
|
|
/// self.lower(condition)
|
|
/// }
|
|
///
|
|
/// fn supports(&self, condition: &ASTNode) -> bool {
|
|
/// Self::is_supported_condition(condition)
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
pub trait ConditionLoweringBox<S: ScopeManager> {
|
|
/// Lower condition AST to ValueId
|
|
///
|
|
/// This method translates an AST condition expression (e.g., `i < 10`,
|
|
/// `digit_pos < 0`) into a JoinIR ValueId representing the boolean result.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `condition` - AST node representing the boolean condition
|
|
/// * `context` - Lowering context (loop var, scope, allocator)
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `Ok(ValueId)` - Boolean result ValueId
|
|
/// * `Err(String)` - Lowering error (unsupported pattern, variable not found, etc.)
|
|
///
|
|
/// # Fail-Fast Principle
|
|
///
|
|
/// Implementations MUST return `Err` immediately for unsupported patterns.
|
|
/// Callers can then decide whether to fall back to alternative lowering paths.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```ignore
|
|
/// // Pattern 2: Break condition lowering
|
|
/// let cond_value = lowerer.lower_condition(&break_cond_ast, &context)?;
|
|
///
|
|
/// // Use cond_value in conditional Jump
|
|
/// let break_jump = JoinInst::Jump {
|
|
/// target: k_exit,
|
|
/// args: vec![loop_var_id],
|
|
/// condition: Some(cond_value),
|
|
/// };
|
|
/// ```
|
|
fn lower_condition(
|
|
&mut self,
|
|
condition: &ASTNode,
|
|
context: &ConditionContext<S>,
|
|
) -> Result<ValueId, String>;
|
|
|
|
/// Check if this lowerer supports the given condition pattern
|
|
///
|
|
/// This method allows callers to check support BEFORE attempting lowering,
|
|
/// enabling early fallback to alternative strategies.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `condition` - AST node to check
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// * `true` - This lowerer can handle this pattern
|
|
/// * `false` - This lowerer cannot handle this pattern (caller should use alternative)
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```ignore
|
|
/// if lowerer.supports(&break_cond_ast) {
|
|
/// let value = lowerer.lower_condition(&break_cond_ast, &context)?;
|
|
/// } else {
|
|
/// // Fall back to legacy lowering
|
|
/// let value = legacy_lower_condition(&break_cond_ast, env)?;
|
|
/// }
|
|
/// ```
|
|
fn supports(&self, condition: &ASTNode) -> bool;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::ast::{BinaryOperator, LiteralValue, Span};
|
|
use crate::mir::builder::MirBuilder;
|
|
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
|
|
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
|
|
use crate::mir::join_ir::lowering::expr_lowerer::{ExprContext, ExprLowerer};
|
|
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
|
|
|
|
fn span() -> Span {
|
|
Span::unknown()
|
|
}
|
|
|
|
fn var(name: &str) -> ASTNode {
|
|
ASTNode::Variable {
|
|
name: name.to_string(),
|
|
span: span(),
|
|
}
|
|
}
|
|
|
|
fn lit_i(value: i64) -> ASTNode {
|
|
ASTNode::Literal {
|
|
value: LiteralValue::Integer(value),
|
|
span: span(),
|
|
}
|
|
}
|
|
|
|
fn bin(op: BinaryOperator, left: ASTNode, right: ASTNode) -> ASTNode {
|
|
ASTNode::BinaryOp {
|
|
operator: op,
|
|
left: Box::new(left),
|
|
right: Box::new(right),
|
|
span: span(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_condition_lowering_box_trait_exists() {
|
|
// This test verifies that the ConditionLoweringBox trait can be used
|
|
// with ExprLowerer (implementation added in Step 2).
|
|
|
|
let mut condition_env = ConditionEnv::new();
|
|
condition_env.insert("i".to_string(), ValueId(100));
|
|
|
|
let carrier_info = CarrierInfo {
|
|
loop_var_name: "i".to_string(),
|
|
loop_var_id: ValueId(1),
|
|
carriers: vec![],
|
|
trim_helper: None,
|
|
promoted_loopbodylocals: vec![],
|
|
#[cfg(feature = "normalized_dev")]
|
|
promoted_bindings: std::collections::BTreeMap::new(),
|
|
};
|
|
|
|
let scope = Pattern2ScopeManager {
|
|
condition_env: &condition_env,
|
|
loop_body_local_env: None,
|
|
captured_env: None,
|
|
carrier_info: &carrier_info,
|
|
};
|
|
|
|
let mut builder = MirBuilder::new();
|
|
let mut alloc_counter = 1000u32;
|
|
let mut alloc_fn = || {
|
|
let id = ValueId(alloc_counter);
|
|
alloc_counter += 1;
|
|
id
|
|
};
|
|
|
|
let context = ConditionContext {
|
|
loop_var_name: "i".to_string(),
|
|
loop_var_id: ValueId(1),
|
|
scope: &scope,
|
|
alloc_value: &mut alloc_fn,
|
|
};
|
|
|
|
// AST: i < 10
|
|
let ast = bin(BinaryOperator::Less, var("i"), lit_i(10));
|
|
|
|
// ExprLowerer implements ConditionLoweringBox (Step 2)
|
|
let mut expr_lowerer = ExprLowerer::new(&scope, ExprContext::Condition, &mut builder);
|
|
|
|
// Check support
|
|
assert!(
|
|
expr_lowerer.supports(&ast),
|
|
"ExprLowerer should support i < 10"
|
|
);
|
|
|
|
// Lower condition via ConditionLoweringBox trait (Step 2 implemented)
|
|
let result = expr_lowerer.lower_condition(&ast, &context);
|
|
assert!(result.is_ok(), "i < 10 should lower successfully via trait");
|
|
}
|
|
|
|
#[test]
|
|
fn test_condition_context_structure() {
|
|
// Verify ConditionContext can be constructed and fields are accessible
|
|
|
|
let mut condition_env = ConditionEnv::new();
|
|
condition_env.insert("i".to_string(), ValueId(100));
|
|
|
|
let carrier_info = CarrierInfo {
|
|
loop_var_name: "i".to_string(),
|
|
loop_var_id: ValueId(1),
|
|
carriers: vec![],
|
|
trim_helper: None,
|
|
promoted_loopbodylocals: vec![],
|
|
#[cfg(feature = "normalized_dev")]
|
|
promoted_bindings: std::collections::BTreeMap::new(),
|
|
};
|
|
|
|
let scope = Pattern2ScopeManager {
|
|
condition_env: &condition_env,
|
|
loop_body_local_env: None,
|
|
captured_env: None,
|
|
carrier_info: &carrier_info,
|
|
};
|
|
|
|
let mut alloc_counter = 1000u32;
|
|
let mut alloc_fn = || {
|
|
let id = ValueId(alloc_counter);
|
|
alloc_counter += 1;
|
|
id
|
|
};
|
|
|
|
let context = ConditionContext {
|
|
loop_var_name: "i".to_string(),
|
|
loop_var_id: ValueId(1),
|
|
scope: &scope,
|
|
alloc_value: &mut alloc_fn,
|
|
};
|
|
|
|
assert_eq!(context.loop_var_name, "i");
|
|
assert_eq!(context.loop_var_id, ValueId(1));
|
|
|
|
// Verify allocator works
|
|
let vid = (context.alloc_value)();
|
|
assert_eq!(vid, ValueId(1000));
|
|
}
|
|
}
|