Phase 73 plans migration from name-based to BindingId-based scope management in JoinIR lowering, aligning with MIR's lexical scope model. Design decision: Option A (Parallel BindingId Layer) with gradual migration. Migration roadmap: Phases 74-77, ~8-12 hours total, zero production impact. Changes: - phase73-scope-manager-design.md: SSOT design (~700 lines) - phase73-completion-summary.md: Deliverables summary - phase73-index.md: Navigation index - scope_manager_bindingid_poc/: Working PoC (437 lines, dev-only) Tests: 6/6 PoC tests PASS, lib 950/950 PASS Implementation: Parallel layer (no changes to existing code paths) 🤖 Generated with Claude Code Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
9.2 KiB
Phase 73: Completion Summary
Date: 2025-12-13 Status: ✅ Complete (Design Phase) Scope: JoinIR ScopeManager → BindingId-Based Design
Deliverables
1. Design Document (SSOT)
File: docs/development/current/main/phase73-scope-manager-design.md
Contents:
- ✅ Current state analysis (MIR + JoinIR scope systems)
- ✅ Problem identification (name-based vs BindingId mismatch)
- ✅ Proposed architecture (Option A: Parallel BindingId Layer)
- ✅ Integration with MIR Builder (binding_map additions)
- ✅ Migration path (Phases 74-77 roadmap)
- ✅ Example scenarios (shadowing, promoted variables)
Key Insights:
- MIR builder uses BindingId for lexical scope tracking (Phase 68-69)
- JoinIR lowering uses name-based lookup (fragile for shadowing)
- Naming convention hacks (
is_digit_pos,is_ch_match) can be replaced with BindingId maps - Gradual migration strategy minimizes risk
2. Proof-of-Concept Implementation
File: src/mir/join_ir/lowering/scope_manager_bindingid_poc/mod.rs
Status: ✅ All tests passing (6/6)
Feature Gate: #[cfg(feature = "normalized_dev")] (dev-only)
Implemented Structures:
BindingIdtype wrapperConditionEnvV2(parallel name + BindingId lookup)CarrierInfoV2(BindingId-based promotion tracking)ScopeManagerV2trait +Pattern2ScopeManagerV2implementation
Test Coverage:
test mir::join_ir::lowering::scope_manager_bindingid_poc::tests::test_condition_env_v2_basic ... ok
test mir::join_ir::lowering::scope_manager_bindingid_poc::tests::test_shadowing_simulation ... ok
test mir::join_ir::lowering::scope_manager_bindingid_poc::tests::test_promoted_binding_resolution ... ok
test mir::join_ir::lowering::scope_manager_bindingid_poc::tests::test_scope_manager_v2_binding_lookup ... ok
test mir::join_ir::lowering::scope_manager_bindingid_poc::tests::test_scope_manager_v2_promoted_lookup ... ok
test mir::join_ir::lowering::scope_manager_bindingid_poc::tests::test_unified_lookup_fallback ... ok
Key Validations:
- ✅ Parallel lookup (BindingId + name fallback) works
- ✅ Shadowing simulation (multiple bindings for same name)
- ✅ Promoted variable resolution (BindingId → BindingId mapping)
- ✅ Unified lookup with graceful fallback
Design Highlights
Problem Statement
Before (Current):
// JoinIR lowering (name-based)
env.get("digit_pos") → searches for "is_digit_pos" via naming convention
→ fragile, breaks if naming convention changes
After (Phase 76+):
// JoinIR lowering (BindingId-based)
env.get_by_binding(BindingId(5)) → resolves promoted_bindings[BindingId(5)] = BindingId(10)
→ type-safe, no string matching
Proposed Architecture (Option A)
Gradual Migration Strategy:
- Phase 74: Add BindingId infrastructure (binding_map, binding_to_join)
- Phase 75: Migrate Pattern 1 (simple, no carriers)
- Phase 76: Migrate Pattern 2 (carrier promotion)
- Phase 77: Migrate Pattern 3-4, remove legacy code
Backward Compatibility:
- New fields added alongside existing name-based maps
- Legacy code continues to work during transition
- Fallback mechanism ensures no breakage
Integration Points
MirBuilder Changes (Phase 74)
pub struct MirBuilder {
pub variable_map: HashMap<String, ValueId>, // Existing (SSA conversion)
pub binding_map: HashMap<String, BindingId>, // NEW (lexical scope)
next_binding_id: u32, // NEW (allocator)
}
ConditionEnv Changes (Phase 74)
pub struct ConditionEnv {
name_to_join: BTreeMap<String, ValueId>, // Legacy (keep)
binding_to_join: BTreeMap<BindingId, ValueId>, // NEW (Phase 74+)
name_to_binding: BTreeMap<String, BindingId>, // NEW (shadowing)
}
CarrierInfo Changes (Phase 76)
pub struct CarrierInfo {
promoted_loopbodylocals: Vec<String>, // Legacy (Phase 224)
promoted_bindings: BTreeMap<BindingId, BindingId>, // NEW (Phase 76+)
}
No Production Code Changes
Confirmation:
- ✅ No changes to
src/mir/builder.rs - ✅ No changes to
src/mir/join_ir/lowering/*.rs(except mod.rs for PoC) - ✅ PoC is feature-gated (
normalized_devonly) - ✅ All existing tests still pass
Modified Files:
docs/development/current/main/phase73-scope-manager-design.md(new)docs/development/current/main/phase73-completion-summary.md(new)src/mir/join_ir/lowering/scope_manager_bindingid_poc/mod.rs(new, dev-only)src/mir/join_ir/lowering/mod.rs(1 line added for PoC module)
Migration Roadmap
Phase 74: Infrastructure (Estimated 2-3 hours)
Goal: Add BindingId tracking without breaking existing code
Tasks:
- Add
binding_maptoMirBuilder - Add
binding_to_jointoConditionEnv - Update
declare_local_in_current_scopeto returnBindingId - Add BindingId allocator tests
Acceptance: All existing tests pass, BindingId populated
Phase 75: Pattern 1 Pilot (Estimated 1-2 hours)
Goal: Prove BindingId integration with simplest pattern
Tasks:
- Update
CarrierInfo::from_variable_mapto acceptbinding_map - Migrate Pattern 1 lowering to use BindingId
- Add E2E test with BindingId
Acceptance: Pattern 1 uses BindingId, legacy fallback works
Phase 76: Pattern 2 Carrier Promotion (Estimated 2-3 hours)
Goal: Eliminate naming convention hacks
Tasks:
- Add
promoted_bindings: BTreeMap<BindingId, BindingId>toCarrierInfo - Update
resolve_promoted_join_idto use BindingId - Migrate Pattern 2 lowering
Acceptance: DigitPos pattern works without string matching
Phase 77: Pattern 3-4 + Cleanup (Estimated 2-3 hours)
Goal: Complete migration, remove legacy code
Tasks:
- Migrate Pattern 3 (multi-carrier)
- Migrate Pattern 4 (generic case A)
- Remove
name_to_join,promoted_loopbodylocals(legacy fields)
Acceptance: All patterns BindingId-only, full test suite passes
Open Questions (for Future Phases)
Q1: BindingId Scope (Per-Function vs Global)
Current Assumption: Per-function (like ValueId)
Reasoning:
- Each function has independent binding scope
- No cross-function binding references
- Simpler allocation (no global state)
Alternative: Global BindingId pool (for Phase 63 ownership analysis integration)
Q2: Captured Variable Handling
Proposed: Add binding_id to CapturedVar
pub struct CapturedVar {
name: String,
host_id: ValueId,
host_binding: BindingId, // Phase 73+ (NEW)
is_immutable: bool,
}
Impact: Requires updating function_scope_capture module
Q3: Performance Impact
Concern: Dual maps (binding_to_join + name_to_join) double memory
Mitigation:
- Phase 74-76: Both maps active (transition period)
- Phase 77: Remove
name_to_joinafter migration - BTreeMap overhead minimal (<10 variables per loop typically)
Measurement: Profile after Phase 74 implementation
Success Criteria (Phase 73)
Design Document ✅
- Current state analysis (MIR + JoinIR)
- Proposed architecture (Option A)
- Integration points (MirBuilder changes)
- Migration path (Phases 74-77)
- Example scenarios
Proof-of-Concept ✅
- BindingId type + structures
- Parallel lookup (BindingId + name)
- Shadowing simulation test
- Promoted variable resolution test
- All tests passing (6/6)
No Production Impact ✅
- Feature-gated (
normalized_dev) - No production code changes
- Existing tests unaffected
Documentation ✅
- SSOT design document
- Completion summary (this document)
- PoC code comments
References
Related Phases
- Phase 68-69: MIR lexical scope + shadowing (existing)
- Phase 63: Ownership analysis (dev-only, uses BindingId)
- Phase 231: ScopeManager trait (current implementation)
- Phase 224: Promoted LoopBodyLocal (naming convention hacks)
Key Files
docs/development/current/main/phase73-scope-manager-design.md(SSOT)src/mir/join_ir/lowering/scope_manager_bindingid_poc/mod.rs(PoC)src/mir/builder/vars/lexical_scope.rs(MIR lexical scope)src/mir/join_ir/lowering/scope_manager.rs(current ScopeManager)src/mir/join_ir/lowering/carrier_info.rs(current CarrierInfo)
Estimated Total Effort (Phases 74-77)
| Phase | Task | Hours |
|---|---|---|
| 74 | Infrastructure | 2-3 |
| 75 | Pattern 1 Pilot | 1-2 |
| 76 | Pattern 2 Promotion | 2-3 |
| 77 | Pattern 3-4 + Cleanup | 2-3 |
| Total | Full Migration | 8-12 |
Risk Level: Low (gradual migration, backward compatible)
Next Steps
- User Review: Confirm design makes sense
- Phase 74 Start: Implement BindingId infrastructure
- Iterative Migration: Phases 75-77 (one pattern at a time)
Conclusion
Phase 73 Success: ✅ Design + PoC Complete
Key Achievements:
- Comprehensive design document (SSOT for BindingId migration)
- Working proof-of-concept (6 tests passing)
- Clear migration path (Phases 74-77 roadmap)
- No production code impact (feature-gated)
Ready for Phase 74: Infrastructure implementation can begin immediately.