feat(normalization): Phase 252 P1 - DebugOutputBox unification + this.methodcall tests

**P1-1: Local Refactor**:
- loop_with_if_phi_if_sum.rs: Replace 8 eprintln! with DebugOutputBox
- Default output: 0 lines (clean)
- With NYASH_JOINIR_DEBUG=1: Rich trace output

**P1-2: Unit Tests** (3/3 PASS):
- test_this_methodcall_in_condition: BoxCall generation
- test_this_methodcall_requires_context: Static box requirement
- test_this_methodcall_disallowed_method: Method whitelist enforcement

**P1-3: v2 Smoke Fixture**:
- phase252_p0_this_methodcall_break_cond_min.hako
- StringUtils.count_leading_digits with this.is_digit break condition
- VM + LLVM integration test scripts

**P1-4: Test Fixes**:
- condition_lowering_box.rs: current_static_box_name: None
- loop_with_break_minimal/tests.rs: current_static_box_name: None

🧪 Tests:
- cargo test condition_lowerer: 3 new tests PASS
- cargo check --lib: PASS (0 errors)

📊 Quick Profile Status:
- json_pp_vm:  PASS
- json_lint_vm:  FAIL (deferred to Phase 253)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-19 20:30:58 +09:00
parent 9336785680
commit 0dd9ec9704
7 changed files with 375 additions and 47 deletions

View File

@ -0,0 +1,28 @@
// Phase 252 P0: this.methodcall in break condition
// Minimal test fixture - simplified to avoid '-' operator (Phase 253 issue)
static box StringUtils {
is_digit(ch) {
return ch >= "0"
}
count_leading_digits(s) {
local i = 0
local len = s.length()
loop(i < len) {
local ch = s.substring(i, i + 1)
if not this.is_digit(ch) {
break
}
i = i + 1
}
return i
}
}
static box Main {
main() {
local result = StringUtils.count_leading_digits("123abc")
return result
}
}

View File

@ -50,6 +50,12 @@ use super::method_call_lowerer::MethodCallLowerer;
/// 1. ConditionEnv (loop parameters, captured variables)
/// 2. LoopBodyLocalEnv (body-local variables like `ch`)
///
/// # Phase 252: This-Method Support
///
/// When lowering conditions in static box methods (e.g., `StringUtils.trim_end/1`),
/// the `current_static_box_name` parameter enables `this.method(...)` calls to be
/// resolved to the appropriate static box method.
///
/// # Example
///
/// ```ignore
@ -73,6 +79,7 @@ use super::method_call_lowerer::MethodCallLowerer;
/// &mut alloc_value,
/// &env,
/// Some(&body_env), // Phase 92 P2-2: Body-local support
/// Some("StringUtils"), // Phase 252: Static box name for this.method
/// )?;
/// ```
pub fn lower_condition_to_joinir(
@ -80,19 +87,27 @@ pub fn lower_condition_to_joinir(
alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv,
body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
current_static_box_name: Option<&str>, // Phase 252
) -> Result<(ValueId, Vec<JoinInst>), String> {
let mut instructions = Vec::new();
let result_value = lower_condition_recursive(cond_ast, alloc_value, env, body_local_env, &mut instructions)?;
let result_value = lower_condition_recursive(
cond_ast,
alloc_value,
env,
body_local_env,
current_static_box_name,
&mut instructions,
)?;
Ok((result_value, instructions))
}
/// Convenience wrapper: lower a condition without body-local support.
/// Convenience wrapper: lower a condition without body-local or static box support.
pub fn lower_condition_to_joinir_no_body_locals(
cond_ast: &ASTNode,
alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv,
) -> Result<(ValueId, Vec<JoinInst>), String> {
lower_condition_to_joinir(cond_ast, alloc_value, env, None)
lower_condition_to_joinir(cond_ast, alloc_value, env, None, None)
}
/// Recursive helper for condition lowering
@ -102,11 +117,17 @@ pub fn lower_condition_to_joinir_no_body_locals(
/// # Phase 92 P2-2
///
/// Added `body_local_env` parameter to support body-local variable resolution.
///
/// # Phase 252
///
/// Added `current_static_box_name` parameter to support `this.method(...)` calls
/// in static box method conditions.
fn lower_condition_recursive(
cond_ast: &ASTNode,
alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv,
body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
current_static_box_name: Option<&str>, // Phase 252
instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
match cond_ast {
@ -123,10 +144,10 @@ fn lower_condition_recursive(
| BinaryOperator::LessEqual
| BinaryOperator::GreaterEqual
| BinaryOperator::Greater => {
lower_comparison(operator, left, right, alloc_value, env, body_local_env, instructions)
lower_comparison(operator, left, right, alloc_value, env, body_local_env, current_static_box_name, instructions)
}
BinaryOperator::And => lower_logical_and(left, right, alloc_value, env, body_local_env, instructions),
BinaryOperator::Or => lower_logical_or(left, right, alloc_value, env, body_local_env, instructions),
BinaryOperator::And => lower_logical_and(left, right, alloc_value, env, body_local_env, current_static_box_name, instructions),
BinaryOperator::Or => lower_logical_or(left, right, alloc_value, env, body_local_env, current_static_box_name, instructions),
_ => Err(format!(
"Unsupported binary operator in condition: {:?}",
operator
@ -138,7 +159,7 @@ fn lower_condition_recursive(
operator: UnaryOperator::Not,
operand,
..
} => lower_not_operator(operand, alloc_value, env, body_local_env, instructions),
} => lower_not_operator(operand, alloc_value, env, body_local_env, current_static_box_name, instructions),
// Phase 92 P2-2: Variables - resolve from ConditionEnv or LoopBodyLocalEnv
ASTNode::Variable { name, .. } => {
@ -158,6 +179,69 @@ fn lower_condition_recursive(
// Literals - emit as constants
ASTNode::Literal { value, .. } => lower_literal(value, alloc_value, instructions),
// Phase 252: MethodCall support (this.method or builtin methods)
ASTNode::MethodCall {
object,
method,
arguments,
..
} => {
// Check if this is a this.method(...) call
match object.as_ref() {
ASTNode::Me { .. } => {
// this.method(...) - requires current_static_box_name
let box_name = current_static_box_name.ok_or_else(|| {
format!(
"this.{}(...) requires current_static_box_name (not in static box context)",
method
)
})?;
// Check if method is allowed in condition context via UserMethodPolicy
if !super::user_method_policy::UserMethodPolicy::allowed_in_condition(box_name, method) {
return Err(format!(
"User-defined method not allowed in loop condition: {}.{}() (not whitelisted)",
box_name, method
));
}
// Lower arguments using lower_for_init whitelist
// (Arguments are value expressions, not conditions, so we use init whitelist)
let mut arg_vals = Vec::new();
for arg_ast in arguments {
let arg_val = lower_value_expression(
arg_ast,
alloc_value,
env,
body_local_env,
current_static_box_name,
instructions,
)?;
arg_vals.push(arg_val);
}
// Emit BoxCall instruction
let dst = alloc_value();
instructions.push(JoinInst::Compute(MirLikeInst::BoxCall {
dst: Some(dst),
box_name: box_name.to_string(),
method: method.clone(),
args: arg_vals,
}));
Ok(dst)
}
_ => {
// Not this.method - treat as value expression (builtin methods via CoreMethodId)
lower_value_expression(object, alloc_value, env, body_local_env, current_static_box_name, instructions)?;
Err(format!(
"MethodCall on non-this object not yet supported in condition: {:?}",
cond_ast
))
}
}
}
_ => Err(format!("Unsupported AST node in condition: {:?}", cond_ast)),
}
}
@ -170,11 +254,12 @@ fn lower_comparison(
alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv,
body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
current_static_box_name: Option<&str>, // Phase 252
instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
// Lower left and right sides
let lhs = lower_value_expression(left, alloc_value, env, body_local_env, instructions)?;
let rhs = lower_value_expression(right, alloc_value, env, body_local_env, instructions)?;
let lhs = lower_value_expression(left, alloc_value, env, body_local_env, current_static_box_name, instructions)?;
let rhs = lower_value_expression(right, alloc_value, env, body_local_env, current_static_box_name, instructions)?;
let dst = alloc_value();
let cmp_op = match operator {
@ -205,11 +290,12 @@ fn lower_logical_and(
alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv,
body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
current_static_box_name: Option<&str>, // Phase 252
instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
// Logical AND: evaluate both sides and combine
let lhs = lower_condition_recursive(left, alloc_value, env, body_local_env, instructions)?;
let rhs = lower_condition_recursive(right, alloc_value, env, body_local_env, instructions)?;
let lhs = lower_condition_recursive(left, alloc_value, env, body_local_env, current_static_box_name, instructions)?;
let rhs = lower_condition_recursive(right, alloc_value, env, body_local_env, current_static_box_name, instructions)?;
let dst = alloc_value();
// Emit BinOp And instruction
@ -230,11 +316,12 @@ fn lower_logical_or(
alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv,
body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
current_static_box_name: Option<&str>, // Phase 252
instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
// Logical OR: evaluate both sides and combine
let lhs = lower_condition_recursive(left, alloc_value, env, body_local_env, instructions)?;
let rhs = lower_condition_recursive(right, alloc_value, env, body_local_env, instructions)?;
let lhs = lower_condition_recursive(left, alloc_value, env, body_local_env, current_static_box_name, instructions)?;
let rhs = lower_condition_recursive(right, alloc_value, env, body_local_env, current_static_box_name, instructions)?;
let dst = alloc_value();
// Emit BinOp Or instruction
@ -254,9 +341,10 @@ fn lower_not_operator(
alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv,
body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
current_static_box_name: Option<&str>, // Phase 252
instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
let operand_val = lower_condition_recursive(operand, alloc_value, env, body_local_env, instructions)?;
let operand_val = lower_condition_recursive(operand, alloc_value, env, body_local_env, current_static_box_name, instructions)?;
let dst = alloc_value();
// Emit UnaryOp Not instruction
@ -308,11 +396,17 @@ fn lower_literal(
///
/// Added `body_local_env` parameter to support body-local variable resolution
/// (e.g., `ch` in `ch == '\\'`).
///
/// # Phase 252
///
/// Added `current_static_box_name` parameter to support `this.method(...)` calls
/// in argument expressions.
pub fn lower_value_expression(
expr: &ASTNode,
alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv,
body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
current_static_box_name: Option<&str>, // Phase 252
instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
match expr {
@ -340,7 +434,7 @@ pub fn lower_value_expression(
left,
right,
..
} => lower_arithmetic_binop(operator, left, right, alloc_value, env, body_local_env, instructions),
} => lower_arithmetic_binop(operator, left, right, alloc_value, env, body_local_env, current_static_box_name, instructions),
// Phase 224-C: MethodCall support with arguments (e.g., s.length(), s.indexOf(ch))
ASTNode::MethodCall {
@ -350,7 +444,7 @@ pub fn lower_value_expression(
..
} => {
// 1. Lower receiver (object) to ValueId
let recv_val = lower_value_expression(object, alloc_value, env, body_local_env, instructions)?;
let recv_val = lower_value_expression(object, alloc_value, env, body_local_env, current_static_box_name, instructions)?;
// 2. Lower method call using MethodCallLowerer (will lower arguments internally)
MethodCallLowerer::lower_for_condition(
@ -378,10 +472,11 @@ fn lower_arithmetic_binop(
alloc_value: &mut dyn FnMut() -> ValueId,
env: &ConditionEnv,
body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
current_static_box_name: Option<&str>, // Phase 252
instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
let lhs = lower_value_expression(left, alloc_value, env, body_local_env, instructions)?;
let rhs = lower_value_expression(right, alloc_value, env, body_local_env, instructions)?;
let lhs = lower_value_expression(left, alloc_value, env, body_local_env, current_static_box_name, instructions)?;
let rhs = lower_value_expression(right, alloc_value, env, body_local_env, current_static_box_name, instructions)?;
let dst = alloc_value();
let bin_op = match operator {
@ -616,7 +711,7 @@ mod tests {
};
// Phase 92 P2-2: Use lower_condition_to_joinir with body_local_env
let result = lower_condition_to_joinir(&ast, &mut alloc_value, &env, Some(&body_local_env));
let result = lower_condition_to_joinir(&ast, &mut alloc_value, &env, Some(&body_local_env), None);
assert!(
result.is_ok(),
"Body-local variable resolution should succeed"
@ -678,7 +773,7 @@ mod tests {
span: Span::unknown(),
};
let result = lower_condition_to_joinir(&ast, &mut alloc_value, &env, Some(&body_local_env));
let result = lower_condition_to_joinir(&ast, &mut alloc_value, &env, Some(&body_local_env), None);
assert!(result.is_ok(), "Variable resolution should succeed");
let (_cond_value, instructions) = result.unwrap();
@ -724,7 +819,7 @@ mod tests {
span: Span::unknown(),
};
let result = lower_condition_to_joinir(&ast, &mut alloc_value, &env, Some(&body_local_env));
let result = lower_condition_to_joinir(&ast, &mut alloc_value, &env, Some(&body_local_env), None);
assert!(result.is_err(), "Undefined variable should fail");
let err = result.unwrap_err();
@ -737,4 +832,145 @@ mod tests {
"Error message should indicate variable was not found"
);
}
/// Phase 252 P1: Test this.methodcall(...) in conditions
///
/// Verifies that user-defined static box method calls work in conditions
#[test]
fn test_this_methodcall_in_condition() {
let env = create_test_env();
let mut value_counter = 2u32;
let mut alloc_value = || {
let id = ValueId(value_counter);
value_counter += 1;
id
};
// AST: not this.is_whitespace(ch)
// Simulates StringUtils.trim_end break condition
let method_call = ASTNode::MethodCall {
object: Box::new(ASTNode::Me {
span: Span::unknown(),
}),
method: "is_whitespace".to_string(),
arguments: vec![ASTNode::Variable {
name: "ch".to_string(),
span: Span::unknown(),
}],
span: Span::unknown(),
};
let ast = ASTNode::UnaryOp {
operator: crate::ast::UnaryOperator::Not,
operand: Box::new(method_call),
span: Span::unknown(),
};
// Register 'ch' variable for the test
let mut env = env;
env.insert("ch".to_string(), ValueId(100));
let result = lower_condition_to_joinir(
&ast,
&mut alloc_value,
&env,
None,
Some("StringUtils"), // Phase 252: static box context
);
assert!(result.is_ok(), "this.methodcall should succeed: {:?}", result);
let (_cond_value, instructions) = result.unwrap();
// Should have: BoxCall for is_whitespace, UnaryOp(Not)
assert!(
instructions.len() >= 2,
"Should generate BoxCall + Not instructions"
);
// Verify BoxCall instruction exists
let has_box_call = instructions.iter().any(|inst| matches!(
inst,
JoinInst::Compute(MirLikeInst::BoxCall { method, .. }) if method == "is_whitespace"
));
assert!(
has_box_call,
"Should generate BoxCall for is_whitespace"
);
}
/// Phase 252 P1: Test this.methodcall fails without static box context
#[test]
fn test_this_methodcall_requires_context() {
let env = create_test_env();
let mut value_counter = 2u32;
let mut alloc_value = || {
let id = ValueId(value_counter);
value_counter += 1;
id
};
// AST: this.is_whitespace(ch)
let ast = ASTNode::MethodCall {
object: Box::new(ASTNode::Me {
span: Span::unknown(),
}),
method: "is_whitespace".to_string(),
arguments: vec![],
span: Span::unknown(),
};
let result = lower_condition_to_joinir(
&ast,
&mut alloc_value,
&env,
None,
None, // No static box context
);
assert!(result.is_err(), "this.methodcall should fail without context");
let err = result.unwrap_err();
assert!(
err.contains("current_static_box_name"),
"Error should mention missing static box context"
);
}
/// Phase 252 P1: Test disallowed method fails
#[test]
fn test_this_methodcall_disallowed_method() {
let env = create_test_env();
let mut value_counter = 2u32;
let mut alloc_value = || {
let id = ValueId(value_counter);
value_counter += 1;
id
};
// AST: this.trim("test") - trim is NOT allowed in conditions
let ast = ASTNode::MethodCall {
object: Box::new(ASTNode::Me {
span: Span::unknown(),
}),
method: "trim".to_string(),
arguments: vec![],
span: Span::unknown(),
};
let result = lower_condition_to_joinir(
&ast,
&mut alloc_value,
&env,
None,
Some("StringUtils"),
);
assert!(result.is_err(), "Disallowed method should fail");
let err = result.unwrap_err();
assert!(
err.contains("not allowed") || err.contains("not whitelisted"),
"Error should indicate method is not allowed: {}",
err
);
}
}

View File

@ -30,6 +30,7 @@ use crate::mir::ValueId;
/// * `loop_var_id` - ValueId of the loop variable in JoinIR space
/// * `scope` - Reference to ScopeManager for variable lookup
/// * `alloc_value` - ValueId allocator function
/// * `current_static_box_name` - Phase 252: Name of the static box being lowered (for this.method)
///
/// # Example
///
@ -39,6 +40,7 @@ use crate::mir::ValueId;
/// loop_var_id: ValueId(1),
/// scope: &scope_manager,
/// alloc_value: &mut alloc_fn,
/// current_static_box_name: Some("StringUtils".to_string()), // Phase 252
/// };
///
/// let value_id = lowerer.lower_condition(&ast, &context)?;
@ -55,6 +57,24 @@ pub struct ConditionContext<'a, S: ScopeManager> {
/// ValueId allocator function
pub alloc_value: &'a mut dyn FnMut() -> ValueId,
/// Phase 252: Name of the static box being lowered (for this.method(...) support)
///
/// When lowering a static box method (e.g., `StringUtils.trim_end/1`),
/// this field contains the box name ("StringUtils"). This allows
/// `this.is_whitespace(...)` to be resolved to `StringUtils.is_whitespace(...)`.
///
/// # Example
///
/// ```ignore
/// // Function: StringUtils.trim_end/1
/// // Condition: not this.is_whitespace(s.substring(i, i + 1))
/// // current_static_box_name: Some("StringUtils")
/// // → Resolves to: StringUtils.is_whitespace(...)
/// ```
///
/// Set to `None` for non-static-box contexts (e.g., `Main.main()` loops).
pub current_static_box_name: Option<String>,
}
/// Phase 244: Unified condition lowering interface
@ -230,6 +250,7 @@ mod tests {
loop_var_id: ValueId(1),
scope: &scope,
alloc_value: &mut alloc_fn,
current_static_box_name: None, // Phase 252: No static box in test
};
// AST: i < 10
@ -285,6 +306,7 @@ mod tests {
loop_var_id: ValueId(1),
scope: &scope,
alloc_value: &mut alloc_fn,
current_static_box_name: None, // Phase 252: No static box in test
};
assert_eq!(context.loop_var_name, "i");

View File

@ -149,6 +149,7 @@ fn test_pattern2_header_condition_via_exprlowerer() {
condition_only_recipe: None, // Phase 93 P0: None for normal loops
body_local_derived_recipe: None, // Phase 94: None for normal loops
balanced_depth_scan_recipe: None, // Phase 107: None for normal loops
current_static_box_name: None, // Phase 252: No static box context in test
});
assert!(result.is_ok(), "ExprLowerer header path should succeed");

View File

@ -32,6 +32,7 @@ use crate::ast::ASTNode;
use crate::mir::join_ir::lowering::carrier_info::JoinFragmentMeta;
use crate::mir::join_ir::lowering::condition_env::ConditionEnv;
use crate::mir::join_ir::lowering::condition_lowerer::lower_value_expression;
use crate::mir::join_ir::lowering::debug_output_box::DebugOutputBox; // Phase 252 P1
use crate::mir::join_ir::lowering::exit_meta_builder::IfSumExitMetaBuilderBox;
#[cfg(debug_assertions)]
use crate::mir::join_ir::lowering::condition_pattern::{
@ -65,7 +66,9 @@ pub fn lower_if_sum_pattern(
cond_env: &ConditionEnv,
join_value_space: &mut JoinValueSpace,
) -> Result<(JoinModule, JoinFragmentMeta), String> {
eprintln!("[joinir/pattern3/if-sum] Starting AST-based if-sum lowering");
// Phase 252 P1: Use DebugOutputBox for unified trace output
let trace = DebugOutputBox::new_dev("joinir/pattern3/if-sum");
trace.log("start", "Starting AST-based if-sum lowering");
// Allocator for extracting condition values
let mut alloc_value = || join_value_space.alloc_local();
@ -86,9 +89,9 @@ pub fn lower_if_sum_pattern(
// Uses cond_env for variable resolution (e.g., `len` in `i < len`)
let (loop_var, loop_op, loop_lhs_val, loop_limit_val, loop_limit_insts) =
extract_loop_condition(loop_condition, &mut alloc_value, cond_env)?;
eprintln!(
"[joinir/pattern3/if-sum] Loop condition: {} {:?} ValueId({})",
loop_var, loop_op, loop_limit_val.0
trace.log(
"loop-cond",
&format!("{} {:?} ValueId({})", loop_var, loop_op, loop_limit_val.0)
);
// Step 2: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=ValueId)
@ -96,23 +99,23 @@ pub fn lower_if_sum_pattern(
// Phase 242-EX-A: Now supports complex LHS
let (if_var, if_op, if_lhs_val, if_value_val, if_value_insts) =
extract_if_condition(if_stmt, &mut alloc_value, cond_env)?;
eprintln!(
"[joinir/pattern3/if-sum] If condition: {} {:?} ValueId({})",
if_var, if_op, if_value_val.0
trace.log(
"if-cond",
&format!("{} {:?} ValueId({})", if_var, if_op, if_value_val.0)
);
// Step 3: Extract then-branch update (e.g., sum = sum + 1 → var="sum", addend=1)
let (update_var, update_addend) = extract_then_update(if_stmt)?;
eprintln!(
"[joinir/pattern3/if-sum] Then update: {} += {}",
update_var, update_addend
trace.log(
"then-update",
&format!("{} += {}", update_var, update_addend)
);
// Step 4: Extract counter update (e.g., i = i + 1 → var="i", step=1)
let (counter_var, counter_step) = extract_counter_update(body, &loop_var)?;
eprintln!(
"[joinir/pattern3/if-sum] Counter update: {} += {}",
counter_var, counter_step
trace.log(
"counter-update",
&format!("{} += {}", counter_var, counter_step)
);
// Step 5: Generate JoinIR
@ -331,18 +334,18 @@ pub fn lower_if_sum_pattern(
// sum_final is the k_exit return value - this is what `return sum` should use
let fragment_meta = JoinFragmentMeta::with_expr_result(sum_final, exit_meta);
eprintln!("[joinir/pattern3/if-sum] Generated AST-based JoinIR");
eprintln!(
"[joinir/pattern3/if-sum] Loop: {} {:?} ValueId({})",
loop_var, loop_op, loop_limit_val.0
trace.log("complete", "Generated AST-based JoinIR");
trace.log(
"summary-loop",
&format!("{} {:?} ValueId({})", loop_var, loop_op, loop_limit_val.0)
);
eprintln!(
"[joinir/pattern3/if-sum] If: {} {:?} ValueId({})",
if_var, if_op, if_value_val.0
trace.log(
"summary-if",
&format!("{} {:?} ValueId({})", if_var, if_op, if_value_val.0)
);
eprintln!(
"[joinir/pattern3/if-sum] Phase 215-2: expr_result={:?}",
sum_final
trace.log(
"summary-result",
&format!("expr_result={:?}", sum_final)
);
Ok((join_module, fragment_meta))
@ -411,7 +414,7 @@ where
column: 1,
},
};
lower_value_expression(&var_node, alloc_value, cond_env, None, &mut limit_instructions)? // Phase 92 P2-2
lower_value_expression(&var_node, alloc_value, cond_env, None, None, &mut limit_instructions)? // Phase 92 P2-2 + Phase 252
}
};
@ -447,10 +450,10 @@ where
// Lower left-hand side (complex expression)
let mut instructions = Vec::new();
let lhs_val = lower_value_expression(left, alloc_value, cond_env, None, &mut instructions)?; // Phase 92 P2-2
let lhs_val = lower_value_expression(left, alloc_value, cond_env, None, None, &mut instructions)?; // Phase 92 P2-2 + Phase 252
// Lower right-hand side
let rhs_val = lower_value_expression(right, alloc_value, cond_env, None, &mut instructions)?; // Phase 92 P2-2
let rhs_val = lower_value_expression(right, alloc_value, cond_env, None, None, &mut instructions)?; // Phase 92 P2-2 + Phase 252
// Extract base variable name from LHS if possible
let var_name = extract_base_variable(left);

View File

@ -0,0 +1,19 @@
#!/bin/bash
# Phase 252 P0: this.methodcall in break condition (LLVM)
set -euo pipefail
HAKO_PATH="apps/tests/phase252_p0_this_methodcall_break_cond_min.hako"
# Phase 252: Test StringUtils.count_leading_digits with this.is_digit break condition
EXPECTED_EXIT=3 # "123abc" has 3 leading digits
NYASH_LLVM_USE_HARNESS=1 $HAKORUNE_BIN --backend llvm "$HAKO_PATH"
actual_exit=$?
if [[ $actual_exit -eq $EXPECTED_EXIT ]]; then
echo "✅ phase252_p0_this_methodcall_break_cond_llvm_exe: PASS (exit=$actual_exit)"
exit 0
else
echo "❌ phase252_p0_this_methodcall_break_cond_llvm_exe: FAIL (expected=$EXPECTED_EXIT, got=$actual_exit)"
exit 1
fi

View File

@ -0,0 +1,19 @@
#!/bin/bash
# Phase 252 P0: this.methodcall in break condition (VM)
set -euo pipefail
HAKO_PATH="apps/tests/phase252_p0_this_methodcall_break_cond_min.hako"
# Phase 252: Test StringUtils.count_leading_digits with this.is_digit break condition
EXPECTED_EXIT=3 # "123abc" has 3 leading digits
$HAKORUNE_BIN --backend vm "$HAKO_PATH"
actual_exit=$?
if [[ $actual_exit -eq $EXPECTED_EXIT ]]; then
echo "✅ phase252_p0_this_methodcall_break_cond_vm: PASS (exit=$actual_exit)"
exit 0
else
echo "❌ phase252_p0_this_methodcall_break_cond_vm: FAIL (expected=$EXPECTED_EXIT, got=$actual_exit)"
exit 1
fi