feat(joinir): Phase 131-11 A-C - InfiniteEarlyExit パターン追加(検出部分)
## Step A: Feature Detection - LoopPatternKind::InfiniteEarlyExit (Pattern 5) 追加 - LoopFeatures::is_infinite_loop フィールド追加 - detect_infinite_loop() で loop(true) 検出 ## Step B: Classification Logic - classify() を更新: Pattern 5 を Pattern 4 より優先 - Pattern 4 を狭化: has_continue && !has_break のみ - 誤ルーティング完全除去 ## Step C: Pattern Module - pattern5_infinite_early_exit.rs 新規作成 - Fail-Fast 設計: 超狭い shape guard - lowering はスケルトン(Phase 131-11-D で実装) ## 動作確認 - Pattern 5 正常検出 ✅ - Shape guards 動作(1 break, 1 continue, 1 carrier)✅ - Pattern 4 誤ルーティング回避 ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<Option<ValueId>, 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
|
||||
))
|
||||
}
|
||||
@ -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,
|
||||
},
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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),
|
||||
};
|
||||
|
||||
|
||||
@ -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),
|
||||
};
|
||||
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user