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>
This commit is contained in:
@ -101,6 +101,8 @@ Phase 文書は歴史や検証ログも含むので、「JoinIR の現役設計
|
|||||||
- → `docs/development/current/main/phase131-2-summary.md`(要点)
|
- → `docs/development/current/main/phase131-2-summary.md`(要点)
|
||||||
- LLVM(Python llvmlite)lowering の不具合切り分けで迷っているとき
|
- LLVM(Python llvmlite)lowering の不具合切り分けで迷っているとき
|
||||||
- → `docs/development/current/main/phase131-3-llvm-lowering-inventory.md`(再現ケース表 + 根本原因候補)
|
- → `docs/development/current/main/phase131-3-llvm-lowering-inventory.md`(再現ケース表 + 根本原因候補)
|
||||||
|
- → `docs/development/current/main/phase131-11-case-c-summary.md`(Case C: `loop(true)` + break/continue の本命計画)
|
||||||
|
- → `docs/development/current/main/case-c-infinite-loop-analysis.md`(Case C: 詳細調査ログ)
|
||||||
- → `docs/development/current/main/phase131-5-taglink-fix-summary.md`(TAG-LINK: symbol 名の修正ログ)
|
- → `docs/development/current/main/phase131-5-taglink-fix-summary.md`(TAG-LINK: symbol 名の修正ログ)
|
||||||
- → `docs/development/current/main/phase131-6-ssa-dominance-diagnosis.md`(TAG-RUN の初期診断ログ・歴史)
|
- → `docs/development/current/main/phase131-6-ssa-dominance-diagnosis.md`(TAG-RUN の初期診断ログ・歴史)
|
||||||
- → `docs/development/current/main/phase87-selfhost-llvm-exe-line.md`(実行パイプラインのSSOT)
|
- → `docs/development/current/main/phase87-selfhost-llvm-exe-line.md`(実行パイプラインのSSOT)
|
||||||
|
|||||||
@ -76,6 +76,9 @@
|
|||||||
- LLVM(Python llvmlite)lowering の棚卸し(Phase 131-3..10):
|
- LLVM(Python llvmlite)lowering の棚卸し(Phase 131-3..10):
|
||||||
- `docs/development/current/main/phase131-3-llvm-lowering-inventory.md`
|
- `docs/development/current/main/phase131-3-llvm-lowering-inventory.md`
|
||||||
- 状態: Case B(Pattern1/loop_min_while)は EMIT/LINK/RUN まで復旧済み。残りは Case C の JoinIR ループパターン穴。
|
- 状態: Case B(Pattern1/loop_min_while)は EMIT/LINK/RUN まで復旧済み。残りは Case C の JoinIR ループパターン穴。
|
||||||
|
- Case C の調査と実装計画:
|
||||||
|
- `docs/development/current/main/phase131-11-case-c-summary.md`
|
||||||
|
- `docs/development/current/main/case-c-infinite-loop-analysis.md`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
703
docs/development/current/main/case-c-infinite-loop-analysis.md
Normal file
703
docs/development/current/main/case-c-infinite-loop-analysis.md
Normal file
@ -0,0 +1,703 @@
|
|||||||
|
# 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`
|
||||||
|
|
||||||
|
```nyash
|
||||||
|
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`):
|
||||||
|
|
||||||
|
```rust
|
||||||
|
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**:
|
||||||
|
```rust
|
||||||
|
// 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:
|
||||||
|
```rust
|
||||||
|
// 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**:
|
||||||
|
|
||||||
|
1. **Step 1**: Add `is_infinite_loop` feature to `LoopFeatures`
|
||||||
|
```rust
|
||||||
|
// 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`
|
||||||
|
```rust
|
||||||
|
// 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
|
||||||
|
```rust
|
||||||
|
// 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
|
||||||
|
```rust
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommended Approach: Option C
|
||||||
|
|
||||||
|
**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**:
|
||||||
|
```diff
|
||||||
|
// 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**:
|
||||||
|
```rust
|
||||||
|
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**:
|
||||||
|
```rust
|
||||||
|
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)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[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`):
|
||||||
|
```nyash
|
||||||
|
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`):
|
||||||
|
```nyash
|
||||||
|
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):
|
||||||
|
```nyash
|
||||||
|
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**:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### 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](./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
|
||||||
312
docs/development/current/main/phase131-11-case-c-summary.md
Normal file
312
docs/development/current/main/phase131-11-case-c-summary.md
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
# Phase 131-11: Case C 本命タスク - 調査完了レポート
|
||||||
|
|
||||||
|
**Date**: 2025-12-14
|
||||||
|
**Status**: ✅ Root Cause Analysis Complete - Ready for Implementation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Task Summary
|
||||||
|
|
||||||
|
**Goal**: Make `loop(true) { ... break ... continue }` compile and run in JoinIR
|
||||||
|
|
||||||
|
**Test File**: `apps/tests/llvm_stage3_loop_only.hako`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Root Cause (完全解明済み)
|
||||||
|
|
||||||
|
### 問題の核心
|
||||||
|
|
||||||
|
**Case C fails because**:
|
||||||
|
1. **Pattern Gap**: `loop(true)` (infinite loop) is NOT recognized by any of Patterns 1-4
|
||||||
|
2. **Loop Variable Extraction Fails**: `extract_loop_variable_from_condition()` expects binary comparison (`i < 3`), not boolean literal (`true`)
|
||||||
|
3. **Classification Priority Bug**: `has_continue = true` routes to Pattern 4, but Pattern 4 expects a loop variable
|
||||||
|
|
||||||
|
### Failure Flow (5 Steps)
|
||||||
|
|
||||||
|
```
|
||||||
|
1. LoopPatternContext::new()
|
||||||
|
└─ has_continue=true, has_break=true (✅ detected correctly)
|
||||||
|
|
||||||
|
2. classify(features)
|
||||||
|
└─ Returns Pattern4Continue (❌ WRONG - should be Pattern2 or new Pattern5)
|
||||||
|
|
||||||
|
3. Pattern4::can_lower()
|
||||||
|
└─ Tries extract_loop_variable_from_condition(BoolLiteral(true))
|
||||||
|
|
||||||
|
4. extract_loop_variable_from_condition()
|
||||||
|
└─ ❌ Error: "Unsupported loop condition pattern"
|
||||||
|
└─ Expected: BinaryOp comparison (i < 3)
|
||||||
|
└─ Actual: BoolLiteral(true)
|
||||||
|
|
||||||
|
5. Pattern falls through
|
||||||
|
└─ No pattern matches → freeze() error
|
||||||
|
```
|
||||||
|
|
||||||
|
### 現在のパターン対応表
|
||||||
|
|
||||||
|
| Pattern | Condition Type | Break | Continue | Supported? |
|
||||||
|
|---------|---------------|-------|----------|------------|
|
||||||
|
| Pattern 1 | Comparison (`i < 3`) | ❌ | ❌ | ✅ |
|
||||||
|
| Pattern 2 | Comparison (`i < 3`) | ✅ | ❌ | ✅ |
|
||||||
|
| Pattern 3 | Comparison (`i < 3`) | ❌ | ❌ | ✅ |
|
||||||
|
| Pattern 4 | Comparison (`i < 3`) | ❌ | ✅ | ✅ |
|
||||||
|
| **Case C** | **Boolean (`true`)** | ✅ | ✅ | ❌ **GAP!** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Recommended Solution: Dedicated “InfiniteEarlyExit” pattern (recommended)
|
||||||
|
|
||||||
|
**Approach**: Add `is_infinite_loop` feature + fix classification, then add a dedicated JoinIR loop pattern module for `loop(true)` with early-exit.
|
||||||
|
|
||||||
|
### Why this approach?
|
||||||
|
|
||||||
|
1. **Correctness-first**: `has_break && has_continue` must not route to Pattern 4 (Pattern 4 assumes a loop variable in the condition).
|
||||||
|
2. **No naming collision**: Existing “Trim/P5” already exists; avoid calling this “Pattern 5”.
|
||||||
|
3. **Minimal + Fail-Fast**: Support exactly the Case C skeleton first, reject everything else.
|
||||||
|
4. **Keeps Pattern2 invariants**: Pattern2 is “break but no continue”; extending it to include continue blurs its contract.
|
||||||
|
|
||||||
|
### Implementation Strategy (3 Phases)
|
||||||
|
|
||||||
|
#### Phase 131-11-A: Feature Detection (30 min)
|
||||||
|
|
||||||
|
**Add `is_infinite_loop` field to LoopFeatures**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// 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>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update extract_features to detect loop(true)**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update callers**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/mir/builder/control_flow/joinir/patterns/router.rs (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 Fix (15 min)
|
||||||
|
|
||||||
|
**Fix classify() so break+continue does not misroute to Pattern 4**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/mir/loop_pattern_detection/mod.rs
|
||||||
|
pub fn classify(features: &LoopFeatures) -> LoopPatternKind {
|
||||||
|
+ // NEW: Case C core: infinite loop + break + continue → dedicated pattern kind
|
||||||
|
+ if features.is_infinite_loop && features.has_break && features.has_continue {
|
||||||
|
+ return LoopPatternKind::InfiniteEarlyExit;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
// Pattern 4: Continue (existing)
|
||||||
|
if features.has_continue && !features.has_break {
|
||||||
|
return LoopPatternKind::Pattern4Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... rest unchanged
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Phase 131-11-C: InfiniteEarlyExit lowering (2-3 hours)
|
||||||
|
|
||||||
|
**Add a dedicated pattern module and keep Pattern2 untouched**
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src/mir/builder/control_flow/joinir/patterns/pattern_infinite_early_exit.rs
|
||||||
|
pub fn can_lower(ctx: &LoopPatternContext) -> bool {
|
||||||
|
matches!(ctx.pattern_kind, LoopPatternKind::InfiniteEarlyExit)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower(builder: &mut MirBuilder, ctx: &LoopPatternContext) -> Result<Option<ValueId>, String> {
|
||||||
|
// Shape guard (Fail-Fast):
|
||||||
|
// - condition must be `true` literal
|
||||||
|
// - exactly one break site and one continue site
|
||||||
|
// - no nested loop / nested break/continue
|
||||||
|
// Lowering outline:
|
||||||
|
// - header: unconditional enter step
|
||||||
|
// - step: body core → break-check → continue (tailcall to step) / exit
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Modified Files Summary
|
||||||
|
|
||||||
|
**Total**: 5–6 files modified (+1 new pattern module)
|
||||||
|
|
||||||
|
1. **`src/mir/loop_pattern_detection/mod.rs`**
|
||||||
|
- Add `is_infinite_loop: bool` field to `LoopFeatures` struct
|
||||||
|
- Add `LoopPatternKind::InfiniteEarlyExit`
|
||||||
|
- Update `classify()` (infinite+break+continue must not route to Pattern4)
|
||||||
|
|
||||||
|
2. **`src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs`**
|
||||||
|
- Update `extract_features()` signature (add `condition: &ASTNode` param)
|
||||||
|
- Detect `loop(true)` in condition
|
||||||
|
|
||||||
|
3. **`src/mir/builder/control_flow/joinir/patterns/router.rs`**
|
||||||
|
- Pass condition into `extract_features()`
|
||||||
|
- Ensure `LOOP_PATTERNS` table routes `InfiniteEarlyExit`
|
||||||
|
|
||||||
|
4. **`src/mir/builder/control_flow/joinir/patterns/mod.rs`**
|
||||||
|
- `pub(in crate::mir::builder) mod pattern_infinite_early_exit;` (new module export)
|
||||||
|
|
||||||
|
5. **`src/mir/builder/control_flow/joinir/patterns/pattern_infinite_early_exit.rs`** (NEW)
|
||||||
|
- Shape guard + lowering implementation for Case C
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧪 Test Strategy
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
**File**: `src/mir/loop_pattern_detection/mod.rs` (tests module)
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[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 Minimal** (`/tmp/case_c_minimal.hako`):
|
||||||
|
```nyash
|
||||||
|
static box Main {
|
||||||
|
main() {
|
||||||
|
local i = 0
|
||||||
|
loop (true) {
|
||||||
|
i = i + 1
|
||||||
|
if i == 3 { break }
|
||||||
|
}
|
||||||
|
print(i)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Expected: Prints `3`
|
||||||
|
|
||||||
|
**Case C Full** (`apps/tests/llvm_stage3_loop_only.hako`):
|
||||||
|
```nyash
|
||||||
|
loop (true) {
|
||||||
|
counter = counter + 1
|
||||||
|
if counter == 3 { break }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Expected: MIR compile error (Fail-Fast - not supported yet)
|
||||||
|
|
||||||
|
### End-to-End Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# VM test
|
||||||
|
./target/release/hakorune /tmp/case_c_minimal.hako
|
||||||
|
# Expected output: 3
|
||||||
|
|
||||||
|
# LLVM test (after Phase 131-11 complete)
|
||||||
|
tools/build_llvm.sh /tmp/case_c_minimal.hako -o /tmp/case_c_exe
|
||||||
|
/tmp/case_c_exe
|
||||||
|
# Expected output: 3
|
||||||
|
echo $?
|
||||||
|
# Expected exit code: 0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⏱️ Timeline Estimate
|
||||||
|
|
||||||
|
**Phase 131-11-A**: 30 minutes (Feature Detection)
|
||||||
|
**Phase 131-11-B**: 15 minutes (Classification Fix)
|
||||||
|
**Phase 131-11-C**: 2-3 hours (Pattern 2 Extension)
|
||||||
|
|
||||||
|
**Total**: 3-4 hours to complete Phase 131-11
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Success Criteria
|
||||||
|
|
||||||
|
**Phase 131-11 Complete** when:
|
||||||
|
1. ✅ Case C (minimal) compiles to MIR successfully
|
||||||
|
2. ✅ Case C (minimal) passes VM execution (prints `3`)
|
||||||
|
3. ✅ Case C (minimal) passes LLVM AOT (EMIT + LINK + RUN)
|
||||||
|
4. ✅ Case C (with continue) fails with clear error message (Fail-Fast)
|
||||||
|
5. ✅ No regression in Cases A, B, B2 (all still pass)
|
||||||
|
6. ✅ Unit tests for classification pass
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 References
|
||||||
|
|
||||||
|
**Detailed Analysis**: [case-c-infinite-loop-analysis.md](./case-c-infinite-loop-analysis.md) (13KB, complete investigation)
|
||||||
|
|
||||||
|
**SSOT**: [phase131-3-llvm-lowering-inventory.md](./phase131-3-llvm-lowering-inventory.md) (updated with root cause)
|
||||||
|
|
||||||
|
**Related Files**:
|
||||||
|
- Pattern Detection: `src/mir/loop_pattern_detection/mod.rs`
|
||||||
|
- Feature Extraction: `src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs`
|
||||||
|
- Pattern Router: `src/mir/builder/control_flow/joinir/patterns/router.rs`
|
||||||
|
- Pattern 2 Lowering: `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚦 Next Steps
|
||||||
|
|
||||||
|
**For ChatGPT** (Implementation):
|
||||||
|
1. Start with Phase 131-11-A (Feature Detection) - 30 min
|
||||||
|
2. Proceed to Phase 131-11-B (Classification Fix) - 15 min
|
||||||
|
3. Implement Phase 131-11-C (Pattern 2 Extension) - 2-3 hours
|
||||||
|
4. Run unit tests + integration tests
|
||||||
|
5. Verify end-to-end LLVM AOT path
|
||||||
|
|
||||||
|
**For Claude** (Review):
|
||||||
|
- Review implementation for Box Theory alignment (Fail-Fast, modular boundaries)
|
||||||
|
- Verify no regression in existing patterns
|
||||||
|
- Check for code duplication opportunities (Pattern 2 finite vs infinite)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: 2025-12-14
|
||||||
|
**Phase**: 131-11 (Case C本命タスク)
|
||||||
|
**Status**: 🎯 Ready for Implementation
|
||||||
@ -175,7 +175,7 @@ RC: 0
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 4. TAG-EMIT: JoinIR Pattern Mismatch (Case C)
|
### 4. TAG-EMIT: JoinIR Pattern Coverage Gap (Case C) - ✅ ROOT CAUSE IDENTIFIED
|
||||||
|
|
||||||
**File**: `apps/tests/llvm_stage3_loop_only.hako`
|
**File**: `apps/tests/llvm_stage3_loop_only.hako`
|
||||||
|
|
||||||
@ -203,10 +203,36 @@ Function: main
|
|||||||
Hint: This loop pattern is not supported. All loops must use JoinIR lowering.
|
Hint: This loop pattern is not supported. All loops must use JoinIR lowering.
|
||||||
```
|
```
|
||||||
|
|
||||||
**Diagnosis**:
|
**Root Cause** (Phase 131-11 Analysis):
|
||||||
- `loop(true)` with `break`/`continue` doesn't match Pattern 1-4
|
1. **Pattern Gap**: `loop(true)` (infinite loop) not recognized by Patterns 1-4
|
||||||
- LoopBuilder fallback was removed (Phase 33 cleanup)
|
2. **Loop Variable Extraction Fails**: `extract_loop_variable_from_condition()` expects binary comparison (`i < 3`), not boolean literal (`true`)
|
||||||
- JoinIR Pattern coverage gap: needs Pattern 5 or Pattern variant for infinite loops with early exit
|
3. **Classification Priority Bug**: `has_continue = true` routes to Pattern 4, but Pattern 4 expects a loop variable
|
||||||
|
|
||||||
|
**Failure Flow**:
|
||||||
|
```
|
||||||
|
1. LoopPatternContext::new() detects has_continue=true, has_break=true
|
||||||
|
2. classify() returns Pattern4Continue (because has_continue)
|
||||||
|
3. Pattern4::can_lower() tries extract_loop_variable_from_condition(BoolLiteral(true))
|
||||||
|
4. ❌ Fails: "Unsupported loop condition pattern"
|
||||||
|
5. No pattern matches → freeze() error
|
||||||
|
```
|
||||||
|
|
||||||
|
**Solution** (Phase 131-11 Recommended):
|
||||||
|
- Add `is_infinite_loop: bool` feature to `LoopFeatures` (detect `loop(true)`).
|
||||||
|
- Fix classification so `has_break && has_continue` does not route to Pattern 4.
|
||||||
|
- Introduce a dedicated pattern kind + lowerer for **infinite loop + early-exit (+ optional continue)**:
|
||||||
|
- Example name: `InfiniteEarlyExit` (avoid “Pattern5” naming collision with existing Trim/P5).
|
||||||
|
- Scope (minimum): `loop(true)` with exactly one `break` site and one `continue` site (Fail-Fast outside this).
|
||||||
|
|
||||||
|
**Files to Modify**:
|
||||||
|
1. `src/mir/loop_pattern_detection/mod.rs` - Add `is_infinite_loop` field, update classify()
|
||||||
|
2. `src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs` - Detect `loop(true)` in condition
|
||||||
|
3. `src/mir/builder/control_flow/joinir/patterns/router.rs` - Pass condition to extract_features()
|
||||||
|
4. `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs` - Add infinite loop lowering path
|
||||||
|
|
||||||
|
**Docs (Phase 131-11)**:
|
||||||
|
- Detailed analysis: `docs/development/current/main/case-c-infinite-loop-analysis.md`
|
||||||
|
- Implementation summary: `docs/development/current/main/phase131-11-case-c-summary.md`
|
||||||
|
|
||||||
**Location**: `src/mir/builder/control_flow/joinir/router.rs` - pattern matching logic
|
**Location**: `src/mir/builder/control_flow/joinir/router.rs` - pattern matching logic
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user