feat(joinir): Phase 92完了 - ConditionalStep + body-local変数サポート

## Phase 92全体の成果

**Phase 92 P0-P2**: ConditionalStep JoinIR生成とbody-local変数サポート
- ConditionalStep(条件付きキャリア更新)のJoinIR生成実装
- Body-local変数(ch等)の条件式での参照サポート
- 変数解決優先度: ConditionEnv → LoopBodyLocalEnv

**Phase 92 P3**: BodyLocalPolicyBox + 安全ガード
- BodyLocalPolicyDecision実装(Accept/Reject判定)
- BodyLocalSlot + DualValueRewriter(JoinIR/MIR二重書き込み)
- Fail-Fast契約(Cannot promote LoopBodyLocal検出)

**Phase 92 P4**: E2E固定+回帰最小化 (本コミット)
- Unit test 3本追加(body-local変数解決検証)
- Integration smoke追加(phase92_pattern2_baseline.sh、2ケースPASS)
- P4-E2E-PLAN.md、P4-COMPLETION.md作成

## 主要な実装

### ConditionalStep(条件付きキャリア更新)
- `conditional_step_emitter.rs`: JoinIR Select命令生成
- `loop_with_break_minimal.rs`: ConditionalStep検出と統合
- `loop_with_continue_minimal.rs`: Pattern4対応

### Body-local変数サポート
- `condition_lowerer.rs`: body-local変数解決機能
  - `lower_condition_to_joinir`: body_local_env パラメータ追加
  - 変数解決優先度実装(ConditionEnv優先)
  - Unit test 3本追加: 変数解決/優先度/エラー
- `header_break_lowering.rs`: break条件でbody-local変数参照
- 7ファイルで後方互換ラッパー(lower_condition_to_joinir_no_body_locals)

### Body-local Policy & Safety
- `body_local_policy.rs`: BodyLocalPolicyDecision(Accept/Reject)
- `body_local_slot.rs`: JoinIR/MIR二重書き込み
- `dual_value_rewriter.rs`: ValueId書き換えヘルパー

## テスト体制

### Unit Tests (+3)
- `test_body_local_variable_resolution`: body-local変数解決
- `test_variable_resolution_priority`: 変数解決優先度(ConditionEnv優先)
- `test_undefined_variable_error`: 未定義変数エラー
- 全7テストPASS(cargo test --release condition_lowerer::tests)

### Integration Smoke (+1)
- `phase92_pattern2_baseline.sh`:
  - Case A: loop_min_while.hako (Pattern2 baseline)
  - Case B: phase92_conditional_step_minimal.hako (条件付きインクリメント)
  - 両ケースPASS、integration profileで発見可能

### 退行確認
-  既存Pattern2Breakテスト正常(退行なし)
-  Phase 135 smoke正常(MIR検証PASS)

## アーキテクチャ設計

### 変数解決メカニズム
```rust
// Priority 1: ConditionEnv (loop params, captured)
if let Some(value_id) = env.get(name) { return Ok(value_id); }
// Priority 2: LoopBodyLocalEnv (body-local like `ch`)
if let Some(body_env) = body_local_env {
    if let Some(value_id) = body_env.get(name) { return Ok(value_id); }
}
```

### Fail-Fast契約
- Delta equality check (conditional_step_emitter.rs)
- Variable resolution error messages (ConditionEnv)
- Body-local promotion rejection (BodyLocalPolicyDecision::Reject)

## ドキュメント

- `P4-E2E-PLAN.md`: 3レベルテスト戦略(Level 1-2完了、Level 3延期)
- `P4-COMPLETION.md`: Phase 92完了報告
- `README.md`: Phase 92全体のまとめ

## 将来の拡張(Phase 92スコープ外)

- Body-local promotionシステム拡張
- P5bパターン認識の汎化(flagベース条件サポート)
- 完全なP5b E2Eテスト(body-local promotion実装後)

🎯 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-16 21:37:07 +09:00
parent 568619df89
commit d2972c1437
43 changed files with 2615 additions and 980 deletions

View File

@ -30,6 +30,7 @@ mod value_collector;
pub use loop_header_phi_builder::LoopHeaderPhiBuilder;
pub use loop_header_phi_info::LoopHeaderPhiInfo;
use super::trace;
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
use crate::mir::{MirModule, ValueId};
@ -77,13 +78,15 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
debug: bool,
) -> Result<Option<ValueId>, String> {
let verbose = debug || crate::config::env::joinir_dev_enabled();
let trace = trace::trace();
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] merge_joinir_mir_blocks called with {} functions",
mir_module.functions.len()
);
}
),
debug,
);
if verbose {
if let Some(boundary) = boundary {
@ -109,31 +112,43 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
})
.collect();
eprintln!(
"[cf_loop/joinir] Boundary join_inputs={:?} host_inputs={:?}",
boundary.join_inputs, boundary.host_inputs
trace.stderr_if(
&format!(
"[cf_loop/joinir] Boundary join_inputs={:?} host_inputs={:?}",
boundary.join_inputs, boundary.host_inputs
),
true,
);
eprintln!(
"[cf_loop/joinir] Boundary exit_bindings ({}): {}",
boundary.exit_bindings.len(),
exit_summary.join(", ")
trace.stderr_if(
&format!(
"[cf_loop/joinir] Boundary exit_bindings ({}): {}",
boundary.exit_bindings.len(),
exit_summary.join(", ")
),
true,
);
if !cond_summary.is_empty() {
eprintln!(
"[cf_loop/joinir] Boundary condition_bindings ({}): {}",
cond_summary.len(),
cond_summary.join(", ")
trace.stderr_if(
&format!(
"[cf_loop/joinir] Boundary condition_bindings ({}): {}",
cond_summary.len(),
cond_summary.join(", ")
),
true,
);
}
if let Some(ci) = &boundary.carrier_info {
let carriers: Vec<String> = ci.carriers.iter().map(|c| c.name.clone()).collect();
eprintln!(
"[cf_loop/joinir] Boundary carrier_info: loop_var='{}', carriers={:?}",
ci.loop_var_name, carriers
trace.stderr_if(
&format!(
"[cf_loop/joinir] Boundary carrier_info: loop_var='{}', carriers={:?}",
ci.loop_var_name, carriers
),
true,
);
}
} else {
eprintln!("[cf_loop/joinir] No boundary provided");
trace.stderr_if("[cf_loop/joinir] No boundary provided", true);
}
}
@ -149,23 +164,25 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Phase 171-fix: Add condition_bindings' join_values to used_values for remapping
if let Some(boundary) = boundary {
for binding in &boundary.condition_bindings {
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 171-fix: Adding condition binding '{}' JoinIR {:?} to used_values",
binding.name, binding.join_value
);
}
),
debug,
);
used_values.insert(binding.join_value);
}
// Phase 172-3: Add exit_bindings' join_exit_values to used_values for remapping
for binding in &boundary.exit_bindings {
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 172-3: Adding exit binding '{}' JoinIR {:?} to used_values",
binding.carrier_name, binding.join_exit_value
);
}
),
debug,
);
used_values.insert(binding.join_exit_value);
}
}
@ -237,20 +254,24 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
.collect()
};
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 201-A: Pre-building header PHIs for loop_var='{}' at {:?}",
loop_var_name, entry_block_remapped
);
eprintln!(
),
debug,
);
trace.stderr_if(
&format!(
"[cf_loop/joinir] loop_var_init={:?}, carriers={:?}",
loop_var_init,
other_carriers
.iter()
.map(|(n, _, _, _)| n.as_str())
.collect::<Vec<_>>()
);
}
),
debug,
);
// Build PHI info (this allocates PHI dst ValueIds)
LoopHeaderPhiBuilder::build(
@ -286,22 +307,24 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Phase 201-A: Get reserved PHI dst ValueIds and set in MirBuilder
let reserved_phi_dsts = loop_header_phi_info.reserved_value_ids();
if debug && !reserved_phi_dsts.is_empty() {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 201-A: Reserved PHI dsts: {:?}",
reserved_phi_dsts
);
}
),
debug && !reserved_phi_dsts.is_empty(),
);
// Phase 201-A: Set reserved IDs in MirBuilder so next_value_id() skips them
// This protects against carrier corruption when break conditions emit Const instructions
builder.comp_ctx.reserved_value_ids = reserved_phi_dsts.clone();
if debug && !builder.comp_ctx.reserved_value_ids.is_empty() {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 201-A: Set builder.comp_ctx.reserved_value_ids = {:?}",
builder.comp_ctx.reserved_value_ids
);
}
),
debug && !builder.comp_ctx.reserved_value_ids.is_empty(),
);
// Phase 3: Remap ValueIds (with reserved PHI dsts protection)
remap_values(
@ -313,28 +336,40 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
)?;
// Phase 177-3 DEBUG: Verify remapper state after Phase 3
eprintln!("[DEBUG-177] === Remapper state after Phase 3 ===");
eprintln!("[DEBUG-177] used_values count: {}", used_values.len());
trace.stderr_if("[DEBUG-177] === Remapper state after Phase 3 ===", verbose);
trace.stderr_if(
&format!("[DEBUG-177] used_values count: {}", used_values.len()),
verbose,
);
for value_id in &used_values {
if let Some(remapped) = remapper.get_value(*value_id) {
eprintln!("[DEBUG-177] JoinIR {:?} → Host {:?}", value_id, remapped);
trace.stderr_if(
&format!("[DEBUG-177] JoinIR {:?} → Host {:?}", value_id, remapped),
verbose,
);
} else {
eprintln!("[DEBUG-177] JoinIR {:?} → NOT FOUND ❌", value_id);
trace.stderr_if(
&format!("[DEBUG-177] JoinIR {:?} → NOT FOUND ❌", value_id),
verbose,
);
}
}
// Check condition_bindings specifically
if let Some(boundary) = boundary {
eprintln!("[DEBUG-177] === Condition bindings check ===");
trace.stderr_if("[DEBUG-177] === Condition bindings check ===", verbose);
for binding in &boundary.condition_bindings {
let lookup_result = remapper.get_value(binding.join_value);
eprintln!(
"[DEBUG-177] '{}': JoinIR {:?}{:?}",
binding.name, binding.join_value, lookup_result
trace.stderr_if(
&format!(
"[DEBUG-177] '{}': JoinIR {:?}{:?}",
binding.name, binding.join_value, lookup_result
),
verbose,
);
}
}
eprintln!("[DEBUG-177] ==============================");
trace.stderr_if("[DEBUG-177] ==============================", verbose);
// Phase 3.5: Override remapper for function parameters to use PHI dsts
//
@ -390,15 +425,21 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
.collect();
if !condition_binding_ids.is_empty() {
eprintln!(
"[cf_loop/joinir] Phase 177-3: Protected ValueIds (condition-only, not carriers): {:?}",
condition_binding_ids
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 177-3: Protected ValueIds (condition-only, not carriers): {:?}",
condition_binding_ids
),
verbose,
);
for cb in &boundary.condition_bindings {
let is_carrier = carrier_names.contains(cb.name.as_str());
eprintln!(
"[cf_loop/joinir] Phase 177-3: '{}': JoinIR {:?} (carrier={})",
cb.name, cb.join_value, is_carrier
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 177-3: '{}': JoinIR {:?} (carrier={})",
cb.name, cb.join_value, is_carrier
),
verbose,
);
}
}
@ -407,25 +448,34 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
let loop_step_func_name = "join_func_1";
if function_params.get(main_func_name).is_none() {
eprintln!(
"[cf_loop/joinir] WARNING: function_params.get('{}') returned None. Available keys: {:?}",
main_func_name,
function_params.keys().collect::<Vec<_>>()
trace.stderr_if(
&format!(
"[cf_loop/joinir] WARNING: function_params.get('{}') returned None. Available keys: {:?}",
main_func_name,
function_params.keys().collect::<Vec<_>>()
),
verbose,
);
}
if let Some(main_params) = function_params.get(main_func_name) {
eprintln!(
"[DEBUG-177] Phase 33-21: main ({}) params: {:?}",
main_func_name, main_params
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 33-21: main ({}) params: {:?}",
main_func_name, main_params
),
verbose,
);
eprintln!(
"[DEBUG-177] Phase 33-21: carrier_phis count: {}, names: {:?}",
loop_header_phi_info.carrier_phis.len(),
loop_header_phi_info
.carrier_phis
.iter()
.map(|(n, _)| n.as_str())
.collect::<Vec<_>>()
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 33-21: carrier_phis count: {}, names: {:?}",
loop_header_phi_info.carrier_phis.len(),
loop_header_phi_info
.carrier_phis
.iter()
.map(|(n, _)| n.as_str())
.collect::<Vec<_>>()
),
verbose,
);
// Map main's parameters to header PHI dsts
// main params: [i_init, carrier1_init, ...]
@ -436,15 +486,21 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
if let Some(&main_param) = main_params.get(idx) {
// Phase 177-3: Don't override condition_bindings
if condition_binding_ids.contains(&main_param) {
eprintln!(
"[cf_loop/joinir] Phase 177-3: Skipping override for condition_binding {:?} ('{}')",
main_param, carrier_name
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 177-3: Skipping override for condition_binding {:?} ('{}')",
main_param, carrier_name
),
verbose,
);
continue;
}
eprintln!(
"[DEBUG-177] Phase 33-21: REMAP main param[{}] {:?}{:?} ('{}')",
idx, main_param, entry.phi_dst, carrier_name
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 33-21: REMAP main param[{}] {:?}{:?} ('{}')",
idx, main_param, entry.phi_dst, carrier_name
),
verbose,
);
remapper.set_value(main_param, entry.phi_dst);
}
@ -466,9 +522,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
continue;
}
// This is a body-only carrier - remap it to PHI dst
eprintln!(
"[cf_loop/joinir] Phase 177-3-B: Body-only carrier '{}': JoinIR {:?} → PHI {:?}",
carrier_name, binding.join_value, entry.phi_dst
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 177-3-B: Body-only carrier '{}': JoinIR {:?} → PHI {:?}",
carrier_name, binding.join_value, entry.phi_dst
),
verbose,
);
remapper.set_value(binding.join_value, entry.phi_dst);
}
@ -476,22 +535,31 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Map loop_step's parameters
// DEBUG-177: Always log function_params keys to diagnose multi-carrier issue
eprintln!(
"[DEBUG-177] Phase 33-21: function_params keys: {:?}",
function_params.keys().collect::<Vec<_>>()
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 33-21: function_params keys: {:?}",
function_params.keys().collect::<Vec<_>>()
),
verbose,
);
if function_params.get(loop_step_func_name).is_none() {
eprintln!(
"[cf_loop/joinir] WARNING: function_params.get('{}') returned None. Available keys: {:?}",
loop_step_func_name,
function_params.keys().collect::<Vec<_>>()
trace.stderr_if(
&format!(
"[cf_loop/joinir] WARNING: function_params.get('{}') returned None. Available keys: {:?}",
loop_step_func_name,
function_params.keys().collect::<Vec<_>>()
),
verbose,
);
}
if let Some(loop_step_params) = function_params.get(loop_step_func_name) {
// DEBUG-177: Always log loop_step params
eprintln!(
"[DEBUG-177] Phase 33-21: loop_step ({}) params: {:?}",
loop_step_func_name, loop_step_params
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 33-21: loop_step ({}) params: {:?}",
loop_step_func_name, loop_step_params
),
verbose,
);
// Phase 177-FIX: Process loop_step params but skip if already mapped
//
@ -501,9 +569,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
for loop_step_param in loop_step_params {
// Phase 177-3: Don't override condition_bindings
if condition_binding_ids.contains(loop_step_param) {
eprintln!(
"[DEBUG-177] Phase 177-FIX: Skipping condition_binding {:?}",
loop_step_param
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 177-FIX: Skipping condition_binding {:?}",
loop_step_param
),
verbose,
);
continue;
}
@ -517,9 +588,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
.any(|(name, _)| name == &cb.name)
});
if already_mapped {
eprintln!(
"[DEBUG-177] Phase 177-FIX: Skipping {:?} (already mapped by Phase 177-3-B)",
loop_step_param
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 177-FIX: Skipping {:?} (already mapped by Phase 177-3-B)",
loop_step_param
),
verbose,
);
continue;
}
@ -537,9 +611,12 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
loop_header_phi_info.get_carrier_at_index(param_idx),
loop_header_phi_info.get_entry_at_index(param_idx),
) {
eprintln!(
"[DEBUG-177] Phase 177-STRUCT-2: REMAP loop_step param[{}] {:?}{:?} (carrier '{}')",
param_idx, loop_step_param, entry.phi_dst, carrier_name
trace.stderr_if(
&format!(
"[DEBUG-177] Phase 177-STRUCT-2: REMAP loop_step param[{}] {:?}{:?} (carrier '{}')",
param_idx, loop_step_param, entry.phi_dst, carrier_name
),
verbose,
);
remapper.set_value(*loop_step_param, entry.phi_dst);
}
@ -556,15 +633,17 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Phase 177-3: Don't override condition_bindings
if !condition_binding_ids.contains(&ValueId(0)) {
remapper.set_value(ValueId(0), phi_dst);
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 33-16 fallback: Override remap ValueId(0) → {:?} (PHI dst)",
phi_dst
);
}
),
debug,
);
} else {
eprintln!(
"[cf_loop/joinir] Phase 177-3 fallback: Skipping override for condition_binding ValueId(0)"
trace.stderr_if(
"[cf_loop/joinir] Phase 177-3 fallback: Skipping override for condition_binding ValueId(0)",
verbose,
);
}
}
@ -581,28 +660,35 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Phase 177-3: Don't override condition_bindings
if !condition_binding_ids.contains(&join_value_id) {
remapper.set_value(join_value_id, entry.phi_dst);
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 33-20 fallback: Override remap {:?}{:?} (carrier '{}' PHI dst)",
join_value_id, entry.phi_dst, carrier_name
);
}
),
debug,
);
} else {
eprintln!(
"[cf_loop/joinir] Phase 177-3 fallback: Skipping override for condition_binding {:?} ('{}')",
join_value_id, carrier_name
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 177-3 fallback: Skipping override for condition_binding {:?} ('{}')",
join_value_id, carrier_name
),
verbose,
);
}
}
}
// Phase 177-3 DEBUG: Check remapper after Phase 33-21 overrides
eprintln!("[DEBUG-177] === Remapper state after Phase 33-21 ===");
trace.stderr_if("[DEBUG-177] === Remapper state after Phase 33-21 ===", verbose);
for binding in &boundary.condition_bindings {
let lookup_result = remapper.get_value(binding.join_value);
eprintln!(
"[DEBUG-177] '{}': JoinIR {:?}{:?} (after 33-21)",
binding.name, binding.join_value, lookup_result
trace.stderr_if(
&format!(
"[DEBUG-177] '{}': JoinIR {:?}{:?} (after 33-21)",
binding.name, binding.join_value, lookup_result
),
verbose,
);
}
@ -630,12 +716,13 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// By now, instruction_rewriter has set latch_incoming for all carriers.
// We can finalize the PHIs and insert them into the header block.
if !loop_header_phi_info.carrier_phis.is_empty() {
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 4.5: Finalizing {} header PHIs",
loop_header_phi_info.carrier_phis.len()
);
}
),
debug,
);
LoopHeaderPhiBuilder::finalize(builder, &loop_header_phi_info, debug)?;
}
@ -673,12 +760,16 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// The exit PHI correctly merges values from both exit paths, giving us the final result.
let carrier_phis = &exit_carrier_phis;
if debug && !carrier_phis.is_empty() {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 246-EX: Using EXIT PHI dsts for variable_map (not header): {:?}",
carrier_phis.iter().map(|(n, v)| (n.as_str(), v)).collect::<Vec<_>>()
);
}
carrier_phis
.iter()
.map(|(n, v)| (n.as_str(), v))
.collect::<Vec<_>>()
),
debug && !carrier_phis.is_empty(),
);
// Phase 6: Reconnect boundary (if specified)
// Phase 197-B: Pass remapper to enable per-carrier exit value lookup
@ -694,40 +785,49 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// The header_block in loop_header_phi_info is the remapped entry block
let entry_block = loop_header_phi_info.header_block;
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Entry block (from loop_header_phi_info): {:?}",
entry_block
);
eprintln!(
),
debug,
);
trace.stderr_if(
&format!(
"[cf_loop/joinir] Current block before emit_jump: {:?}",
builder.current_block
);
eprintln!(
),
debug,
);
trace.stderr_if(
&format!(
"[cf_loop/joinir] Jumping to entry block: {:?}",
entry_block
);
}
),
debug,
);
crate::mir::builder::emission::branch::emit_jump(builder, entry_block)?;
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] After emit_jump, current_block: {:?}",
builder.current_block
);
}
),
debug,
);
// Switch to exit block for subsequent code
builder.start_new_block(exit_block_id)?;
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 189: Merge complete: {} functions merged, continuing from {:?}",
mir_module.functions.len(),
exit_block_id
);
}
),
debug,
);
// Phase 200-3: Verify JoinIR contracts (debug only)
#[cfg(debug_assertions)]
@ -742,21 +842,23 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
boundary,
);
}
if debug {
eprintln!("[cf_loop/joinir] Phase 200-3: Contract verification passed");
}
trace.stderr_if(
"[cf_loop/joinir] Phase 200-3: Contract verification passed",
debug,
);
}
}
// Phase 201-A: Clear reserved ValueIds after merge completes
// Future loops will set their own reserved IDs
if !builder.comp_ctx.reserved_value_ids.is_empty() {
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 201-A: Clearing reserved_value_ids (was {:?})",
builder.comp_ctx.reserved_value_ids
);
}
),
debug,
);
builder.comp_ctx.reserved_value_ids.clear();
}
@ -777,12 +879,13 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
if let Some(binding) = loop_var_binding {
if binding.join_exit_value == expr_result_id {
// expr_result is the loop variable! Use exit_phi_result_id
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 246-EX-FIX: expr_result {:?} is loop variable '{}', using exit_phi_result_id {:?}",
expr_result_id, loop_var_name, exit_phi_result_id
);
}
),
debug,
);
exit_phi_result_id
} else {
// expr_result is not the loop variable, resolve as carrier
@ -823,21 +926,23 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
// Return expr_result if present, otherwise fall back to exit_phi_result_id
if let Some(resolved) = expr_result_value {
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 246-EX-FIX: Returning expr_result_value {:?}",
resolved
);
}
),
debug,
);
Ok(Some(resolved))
} else {
// Fallback: return exit_phi_result_id (for legacy patterns or carrier-only loops)
if debug && exit_phi_result_id.is_some() {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 221-R: Returning exit_phi_result_id (fallback): {:?}",
exit_phi_result_id
);
}
),
debug && exit_phi_result_id.is_some(),
);
Ok(exit_phi_result_id)
}
}
@ -854,13 +959,15 @@ fn remap_values(
reserved_ids: &std::collections::HashSet<ValueId>,
debug: bool,
) -> Result<(), String> {
if debug {
eprintln!(
let trace = trace::trace();
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 3: Remapping {} ValueIds (reserved: {})",
used_values.len(),
reserved_ids.len()
);
}
),
debug,
);
for old_value in used_values {
// Phase 201-A: Allocate new ValueId, skipping reserved PHI dsts
@ -870,21 +977,23 @@ fn remap_values(
break candidate;
}
// Skip reserved ID - will try next one
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Phase 201-A: Skipping reserved PHI dst {:?}",
candidate
);
}
),
debug,
);
};
remapper.set_value(*old_value, new_value);
if debug {
eprintln!(
trace.stderr_if(
&format!(
"[cf_loop/joinir] Value remap: {:?}{:?}",
old_value, new_value
);
}
),
debug,
);
}
Ok(())