## 根本原因解明 - 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>
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:
- Modify
extract_loop_variable_from_condition()to handle boolean literals- Return special token like
"__infinite__"forloop(true)
- Return special token like
- Update Pattern 2 to skip loop variable PHI when
loop_var == "__infinite__" - 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
breakfor 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:
- Add
pattern5_infinite_break.rs(new file, ~200 lines) - Register in
patterns/mod.rsandpatterns/router.rs - 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)
Option C: Pattern Classification Fix (Recommended)
Idea: Fix the classification logic to route loop(true) + break to Pattern 2, and add infinite loop support there
Changes:
-
Step 1: Add
is_infinite_loopfeature toLoopFeatures// 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>, } -
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, // ... } } -
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 } -
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) + continuereturns 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)
Recommended Approach: Option C
Rationale:
- Semantic similarity:
loop(true) { break }andloop(i < 3) { break }both use break as the primary exit mechanism - Code reuse: Break condition extraction, exit routing, boundary application all the same
- Fail-Fast: Explicitly returns Unknown for
loop(true) + continue(Case C variant) - Incremental: Can add Pattern 6 for
loop(true) + continuein future phase
Implementation Steps (next section)
Implementation Plan (Option C)
Phase 131-11-A: Feature Detection
Files Modified:
src/mir/loop_pattern_detection/mod.rs- Addis_infinite_loopfieldsrc/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs- Detectloop(true)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:
- Add
is_infinite_loop()helper - Split
lower()into two paths - 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:
-
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 -
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)
-
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:
- ✅ Phase 131-11-A: Feature Detection (is_infinite_loop)
- ✅ Phase 131-11-B: Classification Priority Fix
- ✅ Phase 131-11-C: Pattern 2 Infinite Loop Lowering
- ✅ Unit tests for classification
- ✅ Integration test:
/tmp/case_c_minimal.hako - ✅ 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 = trueroutes to Pattern 4, but Pattern 4 expects a loop variable
Solution (Phase 131-11):
- Add
is_infinite_loopfeature toLoopFeatures - 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