feat(joinir): Phase 78 - BindingId infrastructure for promoted carriers (dev-only)

Phase 78 adds infrastructure to assign BindingIds to synthetic promoted
carriers (e.g., is_digit_pos, is_ch_match), enabling type-safe promoted
variable lookup without string-based naming conventions.

Key Changes:
1. CarrierVar.binding_id field (dev-only):
   - Added Option<BindingId> to track BindingId for each carrier
   - Updated all constructors and struct instantiations

2. CarrierBindingAssigner Box (new file, 273 lines):
   - Allocates BindingIds for promoted carriers via builder.allocate_binding_id()
   - Records original → promoted mapping in promoted_bindings
   - Sets binding_id field on promoted CarrierVar
   - Includes 3 comprehensive unit tests

3. ConditionEnv.register_carrier_binding() (new method):
   - Registers carrier BindingId → ValueId mappings
   - Enables type-safe lookup via binding_id_map

4. Logging cleanup:
   - Gated 6 eprintln! statements with NYASH_JOINIR_DEBUG
   - Unified logging tags to [binding_pilot/*]

Design Decisions:
- Promoters create CarrierInfo, lowering code assigns BindingIds
- CarrierBindingAssigner called from Pattern2/4 lowering (has builder access)
- Clear documentation prevents misuse (promoters lack builder access)

Files modified (18):
- carrier_info.rs: binding_id field added to CarrierVar
- carrier_binding_assigner.rs: New Box for BindingId allocation
- condition_env.rs: register_carrier_binding() method
- mod.rs: Module exports
- pattern2_with_break.rs, pattern4_with_continue.rs: Updated for binding_id
- loop_body_*_promoter.rs: Logging cleanup + binding_id in structs
- phase78-bindingid-promoted-carriers.md: Architecture documentation

Tests: 970/970 PASS (zero regressions)
Status: Infrastructure complete, integration deferred to Phase 79

Next Phase: Wire CarrierBindingAssigner in Pattern2/4 lowering + E2E tests

🤖 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 16:20:33 +09:00
parent 48bdf2fb98
commit 8b48bec962
21 changed files with 815 additions and 107 deletions

View File

@ -173,11 +173,13 @@ impl LoopBodyCarrierPromoter {
};
}
eprintln!(
"[promoter/pattern5] Phase 171-C: Found {} LoopBodyLocal variables: {:?}",
body_locals.len(),
body_locals
);
if std::env::var("NYASH_JOINIR_DEBUG").is_ok() || std::env::var("JOINIR_TEST_DEBUG").is_ok() {
eprintln!(
"[promoter/pattern5] Phase 171-C: Found {} LoopBodyLocal variables: {:?}",
body_locals.len(),
body_locals
);
}
// 2. break 条件を取得
if request.break_cond.is_none() {
@ -194,10 +196,12 @@ impl LoopBodyCarrierPromoter {
// Phase 79: Use TrimDetector for pure detection logic
if let Some(detection) = TrimDetector::detect(break_cond, request.loop_body, var_name)
{
eprintln!(
"[promoter/pattern5] Trim pattern detected! var='{}', literals={:?}",
detection.match_var, detection.comparison_literals
);
if std::env::var("NYASH_JOINIR_DEBUG").is_ok() || std::env::var("JOINIR_TEST_DEBUG").is_ok() {
eprintln!(
"[promoter/pattern5] Trim pattern detected! var='{}', literals={:?}",
detection.match_var, detection.comparison_literals
);
}
// 昇格成功!
let trim_info = TrimPatternInfo {
@ -224,21 +228,23 @@ impl LoopBodyCarrierPromoter {
// - extract_equality_literals
}
/// Phase 78: Log promotion errors with clear messages (for Trim pattern)
/// Phase 78: Log promotion errors with clear messages (for Trim pattern, gated)
#[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
);
if std::env::var("NYASH_JOINIR_DEBUG").is_ok() || std::env::var("JOINIR_TEST_DEBUG").is_ok() {
match error {
BindingRecordError::OriginalNotFound(name) => {
eprintln!(
"[binding_pilot/trim] Original variable '{}' not found in binding_map",
name
);
}
BindingRecordError::PromotedNotFound(name) => {
eprintln!(
"[binding_pilot/trim] Promoted variable '{}' not found in binding_map",
name
);
}
}
}
}

View File

@ -129,11 +129,13 @@ impl DigitPosPromoter {
};
}
eprintln!(
"[digitpos_promoter] Phase 224: Found {} LoopBodyLocal variables: {:?}",
body_locals.len(),
body_locals
);
if std::env::var("NYASH_JOINIR_DEBUG").is_ok() || std::env::var("JOINIR_TEST_DEBUG").is_ok() {
eprintln!(
"[digitpos_promoter] Phase 224: Found {} LoopBodyLocal variables: {:?}",
body_locals.len(),
body_locals
);
}
// Step 2: Get condition AST
let condition = req.break_cond.or(req.continue_cond);
@ -160,10 +162,12 @@ impl DigitPosPromoter {
}
let detection = detection.unwrap();
eprintln!(
"[digitpos_promoter] Pattern detected: {}{} (bool) + {} (int)",
detection.var_name, detection.bool_carrier_name, detection.int_carrier_name
);
if std::env::var("NYASH_JOINIR_DEBUG").is_ok() || std::env::var("JOINIR_TEST_DEBUG").is_ok() {
eprintln!(
"[digitpos_promoter] Pattern detected: {}{} (bool) + {} (int)",
detection.var_name, detection.bool_carrier_name, detection.int_carrier_name
);
}
// Step 4: Build CarrierInfo
use crate::mir::join_ir::lowering::carrier_info::{
@ -177,6 +181,8 @@ impl DigitPosPromoter {
join_id: None, // Will be allocated later
role: CarrierRole::ConditionOnly, // Phase 227: DigitPos is condition-only
init: CarrierInit::BoolConst(false), // Phase 228: Initialize with false
#[cfg(feature = "normalized_dev")]
binding_id: None, // Phase 78: Set by CarrierBindingAssigner
};
// Integer carrier (loop-state, for NumberAccumulation)
@ -186,6 +192,8 @@ impl DigitPosPromoter {
join_id: None, // Will be allocated later
role: CarrierRole::LoopState, // Phase 247-EX: LoopState for accumulation
init: CarrierInit::LoopLocalZero, // Derived in-loop carrier (no host binding)
#[cfg(feature = "normalized_dev")]
binding_id: None, // Phase 78: Set by CarrierBindingAssigner
};
// Create CarrierInfo with a dummy loop_var_name (will be ignored during merge)
@ -214,14 +222,16 @@ impl DigitPosPromoter {
log_promotion_error(&e);
}
eprintln!(
"[digitpos_promoter] Phase 247-EX: A-4 DigitPos pattern promoted: {}{} (bool) + {} (i64)",
detection.var_name, detection.bool_carrier_name, detection.int_carrier_name
);
eprintln!(
"[digitpos_promoter] Phase 229: Recorded promoted variable '{}' (carriers: '{}', '{}')",
detection.var_name, detection.bool_carrier_name, detection.int_carrier_name
);
if std::env::var("NYASH_JOINIR_DEBUG").is_ok() || std::env::var("JOINIR_TEST_DEBUG").is_ok() {
eprintln!(
"[digitpos_promoter] Phase 247-EX: A-4 DigitPos pattern promoted: {}{} (bool) + {} (i64)",
detection.var_name, detection.bool_carrier_name, detection.int_carrier_name
);
eprintln!(
"[digitpos_promoter] Phase 229: Recorded promoted variable '{}' (carriers: '{}', '{}')",
detection.var_name, detection.bool_carrier_name, detection.int_carrier_name
);
}
DigitPosPromotionResult::Promoted {
carrier_info,
@ -239,21 +249,23 @@ impl DigitPosPromoter {
// - is_substring_method_call
}
/// Phase 78: Log promotion errors with clear messages
/// Phase 78: Log promotion errors with clear messages (gated)
#[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
);
if std::env::var("NYASH_JOINIR_DEBUG").is_ok() || std::env::var("JOINIR_TEST_DEBUG").is_ok() {
match error {
BindingRecordError::OriginalNotFound(name) => {
eprintln!(
"[binding_pilot/digitpos] Original variable '{}' not found in binding_map",
name
);
}
BindingRecordError::PromotedNotFound(name) => {
eprintln!(
"[binding_pilot/digitpos] Promoted variable '{}' not found in binding_map",
name
);
}
}
}
}