fix(joinir): collect carrier_inputs for skippable ExitJump (Phase 286C-4.1)

After the Phase 286C-4 refactoring, carrier_inputs was not being
collected when a tail call's TailCallKind is ExitJump and the target
is a skippable continuation. This caused json_lint_vm to fail with:

  [joinir/phase118/exit_phi/missing_carrier_phi]
  exit_bindings carrier 'i' is missing from exit_carrier_phis

Root cause: The 3-stage pipeline refactoring separated Return→Jump
conversion (which collected carrier_inputs) from tail call handling.
When found_tail_call=true, the Return processing was skipped entirely,
but tail call handling didn't collect carrier_inputs.

Fix: Add carrier_inputs collection in the ExitJump (skippable) path
at lines 901-934. This mirrors the fallback logic from the Return
processing path.

Test results: 45/46 PASS (same as before refactoring)
- json_lint_vm: PASS (was FAIL)
- core_direct_array_oob_set_rc_vm: FAIL (unchanged, known issue)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-25 05:05:49 +09:00
parent e55665aa22
commit bfc63b0e3c

View File

@ -872,6 +872,42 @@ fn plan_rewrites(
"[plan_rewrites] ExitJump (skippable): redirecting from {:?} to exit_block_id {:?}",
target_block, ctx.exit_block_id
);
// Phase 286C-4.1: Collect carrier_inputs for skippable exit jump
// This was missing in the refactored code
if let Some(b) = boundary {
for binding in &b.exit_bindings {
// Skip ConditionOnly carriers
if binding.role == crate::mir::join_ir::lowering::carrier_info::CarrierRole::ConditionOnly {
continue;
}
// Try to get carrier value from header PHI
if let Some(phi_dst) = loop_header_phi_info.get_carrier_phi(&binding.carrier_name) {
result.carrier_inputs
.entry(binding.carrier_name.clone())
.or_insert_with(Vec::new)
.push((new_block_id, phi_dst));
log!(
verbose,
"[plan_rewrites] ExitJump carrier '{}': from {:?} value {:?}",
binding.carrier_name, new_block_id, phi_dst
);
} else if b.exit_reconnect_mode == crate::mir::join_ir::lowering::carrier_info::ExitReconnectMode::DirectValue {
// DirectValue fallback: use host_slot
result.carrier_inputs
.entry(binding.carrier_name.clone())
.or_insert_with(Vec::new)
.push((new_block_id, binding.host_slot));
log!(
verbose,
"[plan_rewrites] ExitJump DirectValue fallback for '{}': host_slot {:?}",
binding.carrier_name, binding.host_slot
);
}
}
}
ctx.exit_block_id
} else {
log!(