diff --git a/src/mir/builder/control_flow/plan/facts/feature_facts.rs b/src/mir/builder/control_flow/plan/facts/feature_facts.rs index 3f00ad04..0d75381c 100644 --- a/src/mir/builder/control_flow/plan/facts/feature_facts.rs +++ b/src/mir/builder/control_flow/plan/facts/feature_facts.rs @@ -23,6 +23,18 @@ pub(in crate::mir::builder) struct ExitMapFacts { pub kinds_present: BTreeSet, } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub(in crate::mir::builder) enum CleanupKindFacts { + Return, + Break, + Continue, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(in crate::mir::builder) struct CleanupFacts { + pub kinds_present: BTreeSet, +} + #[derive(Debug, Clone, Default, PartialEq, Eq)] pub(in crate::mir::builder) struct LoopFeatureFacts { pub exit_usage: ExitUsageFacts, @@ -34,8 +46,6 @@ pub(in crate::mir::builder) struct LoopFeatureFacts { #[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], diff --git a/src/mir/builder/control_flow/plan/normalize/canonicalize.rs b/src/mir/builder/control_flow/plan/normalize/canonicalize.rs index 93f85fce..e4cc3d2e 100644 --- a/src/mir/builder/control_flow/plan/normalize/canonicalize.rs +++ b/src/mir/builder/control_flow/plan/normalize/canonicalize.rs @@ -3,7 +3,7 @@ #![allow(dead_code)] use crate::mir::builder::control_flow::plan::facts::feature_facts::{ - ExitKindFacts, ExitUsageFacts, + CleanupKindFacts, ExitKindFacts, ExitUsageFacts, }; use crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonKind; use crate::mir::builder::control_flow::plan::facts::LoopFacts; @@ -15,6 +15,7 @@ pub(in crate::mir::builder) struct CanonicalLoopFacts { pub skeleton_kind: SkeletonKind, pub exit_usage: ExitUsageFacts, pub exit_kinds_present: BTreeSet, + pub cleanup_kinds_present: BTreeSet, } pub(in crate::mir::builder) fn canonicalize_loop_facts(facts: LoopFacts) -> CanonicalLoopFacts { @@ -27,6 +28,12 @@ pub(in crate::mir::builder) fn canonicalize_loop_facts(facts: LoopFacts) -> Cano .as_ref() .map(|map| map.kinds_present.clone()) .unwrap_or_default(), + cleanup_kinds_present: facts + .features + .cleanup + .as_ref() + .map(|cleanup| cleanup.kinds_present.clone()) + .unwrap_or_default(), facts, } } @@ -36,7 +43,8 @@ mod tests { use super::canonicalize_loop_facts; use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span}; use crate::mir::builder::control_flow::plan::facts::feature_facts::{ - ExitKindFacts, ExitMapFacts, ExitUsageFacts, LoopFeatureFacts, + CleanupFacts, CleanupKindFacts, ExitKindFacts, ExitMapFacts, ExitUsageFacts, + LoopFeatureFacts, }; use crate::mir::builder::control_flow::plan::facts::loop_facts::LoopFacts; use crate::mir::builder::control_flow::plan::facts::scan_shapes::{ @@ -65,6 +73,8 @@ mod tests { kinds_present.insert(ExitKindFacts::Break); kinds_present.insert(ExitKindFacts::Continue); kinds_present.insert(ExitKindFacts::Return); + let mut cleanup_kinds_present = BTreeSet::new(); + cleanup_kinds_present.insert(CleanupKindFacts::Return); let facts = LoopFacts { condition_shape: ConditionShape::Unknown, step_shape: StepShape::Unknown, @@ -79,7 +89,9 @@ mod tests { }, exit_map: Some(ExitMapFacts { kinds_present }), value_join: None, - cleanup: None, + cleanup: Some(CleanupFacts { + kinds_present: cleanup_kinds_present, + }), }, scan_with_init: None, split_scan: None, @@ -101,6 +113,10 @@ mod tests { assert!(canonical.exit_kinds_present.contains(&ExitKindFacts::Break)); assert!(canonical.exit_kinds_present.contains(&ExitKindFacts::Continue)); assert!(canonical.exit_kinds_present.contains(&ExitKindFacts::Return)); + assert_eq!(canonical.cleanup_kinds_present.len(), 1); + assert!(canonical + .cleanup_kinds_present + .contains(&CleanupKindFacts::Return)); } #[test] @@ -125,6 +141,7 @@ mod tests { }; let canonical = canonicalize_loop_facts(facts); assert!(canonical.exit_kinds_present.is_empty()); + assert!(canonical.cleanup_kinds_present.is_empty()); } #[test] diff --git a/src/mir/builder/control_flow/plan/planner/build.rs b/src/mir/builder/control_flow/plan/planner/build.rs index f7845a99..55460e31 100644 --- a/src/mir/builder/control_flow/plan/planner/build.rs +++ b/src/mir/builder/control_flow/plan/planner/build.rs @@ -5,7 +5,7 @@ use crate::ast::ASTNode; use crate::mir::builder::control_flow::plan::facts::feature_facts::{ - ExitKindFacts, ExitUsageFacts, + CleanupKindFacts, ExitKindFacts, ExitUsageFacts, }; use crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonKind; use crate::mir::builder::control_flow::plan::normalize::CanonicalLoopFacts; @@ -62,6 +62,10 @@ pub(in crate::mir::builder) fn build_plan_from_facts_ctx( let _skeleton_kind = infer_skeleton_kind(&facts); let _exit_usage = infer_exit_usage(&facts); + debug_assert_cleanup_kinds_match_exit_kinds( + &facts.cleanup_kinds_present, + &facts.exit_kinds_present, + ); let mut candidates = CandidateSet::new(); @@ -140,6 +144,31 @@ fn debug_assert_exit_usage_matches_plan( ) { } +#[cfg(debug_assertions)] +fn debug_assert_cleanup_kinds_match_exit_kinds( + cleanup_kinds_present: &BTreeSet, + exit_kinds_present: &BTreeSet, +) { + for cleanup_kind in cleanup_kinds_present { + let exit_kind = match cleanup_kind { + CleanupKindFacts::Return => ExitKindFacts::Return, + CleanupKindFacts::Break => ExitKindFacts::Break, + CleanupKindFacts::Continue => ExitKindFacts::Continue, + }; + debug_assert!( + exit_kinds_present.contains(&exit_kind), + "cleanup kind requires matching exit kind presence" + ); + } +} + +#[cfg(not(debug_assertions))] +fn debug_assert_cleanup_kinds_match_exit_kinds( + _cleanup_kinds_present: &BTreeSet, + _exit_kinds_present: &BTreeSet, +) { +} + fn push_scan_with_init(candidates: &mut CandidateSet, facts: &CanonicalLoopFacts) { let Some(scan) = &facts.facts.scan_with_init else { return;