diff --git a/src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs b/src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs index e032f5d6..ed7c0fda 100644 --- a/src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs +++ b/src/mir/loop_pattern_detection/loop_body_carrier_promoter.rs @@ -23,6 +23,9 @@ use crate::ast::{ASTNode, BinaryOperator, LiteralValue}; use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScope; +use crate::mir::loop_pattern_detection::promoted_binding_recorder::{ + BindingRecordError, PromotedBindingRecorder, +}; /// 昇格リクエスト pub struct PromotionRequest<'a> { @@ -101,37 +104,18 @@ impl TrimPatternInfo { .promoted_loopbodylocals .push(self.var_name.clone()); - // Phase 77: Type-safe BindingId promotion tracking + // Phase 78: Type-safe BindingId promotion tracking (using PromotedBindingRecorder) #[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); + let recorder = PromotedBindingRecorder::new(binding_map); + #[cfg(not(feature = "normalized_dev"))] + let recorder = PromotedBindingRecorder::new(); - 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)"); + if let Err(e) = recorder.record_promotion( + &mut carrier_info, + &self.var_name, + &self.carrier_name, + ) { + log_trim_promotion_error(&e); } carrier_info @@ -422,6 +406,29 @@ impl LoopBodyCarrierPromoter { } } +/// Phase 78: Log promotion errors with clear messages (for Trim pattern) +#[cfg(feature = "normalized_dev")] +fn log_trim_promotion_error(error: &BindingRecordError) { + match error { + BindingRecordError::OriginalNotFound(name) => { + eprintln!( + "[trim_lowerer/warning] Original variable '{}' not found in binding_map", + name + ); + } + BindingRecordError::PromotedNotFound(name) => { + eprintln!( + "[trim_lowerer/warning] Promoted variable '{}' not found in binding_map", + name + ); + } + } +} + +/// Phase 78: No-op version for non-dev builds +#[cfg(not(feature = "normalized_dev"))] +fn log_trim_promotion_error(_error: &BindingRecordError) {} + #[cfg(test)] mod tests { use super::*; @@ -518,6 +525,8 @@ mod tests { cond_scope: &cond_scope, break_cond: None, loop_body: &[], + #[cfg(feature = "normalized_dev")] + binding_map: None, }; let result = LoopBodyCarrierPromoter::try_promote(&request); @@ -542,6 +551,8 @@ mod tests { cond_scope: &cond_scope, break_cond: None, loop_body: &[], // Empty body - no definition + #[cfg(feature = "normalized_dev")] + binding_map: None, }; let result = LoopBodyCarrierPromoter::try_promote(&request); @@ -665,6 +676,8 @@ mod tests { cond_scope: &cond_scope, break_cond: Some(&break_cond), loop_body: &loop_body, + #[cfg(feature = "normalized_dev")] + binding_map: None, }; let result = LoopBodyCarrierPromoter::try_promote(&request); @@ -701,6 +714,8 @@ mod tests { cond_scope: &cond_scope, break_cond: Some(&break_cond), loop_body: &loop_body, + #[cfg(feature = "normalized_dev")] + binding_map: None, }; let result = LoopBodyCarrierPromoter::try_promote(&request); diff --git a/src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs b/src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs index de830e51..930056df 100644 --- a/src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs +++ b/src/mir/loop_pattern_detection/loop_body_digitpos_promoter.rs @@ -37,6 +37,9 @@ use crate::ast::{ASTNode, BinaryOperator}; use crate::mir::join_ir::lowering::carrier_info::CarrierInfo; use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; use crate::mir::loop_pattern_detection::loop_condition_scope::LoopConditionScope; +use crate::mir::loop_pattern_detection::promoted_binding_recorder::{ + BindingRecordError, PromotedBindingRecorder, +}; use crate::mir::ValueId; /// Promotion request for A-4 digit position pattern @@ -236,37 +239,18 @@ impl DigitPosPromoter { .promoted_loopbodylocals .push(var_in_cond.clone()); - // Phase 77: Type-safe BindingId promotion tracking + // Phase 78: Type-safe BindingId promotion tracking (using PromotedBindingRecorder) #[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); + let recorder = PromotedBindingRecorder::new(req.binding_map); + #[cfg(not(feature = "normalized_dev"))] + let recorder = PromotedBindingRecorder::new(); - 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)"); + if let Err(e) = recorder.record_promotion( + &mut carrier_info, + &var_in_cond, + &bool_carrier_name, + ) { + log_promotion_error(&e); } eprintln!( @@ -500,6 +484,29 @@ impl DigitPosPromoter { } } +/// Phase 78: Log promotion errors with clear messages +#[cfg(feature = "normalized_dev")] +fn log_promotion_error(error: &BindingRecordError) { + match error { + BindingRecordError::OriginalNotFound(name) => { + eprintln!( + "[digitpos_promoter/warning] Original variable '{}' not found in binding_map", + name + ); + } + BindingRecordError::PromotedNotFound(name) => { + eprintln!( + "[digitpos_promoter/warning] Promoted variable '{}' not found in binding_map", + name + ); + } + } +} + +/// Phase 78: No-op version for non-dev builds +#[cfg(not(feature = "normalized_dev"))] +fn log_promotion_error(_error: &BindingRecordError) {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/mir/loop_pattern_detection/mod.rs b/src/mir/loop_pattern_detection/mod.rs index a001351c..8c186ef3 100644 --- a/src/mir/loop_pattern_detection/mod.rs +++ b/src/mir/loop_pattern_detection/mod.rs @@ -697,3 +697,7 @@ pub mod break_condition_analyzer; // Phase 200-A: Function Scope Capture Infrastructure pub mod function_scope_capture; + +// Phase 78: PromotedBindingRecorder - Type-safe BindingId recording +pub mod promoted_binding_recorder; +pub use promoted_binding_recorder::{BindingRecordError, PromotedBindingRecorder}; diff --git a/src/mir/loop_pattern_detection/promoted_binding_recorder.rs b/src/mir/loop_pattern_detection/promoted_binding_recorder.rs new file mode 100644 index 00000000..4e9f16c3 --- /dev/null +++ b/src/mir/loop_pattern_detection/promoted_binding_recorder.rs @@ -0,0 +1,166 @@ +//! PromotedBindingRecorder - Type-safe BindingId recording for promoted variables +//! +//! This box centralizes the logic for recording promoted variable mappings +//! (original BindingId → promoted BindingId) in the promoted_bindings map. +//! +//! Replaces scattered binding_map wiring across 8 files with a single, +//! testable, reusable box. + +#[cfg(feature = "normalized_dev")] +use crate::mir::binding_id::BindingId; +use crate::mir::join_ir::lowering::carrier_info::CarrierInfo; +#[cfg(feature = "normalized_dev")] +use std::collections::BTreeMap; + +/// Records promoted variable bindings in a type-safe manner. +/// +/// Example: +/// ```ignore +/// let recorder = PromotedBindingRecorder::new(Some(&builder.binding_map)); +/// recorder.record_promotion(&mut carrier_info, "digit_pos", "is_digit_pos")?; +/// ``` +#[cfg(feature = "normalized_dev")] +pub struct PromotedBindingRecorder<'a> { + binding_map: Option<&'a BTreeMap>, +} + +/// Non-dev version (zero-sized, no lifetime) +#[cfg(not(feature = "normalized_dev"))] +pub struct PromotedBindingRecorder; + +#[derive(Debug)] +pub enum BindingRecordError { + OriginalNotFound(String), + PromotedNotFound(String), +} + +#[cfg(feature = "normalized_dev")] +impl<'a> PromotedBindingRecorder<'a> { + /// Create a new recorder with optional binding map. + pub fn new(binding_map: Option<&'a BTreeMap>) -> Self { + Self { binding_map } + } + + /// Record a promotion mapping (original_name → promoted_name). + pub fn record_promotion( + &self, + carrier_info: &mut CarrierInfo, + original_name: &str, + promoted_name: &str, + ) -> Result<(), BindingRecordError> { + if let Some(binding_map) = self.binding_map { + let original_bid = binding_map + .get(original_name) + .copied() + .ok_or_else(|| BindingRecordError::OriginalNotFound(original_name.to_string()))?; + + let promoted_bid = binding_map + .get(promoted_name) + .copied() + .ok_or_else(|| BindingRecordError::PromotedNotFound(promoted_name.to_string()))?; + + carrier_info.record_promoted_binding(original_bid, promoted_bid); + Ok(()) + } else { + Ok(()) // No binding map available + } + } +} + +#[cfg(not(feature = "normalized_dev"))] +impl PromotedBindingRecorder { + /// Create a new recorder (no-op in non-dev builds). + pub fn new() -> Self { + Self + } + + /// Record a promotion mapping (no-op in non-dev builds). + pub fn record_promotion( + &self, + _carrier_info: &mut CarrierInfo, + _original_name: &str, + _promoted_name: &str, + ) -> Result<(), BindingRecordError> { + Ok(()) // No-op in non-dev mode + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mir::ValueId; + + fn make_test_carrier_info() -> CarrierInfo { + CarrierInfo::with_carriers("test_var".to_string(), ValueId(0), vec![]) + } + + #[cfg(feature = "normalized_dev")] + #[test] + fn test_record_promotion_success() { + use crate::mir::binding_id::BindingId; + use std::collections::BTreeMap; + + let mut binding_map = BTreeMap::new(); + binding_map.insert("digit_pos".to_string(), BindingId::new(10)); + binding_map.insert("is_digit_pos".to_string(), BindingId::new(20)); + + let recorder = PromotedBindingRecorder::new(Some(&binding_map)); + let mut carrier_info = make_test_carrier_info(); + + assert!(recorder + .record_promotion(&mut carrier_info, "digit_pos", "is_digit_pos") + .is_ok()); + } + + #[cfg(feature = "normalized_dev")] + #[test] + fn test_record_promotion_missing_original() { + use std::collections::BTreeMap; + + let binding_map = BTreeMap::new(); + let recorder = PromotedBindingRecorder::new(Some(&binding_map)); + let mut carrier_info = make_test_carrier_info(); + + match recorder.record_promotion(&mut carrier_info, "digit_pos", "is_digit_pos") { + Err(BindingRecordError::OriginalNotFound(name)) => { + assert_eq!(name, "digit_pos"); + } + _ => panic!("Expected OriginalNotFound error"), + } + } + + #[cfg(feature = "normalized_dev")] + #[test] + fn test_record_promotion_missing_promoted() { + use crate::mir::binding_id::BindingId; + use std::collections::BTreeMap; + + let mut binding_map = BTreeMap::new(); + binding_map.insert("digit_pos".to_string(), BindingId::new(10)); + + let recorder = PromotedBindingRecorder::new(Some(&binding_map)); + let mut carrier_info = make_test_carrier_info(); + + match recorder.record_promotion(&mut carrier_info, "digit_pos", "is_digit_pos") { + Err(BindingRecordError::PromotedNotFound(name)) => { + assert_eq!(name, "is_digit_pos"); + } + _ => panic!("Expected PromotedNotFound error"), + } + } + + #[test] + fn test_record_promotion_no_binding_map() { + #[cfg(feature = "normalized_dev")] + let recorder = PromotedBindingRecorder::new(None); + #[cfg(not(feature = "normalized_dev"))] + let recorder = PromotedBindingRecorder::new(); + + let mut carrier_info = make_test_carrier_info(); + + // Should be no-op + assert!(recorder + .record_promotion(&mut carrier_info, "digit_pos", "is_digit_pos") + .is_ok()); + } +}