diff --git a/src/mir/builder/control_flow.rs b/src/mir/builder/control_flow.rs index 1acda7ee..af704972 100644 --- a/src/mir/builder/control_flow.rs +++ b/src/mir/builder/control_flow.rs @@ -375,18 +375,28 @@ impl super::MirBuilder { /// This bypasses the LoopFrontendBinding JSON path and directly calls /// the Pattern 1 minimal lowerer for apps/tests/loop_min_while.hako /// + /// # Phase 188-Impl-2: Host Variable Integration + /// + /// Extracts the loop variable from the host function (e.g., `i` from `i < 3`) + /// and passes it to the Pattern 1 lowerer along with a ValueId allocator. + /// /// # Pipeline - /// 1. Call simple_while_minimal::lower_simple_while_minimal() → JoinModule - /// 2. convert_join_module_to_mir_with_meta() → MirModule - /// 3. Merge MIR blocks into current_function + /// 1. Extract loop variable name from condition + /// 2. Look up ValueId in host variable_map + /// 3. Create Pattern1Context with host variable + allocator + /// 4. Call simple_while_minimal::lower_simple_while_minimal() → JoinModule + /// 5. convert_join_module_to_mir_with_meta() → MirModule + /// 6. Merge MIR blocks into current_function fn cf_loop_pattern1_minimal( &mut self, - _condition: &ASTNode, + condition: &ASTNode, _body: &[ASTNode], _func_name: &str, debug: bool, ) -> Result, String> { - use crate::mir::join_ir::lowering::simple_while_minimal::lower_simple_while_minimal; + use crate::mir::join_ir::lowering::simple_while_minimal::{ + lower_simple_while_minimal, Pattern1Context, + }; use crate::mir::join_ir_vm_bridge::convert_join_module_to_mir_with_meta; use crate::mir::BasicBlockId; use std::collections::{BTreeMap, BTreeSet}; @@ -395,6 +405,27 @@ impl super::MirBuilder { eprintln!("[cf_loop/joinir/pattern1] Calling Pattern 1 minimal lowerer"); } + // Phase 188-Impl-2: Extract loop variable from condition + // For `i < 3`, extract `i` and look up its ValueId in variable_map + let loop_var_name = self.extract_loop_variable_from_condition(condition)?; + let loop_var_id = self + .variable_map + .get(&loop_var_name) + .copied() + .ok_or_else(|| { + format!( + "[cf_loop/pattern1] Loop variable '{}' not found in variable_map", + loop_var_name + ) + })?; + + if debug { + eprintln!( + "[cf_loop/joinir/pattern1] Loop variable '{}' → {:?}", + loop_var_name, loop_var_id + ); + } + // Create a minimal LoopScopeShape (Phase 188: hardcoded for loop_min_while.hako) // Pattern 1 lowerer ignores the scope anyway, so this is just a placeholder use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; @@ -411,8 +442,16 @@ impl super::MirBuilder { variable_definitions: BTreeMap::new(), }; - // Call Pattern 1 lowerer - let join_module = match lower_simple_while_minimal(scope) { + // Phase 188-Impl-2: Create Pattern1Context with host variable + allocator + // Clone value_gen to move into closure + let mut value_gen_clone = self.value_gen.clone(); + let ctx = Pattern1Context { + loop_var: loop_var_id, + value_allocator: Box::new(move || value_gen_clone.next()), + }; + + // Call Pattern 1 lowerer with host context + let join_module = match lower_simple_while_minimal(scope, Some(ctx)) { Some(module) => module, None => { if debug { @@ -1174,6 +1213,42 @@ impl super::MirBuilder { Ok(result) } + /// Phase 188-Impl-2: Extract loop variable name from condition + /// + /// For `i < 3`, extracts `i`. + /// For `arr.length() > 0`, extracts `arr`. + /// + /// This is a minimal implementation that handles simple comparison patterns. + fn extract_loop_variable_from_condition(&self, condition: &ASTNode) -> Result { + use crate::ast::BinaryOperator; + + match condition { + ASTNode::BinaryOp { + operator, left, .. + } if matches!( + operator, + BinaryOperator::Less + | BinaryOperator::Greater + | BinaryOperator::LessEqual + | BinaryOperator::GreaterEqual + ) => + { + // Binary comparison: extract variable from left side + match &**left { + ASTNode::Variable { name, .. } => Ok(name.clone()), + _ => Err(format!( + "[cf_loop/pattern1] Cannot extract loop variable from condition: {:?}", + condition + )), + } + } + _ => Err(format!( + "[cf_loop/pattern1] Unsupported loop condition pattern: {:?}", + condition + )), + } + } + /// Control-flow: throw pub(super) fn cf_throw(&mut self, expression: ASTNode) -> Result { if self.in_cleanup_block && !self.cleanup_allow_throw { diff --git a/src/mir/join_ir/lowering/loop_to_join.rs b/src/mir/join_ir/lowering/loop_to_join.rs index f7aac932..fe01595e 100644 --- a/src/mir/join_ir/lowering/loop_to_join.rs +++ b/src/mir/join_ir/lowering/loop_to_join.rs @@ -359,7 +359,8 @@ impl LoopToJoinLowerer { eprintln!("[LoopToJoinLowerer] Trying Pattern 1 lowering for {:?}", name); } - if let Some(result) = super::simple_while_minimal::lower_simple_while_minimal(scope.clone()) { + // Phase 188-Impl-2: Use standalone mode (None context) for backward compatibility + if let Some(result) = super::simple_while_minimal::lower_simple_while_minimal(scope.clone(), None) { if self.debug { eprintln!("[LoopToJoinLowerer] Pattern 1 lowering succeeded for {:?}", name); } diff --git a/src/mir/join_ir/lowering/simple_while_minimal.rs b/src/mir/join_ir/lowering/simple_while_minimal.rs index eb7ebe97..078aeff3 100644 --- a/src/mir/join_ir/lowering/simple_while_minimal.rs +++ b/src/mir/join_ir/lowering/simple_while_minimal.rs @@ -49,20 +49,56 @@ use crate::mir::join_ir::{ }; use crate::mir::ValueId; +/// Context passed from the host function to the Pattern 1 lowerer +pub struct Pattern1Context { + /// The loop variable ValueId from the host function (e.g., ValueId(6) for `i`) + pub loop_var: ValueId, + /// ValueId allocator function + pub value_allocator: Box ValueId>, +} + +impl Pattern1Context { + /// Create a standalone context with hardcoded ValueIds (for backward compatibility) + pub fn standalone() -> Self { + let mut counter = 1000u32; + Self { + loop_var: ValueId(counter), + value_allocator: Box::new(move || { + counter += 1; + ValueId(counter) + }), + } + } +} + /// Lower Pattern 1 (Simple While Loop) to JoinIR /// /// This is a minimal implementation for loop_min_while.hako. -/// It generates hardcoded JoinIR for the specific pattern. +/// It generates JoinIR that integrates with the host function's variable bindings. +/// +/// # Phase 188-Impl-2: Host Variable Integration +/// +/// This version accepts the host's loop variable ValueId and allocates fresh IDs +/// for intermediate values. This ensures the generated JoinIR connects properly +/// to the host function's variable bindings. +/// +/// If called without a context (from legacy code), it uses standalone mode with +/// hardcoded ValueIds for backward compatibility. /// /// # Arguments /// /// * `_scope` - LoopScopeShape (reserved for future generic implementation) +/// * `ctx` - Pattern1Context containing host variable bindings (or None for standalone) /// /// # Returns /// /// * `Some(JoinModule)` - Successfully lowered to JoinIR /// * `None` - Pattern not matched (fallback to other lowerers) -pub fn lower_simple_while_minimal(_scope: LoopScopeShape) -> Option { +pub fn lower_simple_while_minimal( + _scope: LoopScopeShape, + ctx: Option, +) -> Option { + let mut ctx = ctx.unwrap_or_else(Pattern1Context::standalone); // Phase 188-Impl-1: Hardcoded JoinIR for loop_min_while.hako // This establishes the infrastructure. Generic implementation in Phase 188-Impl-2+. @@ -76,32 +112,32 @@ pub fn lower_simple_while_minimal(_scope: LoopScopeShape) -> Option let k_exit_id = JoinFuncId::new(2); // ================================================================== - // ValueId allocation (hardcoded for minimal implementation) + // ValueId allocation (Phase 188-Impl-2: Use host variable + allocator) // ================================================================== - let i_init = ValueId(1000); - let loop_result = ValueId(1001); - let const_0_main = ValueId(1002); + // Host's loop variable (e.g., ValueId(6) for `i`) + let i_init = ctx.loop_var; + + // Allocate fresh IDs for local values + let loop_result = (ctx.value_allocator)(); + let const_0_main = (ctx.value_allocator)(); // loop_step locals - let i_param = ValueId(2000); - let const_3 = ValueId(2001); - let cmp_lt = ValueId(2002); - let exit_cond = ValueId(2003); - let const_1 = ValueId(2004); - let i_next = ValueId(2005); + let i_param = (ctx.value_allocator)(); + let const_3 = (ctx.value_allocator)(); + let cmp_lt = (ctx.value_allocator)(); + let exit_cond = (ctx.value_allocator)(); + let const_1 = (ctx.value_allocator)(); + let i_next = (ctx.value_allocator)(); // ================================================================== // main() function // ================================================================== let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![]); - // i_init = 0 - main_func.body.push(JoinInst::Compute(MirLikeInst::Const { - dst: i_init, - value: ConstValue::Integer(0), - })); + // Phase 188-Impl-2: Skip i_init = 0 (host already initialized the variable) + // The host's ValueId (i_init) is already bound to 0 in the host function - // result = loop_step(i_init) + // result = loop_step(i_init) ← Use host's i directly main_func.body.push(JoinInst::Call { func: loop_step_id, args: vec![i_init],