feat(joinir): Phase 172-3 ExitMeta unified return from Pattern2 lowerer

Implement loop exit contract boxification for JoinIR Pattern2:

- lower_loop_with_break_minimal now returns (JoinModule, ExitMeta)
- ExitMeta contains k_exit parameter ValueId for carrier variables
- Pattern2 caller builds exit_bindings from ExitMeta
- merge/mod.rs adds exit_bindings join_exit_values to used_values for remap
- reconnect_boundary uses remapped exit values for variable_map updates

This completes Phases 172-3 through 172-5 of the Loop Exit Contract
boxification plan, enabling proper loop variable propagation after exit.

Test: joinir_min_loop.hako passes (RC: 0)

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-07 02:03:55 +09:00
parent 0dde30ceb8
commit 41c50e4780
3 changed files with 69 additions and 19 deletions

View File

@ -56,6 +56,7 @@
//! Following the "80/20 rule" from CLAUDE.md - get it working first, generalize later.
use crate::ast::ASTNode;
use crate::mir::join_ir::lowering::carrier_info::ExitMeta;
use crate::mir::join_ir::lowering::condition_to_joinir::{lower_condition_to_joinir, ConditionEnv};
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
use crate::mir::join_ir::{
@ -106,11 +107,21 @@ use crate::mir::ValueId;
///
/// The caller must build a ConditionEnv that maps variable names to JoinIR-local ValueIds.
/// This ensures JoinIR never accesses HOST ValueIds directly.
///
/// # Phase 172-3: ExitMeta Return
///
/// Returns `(JoinModule, ExitMeta)` where ExitMeta contains the JoinIR-local ValueId
/// of the k_exit parameter. This allows the caller to build proper exit_bindings.
///
/// # Arguments
///
/// * `loop_var_name` - Name of the loop variable (for ExitMeta construction)
pub fn lower_loop_with_break_minimal(
_scope: LoopScopeShape,
condition: &ASTNode,
env: &ConditionEnv,
) -> Result<JoinModule, String> {
loop_var_name: &str,
) -> Result<(JoinModule, ExitMeta), String> {
// Phase 188-Impl-2: Use local ValueId allocator (sequential from 0)
// JoinIR has NO knowledge of host ValueIds - boundary handled separately
let mut value_counter = 0u32;
@ -294,5 +305,9 @@ pub fn lower_loop_with_break_minimal(
eprintln!("[joinir/pattern2] Condition from AST (not hardcoded)");
eprintln!("[joinir/pattern2] Exit PHI: k_exit receives i from both natural exit and break");
Ok(join_module)
// Phase 172-3: Build ExitMeta with k_exit parameter's ValueId
let exit_meta = ExitMeta::single(loop_var_name.to_string(), i_exit);
eprintln!("[joinir/pattern2] Phase 172-3: ExitMeta {{ {}{:?} }}", loop_var_name, i_exit);
Ok((join_module, exit_meta))
}