Phase 77 design finalizes BindingId infrastructure migration with comprehensive implementation roadmap. Three design documents outline expansion of promoted_bindings tracking to Pattern3/4 and legacy code deprecation strategy. Documents: - phase77-expansion-completion.md: Architecture overview + 5 implementation tasks - PHASE_77_IMPLEMENTATION_GUIDE.md: Step-by-step code changes with exact locations - PHASE_77_EXECUTIVE_SUMMARY.md: Impact analysis and success metrics Key decisions: - DigitPosPromoter/TrimLoopHelper populate promoted_bindings - Pattern3/4 use BindingId priority in variable resolution - 3-phase deletion strategy (Deprecate→Require→Delete) - Dev-only feature gate preserves zero production impact Phase 77 implementation: ~2-3 hours (5 tasks: promoters + patterns + legacy + tests) 🤖 Generated with Claude Code Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
20 KiB
Phase 77: Implementation Guide - Detailed Code Changes
Purpose: Step-by-step implementation guide for Phase 77 BindingId migration Estimated Time: 2-3 hours Prerequisite: Phase 76 complete (promoted_bindings infrastructure exists)
Overview
This guide provides exact code changes for Phase 77 implementation. Each task includes:
- File location
- Current code context
- Exact changes to make
- Testing instructions
Task 1: DigitPosPromoter Integration (45 min)
Goal
Populate promoted_bindings when DigitPos promotion succeeds
Files to Modify
src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs(promoter itself)src/mir/loop_pattern_detection/loop_body_cond_promoter.rs(call site)- Where
ConditionPromotionRequestis constructed (find call sites)
Change 1.1: Add binding_map to DigitPosPromotionRequest
File: src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs
Location: Around line 42-63 (struct definition)
Current Code:
/// Promotion request for A-4 digit position pattern
pub struct DigitPosPromotionRequest<'a> {
/// Loop parameter name (e.g., "p")
#[allow(dead_code)]
pub loop_param_name: &'a str,
/// Condition scope analysis result
pub cond_scope: &'a LoopConditionScope,
/// Loop structure metadata (for future use)
#[allow(dead_code)]
pub(crate) scope_shape: Option<&'a LoopScopeShape>,
/// Break condition AST (Pattern2: Some, Pattern4: None)
pub break_cond: Option<&'a ASTNode>,
/// Continue condition AST (Pattern4: Some, Pattern2: None)
pub continue_cond: Option<&'a ASTNode>,
/// Loop body statements
pub loop_body: &'a [ASTNode],
}
Add After loop_body field:
/// Loop body statements
pub loop_body: &'a [ASTNode],
/// Phase 77: Optional BindingId map for type-safe promotion tracking (dev-only)
///
/// When provided, the promoter will record promoted bindings
/// (e.g., BindingId(5) for "digit_pos" → BindingId(10) for "is_digit_pos")
/// in CarrierInfo.promoted_bindings.
#[cfg(feature = "normalized_dev")]
pub binding_map: Option<&'a std::collections::BTreeMap<crate::mir::BindingId, String>>,
}
Note: We need BindingId → String map (reverse of MirBuilder.binding_map) OR pass both maps. Let's check how to best approach this...
CORRECTION: Actually, MirBuilder.binding_map is BTreeMap<String, BindingId>, so we need:
#[cfg(feature = "normalized_dev")]
pub binding_map: Option<&'a std::collections::BTreeMap<String, crate::mir::BindingId>>,
Change 1.2: Record promoted binding in try_promote()
File: src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs
Location: Around line 225-244 (inside try_promote(), after successful promotion)
Current Code:
// Phase 229: Record promoted variable (no need for condition_aliases)
// Dynamic resolution uses promoted_loopbodylocals + naming convention
carrier_info
.promoted_loopbodylocals
.push(var_in_cond.clone());
eprintln!(
"[digitpos_promoter] Phase 247-EX: A-4 DigitPos pattern promoted: {} → {} (bool) + {} (i64)",
var_in_cond, bool_carrier_name, int_carrier_name
);
eprintln!(
"[digitpos_promoter] Phase 229: Recorded promoted variable '{}' (carriers: '{}', '{}')",
var_in_cond, bool_carrier_name, int_carrier_name
);
return DigitPosPromotionResult::Promoted {
carrier_info,
promoted_var: var_in_cond,
carrier_name: bool_carrier_name, // Return bool carrier name for compatibility
};
Add After promoted_loopbodylocals.push() and Before the eprintln! statements:
// Phase 229: Record promoted variable (no need for condition_aliases)
// Dynamic resolution uses promoted_loopbodylocals + naming convention
carrier_info
.promoted_loopbodylocals
.push(var_in_cond.clone());
// Phase 77: Type-safe BindingId promotion tracking
#[cfg(feature = "normalized_dev")]
if let Some(binding_map) = req.binding_map {
// Look up original and promoted BindingIds
let original_binding_opt = binding_map.get(var_in_cond);
let promoted_bool_binding_opt = binding_map.get(&bool_carrier_name);
match (original_binding_opt, promoted_bool_binding_opt) {
(Some(&original_bid), Some(&promoted_bid)) => {
carrier_info.record_promoted_binding(original_bid, promoted_bid);
eprintln!(
"[digitpos_promoter/phase77] Recorded promoted binding: {} (BindingId({:?})) → {} (BindingId({:?}))",
var_in_cond, original_bid, bool_carrier_name, promoted_bid
);
}
(None, _) => {
eprintln!(
"[digitpos_promoter/phase77] WARNING: Original variable '{}' not found in binding_map",
var_in_cond
);
}
(_, None) => {
eprintln!(
"[digitpos_promoter/phase77] WARNING: Promoted carrier '{}' not found in binding_map",
bool_carrier_name
);
}
}
} else {
eprintln!("[digitpos_promoter/phase77] INFO: binding_map not provided (legacy mode)");
}
eprintln!(
"[digitpos_promoter] Phase 247-EX: A-4 DigitPos pattern promoted: {} → {} (bool) + {} (i64)",
var_in_cond, bool_carrier_name, int_carrier_name
);
// ... rest of code ...
Change 1.3: Update call site in loop_body_cond_promoter.rs
File: src/mir/loop_pattern_detection/loop_body_cond_promoter.rs
Location: Around line 203-210 (where DigitPosPromotionRequest is constructed)
Current Code:
// Step 2: Try A-4 DigitPos promotion
let digitpos_request = DigitPosPromotionRequest {
loop_param_name: req.loop_param_name,
cond_scope: req.cond_scope,
scope_shape: req.scope_shape,
break_cond: req.break_cond,
continue_cond: req.continue_cond,
loop_body: req.loop_body,
};
Change To:
// Step 2: Try A-4 DigitPos promotion
let digitpos_request = DigitPosPromotionRequest {
loop_param_name: req.loop_param_name,
cond_scope: req.cond_scope,
scope_shape: req.scope_shape,
break_cond: req.break_cond,
continue_cond: req.continue_cond,
loop_body: req.loop_body,
#[cfg(feature = "normalized_dev")]
binding_map: req.binding_map,
};
PREREQUISITE: ConditionPromotionRequest must also have a binding_map field.
Change 1.4: Add binding_map to ConditionPromotionRequest
File: src/mir/loop_pattern_detection/loop_body_cond_promoter.rs
Location: Around line 37-66 (struct definition)
Find:
pub struct ConditionPromotionRequest<'a> {
// ... existing fields ...
}
Add Field:
pub struct ConditionPromotionRequest<'a> {
// ... existing fields ...
/// Phase 77: Optional BindingId map for type-safe promotion tracking (dev-only)
#[cfg(feature = "normalized_dev")]
pub binding_map: Option<&'a std::collections::BTreeMap<String, crate::mir::BindingId>>,
}
Change 1.5: Find and update all ConditionPromotionRequest construction sites
Action: Search for where ConditionPromotionRequest is instantiated
Command:
cd /home/tomoaki/git/hakorune-selfhost
grep -rn "ConditionPromotionRequest {" src/mir/
Expected Locations:
- Pattern2 lowering
- Pattern4 lowering
- Other promotion contexts
For Each Location: Add binding_map field (dev-only):
let req = ConditionPromotionRequest {
// ... existing fields ...
#[cfg(feature = "normalized_dev")]
binding_map: Some(&builder.binding_map), // or None if builder not available
};
Task 2: TrimLoopHelper Integration (30 min)
Goal
Populate promoted_bindings when Trim promotion succeeds
Files to Modify
src/mir/loop_pattern_detection/trim_loop_helper.rs- Call sites constructing
TrimPromotionRequest
Change 2.1: Add binding_map to TrimPromotionRequest
File: src/mir/loop_pattern_detection/trim_loop_helper.rs
Find: TrimPromotionRequest struct definition
Add Field:
/// Phase 77: Optional BindingId map for type-safe promotion tracking (dev-only)
#[cfg(feature = "normalized_dev")]
pub binding_map: Option<&'a std::collections::BTreeMap<String, crate::mir::BindingId>>,
Change 2.2: Record promoted binding in try_promote()
File: src/mir/loop_pattern_detection/trim_loop_helper.rs
Location: Inside try_promote(), after successful carrier creation
Pattern: Similar to DigitPos, add after carrier creation:
// Phase 77: Type-safe BindingId promotion tracking
#[cfg(feature = "normalized_dev")]
if let Some(binding_map) = req.binding_map {
let original_binding_opt = binding_map.get(&trim_info.var_name);
let promoted_binding_opt = binding_map.get(&trim_info.carrier_name);
match (original_binding_opt, promoted_binding_opt) {
(Some(&original_bid), Some(&promoted_bid)) => {
carrier_info.record_promoted_binding(original_bid, promoted_bid);
eprintln!(
"[trim_lowerer/phase77] Recorded promoted binding: {} (BindingId({:?})) → {} (BindingId({:?}))",
trim_info.var_name, original_bid, trim_info.carrier_name, promoted_bid
);
}
(None, _) => {
eprintln!(
"[trim_lowerer/phase77] WARNING: Original variable '{}' not found in binding_map",
trim_info.var_name
);
}
(_, None) => {
eprintln!(
"[trim_lowerer/phase77] WARNING: Promoted carrier '{}' not found in binding_map",
trim_info.carrier_name
);
}
}
}
Change 2.3: Update TrimPromotionRequest call sites
Action: Find all TrimPromotionRequest instantiations
Command:
grep -rn "TrimPromotionRequest {" src/mir/
For Each Location: Add binding_map field:
let trim_request = TrimPromotionRequest {
// ... existing fields ...
#[cfg(feature = "normalized_dev")]
binding_map: req.binding_map, // propagate from parent request
};
Task 3: Pattern3/4 BindingId Lookup (45 min)
Goal
Enable Pattern3/4 to use BindingId priority lookup (dev-only)
Approach
- Add dev-only variants of condition lowering functions
- Pass
binding_mapto ConditionEnv construction - Use Phase 75
resolve_var_with_binding()API
Change 3.1: Pattern3 BindingId Lookup Variant (dev-only)
File: src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs
Location: Where ConditionEnv is constructed for P3
Current Pattern (approximate):
// Build ConditionEnv with loop params and carriers
let condition_env = ConditionEnv::new();
condition_env.insert("i", ...);
// ... etc ...
Add Dev-Only Variant:
#[cfg(feature = "normalized_dev")]
fn build_condition_env_with_bindings(
&mut self,
binding_map: &std::collections::BTreeMap<String, crate::mir::BindingId>,
// ... other params ...
) -> ConditionEnv {
let mut env = ConditionEnv::new();
// For each variable, insert with BindingId context
// (Phase 75 infrastructure enables this)
env
}
Note: Actual implementation depends on existing P3 code structure. Key idea: propagate binding_map to enable BindingId-aware resolution.
Change 3.2: Pattern4 Similar Changes
File: src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs
Similar Approach: Add dev-only BindingId-aware ConditionEnv construction
Task 4: Legacy Code Deprecation (15 min)
Goal
Mark legacy name-based code as deprecated, add warnings
Change 4.1: Deprecate resolve_promoted_join_id
File: src/mir/join_ir/lowering/carrier_info.rs
Location: Around line 490 (function definition)
Current:
pub fn resolve_promoted_join_id(&self, original_name: &str) -> Option<ValueId> {
Change To:
/// Phase 77: DEPRECATED - Use resolve_promoted_with_binding() for type-safe lookup
///
/// This method uses fragile naming conventions ("is_*", "is_*_match") and will
/// be removed in Phase 78+ when all call sites migrate to BindingId-based lookup.
#[cfg(feature = "normalized_dev")]
#[deprecated(
since = "phase77",
note = "Use resolve_promoted_with_binding() for type-safe BindingId lookup"
)]
pub fn resolve_promoted_join_id(&self, original_name: &str) -> Option<ValueId> {
eprintln!(
"[phase77/legacy/carrier_info] WARNING: Using deprecated name-based promoted lookup for '{}'",
original_name
);
Add At Function Start (after deprecation warning):
eprintln!(
"[phase77/legacy/carrier_info] WARNING: Using deprecated name-based promoted lookup for '{}'",
original_name
);
Change 4.2: Add Fallback Warning in Pattern2ScopeManager
File: src/mir/join_ir/lowering/scope_manager.rs
Location: Around line 155-160 (in lookup_with_binding())
Current:
// Step 3: Legacy name-based fallback
self.lookup(name)
Change To:
// Step 3: Legacy name-based fallback
#[cfg(feature = "normalized_dev")]
if binding_id.is_some() {
eprintln!(
"[phase76/fallback] BindingId({:?}) for '{}' not resolved, falling back to name-based lookup",
binding_id, name
);
}
self.lookup(name)
Task 5: E2E Verification Tests (30 min)
Goal
Add 4 end-to-end tests verifying BindingId promotion flow
Test File Creation
Create New File (or add to existing):
tests/phase77_binding_promotion.rs
Content:
//! Phase 77: End-to-End BindingId Promotion Tests
//!
//! Verifies that promoted_bindings are populated and used correctly
//! across DigitPos, Trim, Pattern3, and Pattern4.
#![cfg(feature = "normalized_dev")]
use nyash_rust::mir::BindingId;
use std::collections::BTreeMap;
/// Helper: Create a test binding_map
fn test_binding_map() -> BTreeMap<String, BindingId> {
let mut map = BTreeMap::new();
map.insert("digit_pos".to_string(), BindingId(5));
map.insert("is_digit_pos".to_string(), BindingId(10));
map.insert("ch".to_string(), BindingId(6));
map.insert("is_ch_match".to_string(), BindingId(11));
map
}
#[test]
fn test_phase77_digitpos_end_to_end_binding_resolution() {
// TODO: Implement actual fixture-based test
// 1. Create AST with digit_pos pattern
// 2. Run through DigitPosPromoter with binding_map
// 3. Verify promoted_bindings contains (5 → 10)
// 4. Verify ScopeManager resolves via BindingId
// Placeholder assertion
let binding_map = test_binding_map();
assert_eq!(binding_map.get("digit_pos"), Some(&BindingId(5)));
assert_eq!(binding_map.get("is_digit_pos"), Some(&BindingId(10)));
}
#[test]
fn test_phase77_trim_end_to_end_binding_resolution() {
// TODO: Similar to digitpos test, but for trim pattern
let binding_map = test_binding_map();
assert_eq!(binding_map.get("ch"), Some(&BindingId(6)));
assert_eq!(binding_map.get("is_ch_match"), Some(&BindingId(11)));
}
#[test]
fn test_phase77_pattern3_binding_lookup() {
// TODO: Test Pattern3 if-sum with BindingId lookup
// Verify ConditionEnv.resolve_var_with_binding() is used
}
#[test]
fn test_phase77_pattern4_binding_lookup() {
// TODO: Test Pattern4 continue with BindingId lookup
}
Note: Full test implementation requires actual fixtures. The above provides a skeleton.
Task 6: Documentation Updates (15 min)
Goal
Update CURRENT_TASK.md and create Phase 77 summary
Change 6.1: Update CURRENT_TASK.md
File: CURRENT_TASK.md
Add After Phase 75/76 entries:
36. **Phase 77-EXPANSION(完了✅ 2025-12-13)**: BindingId migration expansion (dev-only)
- DigitPosPromoter/TrimLoopHelper populate promoted_bindings with BindingId mappings
- Pattern3/4 extended with BindingId priority lookup (dev-only variants)
- Legacy name-based code deprecated (deletion deferred to Phase 78+)
- 4 E2E verification tests added
- 詳細: [phase77-expansion-completion.md](docs/development/current/main/phase77-expansion-completion.md)
Change 6.2: Create Phase 77 Summary
File: docs/development/current/main/PHASE_77_SUMMARY.md
Content (brief version):
# Phase 77: Expansion - Summary
**Status**: COMPLETE ✅
**Date**: 2025-12-13
**Duration**: ~3 hours
## What Changed
1. ✅ DigitPosPromoter populates promoted_bindings
2. ✅ TrimLoopHelper populates promoted_bindings
3. ✅ Pattern3/4 use BindingId lookup (dev-only)
4. ✅ Legacy code deprecated (~40 lines marked)
5. ✅ 4 E2E tests added
## Test Results
- `cargo test --release --lib`: 958/958 PASS ✅
- Phase 77 tests: 4/4 PASS ✅
- No regressions
## Migration Status
- Phase 74: Infrastructure ✅
- Phase 75: Pilot ✅
- Phase 76: Promotion Infra ✅
- **Phase 77: Expansion ✅** (THIS PHASE)
- Phase 78+: Legacy Deletion (pending)
## Next Steps
Phase 78+ will:
- Remove deprecated legacy code (~40 lines)
- Make BindingId required in production paths
- Delete `promoted_loopbodylocals` field
Testing Checklist
Before Implementation
cargo build --libsucceeds (baseline)cargo test --release --lib958/958 PASS (baseline)
After Each Task
- Task 1: DigitPos tests pass
- Task 2: Trim tests pass
- Task 3: Pattern3/4 tests pass
- Task 4: Deprecation warnings appear in logs
- Task 5: New E2E tests pass
Final Verification
cargo build --lib --features normalized_devsucceedscargo test --release --lib958/958 PASS (no regressions)cargo test --release --lib --features normalized_devincludes new tests- Deprecation warnings logged (verify with
JOINIR_TEST_DEBUG=1)
Troubleshooting
Issue: binding_map not available in call chain
Symptom: Compiler error about missing field
Solution:
- Check if
MirBuilderis accessible in context - If not, add
binding_mapparameter through call chain - Use
Option<&BTreeMap<...>>for optional threading
Issue: BindingId not found in binding_map
Symptom: Warning logs show "not found in binding_map"
Diagnosis:
- Promoted carrier name might be generated dynamically
- Check if carrier is created after binding_map lookup
- May need to defer recording until carrier is added to binding_map
Solution:
- Option A: Update MirBuilder.binding_map when creating promoted carriers
- Option B: Record promotion later in lowering pipeline
Issue: Tests fail with "method not found"
Symptom: resolve_var_with_binding not found
Solution: Check Phase 75 implementation is complete:
ConditionEnvhasresolve_var_with_binding()methodScopeManagertrait haslookup_with_binding()method
Time Estimates
- Task 1 (DigitPos): 45 min
- Task 2 (Trim): 30 min
- Task 3 (P3/P4): 45 min
- Task 4 (Deprecation): 15 min
- Task 5 (Tests): 30 min
- Task 6 (Docs): 15 min
Total: 3 hours (with buffer for debugging)
Success Criteria
When Phase 77 is complete:
✅ All promoters populate promoted_bindings ✅ Pattern3/4 can use BindingId lookup ✅ Legacy code deprecated (not deleted) ✅ 958/958 tests still PASS ✅ 4 new E2E tests PASS ✅ Documentation complete
Next Phase: Phase 78 - Delete deprecated code (~40 lines)