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:
nyash-codex
2025-12-14 09:59:34 +09:00
parent e1d706d2e0
commit 233a49d902
8 changed files with 253 additions and 17 deletions

View File

@ -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.

View File

@ -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

View File

@ -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
))
}

View File

@ -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,
},