Files
hakorune/src/mir/join_ir/lowering/condition_pattern.rs

256 lines
8.3 KiB
Rust
Raw Normal View History

//! 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));
}
}