refactor(mir): phase260 p0.1 strangler hardening + smoke fixtures

This commit is contained in:
2025-12-21 05:47:37 +09:00
parent 4dfe3349bf
commit 1fe5be347d
28 changed files with 442 additions and 504 deletions

View File

@ -10,6 +10,8 @@
//! silently fall back to an unrelated route.
use crate::ast::ASTNode;
use crate::ast::UnaryOperator;
use crate::ast::LiteralValue;
use crate::mir::loop_pattern_detection::break_condition_analyzer::BreakConditionAnalyzer;
use super::policies::{loop_true_read_digits_policy, PolicyDecision};
@ -24,6 +26,21 @@ pub(crate) struct Pattern2BreakConditionRouting {
pub(crate) struct Pattern2BreakConditionPolicyRouterBox;
impl Pattern2BreakConditionPolicyRouterBox {
fn negate_condition(condition: &ASTNode) -> ASTNode {
match condition {
ASTNode::UnaryOp {
operator: UnaryOperator::Not,
operand,
..
} => operand.as_ref().clone(),
other => ASTNode::UnaryOp {
operator: UnaryOperator::Not,
operand: Box::new(other.clone()),
span: other.span(),
},
}
}
pub(crate) fn route(condition: &ASTNode, body: &[ASTNode]) -> Result<Pattern2BreakConditionRouting, String> {
// loop(true) read-digits family:
// - multiple breaks exist; normalize as:
@ -36,7 +53,24 @@ impl Pattern2BreakConditionPolicyRouterBox {
}),
PolicyDecision::Reject(reason) => Err(format!("[cf_loop/pattern2] {}", reason)),
PolicyDecision::None => Ok(Pattern2BreakConditionRouting {
// Phase 260 P0.1: If the loop has an explicit header condition and the body
// does not contain a top-level break-guard pattern, the exit condition is
// structurally derived as `!(loop_condition)`.
break_condition_node: BreakConditionAnalyzer::extract_break_condition_node(body)
.or_else(|_| {
if matches!(
condition,
ASTNode::Literal {
value: LiteralValue::Bool(true),
..
}
) {
Err("[cf_loop/pattern2] loop(true) requires a break guard pattern"
.to_string())
} else {
Ok(Self::negate_condition(condition))
}
})
.map_err(|_| {
"[cf_loop/pattern2] Failed to extract break condition from loop body".to_string()
})?,
@ -46,4 +80,3 @@ impl Pattern2BreakConditionPolicyRouterBox {
}
}
}

View File

@ -853,15 +853,15 @@ mod tests {
#[test]
fn test_is_const_step_pattern_negative() {
// i - 1
// i - j (non-const step)
let value = ASTNode::BinaryOp {
operator: BinaryOperator::Subtract,
left: Box::new(ASTNode::Variable {
name: "i".to_string(),
span: Span::unknown(),
}),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
right: Box::new(ASTNode::Variable {
name: "j".to_string(),
span: Span::unknown(),
}),
span: Span::unknown(),