diff --git a/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs b/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs index 33bc5663..feafa199 100644 --- a/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs +++ b/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs @@ -71,6 +71,7 @@ pub(crate) fn detect_break_in_body(body: &[ASTNode]) -> bool { /// /// # Arguments /// +/// * `condition` - Loop condition AST node (Phase 131-11: for infinite loop detection) /// * `body` - Loop body statements to analyze /// * `has_continue` - Pre-computed continue presence (for optimization) /// * `has_break` - Pre-computed break presence (for optimization) @@ -78,7 +79,12 @@ pub(crate) fn detect_break_in_body(body: &[ASTNode]) -> bool { /// # Returns /// /// A LoopFeatures struct containing all detected structural characteristics -pub(crate) fn extract_features(body: &[ASTNode], has_continue: bool, has_break: bool) -> LoopFeatures { +pub(crate) fn extract_features( + condition: &ASTNode, + body: &[ASTNode], + has_continue: bool, + has_break: bool, +) -> LoopFeatures { // Phase 212.5: Detect ANY if statement in loop body (structural detection) let has_if = detect_if_in_body(body); @@ -88,6 +94,9 @@ pub(crate) fn extract_features(body: &[ASTNode], has_continue: bool, has_break: // Count carrier variables (approximation based on assignments) let carrier_count = count_carriers_in_body(body); + // Phase 131-11: Detect infinite loop (condition == true) + let is_infinite_loop = detect_infinite_loop(condition); + LoopFeatures { has_break, has_continue, @@ -96,11 +105,31 @@ pub(crate) fn extract_features(body: &[ASTNode], has_continue: bool, has_break: carrier_count, break_count: if has_break { 1 } else { 0 }, continue_count: if has_continue { 1 } else { 0 }, + is_infinite_loop, // Phase 170-C-2b: AST-based extraction doesn't have carrier names yet update_summary: None, } } +/// Phase 131-11: Detect infinite loop (condition == Literal(Bool(true))) +/// +/// # Arguments +/// +/// * `condition` - Loop condition AST node +/// +/// # Returns +/// +/// `true` if condition is a boolean literal with value true +fn detect_infinite_loop(condition: &ASTNode) -> bool { + matches!( + condition, + ASTNode::Literal { + value: crate::ast::LiteralValue::Bool(true), + .. + } + ) +} + /// Phase 212.5: Detect ANY if statement in loop body (structural detection) /// /// This function detects any if statement, regardless of whether it has an else branch. diff --git a/src/mir/builder/control_flow/joinir/patterns/mod.rs b/src/mir/builder/control_flow/joinir/patterns/mod.rs index fe4b3a84..c00ba383 100644 --- a/src/mir/builder/control_flow/joinir/patterns/mod.rs +++ b/src/mir/builder/control_flow/joinir/patterns/mod.rs @@ -55,6 +55,7 @@ pub(in crate::mir::builder) mod pattern2_with_break; pub(in crate::mir::builder) mod pattern3_with_if_phi; pub(in crate::mir::builder) mod pattern4_carrier_analyzer; pub(in crate::mir::builder) mod pattern4_with_continue; +pub(in crate::mir::builder) mod pattern5_infinite_early_exit; // Phase 131-11 pub(in crate::mir::builder) mod pattern_pipeline; pub(in crate::mir::builder) mod router; pub(in crate::mir::builder) mod trim_loop_lowering; // Phase 180: Dedicated Trim/P5 lowering module diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern5_infinite_early_exit.rs b/src/mir/builder/control_flow/joinir/patterns/pattern5_infinite_early_exit.rs new file mode 100644 index 00000000..4180d1e6 --- /dev/null +++ b/src/mir/builder/control_flow/joinir/patterns/pattern5_infinite_early_exit.rs @@ -0,0 +1,155 @@ +//! Pattern 5: Infinite Loop with Early Exit (Phase 131-11) +//! +//! # Pattern Overview +//! +//! Handles `loop(true)` with both `break` and `continue` statements. +//! This is a specialized pattern for infinite loops with early exit conditions. +//! +//! # Example +//! +//! ```nyash +//! local counter = 0 +//! loop (true) { +//! counter = counter + 1 +//! if counter == 3 { break } +//! continue +//! } +//! ``` +//! +//! # Shape Guard (Fail-Fast) +//! +//! - Condition MUST be `true` literal (infinite loop) +//! - MUST have exactly 1 break statement +//! - MUST have exactly 1 continue statement +//! - Carriers: MUST be exactly 1 counter-like variable +//! - No nested loops (not yet supported) +//! +//! # Implementation Status +//! +//! Phase 131-11-C: Minimal skeleton with Fail-Fast guards +//! - Shape validation implemented +//! - Lowering logic: TODO (will fail with explicit error) + +use super::super::trace; +use crate::mir::builder::MirBuilder; +use crate::mir::loop_pattern_detection::LoopPatternKind; +use crate::mir::ValueId; + +use super::router::LoopPatternContext; + +/// Phase 131-11: Pattern detection for InfiniteEarlyExit +/// +/// This function checks if the loop matches Pattern 5 characteristics. +/// Uses Fail-Fast approach: narrow shape guard to avoid false positives. +pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &LoopPatternContext) -> bool { + let debug = ctx.debug; + + // Step 1: Check pattern classification + if ctx.pattern_kind != LoopPatternKind::InfiniteEarlyExit { + if debug { + trace::trace().debug( + "pattern5/detect", + &format!( + "Pattern kind mismatch: expected InfiniteEarlyExit, got {:?}", + ctx.pattern_kind + ), + ); + } + return false; + } + + // Step 2: Shape guard - infinite loop condition (true literal) + if !ctx.features.is_infinite_loop { + if debug { + trace::trace().debug("pattern5/detect", "Not an infinite loop (condition != true)"); + } + return false; + } + + // Step 3: Shape guard - exactly 1 break + if ctx.features.break_count != 1 { + if debug { + trace::trace().debug( + "pattern5/detect", + &format!( + "Break count mismatch: expected 1, got {}", + ctx.features.break_count + ), + ); + } + return false; + } + + // Step 4: Shape guard - exactly 1 continue + if ctx.features.continue_count != 1 { + if debug { + trace::trace().debug( + "pattern5/detect", + &format!( + "Continue count mismatch: expected 1, got {}", + ctx.features.continue_count + ), + ); + } + return false; + } + + // Step 5: Shape guard - exactly 1 carrier (counter-like) + // Phase 131-11-C: Start with minimal carrier requirement + if ctx.features.carrier_count != 1 { + if debug { + trace::trace().debug( + "pattern5/detect", + &format!( + "Carrier count mismatch: expected 1, got {}", + ctx.features.carrier_count + ), + ); + } + return false; + } + + // All shape guards passed + if debug { + trace::trace().debug( + "pattern5/detect", + &format!( + "Pattern 5 detected: infinite={}, break={}, continue={}, carriers={}", + ctx.features.is_infinite_loop, + ctx.features.break_count, + ctx.features.continue_count, + ctx.features.carrier_count + ), + ); + } + + true +} + +/// Phase 131-11: Lower InfiniteEarlyExit pattern to JoinIR +/// +/// # Implementation Status +/// +/// Phase 131-11-C: Minimal skeleton - returns Fail-Fast error +/// Phase 131-11-D: TODO - implement actual lowering logic +pub(crate) fn lower( + _builder: &mut MirBuilder, + ctx: &LoopPatternContext, +) -> Result, String> { + let debug = ctx.debug; + + if debug { + trace::trace().debug( + "pattern5/lower", + "Phase 131-11-C: Pattern 5 lowering not yet implemented", + ); + } + + // Phase 131-11-C: Fail-Fast with explicit error message + Err(format!( + "Pattern 5 (InfiniteEarlyExit) lowering not yet implemented (Phase 131-11-C skeleton)\n\ + Detected: loop(true) with {} break(s), {} continue(s), {} carrier(s)\n\ + Next step: Implement lowering logic in Phase 131-11-D", + ctx.features.break_count, ctx.features.continue_count, ctx.features.carrier_count + )) +} diff --git a/src/mir/builder/control_flow/joinir/patterns/router.rs b/src/mir/builder/control_flow/joinir/patterns/router.rs index 3da3cfbb..78878d18 100644 --- a/src/mir/builder/control_flow/joinir/patterns/router.rs +++ b/src/mir/builder/control_flow/joinir/patterns/router.rs @@ -69,6 +69,7 @@ impl<'a> LoopPatternContext<'a> { /// Phase 194+: Automatically detects continue/break statements in body /// Phase 192: Extract features and classify pattern from AST /// Phase 193: Feature extraction delegated to ast_feature_extractor module + /// Phase 131-11: Detects infinite loop condition pub(crate) fn new( condition: &'a ASTNode, body: &'a [ASTNode], @@ -80,7 +81,8 @@ impl<'a> LoopPatternContext<'a> { let has_break = ast_features::detect_break_in_body(body); // Phase 193: Extract features using modularized extractor - let features = ast_features::extract_features(body, has_continue, has_break); + // Phase 131-11: Pass condition for infinite loop detection + let features = ast_features::extract_features(condition, body, has_continue, has_break); // Phase 192: Classify pattern based on features let pattern_kind = crate::mir::loop_pattern_detection::classify(&features); @@ -135,10 +137,14 @@ pub(crate) struct LoopPatternEntry { /// Static table of all registered loop patterns. /// Patterns are tried in priority order (lowest first). /// -/// # Current Patterns (Phase 192: Structure-based detection) +/// # Current Patterns (Phase 131-11: Pattern 5 added) /// /// All patterns now use structure-based detection via LoopFeatures and classify(): /// +/// - Pattern 5 (priority 1): Infinite Loop with Early Exit (llvm_stage3_loop_only.hako) [Phase 131-11] +/// - Detection: pattern_kind == InfiniteEarlyExit +/// - Structure: is_infinite_loop && has_break && has_continue +/// /// - Pattern 4 (priority 5): Loop with Continue (loop_continue_pattern4.hako) /// - Detection: pattern_kind == Pattern4Continue /// - Structure: has_continue && !has_break @@ -157,9 +163,15 @@ pub(crate) struct LoopPatternEntry { /// /// Note: func_name is now only used for debug logging, not pattern detection pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[ + LoopPatternEntry { + name: "Pattern5_InfiniteEarlyExit", + priority: 1, // Highest priority - most specific (infinite loop with break+continue) + detect: super::pattern5_infinite_early_exit::can_lower, + lower: super::pattern5_infinite_early_exit::lower, + }, LoopPatternEntry { name: "Pattern4_WithContinue", - priority: 5, // Highest priority - continue is most specific + priority: 5, // Second priority - continue without break detect: super::pattern4_with_continue::can_lower, lower: super::pattern4_with_continue::lower, }, diff --git a/src/mir/join_ir/lowering/loop_pattern_router.rs b/src/mir/join_ir/lowering/loop_pattern_router.rs index 07ae1461..2d82fecd 100644 --- a/src/mir/join_ir/lowering/loop_pattern_router.rs +++ b/src/mir/join_ir/lowering/loop_pattern_router.rs @@ -164,6 +164,10 @@ pub fn try_lower_loop_pattern_to_joinir( return Some(inst); } } + LoopPatternKind::InfiniteEarlyExit => { + // Phase 131-11: Not implemented yet in LoopForm-based router + eprintln!("[try_lower_loop_pattern] ⚠️ Pattern 5 (InfiniteEarlyExit) not implemented in LoopForm router"); + } LoopPatternKind::Unknown => { eprintln!("[try_lower_loop_pattern] ❌ Unknown pattern, fallback to existing lowering"); } diff --git a/src/mir/join_ir/lowering/loop_scope_shape/case_a_lowering_shape.rs b/src/mir/join_ir/lowering/loop_scope_shape/case_a_lowering_shape.rs index a78fcf01..2c381a31 100644 --- a/src/mir/join_ir/lowering/loop_scope_shape/case_a_lowering_shape.rs +++ b/src/mir/join_ir/lowering/loop_scope_shape/case_a_lowering_shape.rs @@ -314,6 +314,7 @@ impl CaseALoweringShape { carrier_count, break_count: 0, continue_count: 0, + is_infinite_loop: false, // Phase 131-11: Unknown from LoopScopeShape alone update_summary: Some(update_summary), }; diff --git a/src/mir/join_ir/lowering/loop_view_builder.rs b/src/mir/join_ir/lowering/loop_view_builder.rs index 95a70065..910c0376 100644 --- a/src/mir/join_ir/lowering/loop_view_builder.rs +++ b/src/mir/join_ir/lowering/loop_view_builder.rs @@ -82,6 +82,7 @@ impl LoopViewBuilder { carrier_count: scope.carriers.len(), break_count: 0, continue_count: 0, + is_infinite_loop: false, // Phase 131-11: Unknown from LoopScopeShape alone update_summary: Some(update_summary), }; diff --git a/src/mir/loop_pattern_detection/mod.rs b/src/mir/loop_pattern_detection/mod.rs index eaf556e9..17352877 100644 --- a/src/mir/loop_pattern_detection/mod.rs +++ b/src/mir/loop_pattern_detection/mod.rs @@ -28,7 +28,7 @@ use crate::mir::loop_form::LoopForm; /// Loop pattern classification based on structure. /// -/// This enum represents the 4 main loop patterns we support. +/// This enum represents the 5 main loop patterns we support. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LoopPatternKind { /// Pattern 1: Simple While Loop @@ -52,6 +52,12 @@ pub enum LoopPatternKind { /// - No break statements (for simplicity) Pattern4Continue, + /// Pattern 5: Infinite Loop with Early Exit (Phase 131-11) + /// - Infinite loop: condition is `loop(true)` + /// - Has both break AND continue + /// - Minimal carrier (1 counter-like variable) + InfiniteEarlyExit, + /// Pattern not recognized Unknown, } @@ -66,13 +72,14 @@ impl LoopPatternKind { 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::InfiniteEarlyExit => "Pattern 5: Infinite Loop with Early Exit", LoopPatternKind::Unknown => "Unknown Pattern", } } /// Phase 193-3: Get numeric pattern ID /// - /// Returns the pattern number (1-4) or 0 for unknown. + /// Returns the pattern number (1-5) or 0 for unknown. /// Useful for priority sorting. pub fn pattern_id(&self) -> u8 { match self { @@ -80,6 +87,7 @@ impl LoopPatternKind { LoopPatternKind::Pattern2Break => 2, LoopPatternKind::Pattern3IfPhi => 3, LoopPatternKind::Pattern4Continue => 4, + LoopPatternKind::InfiniteEarlyExit => 5, LoopPatternKind::Unknown => 0, } } @@ -97,7 +105,9 @@ impl LoopPatternKind { pub fn has_special_control_flow(&self) -> bool { matches!( self, - LoopPatternKind::Pattern2Break | LoopPatternKind::Pattern4Continue + LoopPatternKind::Pattern2Break + | LoopPatternKind::Pattern4Continue + | LoopPatternKind::InfiniteEarlyExit ) } @@ -137,6 +147,9 @@ pub struct LoopFeatures { /// Number of continue targets pub continue_count: usize, + /// Phase 131-11: Is this an infinite loop? (condition == true) + pub is_infinite_loop: bool, + /// Phase 170-C-2b: Carrier update pattern summary /// /// Contains UpdateKind (CounterLike/AccumulationLike/Other) for each carrier. @@ -152,14 +165,15 @@ impl LoopFeatures { /// 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: {} }}", + "LoopFeatures {{ break: {}, continue: {}, if: {}, if_else_phi: {}, carriers: {}, break_count: {}, continue_count: {}, infinite: {} }}", self.has_break, self.has_continue, self.has_if, self.has_if_else_phi, self.carrier_count, self.break_count, - self.continue_count + self.continue_count, + self.is_infinite_loop ) } @@ -236,6 +250,7 @@ pub(crate) fn extract_features( carrier_count, break_count, continue_count, + is_infinite_loop: false, // Phase 131-11: LoopForm doesn't have condition info, default to false update_summary, } } @@ -246,19 +261,24 @@ pub(crate) fn extract_features( /// structure-based rules. It does NOT depend on function names or /// variable names like "sum". /// -/// # Pattern Classification Rules (Phase 212.5: Structural if detection) +/// # Pattern Classification Rules (Phase 131-11: InfiniteEarlyExit added) /// -/// 1. **Pattern 4 (Continue)**: `has_continue == true` -/// - Priority: Check first (most specific) +/// 1. **Pattern 5 (InfiniteEarlyExit)**: `is_infinite_loop && has_break && has_continue` +/// - Priority: Check first (most specific - infinite loop with both break and continue) +/// - Phase 131-11: New pattern for `loop(true) { break + continue }` /// -/// 2. **Pattern 3 (If-PHI)**: `has_if && carrier_count >= 1 && !has_break && !has_continue` +/// 2. **Pattern 4 (Continue)**: `has_continue && !has_break` +/// - Priority: Check second (has continue but no break) +/// - Phase 131-11: Narrowed to exclude break+continue cases +/// +/// 3. **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` +/// 4. **Pattern 2 (Break)**: `has_break && !has_continue` /// - Has break but no continue /// -/// 4. **Pattern 1 (Simple While)**: `!has_break && !has_continue && !has_if` +/// 5. **Pattern 1 (Simple While)**: `!has_break && !has_continue && !has_if` /// - Phase 212.5: Exclude loops with if statements /// - No control flow alterations /// @@ -274,8 +294,15 @@ pub(crate) fn extract_features( /// 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 { + // Phase 131-11: Pattern 5: InfiniteEarlyExit (highest priority - most specific) + // MUST check before Pattern 4 to avoid misrouting break+continue cases + if features.is_infinite_loop && features.has_break && features.has_continue { + return LoopPatternKind::InfiniteEarlyExit; + } + + // Pattern 4: Continue (no break) + // Phase 131-11: Narrowed to exclude break+continue (those go to Pattern 5) + if features.has_continue && !features.has_break { return LoopPatternKind::Pattern4Continue; } @@ -335,6 +362,12 @@ pub fn classify_with_diagnosis(features: &LoopFeatures) -> (LoopPatternKind, Str LoopPatternKind::Pattern1SimpleWhile => { "Simple while loop with no special control flow".to_string() } + LoopPatternKind::InfiniteEarlyExit => { + format!( + "Infinite loop (loop(true)) with both break and continue (break_count={}, continue_count={})", + features.break_count, features.continue_count + ) + } LoopPatternKind::Unknown => { format!("Unknown pattern: {}", features.debug_stats()) }