From 5ea120ca196ebf522d2ca2cecab9926e07329239 Mon Sep 17 00:00:00 2001 From: tomoaki Date: Mon, 29 Dec 2025 18:23:28 +0900 Subject: [PATCH] phase29an(p4): require skeleton/features in loop facts --- .../control_flow/plan/facts/loop_facts.rs | 65 +++++++++++++++- .../control_flow/plan/planner/build.rs | 76 +++++++++++-------- 2 files changed, 107 insertions(+), 34 deletions(-) diff --git a/src/mir/builder/control_flow/plan/facts/loop_facts.rs b/src/mir/builder/control_flow/plan/facts/loop_facts.rs index 7ca2350c..603f9067 100644 --- a/src/mir/builder/control_flow/plan/facts/loop_facts.rs +++ b/src/mir/builder/control_flow/plan/facts/loop_facts.rs @@ -43,8 +43,8 @@ use super::feature_facts::{LoopFeatureFacts, try_extract_loop_feature_facts}; pub(in crate::mir::builder) struct LoopFacts { pub condition_shape: ConditionShape, pub step_shape: StepShape, - pub skeleton: Option, - pub features: Option, + pub skeleton: SkeletonFacts, + pub features: LoopFeatureFacts, pub scan_with_init: Option, pub split_scan: Option, pub pattern1_simplewhile: Option, @@ -146,8 +146,15 @@ fn try_build_loop_facts_inner( return Ok(None); } - let skeleton = try_extract_loop_skeleton_facts(condition, body)?; - let features = Some(try_extract_loop_feature_facts(body)?); + let skeleton = match try_extract_loop_skeleton_facts(condition, body)? { + Some(skeleton) => skeleton, + None => { + return Err(Freeze::bug( + "loop facts require skeleton when patterns are present", + )); + } + }; + let features = try_extract_loop_feature_facts(body)?; Ok(Some(LoopFacts { condition_shape, @@ -259,6 +266,56 @@ fn try_extract_step_shape(body: &[ASTNode]) -> Result, Freeze> })) } +#[cfg(test)] +mod tests { + use super::{try_build_loop_facts, LoopFacts}; + use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span}; + use crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonKind; + + fn v(name: &str) -> ASTNode { + ASTNode::Variable { + name: name.to_string(), + span: Span::unknown(), + } + } + + fn lit_int(value: i64) -> ASTNode { + ASTNode::Literal { + value: LiteralValue::Integer(value), + span: Span::unknown(), + } + } + + #[test] + fn loop_facts_require_skeleton_and_features_when_present() { + let condition = ASTNode::BinaryOp { + operator: BinaryOperator::Less, + left: Box::new(v("i")), + right: Box::new(lit_int(3)), + span: Span::unknown(), + }; + let body = vec![ASTNode::Assignment { + target: Box::new(v("i")), + value: Box::new(ASTNode::BinaryOp { + operator: BinaryOperator::Add, + left: Box::new(v("i")), + right: Box::new(lit_int(1)), + span: Span::unknown(), + }), + span: Span::unknown(), + }]; + + let facts = try_build_loop_facts(&condition, &body) + .expect("Ok") + .expect("Some"); + assert_eq!(facts.skeleton.kind, SkeletonKind::Loop); + assert!(!facts.features.exit_usage.has_break); + assert!(!facts.features.exit_usage.has_continue); + assert!(!facts.features.exit_usage.has_return); + let _: LoopFacts = facts; + } +} + fn try_extract_scan_with_init_facts( body: &[ASTNode], condition_shape: &ConditionShape, diff --git a/src/mir/builder/control_flow/plan/planner/build.rs b/src/mir/builder/control_flow/plan/planner/build.rs index d95fd457..4ea39ea4 100644 --- a/src/mir/builder/control_flow/plan/planner/build.rs +++ b/src/mir/builder/control_flow/plan/planner/build.rs @@ -70,15 +70,11 @@ pub(in crate::mir::builder) fn build_plan_from_facts_ctx( } fn infer_skeleton_kind(facts: &CanonicalLoopFacts) -> Option { - facts.facts.skeleton.as_ref().map(|s| s.kind) + Some(facts.facts.skeleton.kind) } fn infer_exit_usage(facts: &CanonicalLoopFacts) -> Option { - facts - .facts - .features - .as_ref() - .map(|features| features.exit_usage.clone()) + Some(facts.facts.features.exit_usage.clone()) } fn push_scan_with_init(candidates: &mut CandidateSet, facts: &CanonicalLoopFacts) { @@ -318,8 +314,10 @@ mod tests { let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, - skeleton: None, - features: None, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts::default(), scan_with_init: None, split_scan: Some(SplitScanFacts { s_var: "s".to_string(), @@ -357,8 +355,10 @@ mod tests { let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, - skeleton: None, - features: None, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts::default(), scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -380,8 +380,10 @@ mod tests { let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, - skeleton: None, - features: None, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts::default(), scan_with_init: Some(ScanWithInitFacts { loop_var: "i".to_string(), haystack: "s".to_string(), @@ -413,10 +415,10 @@ mod tests { let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, - skeleton: Some(SkeletonFacts { + skeleton: SkeletonFacts { kind: SkeletonKind::Loop, - }), - features: Some(LoopFeatureFacts { + }, + features: LoopFeatureFacts { exit_usage: ExitUsageFacts { has_break: true, has_continue: false, @@ -424,7 +426,7 @@ mod tests { }, value_join: None, cleanup: None, - }), + }, scan_with_init: Some(ScanWithInitFacts { loop_var: "i".to_string(), haystack: "s".to_string(), @@ -467,8 +469,10 @@ mod tests { let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, - skeleton: None, - features: None, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts::default(), scan_with_init: None, split_scan: None, pattern1_simplewhile: Some(Pattern1SimpleWhileFacts { @@ -531,8 +535,10 @@ mod tests { let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, - skeleton: None, - features: None, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts::default(), scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -594,8 +600,10 @@ mod tests { let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, - skeleton: None, - features: None, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts::default(), scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -643,8 +651,10 @@ mod tests { let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, - skeleton: None, - features: None, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts::default(), scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -693,8 +703,10 @@ mod tests { let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, - skeleton: None, - features: None, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts::default(), scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -751,8 +763,10 @@ mod tests { let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, - skeleton: None, - features: None, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts::default(), scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -815,8 +829,10 @@ mod tests { let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, - skeleton: None, - features: None, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts::default(), scan_with_init: None, split_scan: None, pattern1_simplewhile: None,