Files
hakorune/docs/development/current/main/case-c-infinite-loop-analysis.md
nyash-codex e1d706d2e0 docs(phase131): Case C 調査完了 - InfiniteEarlyExit パターン追加方針
## 根本原因解明
- loop(true) が Pattern4 に誤ルーティング
- Pattern4 は BinaryOp 比較を期待、boolean literal で失敗

## 解決方針
- 新パターン InfiniteEarlyExit 追加(Pattern 2 拡張ではなく)
- classify() の優先度修正
- Shape guard で最小受理(break+continue 各1箇所)

## 作成ドキュメント
- case-c-infinite-loop-analysis.md (13KB詳細分析)
- phase131-11-case-c-summary.md (4KBサマリー)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 09:47:23 +09:00

21 KiB

Case C Analysis: loop(true) + break/continue Pattern

Date: 2025-12-14 Phase: 131-11 (Case C本命タスク) Status: 🔍 Root Cause Analysis Complete

Executive Summary

Problem: loop(true) { ... break ... continue } fails JoinIR pattern matching Root Cause: Loop variable extraction expects comparison operators, not boolean literals Pattern Gap: Need a dedicated “infinite loop + early exit” pattern (avoid “Pattern5” naming collision with existing Trim/P5), or a carefully-scoped Pattern2 extension.


Test Case

File: apps/tests/llvm_stage3_loop_only.hako

static box Main {
  main() {
    local counter = 0
    loop (true) {
      counter = counter + 1
      if counter == 3 { break }
      continue
    }
    print("Result: " + counter)
    return 0
  }
}

Error:

❌ MIR compilation error: [joinir/freeze] Loop lowering failed:
   JoinIR does not support this pattern, and LoopBuilder has been removed.
Function: main
Hint: This loop pattern is not supported. All loops must use JoinIR lowering.

Root Cause Analysis

1. Pattern Detection Flow

Current Flow (for loop(true)):

1. LoopPatternContext::new() extracts features from AST
   ├─ has_break = true (✅ detected)
   ├─ has_continue = true (✅ detected)
   └─ pattern_kind = Pattern4Continue (❌ WRONG - should be Pattern2 or new Pattern5)

2. Pattern 4 (Continue) detect function called
   └─ Tries to extract loop variable from condition
      └─ extract_loop_variable_from_condition(BoolLiteral(true))
         └─ ❌ FAILS - expects BinaryOp comparison, not boolean literal

3. Pattern falls through, no pattern matches
   └─ Returns Ok(None) - "No pattern matched"
      └─ Main router calls freeze() error

Why it fails:

  • Location: src/mir/builder/control_flow/utils.rs:25-52
  • Function: extract_loop_variable_from_condition()
  • Expected: Binary comparison like i < 3
  • Actual: Boolean literal true
  • Result: Error "Unsupported loop condition pattern"

2. Pattern Classification Issue

Classification logic (src/mir/loop_pattern_detection/mod.rs:276-305):

pub fn classify(features: &LoopFeatures) -> LoopPatternKind {
    // Pattern 4: Continue (highest priority)
    if features.has_continue {
        return LoopPatternKind::Pattern4Continue;  // ← Case C goes here
    }

    // Pattern 3: If-PHI (check before Pattern 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
    if !features.has_break && !features.has_continue && !features.has_if {
        return LoopPatternKind::Pattern1SimpleWhile;
    }

    LoopPatternKind::Unknown
}

Problem: Case C has has_continue = true, so it routes to Pattern 4, but:

  • Pattern 4 expects a loop variable in condition (e.g., i < 10)
  • loop(true) has no loop variable - it's an infinite loop

Pattern Coverage Gap

Current Patterns (4 total)

Pattern Condition Type Break Continue Notes
Pattern 1 Comparison (i < 3) Simple while loop
Pattern 2 Comparison (i < 3) Loop with conditional break
Pattern 3 Comparison (i < 3) Loop with if-else PHI
Pattern 4 Comparison (i < 3) Loop with continue

Missing Pattern: Infinite Loop with Early Exit

Case C characteristics:

  • Condition: Boolean literal (true) - infinite loop
  • Break: Yes (exit condition inside loop)
  • Continue: Yes (skip iteration)
  • Carrier: Single variable (counter)

Pattern Gap: None of the 4 patterns handle infinite loops!


Implementation Options

Option A: Pattern 2 Extension (Break-First Variant)

Idea: Extend Pattern 2 to handle both:

  • loop(i < 3) { if cond { break } } (existing)
  • loop(true) { if cond { break } } (new)

Changes:

  1. Modify extract_loop_variable_from_condition() to handle boolean literals
    • Return special token like "__infinite__" for loop(true)
  2. Update Pattern 2 to skip loop variable PHI when loop_var == "__infinite__"
  3. Use break condition as the only exit mechanism

Pros:

  • Minimal code changes (1 file: utils.rs)
  • Reuses existing Pattern 2 infrastructure
  • Matches semantic similarity (both use break for exit)

Cons:

  • Couples two different loop forms (finite vs infinite)
  • Special-case handling (__infinite__ token) is a code smell
  • Pattern 2 assumes loop variable exists in multiple places

Option B: New Pattern 5 (Infinite Loop with Early Exit)

Idea: Create dedicated Pattern 5 for infinite loops

Structure:

// src/mir/builder/control_flow/joinir/patterns/pattern5_infinite_break.rs

/// Pattern 5: Infinite Loop with Early Exit
///
/// Handles:
/// - loop(true) { ... break ... }
/// - loop(true) { ... continue ... }
/// - loop(true) { ... break ... continue ... }
///
/// Key differences from Pattern 2:
/// - No loop variable in condition
/// - Break condition is the ONLY exit mechanism
/// - Continue jumps to top (no loop variable increment)
pub fn can_lower(builder: &MirBuilder, ctx: &LoopPatternContext) -> bool {
    // Check 1: Condition must be boolean literal `true`
    matches!(ctx.condition, ASTNode::BoolLiteral { value: true, .. })
    // Check 2: Must have break statement
    && ctx.has_break
}

pub fn lower(builder: &mut MirBuilder, ctx: &LoopPatternContext)
    -> Result<Option<ValueId>, String> {
    // Similar to Pattern 2, but:
    // - No loop variable PHI
    // - Break condition becomes the only exit test
    // - Continue jumps directly to loop header
}

Changes:

  1. Add pattern5_infinite_break.rs (new file, ~200 lines)
  2. Register in patterns/mod.rs and patterns/router.rs
  3. Update classify() to add Pattern 5 before Pattern 4:
    // Pattern 5: Infinite loop (highest priority after continue)
    if matches!(condition, ASTNode::BoolLiteral { value: true, .. })
        && (features.has_break || features.has_continue) {
        return LoopPatternKind::Pattern5InfiniteLoop;
    }
    

Pros:

  • Fail-Fast principle: Clear separation of concerns
  • Independent testability (Pattern 5 doesn't affect Pattern 2)
  • Easy to extend for loop(true) without break (future Pattern 6?)
  • Matches Box Theory modularization philosophy

Cons:

  • More code (~200 lines new file)
  • Duplicates some logic from Pattern 2 (break condition extraction)

Idea: Fix the classification logic to route loop(true) + break to Pattern 2, and add infinite loop support there

Changes:

  1. Step 1: Add is_infinite_loop feature to LoopFeatures

    // src/mir/loop_pattern_detection/mod.rs
    pub struct LoopFeatures {
        pub has_break: bool,
        pub has_continue: bool,
        pub has_if: bool,
        pub has_if_else_phi: bool,
        pub carrier_count: usize,
        pub break_count: usize,
        pub continue_count: usize,
        pub is_infinite_loop: bool,  // NEW: true for loop(true)
        pub update_summary: Option<LoopUpdateSummary>,
    }
    
  2. Step 2: Detect infinite loop in ast_feature_extractor.rs

    // src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs
    pub(crate) fn extract_features(
        condition: &ASTNode,  // NEW: need condition for infinite loop detection
        body: &[ASTNode],
        has_continue: bool,
        has_break: bool
    ) -> LoopFeatures {
        let is_infinite_loop = matches!(condition, ASTNode::BoolLiteral { value: true, .. });
        // ... rest of extraction
        LoopFeatures {
            has_break,
            has_continue,
            // ... other fields
            is_infinite_loop,
            // ...
        }
    }
    
  3. Step 3: Update classification priority

    // src/mir/loop_pattern_detection/mod.rs:classify()
    pub fn classify(features: &LoopFeatures) -> LoopPatternKind {
        // PRIORITY FIX: Infinite loop with break -> Pattern 2
        // (check BEFORE Pattern 4 Continue)
        if features.is_infinite_loop && features.has_break && !features.has_continue {
            return LoopPatternKind::Pattern2Break;
        }
    
        // Pattern 4: Continue
        if features.has_continue {
            // Infinite loop with continue -> needs special handling
            if features.is_infinite_loop {
                // Option: Create Pattern6InfiniteLoopContinue
                // For now: return Unknown to fail fast
                return LoopPatternKind::Unknown;
            }
            return LoopPatternKind::Pattern4Continue;
        }
    
        // ... rest of classification
    }
    
  4. Step 4: Make Pattern 2 handle infinite loops

    // src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs
    
    // In can_lower():
    pub fn can_lower(builder: &MirBuilder, ctx: &LoopPatternContext) -> bool {
        // Check if classified as Pattern 2
        ctx.pattern_kind == LoopPatternKind::Pattern2Break
    }
    
    // In lower():
    pub fn lower(builder: &mut MirBuilder, ctx: &LoopPatternContext)
        -> Result<Option<ValueId>, String> {
    
        // Check if infinite loop
        let is_infinite = matches!(ctx.condition, ASTNode::BoolLiteral { value: true, .. });
    
        if is_infinite {
            // Infinite loop path: no loop variable extraction
            // Use break condition as the only exit
            return lower_infinite_loop_with_break(builder, ctx);
        } else {
            // Existing finite loop path
            return lower_finite_loop_with_break(builder, ctx);
        }
    }
    
    fn lower_infinite_loop_with_break(...) -> Result<Option<ValueId>, String> {
        // Similar to existing Pattern 2, but:
        // - Skip loop variable extraction
        // - Skip loop variable PHI
        // - Generate infinite loop header (always jump to body)
        // - Break condition becomes the only exit test
    }
    

Pros:

  • Reuses Pattern 2 infrastructure (break condition extraction, exit routing)
  • Clear separation via helper function (lower_infinite_loop_with_break)
  • Fail-Fast for unsupported cases (loop(true) + continue returns Unknown)
  • Incremental implementation (can add Pattern 6 for continue later)

Cons:

  • Pattern 2 becomes more complex (2 lowering paths)
  • Need to update 3+ files (features, classifier, pattern2)

Rationale:

  1. Semantic similarity: loop(true) { break } and loop(i < 3) { break } both use break as the primary exit mechanism
  2. Code reuse: Break condition extraction, exit routing, boundary application all the same
  3. Fail-Fast: Explicitly returns Unknown for loop(true) + continue (Case C variant)
  4. Incremental: Can add Pattern 6 for loop(true) + continue in future phase

Implementation Steps (next section)


Implementation Plan (Option C)

Phase 131-11-A: Feature Detection

Files Modified:

  1. src/mir/loop_pattern_detection/mod.rs - Add is_infinite_loop field
  2. src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs - Detect loop(true)
  3. src/mir/builder/control_flow/joinir/patterns/router.rs - Pass condition to extract_features

Changes:

// LoopFeatures struct
pub struct LoopFeatures {
    pub has_break: bool,
    pub has_continue: bool,
    pub has_if: bool,
    pub has_if_else_phi: bool,
    pub carrier_count: usize,
    pub break_count: usize,
    pub continue_count: usize,
+   pub is_infinite_loop: bool,
    pub update_summary: Option<LoopUpdateSummary>,
}

// extract_features signature
- 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

// In LoopPatternContext::new()
- let features = ast_features::extract_features(body, has_continue, has_break);
+ let features = ast_features::extract_features(condition, body, has_continue, has_break);

Phase 131-11-B: Classification Priority Fix

File Modified: src/mir/loop_pattern_detection/mod.rs

Change:

pub fn classify(features: &LoopFeatures) -> LoopPatternKind {
    // NEW: Infinite loop with break -> Pattern 2 (BEFORE Pattern 4 check!)
    if features.is_infinite_loop && features.has_break && !features.has_continue {
        return LoopPatternKind::Pattern2Break;
    }

    // Pattern 4: Continue (existing)
    if features.has_continue {
        if features.is_infinite_loop {
            // Fail-Fast: loop(true) + continue not supported yet
            return LoopPatternKind::Unknown;
        }
        return LoopPatternKind::Pattern4Continue;
    }

    // ... rest of classification unchanged
}

Phase 131-11-C: Pattern 2 Infinite Loop Lowering

File Modified: src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs

Changes:

  1. Add is_infinite_loop() helper
  2. Split lower() into two paths
  3. Implement lower_infinite_loop_with_break()

Pseudo-code:

pub fn lower(builder: &mut MirBuilder, ctx: &LoopPatternContext)
    -> Result<Option<ValueId>, String> {

    if is_infinite_loop(ctx.condition) {
        lower_infinite_loop_with_break(builder, ctx)
    } else {
        lower_finite_loop_with_break(builder, ctx)  // existing code
    }
}

fn is_infinite_loop(condition: &ASTNode) -> bool {
    matches!(condition, ASTNode::BoolLiteral { value: true, .. })
}

fn lower_infinite_loop_with_break(
    builder: &mut MirBuilder,
    ctx: &LoopPatternContext
) -> Result<Option<ValueId>, String> {
    // Similar to existing Pattern 2, but:
    // 1. Skip loop variable extraction (no loop_var_name)
    // 2. Skip loop variable PHI (no counter increment)
    // 3. Loop header unconditionally jumps to body (no condition check)
    // 4. Break condition becomes the only exit test
    // 5. Carriers are still tracked and merged at exit
}

Test Strategy

Unit Tests

File: src/mir/loop_pattern_detection/mod.rs (tests module)

#[test]
fn test_classify_infinite_loop_with_break() {
    let features = LoopFeatures {
        has_break: true,
        has_continue: false,
        is_infinite_loop: true,
        // ... other fields
    };
    assert_eq!(classify(&features), LoopPatternKind::Pattern2Break);
}

#[test]
fn test_classify_infinite_loop_with_continue_unsupported() {
    let features = LoopFeatures {
        has_break: false,
        has_continue: true,
        is_infinite_loop: true,
        // ... other fields
    };
    assert_eq!(classify(&features), LoopPatternKind::Unknown);
}

Integration Tests

Case C Variants:

  1. Minimal (/tmp/case_c_minimal.hako):

    static box Main {
      main() {
        local i = 0
        loop (true) {
          i = i + 1
          if i == 3 { break }
        }
        print(i)
        return 0
      }
    }
    

    Expected: Prints 3

  2. With Continue (apps/tests/llvm_stage3_loop_only.hako):

    static box Main {
      main() {
        local counter = 0
        loop (true) {
          counter = counter + 1
          if counter == 3 { break }
          continue
        }
        print("Result: " + counter)
        return 0
      }
    }
    

    Expected: MIR compile error (Fail-Fast - not supported yet)

  3. Multi-Carrier (future test):

    loop (true) {
      i = i + 1
      sum = sum + i
      if sum > 10 { break }
    }
    

Migration Path

Phase 131-11: Infinite Loop with Break (Priority 1)

Goal: Make Case C (minimal) compile and run

Tasks:

  1. Phase 131-11-A: Feature Detection (is_infinite_loop)
  2. Phase 131-11-B: Classification Priority Fix
  3. Phase 131-11-C: Pattern 2 Infinite Loop Lowering
  4. Unit tests for classification
  5. Integration test: /tmp/case_c_minimal.hako
  6. LLVM end-to-end test (EMIT + LINK + RUN)

Success Criteria:

  • Case C (minimal) passes VM
  • Case C (minimal) passes LLVM AOT
  • Case C (with continue) fails with clear error

Phase 131-12: Infinite Loop with Continue (Priority 2)

Goal: Support loop(true) + continue (Case C full variant)

Approach: Create Pattern 6 or extend Pattern 4

Not started yet - pending Phase 131-11 completion


SSOT Update

File: docs/development/current/main/phase131-3-llvm-lowering-inventory.md

Section to Add:

### 4. TAG-EMIT: JoinIR Pattern Coverage Gap (Case C)

**File**: `apps/tests/llvm_stage3_loop_only.hako`

**Code**:
```nyash
static box Main {
  main() {
    local counter = 0
    loop (true) {
      counter = counter + 1
      if counter == 3 { break }
      continue
    }
    print("Result: " + counter)
    return 0
  }
}

MIR Compilation: FAILURE

❌ MIR compilation error: [joinir/freeze] Loop lowering failed:
   JoinIR does not support this pattern, and LoopBuilder has been removed.

Root Cause:

  • Pattern Gap: loop(true) (infinite loop) is not recognized by any of Patterns 1-4
  • Loop Variable Extraction Fails: extract_loop_variable_from_condition() expects binary comparison (i < 3), not boolean literal (true)
  • Classification Priority Bug: has_continue = true routes to Pattern 4, but Pattern 4 expects a loop variable

Solution (Phase 131-11):

  • Add is_infinite_loop feature to LoopFeatures
  • Update classification priority: infinite loop + break → Pattern 2
  • Extend Pattern 2 to handle infinite loops (no loop variable PHI)

Location:

  • Feature detection: src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs
  • Classification: src/mir/loop_pattern_detection/mod.rs:classify()
  • Lowering: src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs

Analysis: docs/development/current/main/case-c-infinite-loop-analysis.md


---

## Box Theory Alignment

### Fail-Fast Principle ✅

- **Unsupported patterns return Unknown** (not fallback to broken code)
- **Clear error messages** ("JoinIR does not support this pattern")
- **No silent degradation** (LoopBuilder removed, no hidden fallback)

### Modular Boundaries ✅

- **Feature extraction**: Pure function, no MirBuilder dependency
- **Classification**: Centralized SSOT (`classify()` function)
- **Pattern lowering**: Isolated modules (pattern2_with_break.rs)

### Incremental Extension ✅

- **Phase 131-11**: Add infinite loop with break (Pattern 2 extension)
- **Phase 131-12**: Add infinite loop with continue (Pattern 6 new)
- **No regression risk**: Existing patterns unchanged

---

## References

### Related Files

**Pattern Detection**:
- `src/mir/loop_pattern_detection/mod.rs` - Classification logic
- `src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs` - Feature extraction
- `src/mir/builder/control_flow/joinir/patterns/router.rs` - Pattern routing

**Pattern 2 Implementation**:
- `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs` - Break pattern lowering
- `src/mir/builder/control_flow/utils.rs` - Loop variable extraction

**Testing**:
- `apps/tests/llvm_stage3_loop_only.hako` - Case C test file
- `apps/tests/loop_min_while.hako` - Case B (working reference)

### Documentation

- **Phase 131-3 Inventory**: `docs/development/current/main/phase131-3-llvm-lowering-inventory.md`
- **JoinIR Architecture**: `docs/development/current/main/joinir-architecture-overview.md`
- **Pattern Design**: `docs/private/roadmap2/phases/phase-188-joinir-loop-pattern-expansion/design.md`

---

## Appendix: AST Structure Comparison

### Case B: `loop(i < 3)` (Working)

Loop { condition: BinaryOp { operator: Less, left: Variable { name: "i" }, right: IntLiteral { value: 3 } }, body: [...] }


**extract_loop_variable_from_condition()**: ✅ Returns `"i"`

### Case C: `loop(true)` (Failing)

Loop { condition: BoolLiteral { value: true }, body: [...] }


**extract_loop_variable_from_condition()**: ❌ Error "Unsupported loop condition pattern"

---

## Timeline Estimate

**Phase 131-11-A (Feature Detection)**: 30 minutes
- Add `is_infinite_loop` field to LoopFeatures
- Update extract_features signature
- Update callers (LoopPatternContext)

**Phase 131-11-B (Classification Fix)**: 15 minutes
- Update classify() priority order
- Add unit tests

**Phase 131-11-C (Pattern 2 Extension)**: 2-3 hours
- Implement `is_infinite_loop()` helper
- Implement `lower_infinite_loop_with_break()`
- Handle carriers without loop variable PHI
- Integration testing

**Total**: 3-4 hours to complete Phase 131-11

---

**Last Updated**: 2025-12-14
**Author**: Claude (Analysis Phase)
**Next Step**: Get user confirmation on Option C approach