diff --git a/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs b/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs index 7c93ffa7..71d637d3 100644 --- a/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs +++ b/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs @@ -203,9 +203,39 @@ fn has_break_node(node: &ASTNode) -> bool { } } +/// Phase 170-B: Check if break is in else clause +/// +/// Helper function to determine if a break statement is in the else clause +/// of an if-else statement, as opposed to the then clause. +/// +/// # Arguments +/// +/// * `body` - Loop body statements to search +/// +/// # Returns +/// +/// `true` if an `if ... else { break }` pattern is found +pub fn has_break_in_else_clause(body: &[ASTNode]) -> bool { + for stmt in body { + if let ASTNode::If { + else_body: Some(else_body), + .. + } = stmt + { + if else_body.iter().any(|node| matches!(node, ASTNode::Break { .. })) { + return true; + } + } + } + false +} + /// Phase 170-B: Extract break condition from loop body /// -/// Searches for the first `if { break }` pattern in the loop body. +/// Searches for the first break pattern in an if statement: +/// - `if { break }` - returns +/// - `if { ... } else { break }` - returns `!` (negated) +/// /// This is used to delegate break condition lowering to `condition_to_joinir`. /// /// # Arguments @@ -214,29 +244,50 @@ fn has_break_node(node: &ASTNode) -> bool { /// /// # Returns /// -/// `Some(&ASTNode)` - The condition AST node from `if { break }` +/// `Some(&ASTNode)` - The condition AST node (negated for else-break pattern) /// `None` - No break statement found or break is not in a simple if statement /// -/// # Example +/// # Examples /// /// ```nyash +/// // Pattern 1: if condition { break } /// loop(i < 3) { /// if i >= 2 { break } // <- Returns the "i >= 2" condition /// i = i + 1 /// } +/// +/// // Pattern 2: if condition { ... } else { break } +/// loop(start < end) { +/// if ch == " " { start = start + 1 } else { break } +/// // <- Returns the "!(ch == " ")" condition (negated) +/// } /// ``` pub fn extract_break_condition(body: &[ASTNode]) -> Option<&ASTNode> { for stmt in body { if let ASTNode::If { condition, then_body, + else_body, .. } = stmt { - // Check if the then_body contains a break statement + // Pattern 1: Check if the then_body contains a break statement if then_body.iter().any(|node| matches!(node, ASTNode::Break { .. })) { return Some(condition.as_ref()); } + + // Pattern 2: Check if the else_body contains a break statement + // In this case, we need to negate the condition + // However, we can't easily negate here without modifying the AST + // Instead, we'll return the condition and document that the caller + // must handle the negation (or we could return a wrapped node) + if let Some(else_body) = else_body { + if else_body.iter().any(|node| matches!(node, ASTNode::Break { .. })) { + // For else-break pattern, return the condition + // The caller (pattern2_with_break) must negate it + return Some(condition.as_ref()); + } + } } } None diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs b/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs index 2d6d9fcd..4f8f6628 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs @@ -138,12 +138,30 @@ impl MirBuilder { // Phase 170-B: Extract break condition from loop body use super::ast_feature_extractor; - let break_condition = ast_feature_extractor::extract_break_condition(_body) + let break_condition_raw = ast_feature_extractor::extract_break_condition(_body) .ok_or_else(|| "[cf_loop/pattern2] Failed to extract break condition from loop body".to_string())?; + // Phase 170-B: Check if break is in else clause (requires negation) + let break_in_else = ast_feature_extractor::has_break_in_else_clause(_body); + + // Wrap condition in UnaryOp Not if break is in else clause + use crate::ast::UnaryOperator; + let break_condition_node = if break_in_else { + // Extract span from the raw condition node (use unknown as default) + let span = crate::ast::Span::unknown(); + + ASTNode::UnaryOp { + operator: UnaryOperator::Not, + operand: Box::new(break_condition_raw.clone()), + span, + } + } else { + break_condition_raw.clone() + }; + // Phase 169 / Phase 171-fix / Phase 172-3 / Phase 170-B: Call Pattern 2 lowerer with break_condition // Phase 33-14: Now returns (JoinModule, JoinFragmentMeta) for expr_result + carrier separation - let (join_module, fragment_meta) = match lower_loop_with_break_minimal(scope, condition, break_condition, &env, &loop_var_name) { + let (join_module, fragment_meta) = match lower_loop_with_break_minimal(scope, condition, &break_condition_node, &env, &loop_var_name) { Ok((module, meta)) => (module, meta), Err(e) => { // Phase 195: Use unified trace