feat(joinir): Phase 201-5 Pattern 2 lowerer uses JoinValueSpace
Key changes to prevent ValueId collision between frontend and lowerer: 1. loop_step params now use ConditionEnv ValueIds (Param region: 100+) - i_param = env.get(loop_var_name) - ensures condition lowering works - carrier_param_ids from CarrierInfo.join_id 2. main() params and intermediate values use alloc_local() (Local region: 1000+) - No collision with frontend's param allocations 3. CarrierInfo.join_id is now properly set in frontend - carrier.join_id = Some(carrier_join_id) during allocation This fixes the "use of undefined value" error where frontend allocated ValueId(100+) but lowerer used ValueId(0+), causing remapper mismatch. Test results: - All 821 library tests pass - E2E: phase200d_capture_minimal.hako outputs 30 ✓ - Pattern 4: loop_continue_pattern4.hako outputs 25 ✓ - Multi-carrier: loop_continue_multi_carrier.hako outputs 100,10 ✓ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -207,12 +207,14 @@ impl MirBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 201: Allocate carrier ValueIds from Param region
|
// Phase 201: Allocate carrier ValueIds from Param region and SET join_id
|
||||||
// This ensures carriers are also in the safe Param region
|
// This ensures carriers are also in the safe Param region
|
||||||
for carrier in &carrier_info.carriers {
|
// CRITICAL: We must set join_id so the lowerer can access it
|
||||||
let _carrier_join_id = join_value_space.alloc_param();
|
for carrier in &mut carrier_info.carriers {
|
||||||
|
let carrier_join_id = join_value_space.alloc_param();
|
||||||
|
carrier.join_id = Some(carrier_join_id);
|
||||||
eprintln!("[pattern2/phase201] Allocated carrier '{}' param ID: {:?}",
|
eprintln!("[pattern2/phase201] Allocated carrier '{}' param ID: {:?}",
|
||||||
carrier.name, _carrier_join_id);
|
carrier.name, carrier_join_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 191: Create empty body-local environment
|
// Phase 191: Create empty body-local environment
|
||||||
@ -396,6 +398,7 @@ impl MirBuilder {
|
|||||||
&carrier_updates,
|
&carrier_updates,
|
||||||
analysis_body, // Phase 191/192: Pass normalized body AST for init lowering
|
analysis_body, // Phase 191/192: Pass normalized body AST for init lowering
|
||||||
Some(&mut body_local_env), // Phase 191: Pass mutable body-local environment
|
Some(&mut body_local_env), // Phase 191: Pass mutable body-local environment
|
||||||
|
&mut join_value_space, // Phase 201: Unified ValueId allocation (Local region)
|
||||||
) {
|
) {
|
||||||
Ok((module, meta)) => (module, meta),
|
Ok((module, meta)) => (module, meta),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@ -59,6 +59,7 @@ use crate::ast::ASTNode;
|
|||||||
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta, JoinFragmentMeta};
|
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta, JoinFragmentMeta};
|
||||||
use crate::mir::join_ir::lowering::carrier_update_emitter::{emit_carrier_update, emit_carrier_update_with_env};
|
use crate::mir::join_ir::lowering::carrier_update_emitter::{emit_carrier_update, emit_carrier_update_with_env};
|
||||||
use crate::mir::join_ir::lowering::condition_to_joinir::{lower_condition_to_joinir, ConditionEnv};
|
use crate::mir::join_ir::lowering::condition_to_joinir::{lower_condition_to_joinir, ConditionEnv};
|
||||||
|
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
|
||||||
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
use crate::mir::join_ir::lowering::loop_body_local_env::LoopBodyLocalEnv;
|
||||||
use crate::mir::join_ir::lowering::update_env::UpdateEnv;
|
use crate::mir::join_ir::lowering::update_env::UpdateEnv;
|
||||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||||
@ -131,6 +132,7 @@ use std::collections::HashMap;
|
|||||||
/// * `carrier_updates` - Phase 176-3: Update expressions for each carrier variable
|
/// * `carrier_updates` - Phase 176-3: Update expressions for each carrier variable
|
||||||
/// * `body_ast` - Phase 191: Loop body AST for body-local init lowering
|
/// * `body_ast` - Phase 191: Loop body AST for body-local init lowering
|
||||||
/// * `body_local_env` - Phase 185-2: Optional mutable body-local variable environment for init expressions
|
/// * `body_local_env` - Phase 185-2: Optional mutable body-local variable environment for init expressions
|
||||||
|
/// * `join_value_space` - Phase 201: Unified JoinIR ValueId allocator (Local region: 1000+)
|
||||||
pub(crate) fn lower_loop_with_break_minimal(
|
pub(crate) fn lower_loop_with_break_minimal(
|
||||||
_scope: LoopScopeShape,
|
_scope: LoopScopeShape,
|
||||||
condition: &ASTNode,
|
condition: &ASTNode,
|
||||||
@ -140,6 +142,7 @@ pub(crate) fn lower_loop_with_break_minimal(
|
|||||||
carrier_updates: &HashMap<String, UpdateExpr>,
|
carrier_updates: &HashMap<String, UpdateExpr>,
|
||||||
body_ast: &[ASTNode],
|
body_ast: &[ASTNode],
|
||||||
mut body_local_env: Option<&mut LoopBodyLocalEnv>,
|
mut body_local_env: Option<&mut LoopBodyLocalEnv>,
|
||||||
|
join_value_space: &mut JoinValueSpace,
|
||||||
) -> Result<(JoinModule, JoinFragmentMeta), String> {
|
) -> Result<(JoinModule, JoinFragmentMeta), String> {
|
||||||
// Phase 170-D-impl-3: Validate that conditions only use supported variable scopes
|
// Phase 170-D-impl-3: Validate that conditions only use supported variable scopes
|
||||||
// LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables
|
// LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables
|
||||||
@ -160,14 +163,10 @@ pub(crate) fn lower_loop_with_break_minimal(
|
|||||||
loop_cond_scope.var_names()
|
loop_cond_scope.var_names()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Phase 188-Impl-2: Use local ValueId allocator (sequential from 0)
|
// Phase 201: Use JoinValueSpace for unified ValueId allocation
|
||||||
// JoinIR has NO knowledge of host ValueIds - boundary handled separately
|
// - Local region (1000+) ensures no collision with Param region (100-999)
|
||||||
let mut value_counter = 0u32;
|
// - Param region is used by ConditionEnv, CarrierInfo, CapturedEnv
|
||||||
let mut alloc_value = || {
|
let mut alloc_value = || join_value_space.alloc_local();
|
||||||
let id = ValueId(value_counter);
|
|
||||||
value_counter += 1;
|
|
||||||
id
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut join_module = JoinModule::new();
|
let mut join_module = JoinModule::new();
|
||||||
|
|
||||||
@ -179,7 +178,7 @@ pub(crate) fn lower_loop_with_break_minimal(
|
|||||||
let k_exit_id = JoinFuncId::new(2);
|
let k_exit_id = JoinFuncId::new(2);
|
||||||
|
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
// ValueId allocation (Phase 188-Impl-2: Sequential local IDs)
|
// ValueId allocation (Phase 201: Coordinated with ConditionEnv/CarrierInfo)
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
// Phase 176-3: Multi-carrier support - allocate parameters for all carriers
|
// Phase 176-3: Multi-carrier support - allocate parameters for all carriers
|
||||||
let carrier_count = carrier_info.carriers.len();
|
let carrier_count = carrier_info.carriers.len();
|
||||||
@ -190,21 +189,36 @@ pub(crate) fn lower_loop_with_break_minimal(
|
|||||||
carrier_info.carriers.iter().map(|c| &c.name).collect::<Vec<_>>()
|
carrier_info.carriers.iter().map(|c| &c.name).collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
|
|
||||||
// main() parameters: [i_init, carrier1_init, carrier2_init, ...]
|
// Phase 201: main() parameters use Local region (entry point slots)
|
||||||
let i_init = alloc_value(); // ValueId(0) - loop init value
|
// These don't need to match ConditionEnv - they're just input slots for main
|
||||||
|
let i_init = alloc_value(); // e.g., ValueId(1000)
|
||||||
let mut carrier_init_ids: Vec<ValueId> = Vec::new();
|
let mut carrier_init_ids: Vec<ValueId> = Vec::new();
|
||||||
for _ in 0..carrier_count {
|
for _ in 0..carrier_count {
|
||||||
carrier_init_ids.push(alloc_value());
|
carrier_init_ids.push(alloc_value());
|
||||||
}
|
}
|
||||||
let loop_result = alloc_value(); // result from loop_step
|
let loop_result = alloc_value(); // result from loop_step
|
||||||
|
|
||||||
// loop_step() parameters: [i_param, carrier1_param, carrier2_param, ...]
|
// Phase 201: loop_step() parameters MUST match ConditionEnv's ValueIds!
|
||||||
let i_param = alloc_value(); // Parameter for loop variable
|
// This is critical because condition lowering uses ConditionEnv to resolve variables.
|
||||||
|
// If loop_step.params[0] != env.get(loop_var_name), the condition will reference wrong ValueIds.
|
||||||
|
let i_param = env.get(loop_var_name).ok_or_else(||
|
||||||
|
format!("Phase 201: ConditionEnv missing loop variable '{}' - required for loop_step param", loop_var_name)
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Phase 201: Carrier params for loop_step - use CarrierInfo's join_id
|
||||||
let mut carrier_param_ids: Vec<ValueId> = Vec::new();
|
let mut carrier_param_ids: Vec<ValueId> = Vec::new();
|
||||||
for _ in 0..carrier_count {
|
for carrier in &carrier_info.carriers {
|
||||||
carrier_param_ids.push(alloc_value());
|
let carrier_join_id = carrier.join_id.ok_or_else(||
|
||||||
|
format!("Phase 201: CarrierInfo missing join_id for carrier '{}'", carrier.name)
|
||||||
|
)?;
|
||||||
|
carrier_param_ids.push(carrier_join_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/pattern2] Phase 201: loop_step params - i_param={:?}, carrier_params={:?}",
|
||||||
|
i_param, carrier_param_ids
|
||||||
|
);
|
||||||
|
|
||||||
// Phase 169 / Phase 171-fix: Lower condition using condition_to_joinir helper with ConditionEnv
|
// Phase 169 / Phase 171-fix: Lower condition using condition_to_joinir helper with ConditionEnv
|
||||||
// This will allocate ValueIds dynamically based on condition complexity
|
// This will allocate ValueIds dynamically based on condition complexity
|
||||||
let (cond_value, mut cond_instructions) = lower_condition_to_joinir(
|
let (cond_value, mut cond_instructions) = lower_condition_to_joinir(
|
||||||
|
|||||||
Reference in New Issue
Block a user