//! Loop Pattern Detection Module //! //! Phase 188 Task 188-4: Pattern detection helpers for JoinIR loop lowering. //! //! This module provides detection functions for 4 loop patterns: //! - Pattern 1: Simple While Loop (foundational) //! - Pattern 2: Loop with Conditional Break (early exit) //! - Pattern 3: Loop with If-Else PHI (variable mutation) //! - Pattern 4: Loop with Continue (skip iteration) //! //! Phase 194+: Structure-based detection using LoopFeatures. //! Patterns are classified based on CFG structure, not function names. //! //! # Architecture //! //! ``` //! LoopForm → extract_features() → LoopFeatures → classify() → LoopPatternKind //! ``` //! //! Reference: docs/private/roadmap2/phases/phase-188-joinir-loop-pattern-expansion/design.md use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; use crate::mir::loop_form::LoopForm; // ============================================================================ // Pattern Classification System (Phase 194+) // ============================================================================ /// Loop pattern classification based on structure. /// /// This enum represents the 4 main loop patterns we support. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LoopPatternKind { /// Pattern 1: Simple While Loop /// - No break, no continue /// - Single backedge Pattern1SimpleWhile, /// Pattern 2: Loop with Conditional Break /// - Has break statement(s) /// - No continue statements Pattern2Break, /// Pattern 3: Loop with If-Else PHI /// - Has if-else statement with PHI /// - No break, no continue /// - Multiple carrier variables Pattern3IfPhi, /// Pattern 4: Loop with Continue /// - Has continue statement(s) /// - No break statements (for simplicity) Pattern4Continue, /// Pattern not recognized Unknown, } impl LoopPatternKind { /// Phase 193-3: Get human-readable pattern name /// /// Returns the friendly name for this pattern (e.g., "Pattern 1: Simple While") pub fn name(&self) -> &'static str { match self { LoopPatternKind::Pattern1SimpleWhile => "Pattern 1: Simple While Loop", LoopPatternKind::Pattern2Break => "Pattern 2: Loop with Conditional Break", LoopPatternKind::Pattern3IfPhi => "Pattern 3: Loop with If-Else PHI", LoopPatternKind::Pattern4Continue => "Pattern 4: Loop with Continue", LoopPatternKind::Unknown => "Unknown Pattern", } } /// Phase 193-3: Get numeric pattern ID /// /// Returns the pattern number (1-4) or 0 for unknown. /// Useful for priority sorting. pub fn pattern_id(&self) -> u8 { match self { LoopPatternKind::Pattern1SimpleWhile => 1, LoopPatternKind::Pattern2Break => 2, LoopPatternKind::Pattern3IfPhi => 3, LoopPatternKind::Pattern4Continue => 4, LoopPatternKind::Unknown => 0, } } /// Phase 193-3: Check if this is a recognized pattern /// /// Returns false only for Unknown. pub fn is_recognized(&self) -> bool { !matches!(self, LoopPatternKind::Unknown) } /// Phase 193-3: Check if pattern has special control flow /// /// Returns true if pattern involves break or continue. pub fn has_special_control_flow(&self) -> bool { matches!( self, LoopPatternKind::Pattern2Break | LoopPatternKind::Pattern4Continue ) } /// Phase 193-3: Check if pattern involves PHI merging /// /// Returns true if pattern has if-else PHI merge. pub fn has_phi_merge(&self) -> bool { matches!(self, LoopPatternKind::Pattern3IfPhi) } } /// Feature vector extracted from loop structure. /// /// This structure captures all relevant properties needed for pattern classification. /// It is name-agnostic and purely structure-based. #[derive(Debug, Clone)] pub struct LoopFeatures { /// Has break statement(s)? pub has_break: bool, /// Has continue statement(s)? pub has_continue: bool, /// Has if statement(s) in body? pub has_if: bool, /// Has if-else statement with PHI nodes? /// (detected via multiple carriers or specific CFG patterns) pub has_if_else_phi: bool, /// Number of carrier variables (loop variables that are updated) pub carrier_count: usize, /// Number of break targets pub break_count: usize, /// Number of continue targets pub continue_count: usize, /// Phase 170-C-2b: Carrier update pattern summary /// /// Contains UpdateKind (CounterLike/AccumulationLike/Other) for each carrier. /// Used by CaseALoweringShape for more precise shape detection. /// None if carrier names are not available. pub update_summary: Option, } impl LoopFeatures { /// Phase 193-3: Get debug statistics string /// /// Returns a formatted string showing all feature values for debugging. pub fn debug_stats(&self) -> String { format!( "LoopFeatures {{ break: {}, continue: {}, if: {}, if_else_phi: {}, carriers: {}, break_count: {}, continue_count: {} }}", self.has_break, self.has_continue, self.has_if, self.has_if_else_phi, self.carrier_count, self.break_count, self.continue_count ) } /// Phase 193-3: Count total control flow divergences /// /// Returns the total number of break + continue targets. /// Useful for determining loop complexity. pub fn total_divergences(&self) -> usize { self.break_count + self.continue_count } /// Phase 193-3: Check if loop has complex control flow /// /// Returns true if loop has multiple divergences or multiple carriers. pub fn is_complex(&self) -> bool { self.total_divergences() > 1 || self.carrier_count > 1 } /// Phase 193-3: Check if loop is simple (no special features) /// /// Returns true if loop is purely sequential. pub fn is_simple(&self) -> bool { !self.has_break && !self.has_continue && !self.has_if_else_phi && self.carrier_count <= 1 } } /// Extract features from LoopForm for pattern classification. /// /// This function is the entry point for structure-based pattern detection. /// It analyzes the CFG structure without relying on variable names. /// /// # Arguments /// * `loop_form` - The loop structure to analyze /// * `scope` - Optional LoopScopeShape for carrier analysis /// /// # Returns /// * `LoopFeatures` - Feature vector for pattern classification pub(crate) fn extract_features( loop_form: &LoopForm, scope: Option<&LoopScopeShape>, ) -> LoopFeatures { // Phase 194: Basic feature extraction from LoopForm let has_break = !loop_form.break_targets.is_empty(); let has_continue = !loop_form.continue_targets.is_empty(); let break_count = loop_form.break_targets.len(); let continue_count = loop_form.continue_targets.len(); // Phase 194+: Extract carrier_count from LoopScopeShape if available let carrier_count = scope.map(|s| s.carriers.len()).unwrap_or(0); // Pattern 3 heuristic: has_if_else_phi if carrier_count > 1 // This is a conservative heuristic - multiple carriers typically // indicate if-else statements with PHI nodes. let has_if_else_phi = carrier_count > 1; // TODO: Implement has_if detection via CFG analysis // For now, we infer it from carrier_count > 1 (Pattern 3 heuristic) let has_if = has_if_else_phi; // Phase 170-C-2b: Build update_summary from carrier names // Note: carriers is BTreeSet, so each item is already a String let update_summary = scope.map(|s| { let carrier_names: Vec = s.carriers.iter().cloned().collect(); crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates_by_name( &carrier_names, ) }); LoopFeatures { has_break, has_continue, has_if, has_if_else_phi, carrier_count, break_count, continue_count, update_summary, } } /// Classify loop pattern based on feature vector. /// /// This function implements the pattern classification logic using /// structure-based rules. It does NOT depend on function names or /// variable names like "sum". /// /// # Pattern Classification Rules (Phase 212.5: Structural if detection) /// /// 1. **Pattern 4 (Continue)**: `has_continue == true` /// - Priority: Check first (most specific) /// /// 2. **Pattern 3 (If-PHI)**: `has_if && carrier_count >= 1 && !has_break && !has_continue` /// - Phase 212.5: Changed from carrier_count > 1 to structural if detection /// - Includes single-carrier if-update patterns (e.g., if-sum with 1 carrier) /// /// 3. **Pattern 2 (Break)**: `has_break && !has_continue` /// - Has break but no continue /// /// 4. **Pattern 1 (Simple While)**: `!has_break && !has_continue && !has_if` /// - Phase 212.5: Exclude loops with if statements /// - No control flow alterations /// /// # Arguments /// * `features` - Feature vector from extract_features() /// /// # Returns /// * `LoopPatternKind` - Classified pattern /// /// # Phase 183: Unified Detection /// /// This is the single source of truth for pattern classification. /// Both routers (`router.rs` and `loop_pattern_router.rs`) use this /// function to avoid duplicate detection logic. pub fn classify(features: &LoopFeatures) -> LoopPatternKind { // Pattern 4: Continue (highest priority) if features.has_continue { return LoopPatternKind::Pattern4Continue; } // Pattern 3: If-PHI (check before Pattern 1) // Phase 212.5: Structural if detection - route to P3 if has_if && carrier_count >= 1 if features.has_if && features.carrier_count >= 1 && !features.has_break && !features.has_continue { return LoopPatternKind::Pattern3IfPhi; } // Pattern 2: Break if features.has_break && !features.has_continue { return LoopPatternKind::Pattern2Break; } // Pattern 1: Simple While // Phase 212.5: Exclude loops with if statements (they go to P3) if !features.has_break && !features.has_continue && !features.has_if { return LoopPatternKind::Pattern1SimpleWhile; } // Unknown pattern LoopPatternKind::Unknown } /// Phase 193-3: Diagnose pattern classification with details /// /// This function performs classification AND generates diagnostic information. /// Useful for debugging and logging. /// /// # Returns /// * `(LoopPatternKind, String)` - The classified pattern and a diagnostic message pub fn classify_with_diagnosis(features: &LoopFeatures) -> (LoopPatternKind, String) { let pattern = classify(features); let reason = match pattern { LoopPatternKind::Pattern4Continue => { format!( "Has continue statement (continue_count={})", features.continue_count ) } LoopPatternKind::Pattern3IfPhi => { format!( "Has if-else PHI with {} carriers, no break/continue", features.carrier_count ) } LoopPatternKind::Pattern2Break => { format!( "Has break statement (break_count={}), no continue", features.break_count ) } LoopPatternKind::Pattern1SimpleWhile => { "Simple while loop with no special control flow".to_string() } LoopPatternKind::Unknown => { format!("Unknown pattern: {}", features.debug_stats()) } }; (pattern, reason) } // ============================================================================ // Legacy Detection Functions (Phase 188) // ============================================================================ // // NOTE (Phase 179): These functions are still actively used by pattern lowerers. // The term "legacy" refers to Phase 188 implementation style (name-based detection), // not deprecation status. Do NOT move to legacy/ subdirectory - still production code. // // Future work: Gradually migrate to Phase 194+ structure-based detection (extract_features/classify). // ============================================================================ // Pattern 1: Simple While Loop // ============================================================================ /// Detect Pattern 1: Simple While Loop /// /// Returns true ONLY if: /// - Loop condition is simple comparison (no &&, ||) /// - Loop body contains only assignments + prints (no nested loops, no breaks) /// - Loop has single increment/decrement /// - NO break statements (break_targets is empty) /// - NO continue statements (continue_targets is empty) /// - Single backedge (latches.len() == 1) /// /// # Arguments /// * `loop_form` - The loop structure to analyze /// /// # Returns /// * `true` if the loop matches Pattern 1 (Simple While), `false` otherwise /// /// # Reference /// See design.md § Pattern 1 → LoopScopeShape Recognition /// /// # Example /// ```rust,ignore /// let loop_form = /* ... */; /// if is_simple_while_pattern(&loop_form) { /// // Lower to simple while pattern /// } /// ``` pub fn is_simple_while_pattern(loop_form: &LoopForm) -> bool { // Pattern 1 Recognition Criteria (from design.md § Pattern 1): // 1. break_targets: EMPTY (no break statements) // 2. continue_targets: EMPTY (no continue statements) // 3. Single backedge (single latch - LoopShape has singular latch field) // // Note: LoopShape has a singular `latch` field, not `latches`, so we don't // need to check length. The existence of a LoopShape implies a valid latch. // Check 1: No break statements if !loop_form.break_targets.is_empty() { return false; } // Check 2: No continue statements if !loop_form.continue_targets.is_empty() { return false; } // Check 3: All other checks passed // The LoopShape structure guarantees: // - Single preheader, header, body, latch, exit // - Valid loop structure // // Pattern 1 ONLY requires: // - No breaks, no continues // - Natural loop structure (which LoopShape guarantees) // // Advanced checks (nested loops, complex conditions) are deferred to // lowering phase where we can fail gracefully if needed. true } // ============================================================================ // Pattern 2: Loop with Conditional Break // ============================================================================ /// Detect Pattern 2: Loop with Conditional Break /// /// Returns true ONLY if: /// - Loop condition exists /// - Loop body contains exactly ONE if statement with break /// - Break is in then-branch /// - NO nested loops /// - break_targets is NON-EMPTY (has at least one break) /// /// # Arguments /// * `loop_form` - The loop structure to analyze /// /// # Returns /// * `true` if the loop matches Pattern 2 (Break), `false` otherwise /// /// # Reference /// See design.md § Pattern 2 → LoopScopeShape Recognition /// /// # Example /// ```rust,ignore /// let loop_form = /* ... */; /// if is_loop_with_break_pattern(&loop_form) { /// // Lower to loop with break pattern /// } /// ``` pub fn is_loop_with_break_pattern(loop_form: &LoopForm) -> bool { // Pattern 2 Recognition Criteria (from design.md § Pattern 2): // 1. break_targets: NON-EMPTY (at least 1 break) // 2. continue_targets: EMPTY (for simplicity) // 3. Exactly ONE break target // // Phase 188-Impl-2: Minimal implementation // Advanced checks (nested loops, if-statement structure) are deferred to // lowering phase where we can fail gracefully if needed. // Check 1: break_targets is NON-EMPTY (has at least 1 break) if loop_form.break_targets.is_empty() { return false; } // Check 2: Exactly ONE break target (pattern assumes single break) if loop_form.break_targets.len() != 1 { return false; } // Check 3: No continue statements (for simplicity in Pattern 2) if !loop_form.continue_targets.is_empty() { return false; } // Pattern 2 matched // The LoopForm structure guarantees: // - Valid loop structure // - Single break target // - No continues // // Advanced checks (break is in if-statement, etc.) are deferred to // lowering phase for graceful failure. true } // ============================================================================ // Pattern 3: Loop with If-Else PHI // ============================================================================ /// Detect Pattern 3: Loop with If-Else PHI /// /// Returns true ONLY if: /// - Loop has if-else statement assigning to variable(s) /// - Both branches assign to same variable /// - NO nested loops /// - NO break or continue statements /// - Loop has multiple carrier variables (e.g., i + sum) /// /// # Arguments /// * `loop_form` - The loop structure to analyze /// /// # Returns /// * `true` if the loop matches Pattern 3 (If-Else PHI), `false` otherwise /// /// # Reference /// See design.md § Pattern 3 → LoopScopeShape Recognition /// /// # Example /// ```rust,ignore /// let loop_form = /* ... */; /// if is_loop_with_conditional_phi_pattern(&loop_form) { /// // Lower to loop with if-else phi pattern /// } /// ``` pub fn is_loop_with_conditional_phi_pattern(loop_form: &LoopForm) -> bool { // Phase 188-Impl-3: Minimal implementation // Pattern 3 Recognition Criteria (from design.md § Pattern 3): // 1. break_targets: EMPTY (no break statements) // 2. continue_targets: EMPTY (no continue statements) // 3. All Pattern 3 loops are valid Pattern 1 loops with extra PHI nodes // // For now: return true as fallback for Pattern 1 loops // Advanced checks (if-else detection, multiple carriers) are deferred to // lowering phase where we can fail gracefully if needed. // Check 1: No break statements if !loop_form.break_targets.is_empty() { return false; } // Check 2: No continue statements if !loop_form.continue_targets.is_empty() { return false; } // Pattern 3 matched (fallback for now) // Since all Pattern 3 loops are also Pattern 1 loops, we can safely return true // The lowering phase will determine if the specific pattern is supported true } // ============================================================================ // Pattern 4: Loop with Continue // ============================================================================ /// Detect Pattern 4: Loop with Continue /// /// Returns true ONLY if: /// - Loop has continue statement(s) /// - Continue is typically in an if statement /// - NO break statements (for simplicity) /// - Loop has multiple carrier variables /// /// # Arguments /// * `loop_form` - The loop structure to analyze /// /// # Returns /// * `true` if the loop matches Pattern 4 (Continue), `false` otherwise /// /// # Reference /// See design.md § Pattern 4 → LoopScopeShape Recognition /// /// # Example /// ```rust,ignore /// let loop_form = /* ... */; /// if is_loop_with_continue_pattern(&loop_form) { /// // Lower to loop with continue pattern /// } /// ``` pub fn is_loop_with_continue_pattern(loop_form: &LoopForm) -> bool { // Pattern 4 Recognition Criteria: // 1. continue_targets: NON-EMPTY (at least 1 continue) // 2. break_targets: EMPTY (for simplicity in Pattern 4) // 3. At least ONE continue target // // Phase 188-Impl-4: Minimal implementation // Advanced checks (nested loops, if-statement structure) are deferred to // lowering phase where we can fail gracefully if needed. // Check 1: continue_targets is NON-EMPTY (has at least 1 continue) if loop_form.continue_targets.is_empty() { return false; } // Check 2: At least ONE continue target (pattern assumes single continue for now) if loop_form.continue_targets.len() < 1 { return false; } // Check 3: No break statements (for simplicity in Pattern 4) if !loop_form.break_targets.is_empty() { return false; } // Pattern 4 matched // The LoopForm structure guarantees: // - Valid loop structure // - At least one continue target // - No breaks // // Advanced checks (continue is in if-statement, etc.) are deferred to // lowering phase for graceful failure. true } // ============================================================================ // Helper Functions (Future Use) // ============================================================================ /// Count the number of carrier variables in a loop /// /// Carrier variables are loop variables that are updated in the loop body /// and carried through PHI nodes in the header. /// /// # Arguments /// * `loop_form` - The loop structure to analyze /// /// # Returns /// * Number of carrier variables /// /// # TODO /// Implement by analyzing header PHI nodes #[allow(dead_code)] fn count_carrier_variables(_loop_form: &LoopForm) -> usize { // TODO: Implement carrier variable counting // Step 1: Access loop_form.header block // Step 2: Count PHI instructions in header // Step 3: Return count 0 } /// Check if loop body contains nested loops /// /// # Arguments /// * `loop_form` - The loop structure to analyze /// /// # Returns /// * `true` if nested loops found, `false` otherwise /// /// # TODO /// Implement by checking for LoopForm within body blocks #[allow(dead_code)] fn has_nested_loops(_loop_form: &LoopForm) -> bool { // TODO: Implement nested loop detection // Step 1: Traverse body blocks // Step 2: Check for loop headers in body // Step 3: Return true if any found false } /// Check if loop condition is simple (single comparison, no && or ||) /// /// # Arguments /// * `loop_form` - The loop structure to analyze /// /// # Returns /// * `true` if condition is simple, `false` otherwise /// /// # TODO /// Implement by checking header condition complexity #[allow(dead_code)] fn has_simple_condition(_loop_form: &LoopForm) -> bool { // TODO: Implement condition complexity check // Step 1: Access loop_form.header block // Step 2: Find condition instruction // Step 3: Check for && or || operators // Step 4: Return true if no complex operators true // Assume simple for now } #[cfg(test)] mod tests; // Phase 170-D: Loop Condition Scope Analysis Boxes pub mod condition_var_analyzer; pub mod loop_condition_scope; // Phase 170-ultrathink: Error Message Utilities pub mod error_messages; // Phase 171-C: LoopBodyLocal Carrier Promotion pub mod loop_body_carrier_promoter; // Phase 223-3: LoopBodyLocal Condition Promotion (for Pattern4) pub mod loop_body_cond_promoter; // Phase 224: A-4 DigitPos Pattern Promotion pub mod loop_body_digitpos_promoter; // Phase 171-C-5: Trim Pattern Helper pub mod trim_loop_helper; pub use trim_loop_helper::TrimLoopHelper; // Phase 33-23: Break Condition Analysis (Stage 2, Issue 6) pub mod break_condition_analyzer; // Phase 200-A: Function Scope Capture Infrastructure pub mod function_scope_capture; // Phase 78: PromotedBindingRecorder - Type-safe BindingId recording pub mod promoted_binding_recorder; pub use promoted_binding_recorder::{BindingRecordError, PromotedBindingRecorder};