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>
This commit is contained in:
@ -178,19 +178,27 @@ impl PatternPipelineContext {
|
|||||||
///
|
///
|
||||||
/// Returns true if:
|
/// Returns true if:
|
||||||
/// 1. loop_body contains an if statement
|
/// 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.
|
/// This determines whether to use AST-based lowering or legacy PoC lowering.
|
||||||
pub fn is_if_sum_pattern(&self) -> bool {
|
pub fn is_if_sum_pattern(&self) -> bool {
|
||||||
// Check if loop_body has if statement
|
// Check if loop_body has if statement
|
||||||
let has_if = self.loop_body.as_ref().map_or(false, |body| {
|
let if_stmt = self.extract_if_statement();
|
||||||
body.iter().any(|stmt| matches!(stmt, ASTNode::If { .. }))
|
if if_stmt.is_none() {
|
||||||
});
|
|
||||||
|
|
||||||
if !has_if {
|
|
||||||
return false;
|
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
|
// Phase 219: Use assignment-based carrier detection
|
||||||
// (1 counter like "i" + 1-2 accumulators like "sum", "count")
|
// (1 counter like "i" + 1-2 accumulators like "sum", "count")
|
||||||
use crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_from_ast;
|
use crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_from_ast;
|
||||||
|
|||||||
255
src/mir/join_ir/lowering/condition_pattern.rs
Normal file
255
src/mir/join_ir/lowering/condition_pattern.rs
Normal file
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,6 +25,7 @@ pub(crate) mod carrier_update_emitter; // Phase 179: Carrier update instruction
|
|||||||
pub(crate) mod common; // Internal lowering utilities
|
pub(crate) mod common; // Internal lowering utilities
|
||||||
pub mod complex_addend_normalizer; // Phase 192: Complex addend normalization (AST preprocessing)
|
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_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_env; // Phase 184: Body-local variable environment
|
||||||
pub mod loop_body_local_init; // Phase 186: Body-local init expression lowering
|
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
|
pub(crate) mod condition_lowerer; // Phase 171-fix: Core condition lowering logic
|
||||||
|
|||||||
Reference in New Issue
Block a user