diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs b/src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs index a65ba60d..ea3004cb 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs @@ -178,19 +178,27 @@ impl PatternPipelineContext { /// /// Returns true if: /// 1. loop_body contains an if statement - /// 2. carrier composition matches if-sum pattern (1 counter + 1-2 accumulators) + /// 2. if condition is a simple comparison (var CmpOp literal) - Phase 219-fix + /// 3. carrier composition matches if-sum pattern (1 counter + 1-2 accumulators) /// /// This determines whether to use AST-based lowering or legacy PoC lowering. pub fn is_if_sum_pattern(&self) -> bool { // Check if loop_body has if statement - let has_if = self.loop_body.as_ref().map_or(false, |body| { - body.iter().any(|stmt| matches!(stmt, ASTNode::If { .. })) - }); - - if !has_if { + let if_stmt = self.extract_if_statement(); + if if_stmt.is_none() { return false; } + // Phase 219-fix: Check if if condition is a simple comparison + // Complex conditions (e.g., i % 2 == 1) → fallback to legacy mode + if let Some(ASTNode::If { condition, .. }) = if_stmt { + use crate::mir::join_ir::lowering::condition_pattern::is_simple_comparison; + if !is_simple_comparison(condition) { + // Complex condition → legacy mode (PoC lowering) + return false; + } + } + // Phase 219: Use assignment-based carrier detection // (1 counter like "i" + 1-2 accumulators like "sum", "count") use crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_from_ast; diff --git a/src/mir/join_ir/lowering/condition_pattern.rs b/src/mir/join_ir/lowering/condition_pattern.rs new file mode 100644 index 00000000..84adfa67 --- /dev/null +++ b/src/mir/join_ir/lowering/condition_pattern.rs @@ -0,0 +1,255 @@ +//! ConditionPatternBox: if条件パターン判定 +//! +//! Phase 219 regression fix: if条件が「単純比較」かどうかを判定 +//! +//! ## 問題 +//! +//! Phase 219で `is_if_sum_pattern()` をAST-basedに変更した結果、 +//! `loop_if_phi.hako` のような複雑条件 (`i % 2 == 1`) を +//! if-sumパターンと誤判定してしまう問題が発生。 +//! +//! ## 解決策 +//! +//! ConditionPatternBox を導入し、if条件が「単純比較」かどうかを判定する。 +//! AST-based lowerer は単純比較のみ処理可能とし、複雑条件はlegacy modeへフォールバック。 +//! +//! ## 単純比較の定義 +//! +//! 以下のパターンのみ if-sum lowerer で処理可能: +//! - `var > literal` (e.g., `i > 0`) +//! - `var < literal` (e.g., `i < 10`) +//! - `var >= literal` +//! - `var <= literal` +//! - `var == literal` +//! - `var != literal` +//! +//! ## 複雑条件(legacy mode へフォールバック) +//! +//! - `i % 2 == 1` (BinaryOp in LHS) +//! - `a && b` (複合条件) +//! - `method_call() > 0` (MethodCall) +//! - その他 + +use crate::ast::{ASTNode, BinaryOperator}; + +/// if条件のパターン種別 +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ConditionPattern { + /// 単純比較: var CmpOp literal (e.g., i > 0) + SimpleComparison, + /// 複雑条件: BinaryOp, MethodCall, etc. + Complex, +} + +/// if条件ASTを分析してパターンを判定 +/// +/// # Arguments +/// +/// * `cond` - if条件のASTノード +/// +/// # Returns +/// +/// - `ConditionPattern::SimpleComparison` - 単純比較(AST-based lowerer で処理可能) +/// - `ConditionPattern::Complex` - 複雑条件(legacy mode へフォールバック) +/// +/// # Examples +/// +/// ```rust +/// // Simple: i > 0 +/// let simple = ASTNode::BinaryOp { +/// operator: BinaryOperator::Greater, +/// left: Box::new(ASTNode::Variable { name: "i".to_string(), span: Span::unknown() }), +/// right: Box::new(ASTNode::Literal { value: LiteralValue::Integer(0), span: Span::unknown() }), +/// span: Span::unknown(), +/// }; +/// assert_eq!(analyze_condition_pattern(&simple), ConditionPattern::SimpleComparison); +/// +/// // Complex: i % 2 == 1 +/// let complex = ASTNode::BinaryOp { +/// operator: BinaryOperator::Equal, +/// left: Box::new(ASTNode::BinaryOp { ... }), // BinaryOp in LHS +/// right: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), +/// span: Span::unknown(), +/// }; +/// assert_eq!(analyze_condition_pattern(&complex), ConditionPattern::Complex); +/// ``` +pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern { + match cond { + // Comparison operators: ==, !=, <, >, <=, >= + ASTNode::BinaryOp { operator, left, right, .. } => { + // Check if operator is a comparison + let is_comparison = matches!( + operator, + BinaryOperator::Equal + | BinaryOperator::NotEqual + | BinaryOperator::Less + | BinaryOperator::Greater + | BinaryOperator::LessEqual + | BinaryOperator::GreaterEqual + ); + + if !is_comparison { + // Not a comparison (e.g., And, Or) → Complex + return ConditionPattern::Complex; + } + + // Check if LHS is a simple variable + let left_is_var = matches!(left.as_ref(), ASTNode::Variable { .. }); + + // Check if RHS is a literal + let right_is_literal = matches!(right.as_ref(), ASTNode::Literal { .. }); + + if left_is_var && right_is_literal { + ConditionPattern::SimpleComparison + } else { + // Complex LHS/RHS (e.g., i % 2 == 1, method_call() > 0) + ConditionPattern::Complex + } + } + // Any other node type → Complex + _ => ConditionPattern::Complex, + } +} + +/// if条件が単純比較かどうか +/// +/// # Arguments +/// +/// * `cond` - if条件のASTノード +/// +/// # Returns +/// +/// `true` if 単純比較(AST-based lowerer で処理可能) +/// +/// # Examples +/// +/// ```rust +/// // i > 0 → true +/// assert!(is_simple_comparison(&simple_condition)); +/// +/// // i % 2 == 1 → false +/// assert!(!is_simple_comparison(&complex_condition)); +/// ``` +pub fn is_simple_comparison(cond: &ASTNode) -> bool { + analyze_condition_pattern(cond) == ConditionPattern::SimpleComparison +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ast::{LiteralValue, Span}; + + // Helper: Create a simple variable node + fn var(name: &str) -> ASTNode { + ASTNode::Variable { + name: name.to_string(), + span: Span::unknown(), + } + } + + // Helper: Create an integer literal node + fn int_lit(value: i64) -> ASTNode { + ASTNode::Literal { + value: LiteralValue::Integer(value), + span: Span::unknown(), + } + } + + // Helper: Create a BinaryOp node + fn binop(op: BinaryOperator, left: ASTNode, right: ASTNode) -> ASTNode { + ASTNode::BinaryOp { + operator: op, + left: Box::new(left), + right: Box::new(right), + span: Span::unknown(), + } + } + + #[test] + fn test_simple_comparison_greater() { + // i > 0 + let cond = binop(BinaryOperator::Greater, var("i"), int_lit(0)); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); + assert!(is_simple_comparison(&cond)); + } + + #[test] + fn test_simple_comparison_less() { + // i < 10 + let cond = binop(BinaryOperator::Less, var("i"), int_lit(10)); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); + assert!(is_simple_comparison(&cond)); + } + + #[test] + fn test_simple_comparison_equal() { + // i == 5 + let cond = binop(BinaryOperator::Equal, var("i"), int_lit(5)); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); + assert!(is_simple_comparison(&cond)); + } + + #[test] + fn test_simple_comparison_not_equal() { + // i != 0 + let cond = binop(BinaryOperator::NotEqual, var("i"), int_lit(0)); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); + assert!(is_simple_comparison(&cond)); + } + + #[test] + fn test_complex_binop_in_lhs() { + // i % 2 == 1 (BinaryOp in LHS) + let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2)); + let cond = binop(BinaryOperator::Equal, lhs, int_lit(1)); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); + assert!(!is_simple_comparison(&cond)); + } + + #[test] + fn test_complex_binop_in_rhs() { + // i == a + b (BinaryOp in RHS) + let rhs = binop(BinaryOperator::Add, var("a"), var("b")); + let cond = binop(BinaryOperator::Equal, var("i"), rhs); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); + assert!(!is_simple_comparison(&cond)); + } + + #[test] + fn test_complex_logical_and() { + // a && b (logical And) + let cond = binop(BinaryOperator::And, var("a"), var("b")); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); + assert!(!is_simple_comparison(&cond)); + } + + #[test] + fn test_complex_logical_or() { + // a || b (logical Or) + let cond = binop(BinaryOperator::Or, var("a"), var("b")); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); + assert!(!is_simple_comparison(&cond)); + } + + #[test] + fn test_complex_method_call() { + // method_call() > 0 (MethodCall in LHS) + let method_call = ASTNode::MethodCall { + object: Box::new(var("obj")), + method: "get".to_string(), + arguments: vec![], + span: Span::unknown(), + }; + let cond = binop(BinaryOperator::Greater, method_call, int_lit(0)); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); + assert!(!is_simple_comparison(&cond)); + } + + #[test] + fn test_complex_non_binary_op() { + // Just a variable (not a comparison) + let cond = var("condition"); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); + assert!(!is_simple_comparison(&cond)); + } +} diff --git a/src/mir/join_ir/lowering/mod.rs b/src/mir/join_ir/lowering/mod.rs index 78552903..c169c446 100644 --- a/src/mir/join_ir/lowering/mod.rs +++ b/src/mir/join_ir/lowering/mod.rs @@ -25,6 +25,7 @@ pub(crate) mod carrier_update_emitter; // Phase 179: Carrier update instruction pub(crate) mod common; // Internal lowering utilities pub mod complex_addend_normalizer; // Phase 192: Complex addend normalization (AST preprocessing) pub mod condition_env; // Phase 171-fix: Condition expression environment +pub mod condition_pattern; // Phase 219-fix: If condition pattern detection (simple vs complex) pub mod loop_body_local_env; // Phase 184: Body-local variable environment pub mod loop_body_local_init; // Phase 186: Body-local init expression lowering pub(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic