From 5ac2f35866102930c27a4a67a404269675cd23fc Mon Sep 17 00:00:00 2001 From: tomoaki Date: Mon, 29 Dec 2025 18:59:16 +0900 Subject: [PATCH] phase29an(p7): add canonical projections for skeleton/features --- .../plan/normalize/canonicalize.rs | 113 +++++++++++++++++- .../control_flow/plan/planner/build.rs | 2 +- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/src/mir/builder/control_flow/plan/normalize/canonicalize.rs b/src/mir/builder/control_flow/plan/normalize/canonicalize.rs index 34be4e0e..1715d387 100644 --- a/src/mir/builder/control_flow/plan/normalize/canonicalize.rs +++ b/src/mir/builder/control_flow/plan/normalize/canonicalize.rs @@ -2,13 +2,124 @@ #![allow(dead_code)] +use crate::mir::builder::control_flow::plan::facts::feature_facts::ExitUsageFacts; +use crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonKind; use crate::mir::builder::control_flow::plan::facts::LoopFacts; #[derive(Debug, Clone)] pub(in crate::mir::builder) struct CanonicalLoopFacts { pub facts: LoopFacts, + pub skeleton_kind: SkeletonKind, + pub exit_usage: ExitUsageFacts, } pub(in crate::mir::builder) fn canonicalize_loop_facts(facts: LoopFacts) -> CanonicalLoopFacts { - CanonicalLoopFacts { facts } + CanonicalLoopFacts { + skeleton_kind: facts.skeleton.kind, + exit_usage: facts.features.exit_usage.clone(), + facts, + } +} + +#[cfg(test)] +mod tests { + use super::canonicalize_loop_facts; + use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span}; + use crate::mir::builder::control_flow::plan::facts::loop_facts::LoopFacts; + use crate::mir::builder::control_flow::plan::facts::scan_shapes::{ + ConditionShape, StepShape, + }; + 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 canonical_projects_skeleton_and_exit_usage() { + let facts = LoopFacts { + condition_shape: ConditionShape::Unknown, + step_shape: StepShape::Unknown, + skeleton: crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: + crate::mir::builder::control_flow::plan::facts::feature_facts::LoopFeatureFacts { + exit_usage: + crate::mir::builder::control_flow::plan::facts::feature_facts::ExitUsageFacts { + has_break: true, + has_continue: true, + has_return: true, + }, + value_join: None, + cleanup: None, + }, + scan_with_init: None, + split_scan: None, + pattern1_simplewhile: None, + pattern3_ifphi: None, + pattern4_continue: None, + pattern5_infinite_early_exit: None, + pattern8_bool_predicate_scan: None, + pattern9_accum_const_loop: None, + pattern2_break: None, + pattern2_loopbodylocal: None, + }; + let canonical = canonicalize_loop_facts(facts); + assert_eq!(canonical.skeleton_kind, SkeletonKind::Loop); + assert!(canonical.exit_usage.has_break); + assert!(canonical.exit_usage.has_continue); + assert!(canonical.exit_usage.has_return); + } + + #[test] + fn canonical_preserves_loop_facts_content() { + let facts = LoopFacts { + condition_shape: ConditionShape::Unknown, + step_shape: StepShape::Unknown, + skeleton: crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: + crate::mir::builder::control_flow::plan::facts::feature_facts::LoopFeatureFacts::default(), + scan_with_init: None, + split_scan: None, + pattern1_simplewhile: Some( + crate::mir::builder::control_flow::plan::facts::pattern1_simplewhile_facts::Pattern1SimpleWhileFacts { + loop_var: "i".to_string(), + condition: ASTNode::BinaryOp { + operator: BinaryOperator::Less, + left: Box::new(v("i")), + right: Box::new(lit_int(3)), + span: Span::unknown(), + }, + loop_increment: ASTNode::BinaryOp { + operator: BinaryOperator::Add, + left: Box::new(v("i")), + right: Box::new(lit_int(1)), + span: Span::unknown(), + }, + }, + ), + pattern3_ifphi: None, + pattern4_continue: None, + pattern5_infinite_early_exit: None, + pattern8_bool_predicate_scan: None, + pattern9_accum_const_loop: None, + pattern2_break: None, + pattern2_loopbodylocal: None, + }; + let canonical = canonicalize_loop_facts(facts); + assert!(canonical.facts.pattern1_simplewhile.is_some()); + } } diff --git a/src/mir/builder/control_flow/plan/planner/build.rs b/src/mir/builder/control_flow/plan/planner/build.rs index 27bf3ec1..7fbf8d76 100644 --- a/src/mir/builder/control_flow/plan/planner/build.rs +++ b/src/mir/builder/control_flow/plan/planner/build.rs @@ -45,7 +45,7 @@ pub(in crate::mir::builder) fn build_plan_from_facts_ctx( // unreachable in normal execution today. We still implement the SSOT // boundary here so that future Facts work cannot drift. - match facts.facts.skeleton.kind { + match facts.skeleton_kind { SkeletonKind::Loop => {} _ => return Ok(None), }