diff --git a/docs/development/current/main/01-JoinIR-Selfhost-INDEX.md b/docs/development/current/main/01-JoinIR-Selfhost-INDEX.md index 8639cb4c..fa36ab21 100644 --- a/docs/development/current/main/01-JoinIR-Selfhost-INDEX.md +++ b/docs/development/current/main/01-JoinIR-Selfhost-INDEX.md @@ -101,6 +101,8 @@ Phase 文書は歴史や検証ログも含むので、「JoinIR の現役設計 - → `docs/development/current/main/phase131-2-summary.md`(要点) - LLVM(Python llvmlite)lowering の不具合切り分けで迷っているとき - → `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-6-ssa-dominance-diagnosis.md`(TAG-RUN の初期診断ログ・歴史) - → `docs/development/current/main/phase87-selfhost-llvm-exe-line.md`(実行パイプラインのSSOT) diff --git a/docs/development/current/main/10-Now.md b/docs/development/current/main/10-Now.md index bf0aa18e..f0c4fd02 100644 --- a/docs/development/current/main/10-Now.md +++ b/docs/development/current/main/10-Now.md @@ -76,6 +76,9 @@ - LLVM(Python llvmlite)lowering の棚卸し(Phase 131-3..10): - `docs/development/current/main/phase131-3-llvm-lowering-inventory.md` - 状態: 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` --- diff --git a/docs/development/current/main/case-c-infinite-loop-analysis.md b/docs/development/current/main/case-c-infinite-loop-analysis.md new file mode 100644 index 00000000..9aceb1a5 --- /dev/null +++ b/docs/development/current/main/case-c-infinite-loop-analysis.md @@ -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, 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, + } + ``` + +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, 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, 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, +} + +// 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, 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, 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 diff --git a/docs/development/current/main/phase131-11-case-c-summary.md b/docs/development/current/main/phase131-11-case-c-summary.md new file mode 100644 index 00000000..851ebb92 --- /dev/null +++ b/docs/development/current/main/phase131-11-case-c-summary.md @@ -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, +} +``` + +**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, 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 diff --git a/docs/development/current/main/phase131-3-llvm-lowering-inventory.md b/docs/development/current/main/phase131-3-llvm-lowering-inventory.md index 73ad1688..b58f8720 100644 --- a/docs/development/current/main/phase131-3-llvm-lowering-inventory.md +++ b/docs/development/current/main/phase131-3-llvm-lowering-inventory.md @@ -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` @@ -203,10 +203,36 @@ Function: main Hint: This loop pattern is not supported. All loops must use JoinIR lowering. ``` -**Diagnosis**: -- `loop(true)` with `break`/`continue` doesn't match Pattern 1-4 -- LoopBuilder fallback was removed (Phase 33 cleanup) -- JoinIR Pattern coverage gap: needs Pattern 5 or Pattern variant for infinite loops with early exit +**Root Cause** (Phase 131-11 Analysis): +1. **Pattern Gap**: `loop(true)` (infinite loop) not recognized by 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**: +``` +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