feat(joinir): Phase 75 - BindingId pilot lookup (dev-only)
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>
This commit is contained in:
@ -23,6 +23,8 @@ use super::carrier_info::CarrierInfo;
|
||||
use super::condition_env::ConditionEnv;
|
||||
use super::loop_body_local_env::LoopBodyLocalEnv;
|
||||
use crate::mir::loop_pattern_detection::function_scope_capture::CapturedEnv;
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
use crate::mir::BindingId; // Phase 75: BindingId-based lookup pilot
|
||||
use crate::mir::ValueId;
|
||||
|
||||
/// Phase 231: Scope kind for variables
|
||||
@ -68,6 +70,37 @@ pub trait ScopeManager {
|
||||
/// This helps the caller understand where the variable comes from, which
|
||||
/// can affect code generation (e.g., PHI node generation, exit handling).
|
||||
fn scope_of(&self, name: &str) -> Option<VarScopeKind>;
|
||||
|
||||
/// Phase 75: BindingId-based lookup (dev-only, pilot integration)
|
||||
///
|
||||
/// Look up variable by BindingId first, falling back to name-based lookup.
|
||||
/// This supports gradual migration from name-based to BindingId-based
|
||||
/// variable resolution.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `binding_id` - Optional BindingId for the variable
|
||||
/// * `name` - Variable name (used as fallback if BindingId lookup fails)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `Some(ValueId)` if found via BindingId or name, `None` otherwise.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// // BindingId available - priority lookup
|
||||
/// let value_id = scope.lookup_with_binding(Some(BindingId(5)), "x");
|
||||
///
|
||||
/// // BindingId not available - legacy name-based fallback
|
||||
/// let value_id = scope.lookup_with_binding(None, "x");
|
||||
/// ```
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn lookup_with_binding(&self, binding_id: Option<BindingId>, name: &str) -> Option<ValueId> {
|
||||
// Default implementation: BindingId not supported, use name lookup
|
||||
let _ = binding_id; // Suppress unused warning
|
||||
self.lookup(name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 231: Pattern2-specific scope manager (pilot implementation)
|
||||
@ -176,6 +209,98 @@ impl<'a> ScopeManager for Pattern2ScopeManager<'a> {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Phase 76: BindingId-based lookup with promoted binding support (dev-only)
|
||||
///
|
||||
/// Extends Phase 75's BindingId priority lookup to check promoted_bindings map.
|
||||
/// This eliminates name-based hacks (`format!("is_{}", name)`) by using type-safe
|
||||
/// BindingId → BindingId mapping from CarrierInfo.
|
||||
///
|
||||
/// ## Lookup Order
|
||||
///
|
||||
/// 1. Direct BindingId lookup in ConditionEnv (if BindingId provided)
|
||||
/// 2. **NEW (Phase 76)**: Promoted BindingId lookup in CarrierInfo.promoted_bindings
|
||||
/// 3. Fallback to legacy name-based lookup (Phase 75 behavior)
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `binding_id` - Optional BindingId from MirBuilder's binding_map
|
||||
/// * `name` - Variable name (fallback for legacy paths)
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `Some(ValueId)` if found via BindingId/promoted/name, `None` otherwise.
|
||||
///
|
||||
/// # Example (Phase 76 Promotion Path)
|
||||
///
|
||||
/// ```ignore
|
||||
/// // Given:
|
||||
/// // - "digit_pos" has BindingId(5)
|
||||
/// // - "is_digit_pos" has BindingId(10)
|
||||
/// // - CarrierInfo.promoted_bindings[BindingId(5)] = BindingId(10)
|
||||
/// // - ConditionEnv.binding_id_map[BindingId(10)] = ValueId(102)
|
||||
///
|
||||
/// let scope = Pattern2ScopeManager { ... };
|
||||
///
|
||||
/// // Phase 76: BindingId-based promoted resolution (NEW!)
|
||||
/// let result = scope.lookup_with_binding(Some(BindingId(5)), "digit_pos");
|
||||
/// // Step 1: ConditionEnv[BindingId(5)] → None (not a carrier)
|
||||
/// // Step 2: CarrierInfo.promoted_bindings[BindingId(5)] → BindingId(10) ✓
|
||||
/// // Step 3: ConditionEnv[BindingId(10)] → ValueId(102) ✓
|
||||
/// assert_eq!(result, Some(ValueId(102)));
|
||||
///
|
||||
/// // Phase 75: Legacy name-based fallback still works
|
||||
/// let result = scope.lookup_with_binding(None, "digit_pos");
|
||||
/// // → Falls back to format!("is_digit_pos") lookup
|
||||
/// assert_eq!(result, Some(ValueId(102)));
|
||||
/// ```
|
||||
///
|
||||
/// # Phase 77 Migration Note
|
||||
///
|
||||
/// The legacy name-based path (step 3) will be removed in Phase 77 after all
|
||||
/// promoters populate promoted_bindings map and all call sites provide BindingId.
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
fn lookup_with_binding(&self, binding_id: Option<BindingId>, name: &str) -> Option<ValueId> {
|
||||
use crate::mir::BindingId;
|
||||
|
||||
if let Some(bid) = binding_id {
|
||||
// Step 1: Try direct BindingId lookup in ConditionEnv (Phase 75)
|
||||
if let Some(value_id) = self.condition_env.resolve_var_with_binding(Some(bid), name) {
|
||||
if std::env::var("NYASH_JOINIR_DEBUG").is_ok() {
|
||||
eprintln!(
|
||||
"[phase76/direct] BindingId({}) -> ValueId({}) for '{}'",
|
||||
bid.0, value_id.0, name
|
||||
);
|
||||
}
|
||||
return Some(value_id);
|
||||
}
|
||||
|
||||
// Step 2: **NEW (Phase 76)**: Check promoted_bindings map
|
||||
if let Some(promoted_bid) = self.carrier_info.resolve_promoted_with_binding(bid) {
|
||||
// Promoted BindingId found, lookup in ConditionEnv
|
||||
if let Some(value_id) = self.condition_env.resolve_var_with_binding(Some(promoted_bid), name) {
|
||||
if std::env::var("NYASH_JOINIR_DEBUG").is_ok() {
|
||||
eprintln!(
|
||||
"[phase76/promoted] BindingId({}) promoted to BindingId({}) -> ValueId({}) for '{}'",
|
||||
bid.0, promoted_bid.0, value_id.0, name
|
||||
);
|
||||
}
|
||||
return Some(value_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Fallback to legacy name-based lookup (Phase 75 fallback path)
|
||||
if std::env::var("NYASH_JOINIR_DEBUG").is_ok() {
|
||||
eprintln!(
|
||||
"[phase76/fallback] BindingId({}) miss, falling back to name '{}' lookup",
|
||||
bid.0, name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Legacy name-based lookup (Phase 75 behavior, will be removed in Phase 77)
|
||||
self.lookup(name)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -195,6 +320,8 @@ mod tests {
|
||||
carriers: vec![],
|
||||
trim_helper: None,
|
||||
promoted_loopbodylocals: vec![],
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
promoted_bindings: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
let scope = Pattern2ScopeManager {
|
||||
@ -226,6 +353,8 @@ mod tests {
|
||||
}],
|
||||
trim_helper: None,
|
||||
promoted_loopbodylocals: vec![],
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
promoted_bindings: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
let scope = Pattern2ScopeManager {
|
||||
@ -256,6 +385,8 @@ mod tests {
|
||||
}],
|
||||
trim_helper: None,
|
||||
promoted_loopbodylocals: vec!["digit_pos".to_string()],
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
promoted_bindings: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
let scope = Pattern2ScopeManager {
|
||||
@ -283,6 +414,8 @@ mod tests {
|
||||
carriers: vec![],
|
||||
trim_helper: None,
|
||||
promoted_loopbodylocals: vec![],
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
promoted_bindings: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
let scope = Pattern2ScopeManager {
|
||||
@ -316,6 +449,8 @@ mod tests {
|
||||
carriers: vec![],
|
||||
trim_helper: None,
|
||||
promoted_loopbodylocals: vec![],
|
||||
#[cfg(feature = "normalized_dev")]
|
||||
promoted_bindings: std::collections::BTreeMap::new(),
|
||||
};
|
||||
|
||||
let scope = Pattern2ScopeManager {
|
||||
|
||||
Reference in New Issue
Block a user