feat(joinir): Phase 135 P0 - ConditionLoweringBox allocator SSOT (ValueId collision fix)

## Summary
Root cause: ConditionLoweringBox was bypassing ConditionContext.alloc_value (SSOT allocator),
causing ValueId collisions between JoinIR condition params and lowered instructions.

## Changes
1. **ConditionLoweringBox (expr_lowerer.rs)**: Must use ConditionContext.alloc_value
   - Pass &mut ConditionContext to lower_condition (SSOT allocator)
   - Eliminates internal counter usage

2. **Allocator unification (condition_lowerer.rs, method_call_lowerer.rs)**:
   - Accept &mut dyn FnMut() -> ValueId as allocator parameter
   - Ensures all lowering paths use same SSOT allocator

3. **Boundary Copy deduplication (joinir_inline_boundary_injector.rs)**:
   - Deduplicate condition_bindings by dst
   - Fail-Fast if different sources target same dst (MIR SSA violation)

4. **Trim pattern fixes (trim_loop_lowering.rs, trim_pattern_validator.rs, stmts.rs)**:
   - Use builder.next_value_id() instead of value_gen.next() in function context
   - Ensures function-level ValueId allocation respects reserved PHI dsts

## Acceptance
 ./target/release/hakorune --verify apps/tests/phase133_json_skip_whitespace_min.hako
 Smoke: phase135_trim_mir_verify.sh - MIR SSA validation PASS
 Regression: phase132_exit_phi_parity.sh - 3/3 PASS
 Regression: phase133_json_skip_whitespace_llvm_exe.sh - compile-only PASS

🤖 Generated with Claude Code

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-15 18:49:08 +09:00
parent e8e4779942
commit d82c332a40
15 changed files with 186 additions and 70 deletions

View File

@ -265,11 +265,43 @@ impl<'env, 'builder, S: ScopeManager> ConditionLoweringBox<S> for ExprLowerer<'e
fn lower_condition(
&mut self,
condition: &ASTNode,
_context: &ConditionContext<S>,
context: &mut ConditionContext<S>,
) -> Result<ValueId, String> {
// Delegate to existing lower() method
// ConditionContext is unused because ExprLowerer already has scope access
self.lower(condition).map_err(|e| e.to_string())
// Phase 244+ / Phase 201 SSOT: ValueId allocation must be coordinated by the caller.
//
// JoinIR lowering uses JoinValueSpace as SSOT for ValueId regions.
// If we allocate locally here (e.g. starting from 1000), we can collide with
// other JoinIR value users (main params, carrier slots), and after remapping
// this becomes a MIR-level ValueId collision.
if !ast_support::is_supported_condition(condition) {
return Err(format!("Unsupported condition node: {:?}", condition));
}
// Build ConditionEnv from the provided scope (the caller controls the scope + allocator).
#[cfg(feature = "normalized_dev")]
let condition_env =
scope_resolution::build_condition_env_from_scope_with_binding(context.scope, condition, self.builder)
.map_err(|e| e.to_string())?;
#[cfg(not(feature = "normalized_dev"))]
let condition_env = scope_resolution::build_condition_env_from_scope(context.scope, condition)
.map_err(|e| e.to_string())?;
// Delegate to the well-tested lowerer, but use the caller-provided allocator (SSOT).
let (result_value, instructions) =
lower_condition_to_joinir(condition, &mut *context.alloc_value, &condition_env)
.map_err(|e| e.to_string())?;
self.last_instructions = instructions;
if self.debug {
eprintln!(
"[expr_lowerer/phase244] Lowered condition → ValueId({:?}) (context alloc)",
result_value
);
}
Ok(result_value)
}
/// Phase 244: Check if ExprLowerer supports a given condition pattern