diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs b/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs index 8100200e..186b56d5 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs @@ -292,8 +292,19 @@ impl MirBuilder { } } - // Phase 169: Call Pattern 4 lowerer with condition AST - let (join_module, exit_meta) = match lower_loop_with_continue_minimal(scope, condition, self, &carrier_info, &carrier_updates) { + // Phase 202-C: Create JoinValueSpace for unified ValueId allocation + use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace; + let mut join_value_space = JoinValueSpace::new(); + + // Phase 169 / Phase 202-C: Call Pattern 4 lowerer with JoinValueSpace + let (join_module, exit_meta) = match lower_loop_with_continue_minimal( + scope, + condition, + self, + &carrier_info, + &carrier_updates, + &mut join_value_space, + ) { Ok(result) => result, Err(e) => { // Phase 195: Use unified trace diff --git a/src/mir/join_ir/lowering/loop_with_continue_minimal.rs b/src/mir/join_ir/lowering/loop_with_continue_minimal.rs index c3ffc89b..4d986e43 100644 --- a/src/mir/join_ir/lowering/loop_with_continue_minimal.rs +++ b/src/mir/join_ir/lowering/loop_with_continue_minimal.rs @@ -2,6 +2,7 @@ //! //! Phase 195: Initial minimal implementation for single-carrier (sum only) //! Phase 196: Extended to support multiple carrier variables (sum, count, etc.) +//! Phase 202-C: Unified dual counters into JoinValueSpace //! //! Target: apps/tests/loop_continue_pattern4.hako (single carrier) //! apps/tests/loop_continue_multi_carrier.hako (multi carrier) @@ -42,12 +43,25 @@ //! - ExitMeta::multiple() instead of ExitMeta::single() //! - Dynamic parameter/slot allocation based on carrier count //! +//! ## Phase 202-C: JoinValueSpace Unification +//! +//! Previously used dual counters: +//! - `value_counter`: JoinIR internal ValueIds (0, 1, 2, ...) +//! - `join_value_counter`: Condition-only variables (carrier_count + 1, ...) +//! +//! Now unified via JoinValueSpace: +//! - **Param region (100+)**: ConditionEnv, CarrierInfo parameters +//! - **Local region (1000+)**: Const, BinOp, intermediate values +//! +//! This prevents ValueId collisions after remapping (Phase 201 design). +//! //! Following the "80/20 rule" from CLAUDE.md - now generalizing after working. use crate::ast::ASTNode; use crate::mir::builder::MirBuilder; use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta}; 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_scope_shape::LoopScopeShape; use crate::mir::join_ir::lowering::loop_update_analyzer::{UpdateExpr, UpdateRhs}; use crate::mir::join_ir::{ @@ -64,15 +78,16 @@ use std::collections::HashMap; /// Lower Pattern 4 (Loop with Continue) to JoinIR /// /// # Phase 195-196: Pure JoinIR Fragment Generation +/// # Phase 202-C: JoinValueSpace Unification /// -/// This version generates JoinIR using **local ValueIds only** (0, 1, 2, ...). +/// This version generates JoinIR using **JoinValueSpace-allocated ValueIds**. /// It has NO knowledge of the host function's ValueId space. The boundary mapping /// is handled separately via JoinInlineBoundary. /// /// ## Design Philosophy /// /// - **Box A**: JoinIR Frontend (doesn't know about host ValueIds) -/// - **Box B**: This function - converts to JoinIR with local IDs +/// - **Box B**: This function - converts to JoinIR with JoinValueSpace IDs /// - **Box C**: JoinInlineBoundary - stores boundary info /// - **Box D**: merge_joinir_mir_blocks - injects Copy instructions /// @@ -85,19 +100,20 @@ use std::collections::HashMap; /// /// * `_scope` - LoopScopeShape (reserved for future generic implementation) /// * `condition` - Loop condition AST node (e.g., `i < end`) (Phase 169) -/// * `builder` - MirBuilder for variable resolution (Phase 169) +/// * `_builder` - MirBuilder for variable resolution (Phase 169) /// * `carrier_info` - Phase 196: Carrier metadata for dynamic multi-carrier support /// * `carrier_updates` - Phase 197: Update expressions for each carrier variable +/// * `join_value_space` - Phase 202-C: Unified JoinIR ValueId allocator (Local region: 1000+) /// /// # Returns /// /// * `Ok((JoinModule, ExitMeta))` - Successfully lowered to JoinIR with exit metadata /// * `Err(String)` - Pattern not matched or lowering error /// -/// # Boundary Contract (Phase 196 updated) +/// # Boundary Contract (Phase 196 updated, Phase 202-C: JoinValueSpace) /// /// This function returns a JoinModule with: -/// - **Input slots**: ValueId(0) = i_init, ValueId(1..N) = carrier values +/// - **Input slots**: Local region (1000+) = i_init, carrier values /// - **Output slots**: k_exit returns all carrier values /// - **Exit metadata**: ExitMeta containing all carrier bindings /// - **Caller responsibility**: Create JoinInlineBoundary to map ValueIds @@ -107,6 +123,7 @@ pub(crate) fn lower_loop_with_continue_minimal( _builder: &mut MirBuilder, carrier_info: &CarrierInfo, carrier_updates: &HashMap, + join_value_space: &mut JoinValueSpace, ) -> Result<(JoinModule, ExitMeta), String> { // Phase 170-D-impl-3: Validate that loop condition only uses supported variable scopes // LoopConditionScopeBox checks that loop conditions don't reference loop-body-local variables @@ -127,20 +144,11 @@ pub(crate) fn lower_loop_with_continue_minimal( loop_cond_scope.var_names() ); - // Phase 196: Use local ValueId allocator (sequential from 0) - // JoinIR has NO knowledge of host ValueIds - boundary handled separately - let mut value_counter = 0u32; - let mut alloc_value = || { - let id = ValueId(value_counter); - value_counter += 1; - id - }; - let mut join_module = JoinModule::new(); let carrier_count = carrier_info.carriers.len(); eprintln!( - "[joinir/pattern4] Phase 196: Generating JoinIR for {} carriers: {:?}", + "[joinir/pattern4] Phase 202-C: Generating JoinIR for {} carriers: {:?}", carrier_count, carrier_info.carriers.iter().map(|c| &c.name).collect::>() ); @@ -153,43 +161,43 @@ pub(crate) fn lower_loop_with_continue_minimal( let k_exit_id = JoinFuncId::new(2); // ================================================================== - // ValueId allocation (Phase 196: Dynamic based on carrier count) + // ValueId allocation (Phase 202-C: Dynamic based on carrier count) // ================================================================== // main() parameters: [i_init, carrier1_init, carrier2_init, ...] - let i_init = alloc_value(); // ValueId(0) + let i_init = join_value_space.alloc_local(); let mut carrier_init_ids: Vec = Vec::new(); for _ in 0..carrier_count { - carrier_init_ids.push(alloc_value()); + carrier_init_ids.push(join_value_space.alloc_local()); } - let loop_result = alloc_value(); // result from loop_step + let loop_result = join_value_space.alloc_local(); // result from loop_step // loop_step() parameters: [i_param, carrier1_param, carrier2_param, ...] - let i_param = alloc_value(); + let i_param = join_value_space.alloc_local(); let mut carrier_param_ids: Vec = Vec::new(); for _ in 0..carrier_count { - carrier_param_ids.push(alloc_value()); + carrier_param_ids.push(join_value_space.alloc_local()); } - // Phase 171-fix: Build ConditionEnv for condition lowering - // TODO(Phase 171-fix): This is a temporary workaround. Pattern 3 lowerer should build - // ConditionEnv at the call site and pass it here, similar to Pattern 2. - // For now, we build it internally since this function still needs builder for carrier_info. + // Phase 202-C: Build ConditionEnv for condition lowering using JoinValueSpace + // Loop variable and condition-only variables use Param region (100+) use crate::mir::join_ir::lowering::condition_to_joinir::extract_condition_variables; let mut env = ConditionEnv::new(); - // Add loop parameter to env (ValueId(0) in JoinIR space) + // Add loop parameter to env (using i_param which is in Local region) let loop_var_name = carrier_info.loop_var_name.clone(); - env.insert(loop_var_name.clone(), ValueId(0)); // i_param is ValueId(0) equivalent + env.insert(loop_var_name.clone(), i_param); - // Extract and add condition-only variables + // Extract and add condition-only variables (Param region) let condition_var_names = extract_condition_variables(condition, &[loop_var_name.clone()]); - let mut join_value_counter = carrier_count as u32 + 1; // After loop param and carriers for var_name in &condition_var_names { - let join_id = ValueId(join_value_counter); - join_value_counter += 1; + let join_id = join_value_space.alloc_param(); env.insert(var_name.clone(), join_id); } + // Phase 202-C: Create allocator closure AFTER all direct allocations + // This avoids borrow checker issues + let mut alloc_value = || join_value_space.alloc_local(); + // Phase 169 / Phase 171-fix: Lower condition using condition_to_joinir helper with ConditionEnv let (cond_value, mut cond_instructions) = lower_condition_to_joinir( condition, @@ -502,9 +510,10 @@ pub(crate) fn lower_loop_with_continue_minimal( // Set entry point join_module.entry = Some(main_id); - eprintln!("[joinir/pattern4] Generated JoinIR for Loop with Continue Pattern"); + eprintln!("[joinir/pattern4] Phase 202-C: Generated JoinIR for Loop with Continue Pattern"); eprintln!("[joinir/pattern4] Functions: main, loop_step, k_exit"); eprintln!("[joinir/pattern4] Continue: Select-based skip"); + eprintln!("[joinir/pattern4] ValueId allocation: JoinValueSpace (Local region 1000+)"); eprintln!( "[joinir/pattern4] Carriers: {} ({:?})", carrier_count,