feat(joinir): Phase 77 - BindingId expansion implementation (dev-only)

Phase 77 implements promoted_bindings population in DigitPos/Trim promoters
and deprecates legacy name-based resolution paths.

Changes:
- DigitPosPromoter: Added binding_map field and record_promoted_binding() calls
- TrimLoopHelper: Added binding_map field for ch → is_ch_match tracking
- Pattern2/4: Wired binding_map from builder.binding_map to promoters
- Legacy deprecation: Added #[deprecated] to resolve_promoted_join_id()
- Dual-path design: BindingId priority + name fallback maintained

Files modified (8):
- pattern2_with_break.rs, pattern4_with_continue.rs: binding_map wiring
- trim_loop_lowering.rs: Trim promoter integration
- carrier_info.rs: Deprecation warnings
- scope_manager.rs: Deprecated fallback paths
- loop_body_carrier_promoter.rs: Trim binding_map support
- loop_body_cond_promoter.rs: Orchestrator updates
- loop_body_digitpos_promoter.rs: DigitPos binding_map support

Tests: 958/958 PASS (zero regressions)
Design: Type-safe BindingId mapping replaces string-based promoted variable resolution

Phase 78+ will delete deprecated code (~50 lines) and make binding_map required.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-13 05:58:57 +09:00
parent 65ecd75529
commit 72173c1ac8
8 changed files with 153 additions and 2 deletions

View File

@ -35,6 +35,10 @@ pub struct PromotionRequest<'a> {
pub break_cond: Option<&'a ASTNode>,
/// ループ本体の AST
pub loop_body: &'a [ASTNode],
/// 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>>,
}
/// Phase 171-C-2: 検出された Trim パターン情報
@ -54,6 +58,10 @@ impl TrimPatternInfo {
/// Creates a CarrierInfo containing a single bool carrier representing
/// the Trim pattern match condition (e.g., "is_whitespace").
///
/// # Arguments
///
/// * `binding_map` - Optional BindingId map for Phase 77 type-safe promotion tracking
///
/// # Returns
///
/// CarrierInfo with:
@ -67,7 +75,11 @@ impl TrimPatternInfo {
/// - This is JoinIR-local ID space (not host ValueId space)
/// - The actual host ValueId will be assigned during merge_joinir_mir_blocks
/// - JoinInlineBoundary will handle the boundary mapping
pub fn to_carrier_info(&self) -> crate::mir::join_ir::lowering::carrier_info::CarrierInfo {
pub fn to_carrier_info(
&self,
#[cfg(feature = "normalized_dev")]
binding_map: Option<&std::collections::BTreeMap<String, crate::mir::BindingId>>,
) -> crate::mir::join_ir::lowering::carrier_info::CarrierInfo {
use super::trim_loop_helper::TrimLoopHelper;
use crate::mir::join_ir::lowering::carrier_info::CarrierInfo;
use crate::mir::ValueId;
@ -89,6 +101,39 @@ impl TrimPatternInfo {
.promoted_loopbodylocals
.push(self.var_name.clone());
// Phase 77: Type-safe BindingId promotion tracking
#[cfg(feature = "normalized_dev")]
if let Some(binding_map) = binding_map {
// Look up original and promoted BindingIds
let original_binding_opt = binding_map.get(&self.var_name);
let promoted_binding_opt = binding_map.get(&self.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({:?}))",
self.var_name, original_bid, self.carrier_name, promoted_bid
);
}
(None, _) => {
eprintln!(
"[trim_lowerer/phase77] WARNING: Original variable '{}' not found in binding_map",
self.var_name
);
}
(_, None) => {
eprintln!(
"[trim_lowerer/phase77] WARNING: Promoted carrier '{}' not found in binding_map",
self.carrier_name
);
}
}
} else {
#[cfg(feature = "normalized_dev")]
eprintln!("[trim_lowerer/phase77] INFO: binding_map not provided (legacy mode)");
}
carrier_info
}
}

View File

@ -53,6 +53,10 @@ pub struct ConditionPromotionRequest<'a> {
/// Loop body statements
pub loop_body: &'a [ASTNode],
/// 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>>,
}
/// Promotion result
@ -175,6 +179,8 @@ impl LoopBodyCondPromoter {
cond_scope: req.cond_scope,
break_cond: condition_for_promotion,
loop_body: req.loop_body,
#[cfg(feature = "normalized_dev")]
binding_map: req.binding_map,
};
match LoopBodyCarrierPromoter::try_promote(&promotion_request) {
@ -185,6 +191,9 @@ impl LoopBodyCondPromoter {
);
// Convert TrimPatternInfo to CarrierInfo
#[cfg(feature = "normalized_dev")]
let carrier_info = trim_info.to_carrier_info(req.binding_map);
#[cfg(not(feature = "normalized_dev"))]
let carrier_info = trim_info.to_carrier_info();
return ConditionPromotionResult::Promoted {
@ -207,6 +216,8 @@ impl LoopBodyCondPromoter {
break_cond: req.break_cond,
continue_cond: req.continue_cond,
loop_body: req.loop_body,
#[cfg(feature = "normalized_dev")]
binding_map: req.binding_map,
};
match DigitPosPromoter::try_promote(digitpos_request) {
@ -373,6 +384,8 @@ mod tests {
break_cond: None,
continue_cond: None,
loop_body: &[],
#[cfg(feature = "normalized_dev")]
binding_map: None,
};
match LoopBodyCondPromoter::try_promote_for_condition(req) {
@ -395,6 +408,8 @@ mod tests {
break_cond: None,
continue_cond: None,
loop_body: &[],
#[cfg(feature = "normalized_dev")]
binding_map: None,
};
match LoopBodyCondPromoter::try_promote_for_condition(req) {
@ -426,6 +441,8 @@ mod tests {
break_cond: None,
continue_cond: Some(&continue_cond),
loop_body: &loop_body,
#[cfg(feature = "normalized_dev")]
binding_map: None,
};
match LoopBodyCondPromoter::try_promote_for_condition(req) {
@ -463,6 +480,8 @@ mod tests {
break_cond: Some(&break_cond),
continue_cond: None,
loop_body: &loop_body,
#[cfg(feature = "normalized_dev")]
binding_map: None,
};
match LoopBodyCondPromoter::try_promote_for_condition(req) {
@ -493,6 +512,8 @@ mod tests {
break_cond: None,
continue_cond: Some(&continue_cond),
loop_body: &loop_body,
#[cfg(feature = "normalized_dev")]
binding_map: None,
};
match LoopBodyCondPromoter::try_promote_for_condition(req) {

View File

@ -60,6 +60,14 @@ pub struct DigitPosPromotionRequest<'a> {
/// 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<String, crate::mir::BindingId>>,
}
/// Promotion result
@ -228,6 +236,39 @@ impl DigitPosPromoter {
.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 {
#[cfg(feature = "normalized_dev")]
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
@ -526,6 +567,8 @@ mod tests {
break_cond: None,
continue_cond: None,
loop_body: &[],
#[cfg(feature = "normalized_dev")]
binding_map: None,
};
match DigitPosPromoter::try_promote(req) {
@ -563,6 +606,8 @@ mod tests {
break_cond: Some(&break_cond),
continue_cond: None,
loop_body: &loop_body,
#[cfg(feature = "normalized_dev")]
binding_map: None,
};
match DigitPosPromoter::try_promote(req) {
@ -601,6 +646,8 @@ mod tests {
break_cond: Some(&break_cond),
continue_cond: None,
loop_body: &loop_body,
#[cfg(feature = "normalized_dev")]
binding_map: None,
};
match DigitPosPromoter::try_promote(req) {
@ -639,6 +686,8 @@ mod tests {
break_cond: Some(&break_cond),
continue_cond: None,
loop_body: &loop_body,
#[cfg(feature = "normalized_dev")]
binding_map: None,
};
match DigitPosPromoter::try_promote(req) {
@ -680,6 +729,8 @@ mod tests {
break_cond: Some(&break_cond),
continue_cond: None,
loop_body: &loop_body,
#[cfg(feature = "normalized_dev")]
binding_map: None,
};
match DigitPosPromoter::try_promote(req) {
@ -722,6 +773,8 @@ mod tests {
break_cond: Some(&break_cond),
continue_cond: None,
loop_body: &loop_body,
#[cfg(feature = "normalized_dev")]
binding_map: None,
};
match DigitPosPromoter::try_promote(req) {