diff --git a/src/mir/builder/control_flow/plan/facts/feature_facts.rs b/src/mir/builder/control_flow/plan/facts/feature_facts.rs new file mode 100644 index 00000000..c0c1979c --- /dev/null +++ b/src/mir/builder/control_flow/plan/facts/feature_facts.rs @@ -0,0 +1,119 @@ +//! Phase 29an P1: FeatureFacts SSOT (ExitUsage placeholders) + +use crate::ast::ASTNode; +use crate::mir::builder::control_flow::plan::planner::Freeze; + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub(in crate::mir::builder) struct ExitUsageFacts { + pub has_break: bool, + pub has_continue: bool, + pub has_return: bool, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub(in crate::mir::builder) struct LoopFeatureFacts { + pub exit_usage: ExitUsageFacts, + pub value_join: Option, + pub cleanup: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(in crate::mir::builder) struct ValueJoinFacts; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(in crate::mir::builder) struct CleanupFacts; + +pub(in crate::mir::builder) fn try_extract_loop_feature_facts( + body: &[ASTNode], +) -> Result { + let mut exit_usage = ExitUsageFacts::default(); + for stmt in body { + update_exit_usage_from_stmt(stmt, &mut exit_usage); + } + Ok(LoopFeatureFacts { + exit_usage, + value_join: None, + cleanup: None, + }) +} + +fn update_exit_usage_from_stmt(stmt: &ASTNode, usage: &mut ExitUsageFacts) { + match stmt { + ASTNode::Break { .. } => usage.has_break = true, + ASTNode::Continue { .. } => usage.has_continue = true, + ASTNode::Return { .. } => usage.has_return = true, + ASTNode::If { + then_body, + else_body, + .. + } => { + for nested in then_body { + update_exit_usage_from_stmt(nested, usage); + } + if let Some(else_body) = else_body { + for nested in else_body { + update_exit_usage_from_stmt(nested, usage); + } + } + } + ASTNode::Loop { .. } + | ASTNode::While { .. } + | ASTNode::ForRange { .. } => {} + _ => {} + } +} + +#[cfg(test)] +mod tests { + use super::{try_extract_loop_feature_facts, ExitUsageFacts}; + use crate::ast::{ASTNode, LiteralValue, Span}; + + fn lit_bool(value: bool) -> ASTNode { + ASTNode::Literal { + value: LiteralValue::Bool(value), + span: Span::unknown(), + } + } + + #[test] + fn exit_usage_tracks_break_continue_return_in_if() { + let body = vec![ + ASTNode::If { + condition: Box::new(lit_bool(true)), + then_body: vec![ASTNode::Break { + span: Span::unknown(), + }], + else_body: Some(vec![ASTNode::Continue { + span: Span::unknown(), + }]), + span: Span::unknown(), + }, + ASTNode::Return { + value: None, + span: Span::unknown(), + }, + ]; + let facts = try_extract_loop_feature_facts(&body).expect("Ok"); + assert_eq!( + facts.exit_usage, + ExitUsageFacts { + has_break: true, + has_continue: true, + has_return: true, + } + ); + } + + #[test] + fn exit_usage_ignores_nested_loops() { + let nested = ASTNode::Loop { + condition: Box::new(lit_bool(true)), + body: vec![ASTNode::Break { + span: Span::unknown(), + }], + span: Span::unknown(), + }; + let facts = try_extract_loop_feature_facts(&[nested]).expect("Ok"); + assert_eq!(facts.exit_usage, ExitUsageFacts::default()); + } +} 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 32c8bbed..7ca2350c 100644 --- a/src/mir/builder/control_flow/plan/facts/loop_facts.rs +++ b/src/mir/builder/control_flow/plan/facts/loop_facts.rs @@ -37,12 +37,14 @@ use super::pattern2_loopbodylocal_facts::{ Pattern2LoopBodyLocalFacts, try_extract_pattern2_loopbodylocal_facts, }; use super::skeleton_facts::{SkeletonFacts, try_extract_loop_skeleton_facts}; +use super::feature_facts::{LoopFeatureFacts, try_extract_loop_feature_facts}; #[derive(Debug, Clone)] pub(in crate::mir::builder) struct LoopFacts { pub condition_shape: ConditionShape, pub step_shape: StepShape, pub skeleton: Option, + pub features: Option, pub scan_with_init: Option, pub split_scan: Option, pub pattern1_simplewhile: Option, @@ -145,11 +147,13 @@ fn try_build_loop_facts_inner( } let skeleton = try_extract_loop_skeleton_facts(condition, body)?; + let features = Some(try_extract_loop_feature_facts(body)?); Ok(Some(LoopFacts { condition_shape, step_shape, skeleton, + features, scan_with_init, split_scan, pattern1_simplewhile, diff --git a/src/mir/builder/control_flow/plan/facts/mod.rs b/src/mir/builder/control_flow/plan/facts/mod.rs index 948920ff..5da21c4d 100644 --- a/src/mir/builder/control_flow/plan/facts/mod.rs +++ b/src/mir/builder/control_flow/plan/facts/mod.rs @@ -7,6 +7,7 @@ #![allow(dead_code)] pub(in crate::mir::builder) mod loop_facts; +pub(in crate::mir::builder) mod feature_facts; pub(in crate::mir::builder) mod pattern1_simplewhile_facts; pub(in crate::mir::builder) mod pattern3_ifphi_facts; pub(in crate::mir::builder) mod pattern4_continue_facts; diff --git a/src/mir/builder/control_flow/plan/planner/build.rs b/src/mir/builder/control_flow/plan/planner/build.rs index 99ce75d5..0ddefc3b 100644 --- a/src/mir/builder/control_flow/plan/planner/build.rs +++ b/src/mir/builder/control_flow/plan/planner/build.rs @@ -249,6 +249,7 @@ mod tests { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, skeleton: None, + features: None, scan_with_init: None, split_scan: Some(SplitScanFacts { s_var: "s".to_string(), @@ -287,6 +288,7 @@ mod tests { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, skeleton: None, + features: None, scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -309,6 +311,7 @@ mod tests { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, skeleton: None, + features: None, scan_with_init: Some(ScanWithInitFacts { loop_var: "i".to_string(), haystack: "s".to_string(), @@ -354,6 +357,7 @@ mod tests { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, skeleton: None, + features: None, scan_with_init: None, split_scan: None, pattern1_simplewhile: Some(Pattern1SimpleWhileFacts { @@ -417,6 +421,7 @@ mod tests { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, skeleton: None, + features: None, scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -479,6 +484,7 @@ mod tests { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, skeleton: None, + features: None, scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -527,6 +533,7 @@ mod tests { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, skeleton: None, + features: None, scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -576,6 +583,7 @@ mod tests { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, skeleton: None, + features: None, scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -633,6 +641,7 @@ mod tests { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, skeleton: None, + features: None, scan_with_init: None, split_scan: None, pattern1_simplewhile: None, @@ -696,6 +705,7 @@ mod tests { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, skeleton: None, + features: None, scan_with_init: None, split_scan: None, pattern1_simplewhile: None,