Files
hakorune/src/mir/join_ir/lowering/condition_pattern.rs
nyash-codex d0d2a30c56 fix(joinir): Phase 219 regression fix - ConditionPatternBox
Introduced ConditionPatternBox to detect if condition complexity:
- Simple comparisons (var CmpOp literal): use AST-based if-sum lowerer
- Complex conditions (BinaryOp, etc.): fallback to legacy P3 lowerer

This fixes loop_if_phi.hako which was broken by Phase 219's
is_if_sum_pattern() changes.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 03:54:17 +09:00

256 lines
8.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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