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,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user