feat(joinir): Phase 245C - Function parameter capture + test fix
Extend CapturedEnv to include function parameters used in loop conditions, enabling ExprLowerer to resolve variables like `s` in `loop(p < s.length())`. Phase 245C changes: - function_scope_capture.rs: Add collect_names_in_loop_parts() helper - function_scope_capture.rs: Extend analyze_captured_vars_v2() with param capture logic - function_scope_capture.rs: Add 4 new comprehensive tests Test fix: - expr_lowerer/ast_support.rs: Accept all MethodCall nodes for syntax support (validation happens during lowering in MethodCallLowerer) Problem solved: "Variable not found: s" errors in loop conditions Test results: 924/924 PASS (+13 from baseline 911) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -57,6 +57,17 @@ pub fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Debug: show routing decision when requested
|
||||
if ctx.debug {
|
||||
trace::trace().debug(
|
||||
"pattern2/can_lower",
|
||||
&format!(
|
||||
"pattern_kind={:?}, break_count={}, continue_count={}",
|
||||
ctx.pattern_kind, ctx.features.break_count, ctx.features.continue_count
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 188/Refactor: Use common carrier update validation
|
||||
// Extracts loop variable for dummy carrier creation (not used but required by API)
|
||||
let loop_var_name = match builder.extract_loop_variable_from_condition(ctx.condition) {
|
||||
@ -533,7 +544,7 @@ impl MirBuilder {
|
||||
// This is a VALIDATION-ONLY step. We check if ExprLowerer can handle the condition,
|
||||
// but still use the existing proven lowering path. Future phases will replace actual lowering.
|
||||
{
|
||||
use crate::mir::join_ir::lowering::scope_manager::{Pattern2ScopeManager, ScopeManager};
|
||||
use crate::mir::join_ir::lowering::scope_manager::Pattern2ScopeManager;
|
||||
use crate::mir::join_ir::lowering::expr_lowerer::{ExprLowerer, ExprContext, ExprLoweringError};
|
||||
|
||||
let scope_manager = Pattern2ScopeManager {
|
||||
@ -651,3 +662,81 @@ impl MirBuilder {
|
||||
Ok(Some(void_val))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::{BinaryOperator, LiteralValue, Span};
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
|
||||
fn span() -> Span {
|
||||
Span::unknown()
|
||||
}
|
||||
|
||||
fn var(name: &str) -> ASTNode {
|
||||
ASTNode::Variable {
|
||||
name: name.to_string(),
|
||||
span: span(),
|
||||
}
|
||||
}
|
||||
|
||||
fn lit_i(value: i64) -> ASTNode {
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(value),
|
||||
span: span(),
|
||||
}
|
||||
}
|
||||
|
||||
fn bin(op: BinaryOperator, left: ASTNode, right: ASTNode) -> ASTNode {
|
||||
ASTNode::BinaryOp {
|
||||
operator: op,
|
||||
left: Box::new(left),
|
||||
right: Box::new(right),
|
||||
span: span(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_number_like_loop_is_routed_to_pattern2() {
|
||||
let condition = bin(BinaryOperator::Less, var("p"), var("len"));
|
||||
let break_cond = bin(BinaryOperator::Less, var("digit_pos"), lit_i(0));
|
||||
|
||||
let body = vec![
|
||||
ASTNode::If {
|
||||
condition: Box::new(break_cond),
|
||||
then_body: vec![ASTNode::Break { span: span() }],
|
||||
else_body: None,
|
||||
span: span(),
|
||||
},
|
||||
ASTNode::Assignment {
|
||||
target: Box::new(var("p")),
|
||||
value: Box::new(bin(
|
||||
BinaryOperator::Add,
|
||||
var("p"),
|
||||
lit_i(1),
|
||||
)),
|
||||
span: span(),
|
||||
},
|
||||
// num_str = num_str + ch (string append is allowed by CommonPatternInitializer)
|
||||
ASTNode::Assignment {
|
||||
target: Box::new(var("num_str")),
|
||||
value: Box::new(bin(
|
||||
BinaryOperator::Add,
|
||||
var("num_str"),
|
||||
var("ch"),
|
||||
)),
|
||||
span: span(),
|
||||
},
|
||||
];
|
||||
|
||||
let ctx = LoopPatternContext::new(&condition, &body, "parse_number_like", true);
|
||||
let builder = MirBuilder::new();
|
||||
|
||||
assert_eq!(ctx.pattern_kind, LoopPatternKind::Pattern2Break);
|
||||
assert!(
|
||||
can_lower(&builder, &ctx),
|
||||
"Pattern2 lowerer should accept JsonParser-like break loop"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,9 +127,15 @@ impl MirBuilder {
|
||||
&mut join_value_space,
|
||||
)?;
|
||||
|
||||
eprintln!("[pattern3/if-sum] Phase 220-D: ConditionEnv has {} bindings", condition_bindings.len());
|
||||
trace::trace().debug(
|
||||
"pattern3/if-sum",
|
||||
&format!("ConditionEnv bindings = {}", condition_bindings.len()),
|
||||
);
|
||||
for binding in &condition_bindings {
|
||||
eprintln!(" '{}': HOST {:?} → JoinIR {:?}", binding.name, binding.host_value, binding.join_value);
|
||||
trace::trace().debug(
|
||||
"pattern3/if-sum",
|
||||
&format!(" '{}': HOST {:?} → JoinIR {:?}", binding.name, binding.host_value, binding.join_value),
|
||||
);
|
||||
}
|
||||
|
||||
// Call AST-based if-sum lowerer with ConditionEnv
|
||||
|
||||
@ -360,6 +360,7 @@ mod tests {
|
||||
use crate::ast::{BinaryOperator, LiteralValue, Span};
|
||||
|
||||
// Helper: Create a simple condition (i < 10)
|
||||
#[allow(dead_code)]
|
||||
fn test_condition(var_name: &str) -> ASTNode {
|
||||
ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
|
||||
Reference in New Issue
Block a user