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:
@ -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
|
||||
|
||||
Reference in New Issue
Block a user