Files
hakorune/src/mir/builder/binding_context.rs
nyash-codex 1adf57ec54 refactor(mir): Extract BindingContext from MirBuilder (Phase 136 follow-up 4/7)
## Summary
Extracted binding management into dedicated BindingContext struct,
completing step 4 of 7 in the Context Box refactoring plan.

## Changes
- NEW: src/mir/builder/binding_context.rs (BindingContext struct + helpers)
- Modified 7 files to use binding_ctx (SSOT pattern with legacy sync)
- Added comprehensive unit tests for BindingContext

## Extracted Fields
- binding_map: BTreeMap<String, BindingId> → binding_ctx.binding_map

## Benefits
- Clear separation: BindingId mapping isolated from MirBuilder
- Better testability: BindingContext can be tested independently
- Consistent pattern: Same SSOT + legacy sync approach as previous steps

## Tests
- cargo test --release --lib: 1008/1008 passed
- phase135_trim_mir_verify.sh: PASS
- Backward compatibility: 100% maintained (deprecated fields synced)

## Progress
Phase 136 Context Extraction: 4/7 complete (57%)
-  Step 1: TypeContext
-  Step 2: CoreContext
-  Step 3: ScopeContext
-  Step 4: BindingContext (this commit)
-  Step 5: VariableContext
-  Step 6: MetadataContext
-  Step 7: RegionContext

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 20:40:23 +09:00

138 lines
4.2 KiB
Rust

//! Phase 136 follow-up (Step 4/7): BindingContext extraction
//!
//! Consolidates variable binding management:
//! - binding_map: String -> BindingId mapping (parallel to variable_map)
//! - BindingId allocation (via CoreContext.next_binding())
//! - Scope restoration logic (stored in ScopeContext frames)
//!
//! ## Relationship with other contexts:
//! - **CoreContext**: Allocates BindingId via next_binding()
//! - **ScopeContext**: Manages lexical scope frames with restore_binding data
//! - **TypeContext**: Independent (type tracking vs binding tracking)
//!
//! ## Design:
//! - BindingId tracks variable binding identity (survives SSA renaming)
//! - Parallel to ValueId (variable_map), but for binding semantics
//! - Restored on scope exit (see LexicalScopeFrame.restore_binding)
//!
//! Phase 74: BindingId system introduction
//! Phase 136 Step 4/7: Extraction into dedicated context
use crate::mir::BindingId;
use std::collections::BTreeMap;
/// Phase 136 Step 4/7: Binding context for variable binding management
///
/// Manages the mapping from variable names to their BindingId.
/// Parallel to `variable_map` (String -> ValueId), but tracks binding identity.
///
/// ## Key responsibilities:
/// - Maintain current binding_map (String -> BindingId)
/// - Provide lookup/insertion/removal operations
/// - Work with ScopeContext for scope-based restoration
///
/// ## Implementation note:
/// - Uses BTreeMap for deterministic iteration (Phase 25.1 consistency)
/// - BindingId allocation is delegated to CoreContext.next_binding()
#[derive(Debug, Clone)]
pub struct BindingContext {
/// Phase 74: BindingId mapping for lexical variable bindings
/// Maps variable names to their current BindingId.
/// Parallel to `variable_map` (String -> ValueId), but tracks binding identity.
/// Restored on lexical scope exit (see ScopeContext restore_binding).
pub(super) binding_map: BTreeMap<String, BindingId>,
}
impl Default for BindingContext {
fn default() -> Self {
Self::new()
}
}
impl BindingContext {
/// Create a new BindingContext
pub fn new() -> Self {
Self {
binding_map: BTreeMap::new(),
}
}
/// Lookup a variable's BindingId
pub fn lookup(&self, name: &str) -> Option<BindingId> {
self.binding_map.get(name).copied()
}
/// Insert a variable binding
pub fn insert(&mut self, name: String, binding_id: BindingId) {
self.binding_map.insert(name, binding_id);
}
/// Remove a variable binding
pub fn remove(&mut self, name: &str) -> Option<BindingId> {
self.binding_map.remove(name)
}
/// Get immutable reference to the binding map (for BindingMapProvider)
pub fn binding_map(&self) -> &BTreeMap<String, BindingId> {
&self.binding_map
}
/// Check if a variable has a binding
pub fn contains(&self, name: &str) -> bool {
self.binding_map.contains_key(name)
}
/// Get the number of bindings
pub fn len(&self) -> usize {
self.binding_map.len()
}
/// Check if there are no bindings
pub fn is_empty(&self) -> bool {
self.binding_map.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_binding_context_basic() {
let mut ctx = BindingContext::new();
assert!(ctx.is_empty());
assert_eq!(ctx.len(), 0);
let bid = BindingId::new(0);
ctx.insert("x".to_string(), bid);
assert_eq!(ctx.lookup("x"), Some(bid));
assert_eq!(ctx.len(), 1);
assert!(!ctx.is_empty());
ctx.remove("x");
assert_eq!(ctx.lookup("x"), None);
assert!(ctx.is_empty());
}
#[test]
fn test_binding_context_contains() {
let mut ctx = BindingContext::new();
assert!(!ctx.contains("x"));
ctx.insert("x".to_string(), BindingId::new(0));
assert!(ctx.contains("x"));
}
#[test]
fn test_binding_map_access() {
let mut ctx = BindingContext::new();
ctx.insert("a".to_string(), BindingId::new(1));
ctx.insert("b".to_string(), BindingId::new(2));
let map = ctx.binding_map();
assert_eq!(map.len(), 2);
assert_eq!(map.get("a"), Some(&BindingId::new(1)));
assert_eq!(map.get("b"), Some(&BindingId::new(2)));
}
}