feat(plan): Phase 282 P5 - Pattern3 ExtractionBased Migration & Classification
- Pattern3 extraction logic separated to extractors/pattern3.rs - ExtractionBased strategy: pure functions, Fail-Fast, SSOT - Pattern classification restored (AST-based pattern detection) - Pattern1 extractor migrated (extractors/pattern1.rs) - Documentation: phase-282 README updated, joinir-architecture updated 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -198,10 +198,22 @@ fn detect_if_in_body(body: &[ASTNode]) -> bool {
|
||||
/// Effect: Loops with conditional assignment fall through to Pattern1.
|
||||
///
|
||||
/// Phase 264 P1: TODO - Implement accurate if-sum signature detection.
|
||||
fn detect_if_else_phi_in_body(_body: &[ASTNode]) -> bool {
|
||||
// Phase 264 P0: Conservative - always return false
|
||||
// This prevents simple conditional assignments from being classified as Pattern3IfPhi
|
||||
false
|
||||
fn detect_if_else_phi_in_body(body: &[ASTNode]) -> bool {
|
||||
// Phase 282 P5: Proper if-else PHI detection (re-enabled with ExtractionBased safety)
|
||||
//
|
||||
// This function provides initial classification for Pattern3IfPhi.
|
||||
// The actual validation is done by extractors::pattern3::extract_loop_with_if_phi_parts()
|
||||
// which performs deep checks (PHI assignments, no control flow, etc.)
|
||||
//
|
||||
// Here we just check: Does the loop body contain an if-else statement?
|
||||
// This allows Pattern3 to be attempted, and extraction will validate.
|
||||
|
||||
for stmt in body {
|
||||
if matches!(stmt, ASTNode::If { else_body: Some(_), .. }) {
|
||||
return true; // Found if-else
|
||||
}
|
||||
}
|
||||
false // No if-else found
|
||||
}
|
||||
|
||||
/// Count carrier variables (variables assigned in loop body)
|
||||
|
||||
@ -14,11 +14,13 @@
|
||||
//!
|
||||
//! - Pattern1: ✅ Migrated (Phase 282 P3)
|
||||
//! - Pattern2: ✅ Migrated (Phase 282 P4)
|
||||
//! - Pattern3-5: ⏸ Pending (future phases)
|
||||
//! - Pattern3: ✅ Migrated (Phase 282 P5)
|
||||
//! - Pattern4-5: ⏸ Pending (future phases)
|
||||
//! - Pattern6-7: ✅ Plan-based (Phase 273, different path)
|
||||
//! - Pattern8-9: ✅ Already ExtractionBased (Phase 259/270)
|
||||
|
||||
pub(crate) mod pattern1;
|
||||
pub(crate) mod pattern2; // Phase 282 P4: Pattern2 extraction
|
||||
pub(crate) mod pattern3; // Phase 282 P5: Pattern3 extraction
|
||||
|
||||
// Future: pattern3, pattern4, pattern5
|
||||
// Future: pattern4, pattern5
|
||||
|
||||
@ -55,7 +55,9 @@ pub(crate) fn extract_simple_while_parts(
|
||||
}
|
||||
|
||||
/// Validate condition: 比較演算 (左辺が変数)
|
||||
fn validate_condition_structure(condition: &ASTNode) -> Option<String> {
|
||||
///
|
||||
/// Exported for reuse by Pattern3 (Phase 282 P5)
|
||||
pub(crate) fn validate_condition_structure(condition: &ASTNode) -> Option<String> {
|
||||
match condition {
|
||||
ASTNode::BinaryOp { operator, left, .. } => {
|
||||
// 比較演算子チェック
|
||||
|
||||
@ -0,0 +1,509 @@
|
||||
//! Phase 282 P5: Pattern3 (Loop with If-Else PHI) Extraction
|
||||
|
||||
use crate::ast::{ASTNode, BinaryOperator};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Pattern3Parts {
|
||||
pub loop_var: String, // Loop variable name (e.g., "i")
|
||||
pub merged_var: String, // Primary PHI carrier (e.g., "sum")
|
||||
pub carrier_count: usize, // Validation: 1-2 accumulators
|
||||
// Note: has_else (always true), phi_like_merge (implicit) omitted
|
||||
// AST reused from ctx - no duplication
|
||||
}
|
||||
|
||||
/// Extract Pattern3 (Loop with If-Else PHI) parts
|
||||
///
|
||||
/// # Detection Criteria
|
||||
///
|
||||
/// 1. **Condition**: 比較演算 (left=variable)
|
||||
/// 2. **Body**: At least one if-else statement (else REQUIRED)
|
||||
/// 3. **Assignments**: Both branches assign to same variable(s)
|
||||
/// 4. **Control Flow**: NO break/continue/nested-if (return → Ok(None))
|
||||
///
|
||||
/// # Four-Phase Validation
|
||||
///
|
||||
/// **Phase 1**: Validate condition structure (reuse Pattern1)
|
||||
/// **Phase 2**: Find if-else statement (else branch REQUIRED)
|
||||
/// **Phase 3**: Validate PHI assignments (intersection of then/else)
|
||||
/// **Phase 4**: Validate NO control flow
|
||||
///
|
||||
/// # Fail-Fast Rules
|
||||
///
|
||||
/// - `Ok(Some(parts))`: Pattern3 confirmed
|
||||
/// - `Ok(None)`: Not Pattern3 (structural mismatch)
|
||||
/// - `Err(msg)`: Logic bug (malformed AST)
|
||||
pub(crate) fn extract_loop_with_if_phi_parts(
|
||||
condition: &ASTNode,
|
||||
body: &[ASTNode],
|
||||
) -> Result<Option<Pattern3Parts>, String> {
|
||||
// Phase 1: Validate condition (reuse Pattern1)
|
||||
use super::pattern1::validate_condition_structure;
|
||||
let loop_var = match validate_condition_structure(condition) {
|
||||
Some(var) => var,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
// Phase 2: Find if-else statement
|
||||
let if_stmt = match find_if_else_statement(body) {
|
||||
Some(stmt) => stmt,
|
||||
None => return Ok(None), // No if-else → Not Pattern3
|
||||
};
|
||||
|
||||
// Phase 3: Validate PHI assignments
|
||||
let merged_vars = match extract_phi_assignments(if_stmt) {
|
||||
Some(vars) if !vars.is_empty() => vars,
|
||||
_ => return Ok(None), // No matching assignments → Not Pattern3
|
||||
};
|
||||
|
||||
// Phase 4a: Check for return (early Ok(None) - let other patterns try)
|
||||
if has_return_statement(body) {
|
||||
return Ok(None); // Has return → delegate to other patterns
|
||||
}
|
||||
|
||||
// Phase 4b: Validate NO control flow (break/continue/nested-if only)
|
||||
if has_control_flow_statement(body) {
|
||||
return Ok(None); // Has break/continue/nested-if → Not Pattern3
|
||||
}
|
||||
|
||||
// Extract primary carrier (first merged var)
|
||||
let merged_var = merged_vars[0].clone();
|
||||
let carrier_count = merged_vars.len();
|
||||
|
||||
Ok(Some(Pattern3Parts {
|
||||
loop_var,
|
||||
merged_var,
|
||||
carrier_count,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Find if-else statement in loop body
|
||||
///
|
||||
/// Returns first if statement with else branch.
|
||||
/// Allows multiple if statements - just finds the first if-else.
|
||||
fn find_if_else_statement(body: &[ASTNode]) -> Option<&ASTNode> {
|
||||
for stmt in body {
|
||||
if matches!(stmt, ASTNode::If { else_body: Some(_), .. }) {
|
||||
return Some(stmt); // Found first if-else
|
||||
}
|
||||
}
|
||||
None // No if-else found
|
||||
}
|
||||
|
||||
/// Extract variables assigned in BOTH then and else branches
|
||||
fn extract_phi_assignments(if_stmt: &ASTNode) -> Option<Vec<String>> {
|
||||
let (then_body, else_body) = match if_stmt {
|
||||
ASTNode::If {
|
||||
then_body,
|
||||
else_body: Some(else_body),
|
||||
..
|
||||
} => (then_body, else_body),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let then_assignments = extract_assignment_targets(then_body);
|
||||
let else_assignments = extract_assignment_targets(else_body);
|
||||
|
||||
// Find intersection
|
||||
let mut merged_vars = Vec::new();
|
||||
for var in &then_assignments {
|
||||
if else_assignments.contains(var) {
|
||||
merged_vars.push(var.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if merged_vars.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Use first occurrence (AST order) - deterministic and meaningful SSOT
|
||||
// Don't sort alphabetically - preserve natural appearance order
|
||||
Some(merged_vars)
|
||||
}
|
||||
|
||||
fn extract_assignment_targets(body: &[ASTNode]) -> Vec<String> {
|
||||
let mut targets = Vec::new();
|
||||
for stmt in body {
|
||||
if let ASTNode::Assignment { target, .. } = stmt {
|
||||
if let ASTNode::Variable { name, .. } = target.as_ref() {
|
||||
targets.push(name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
targets
|
||||
}
|
||||
|
||||
/// Check for return statements (Pattern3 version)
|
||||
///
|
||||
/// Return → Ok(None) to let other patterns try.
|
||||
/// This is checked separately before control flow validation.
|
||||
fn has_return_statement(body: &[ASTNode]) -> bool {
|
||||
for stmt in body {
|
||||
if has_return_recursive(stmt) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn has_return_recursive(node: &ASTNode) -> bool {
|
||||
match node {
|
||||
ASTNode::Return { .. } => true,
|
||||
ASTNode::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
then_body.iter().any(has_return_recursive)
|
||||
|| else_body
|
||||
.as_ref()
|
||||
.map_or(false, |b| b.iter().any(has_return_recursive))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for control flow (Pattern3 version)
|
||||
///
|
||||
/// Pattern3 allows ONE if-else (that's the PHI pattern).
|
||||
/// Reject: break, continue, NESTED if only (return checked separately).
|
||||
fn has_control_flow_statement(body: &[ASTNode]) -> bool {
|
||||
for stmt in body {
|
||||
if has_control_flow_recursive_p3(stmt) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn has_control_flow_recursive_p3(node: &ASTNode) -> bool {
|
||||
match node {
|
||||
ASTNode::Break { .. } | ASTNode::Continue { .. } => true,
|
||||
// Return removed - checked separately
|
||||
ASTNode::If {
|
||||
then_body,
|
||||
else_body,
|
||||
..
|
||||
} => {
|
||||
// Check for NESTED if (reject)
|
||||
let has_nested_then = then_body.iter().any(|n| matches!(n, ASTNode::If { .. }));
|
||||
let has_nested_else = else_body
|
||||
.as_ref()
|
||||
.map_or(false, |b| b.iter().any(|n| matches!(n, ASTNode::If { .. })));
|
||||
|
||||
if has_nested_then || has_nested_else {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check control flow INSIDE branches
|
||||
then_body.iter().any(has_control_flow_recursive_p3)
|
||||
|| else_body
|
||||
.as_ref()
|
||||
.map_or(false, |b| b.iter().any(has_control_flow_recursive_p3))
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::{LiteralValue, Span};
|
||||
|
||||
#[test]
|
||||
fn test_extract_if_phi_success() {
|
||||
// loop(i < 3) { if (i > 0) { sum = sum + 1 } else { sum = sum + 0 } i = i + 1 }
|
||||
let condition = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "i".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(3),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
|
||||
let body = vec![
|
||||
ASTNode::If {
|
||||
condition: Box::new(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(),
|
||||
}),
|
||||
then_body: vec![ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
value: Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
else_body: Some(vec![ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
value: Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(0),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}]),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable {
|
||||
name: "i".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
value: Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "i".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
];
|
||||
|
||||
let result = extract_loop_with_if_phi_parts(&condition, &body);
|
||||
assert!(result.is_ok());
|
||||
let parts = result.unwrap();
|
||||
assert!(parts.is_some());
|
||||
let parts = parts.unwrap();
|
||||
assert_eq!(parts.loop_var, "i");
|
||||
assert_eq!(parts.merged_var, "sum");
|
||||
assert_eq!(parts.carrier_count, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_no_else_returns_none() {
|
||||
// loop(i < 3) { if (i > 0) { sum = sum + 1 } i = i + 1 } // No else
|
||||
let condition = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "i".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(3),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
|
||||
let body = vec![ASTNode::If {
|
||||
condition: Box::new(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(),
|
||||
}),
|
||||
then_body: vec![ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
value: Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
else_body: None, // ← No else
|
||||
span: Span::unknown(),
|
||||
}];
|
||||
|
||||
let result = extract_loop_with_if_phi_parts(&condition, &body);
|
||||
assert!(result.is_ok());
|
||||
assert!(result.unwrap().is_none()); // No else → Not Pattern3
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_different_vars_returns_none() {
|
||||
// loop(i < 3) { if (i > 0) { sum = 1 } else { count = 1 } i = i + 1 }
|
||||
let condition = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "i".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(3),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
|
||||
let body = vec![ASTNode::If {
|
||||
condition: Box::new(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(),
|
||||
}),
|
||||
then_body: vec![ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(), // then: sum
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
value: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
else_body: Some(vec![ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable {
|
||||
name: "count".to_string(), // else: count (different!)
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
value: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}]),
|
||||
span: Span::unknown(),
|
||||
}];
|
||||
|
||||
let result = extract_loop_with_if_phi_parts(&condition, &body);
|
||||
assert!(result.is_ok());
|
||||
assert!(result.unwrap().is_none()); // Different vars → Not Pattern3
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_with_break_returns_none() {
|
||||
// loop(i < 3) { if (i > 0) { sum = sum + 1; break } else { sum = sum + 0 } i = i + 1 }
|
||||
let condition = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "i".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(3),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
|
||||
let body = vec![ASTNode::If {
|
||||
condition: Box::new(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(),
|
||||
}),
|
||||
then_body: vec![
|
||||
ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
value: Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(1),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
},
|
||||
ASTNode::Break {
|
||||
span: Span::unknown(),
|
||||
}, // ← break
|
||||
],
|
||||
else_body: Some(vec![ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
value: Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "sum".to_string(),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(0),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}),
|
||||
span: Span::unknown(),
|
||||
}]),
|
||||
span: Span::unknown(),
|
||||
}];
|
||||
|
||||
let result = extract_loop_with_if_phi_parts(&condition, &body);
|
||||
assert!(result.is_ok());
|
||||
assert!(result.unwrap().is_none()); // Has break → Not Pattern3
|
||||
}
|
||||
}
|
||||
@ -19,24 +19,79 @@ use crate::mir::ValueId;
|
||||
|
||||
/// Phase 194: Detection function for Pattern 3
|
||||
///
|
||||
/// Phase 192: Updated to structure-based detection
|
||||
/// Phase 282 P5: Updated to ExtractionBased detection with safety valve
|
||||
///
|
||||
/// Pattern 3 matches:
|
||||
/// - Pattern kind is Pattern3IfPhi (has if-else with PHI, no break/continue)
|
||||
///
|
||||
/// NOTE: Priority is now handled by pattern classification, not router order
|
||||
/// - Pattern kind is Pattern3IfPhi (safety valve, O(1) early rejection)
|
||||
/// - Extraction validates: if-else PHI + NO break/continue/nested-if (return → Ok(None))
|
||||
pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
ctx.pattern_kind == LoopPatternKind::Pattern3IfPhi
|
||||
|
||||
// Step 1: Early rejection guard (safety valve, O(1))
|
||||
if ctx.pattern_kind != LoopPatternKind::Pattern3IfPhi {
|
||||
if ctx.debug {
|
||||
trace::trace().debug(
|
||||
"pattern3/can_lower",
|
||||
&format!("reject: pattern_kind={:?}", ctx.pattern_kind),
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 2: ExtractionBased validation (SSOT, deep check)
|
||||
use super::extractors::pattern3::extract_loop_with_if_phi_parts;
|
||||
|
||||
match extract_loop_with_if_phi_parts(ctx.condition, ctx.body) {
|
||||
Ok(Some(_)) => {
|
||||
if ctx.debug {
|
||||
trace::trace().debug("pattern3/can_lower", "accept: extractable (Phase 282 P5)");
|
||||
}
|
||||
true
|
||||
}
|
||||
Ok(None) => {
|
||||
if ctx.debug {
|
||||
trace::trace().debug(
|
||||
"pattern3/can_lower",
|
||||
"reject: not Pattern3 (no if-else PHI or has control flow)",
|
||||
);
|
||||
}
|
||||
false
|
||||
}
|
||||
Err(e) => {
|
||||
if ctx.debug {
|
||||
trace::trace().debug("pattern3/can_lower", &format!("error: {}", e));
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 194: Lowering function for Pattern 3
|
||||
///
|
||||
/// Phase 282 P5: Re-extracts for SSOT (no caching from can_lower)
|
||||
///
|
||||
/// Wrapper around cf_loop_pattern3_with_if_phi to match router signature
|
||||
pub(crate) fn lower(
|
||||
builder: &mut MirBuilder,
|
||||
ctx: &super::router::LoopPatternContext,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
use super::extractors::pattern3::extract_loop_with_if_phi_parts;
|
||||
|
||||
// Re-extract (SSOT principle - no caching from can_lower)
|
||||
let parts = extract_loop_with_if_phi_parts(ctx.condition, ctx.body)?
|
||||
.ok_or_else(|| "[pattern3] Not a loop with if-phi pattern in lower()".to_string())?;
|
||||
|
||||
if ctx.debug {
|
||||
trace::trace().debug(
|
||||
"pattern3/lower",
|
||||
&format!(
|
||||
"loop_var={}, merged_var={}, carriers={} (Phase 282 P5)",
|
||||
parts.loop_var, parts.merged_var, parts.carrier_count
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Call existing orchestrator implementation (completely unchanged)
|
||||
builder.cf_loop_pattern3_with_if_phi(ctx.condition, ctx.body, ctx.func_name, ctx.debug)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user