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(),

View File

@ -69,14 +69,28 @@ impl JoinLoopTrace {
/// Create a new tracer, reading environment variables.
pub fn new() -> Self {
use crate::config::env::is_joinir_debug;
let varmap_enabled = std::env::var("NYASH_TRACE_VARMAP").is_ok();
let joinir_enabled = is_joinir_debug();
let phi_enabled = std::env::var("NYASH_OPTION_C_DEBUG").is_ok();
let mainline_enabled = std::env::var("NYASH_JOINIR_MAINLINE_DEBUG").is_ok();
let loopform_enabled = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok();
let capture_enabled = std::env::var("NYASH_CAPTURE_DEBUG").is_ok();
// IMPORTANT:
// `NYASH_JOINIR_DEV=1` is a semantic/feature toggle used by the smoke SSOT.
// It must not implicitly enable noisy stderr traces.
//
// Dev traces are enabled only when JoinIR debug is explicitly requested.
let dev_enabled = crate::config::env::joinir_dev_enabled() && joinir_enabled;
Self {
varmap_enabled: std::env::var("NYASH_TRACE_VARMAP").is_ok(),
joinir_enabled: is_joinir_debug(),
phi_enabled: std::env::var("NYASH_OPTION_C_DEBUG").is_ok(),
mainline_enabled: std::env::var("NYASH_JOINIR_MAINLINE_DEBUG").is_ok(),
loopform_enabled: std::env::var("NYASH_LOOPFORM_DEBUG").is_ok(),
dev_enabled: crate::config::env::joinir_dev_enabled(),
capture_enabled: std::env::var("NYASH_CAPTURE_DEBUG").is_ok(),
varmap_enabled,
joinir_enabled,
phi_enabled,
mainline_enabled,
loopform_enabled,
dev_enabled,
capture_enabled,
}
}
@ -87,7 +101,6 @@ impl JoinLoopTrace {
|| self.phi_enabled
|| self.mainline_enabled
|| self.loopform_enabled
|| self.dev_enabled
|| self.capture_enabled
}