phase29ak(p2): gate pattern8 facts by static box ctx

This commit is contained in:
2025-12-29 14:49:48 +09:00
parent fc90c74bd3
commit 9a686cd510
6 changed files with 177 additions and 11 deletions

View File

@ -74,7 +74,7 @@ pub(in crate::mir::builder) fn try_build_loop_facts(
condition: &ASTNode,
body: &[ASTNode],
) -> Result<Option<LoopFacts>, Freeze> {
try_build_loop_facts_inner(condition, body, true)
try_build_loop_facts_inner(condition, body, true, true)
}
pub(in crate::mir::builder) fn try_build_loop_facts_with_ctx(
@ -87,13 +87,15 @@ pub(in crate::mir::builder) fn try_build_loop_facts_with_ctx(
Some(_) => false,
None => true,
};
try_build_loop_facts_inner(condition, body, allow_pattern1)
let allow_pattern8 = !ctx.in_static_box;
try_build_loop_facts_inner(condition, body, allow_pattern1, allow_pattern8)
}
fn try_build_loop_facts_inner(
condition: &ASTNode,
body: &[ASTNode],
allow_pattern1: bool,
allow_pattern8: bool,
) -> Result<Option<LoopFacts>, Freeze> {
// Phase 29ai P4/P7: keep Facts conservative; only return Some when we can
// build a concrete pattern fact set (no guesses / no hardcoded names).
@ -112,12 +114,16 @@ fn try_build_loop_facts_inner(
let pattern4_continue = try_extract_pattern4_continue_facts(condition, body)?;
let pattern5_infinite_early_exit =
try_extract_pattern5_infinite_early_exit_facts(condition, body)?;
let pattern8_bool_predicate_scan = try_extract_pattern8_bool_predicate_scan_facts(
condition,
body,
&condition_shape,
&step_shape,
)?;
let pattern8_bool_predicate_scan = if allow_pattern8 {
try_extract_pattern8_bool_predicate_scan_facts(
condition,
body,
&condition_shape,
&step_shape,
)?
} else {
None
};
let pattern9_accum_const_loop = try_extract_pattern9_accum_const_loop_facts(condition, body)?;
let pattern2_break = try_extract_pattern2_break_facts(condition, body)?;
let pattern2_loopbodylocal = try_extract_pattern2_loopbodylocal_facts(condition, body)?;
@ -808,6 +814,96 @@ mod tests {
assert!(facts.pattern1_simplewhile.is_some());
}
#[test]
fn loopfacts_ctx_blocks_pattern8_in_static_box() {
let condition = ASTNode::BinaryOp {
operator: BinaryOperator::Less,
left: Box::new(v("i")),
right: Box::new(ASTNode::MethodCall {
object: Box::new(v("s")),
method: "length".to_string(),
arguments: vec![],
span: Span::unknown(),
}),
span: Span::unknown(),
};
let predicate_if = ASTNode::If {
condition: Box::new(ASTNode::UnaryOp {
operator: crate::ast::UnaryOperator::Not,
operand: Box::new(ASTNode::MethodCall {
object: Box::new(ASTNode::Me {
span: Span::unknown(),
}),
method: "is_digit".to_string(),
arguments: vec![ASTNode::MethodCall {
object: Box::new(v("s")),
method: "substring".to_string(),
arguments: vec![
v("i"),
ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(v("i")),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
},
],
span: Span::unknown(),
}],
span: Span::unknown(),
}),
span: Span::unknown(),
}),
then_body: vec![ASTNode::Return {
value: Some(Box::new(ASTNode::Literal {
value: LiteralValue::Bool(false),
span: Span::unknown(),
})),
span: Span::unknown(),
}],
else_body: None,
span: Span::unknown(),
};
let step = ASTNode::Assignment {
target: Box::new(v("i")),
value: Box::new(ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left: Box::new(v("i")),
right: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
}),
span: Span::unknown(),
};
let body = vec![predicate_if, step];
let allow_ctx = PlannerContext {
pattern_kind: None,
in_static_box: false,
debug: false,
};
let block_ctx = PlannerContext {
pattern_kind: None,
in_static_box: true,
debug: false,
};
let allow = try_build_loop_facts_with_ctx(&allow_ctx, &condition, &body).expect("Ok");
assert!(allow
.as_ref()
.and_then(|facts| facts.pattern8_bool_predicate_scan.as_ref())
.is_some());
let blocked = try_build_loop_facts_with_ctx(&block_ctx, &condition, &body).expect("Ok");
assert!(blocked
.as_ref()
.and_then(|facts| facts.pattern8_bool_predicate_scan.as_ref())
.is_none());
}
#[test]
fn loopfacts_ok_none_when_condition_not_supported() {
let condition = v("i"); // not `i < n`