refactor(joinir): Unify exit binding generation using ExitMetaCollector

Phase 179 Task 2: Modular exit binding generation

Changes:
- Pattern 4 (pattern4_with_continue.rs):
  - REPLACED: Manual exit_bindings loop (28 lines deleted)
  - WITH: ExitMetaCollector::collect() call (10 lines added)
  - ADDED: Validation that all carriers have bindings
  - Net reduction: 10 lines, improved modularity

- Pattern 3 (pattern3_with_if_phi.rs):
  - DOCUMENTED: Why hardcoded ValueId(18) exists
  - ADDED: TODO with migration path to ExitMeta-based approach
  - DEFERRED: Actual migration (requires lowerer return type change)
  - Impact: Low priority - Pattern 3 is test-only

Benefits:
- Unified approach: Pattern 4 now uses same ExitMetaCollector as other patterns
- Reusability: ExitMetaCollector is pattern-agnostic Box
- Maintainability: Less duplicate code, clearer responsibilities
- Testability: ExitMetaCollector has independent unit tests

Notes:
- Pattern 3 migration deferred (lowerer doesn't return ExitMeta yet)
- Comprehensive TODO comment documents migration path
- Pre-existing test failure (stage1_usingresolver) unrelated to this change
This commit is contained in:
nyash-codex
2025-12-08 19:09:06 +09:00
parent 0dc9b838d6
commit 17b9201f96
2 changed files with 34 additions and 27 deletions

View File

@ -7,6 +7,23 @@ use super::super::trace;
/// Phase 179-A: Expected ValueId for k_exit parameter (sum_final) in Pattern 3
/// This corresponds to the exit PHI input in the JoinIR lowering for loop_with_if_phi_minimal
///
/// # TODO (Phase 179 Task 2): Convert to ExitMeta-based exit binding generation
///
/// **Current State**: Hardcoded ValueId(18) - fragile and non-reusable
///
/// **Why it's hardcoded**:
/// - Pattern 3's lowerer (`lower_loop_with_if_phi_pattern`) returns `Option<JoinModule>`
/// - Unlike Pattern 4 which returns `(JoinModule, JoinFragmentMeta)`
/// - No ExitMeta available to dynamically look up exit PHI ValueIds
///
/// **Migration Path** (when Pattern 3 lowerer is updated):
/// 1. Change `lower_loop_with_if_phi_pattern` to return `(JoinModule, JoinFragmentMeta)`
/// 2. Remove this constant
/// 3. Use ExitMeta loop (like Pattern 4 lines 350-378) to generate exit_bindings dynamically
/// 4. See: pattern4_with_continue.rs lines 350-378 for reference implementation
///
/// **Impact**: Low priority - Pattern 3 is test-only and works correctly with hardcoded value
const PATTERN3_K_EXIT_SUM_FINAL_ID: ValueId = ValueId(18);
/// Phase 194: Detection function for Pattern 3

View File

@ -347,34 +347,24 @@ impl MirBuilder {
);
}
// Phase 196: Dynamically generate exit_bindings from ExitMeta and CarrierInfo
let mut exit_bindings = Vec::new();
for (carrier_name, join_exit_value) in &exit_meta.exit_values {
// Find the host_slot for this carrier
let host_slot = carrier_info
.carriers
.iter()
.find(|c| &c.name == carrier_name)
.map(|c| c.host_id)
.ok_or_else(|| {
format!(
"[cf_loop/pattern4] Carrier '{}' not found in CarrierInfo",
carrier_name
)
})?;
exit_bindings.push(
crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding {
carrier_name: carrier_name.clone(),
join_exit_value: *join_exit_value,
host_slot,
},
// Phase 179 Task 2: Use ExitMetaCollector for unified exit binding generation
// Replaces manual loop (Phase 196 implementation) with modular Box approach
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
let exit_bindings = ExitMetaCollector::collect(
self,
&exit_meta,
debug,
);
trace::trace().debug(
"pattern4",
&format!("Generated binding: {} → JoinIR={} Host={}", carrier_name, join_exit_value.0, host_slot.0)
);
// Validate that all expected carriers have bindings
// (ExitMetaCollector silently skips missing carriers, but Pattern 4 expects all)
for carrier in &carrier_info.carriers {
if !exit_bindings.iter().any(|b| b.carrier_name == carrier.name) {
return Err(format!(
"[cf_loop/pattern4] Carrier '{}' not found in variable_map - exit binding missing",
carrier.name
));
}
}
// Phase 196: Build host_inputs dynamically