feat(joinir): Phase 188.3 - Pattern6 (NestedLoopMinimal) 選択ロジック実装

## Phase 188.3 進捗: Phase 2 完了 (6/13 tasks)

### 実装完了 

**Phase 1: Fixture作成**
- apps/tests/phase1883_nested_minimal.hako 追加
  - Add/Compare のみ(乗算なし)
  - 期待 exit code: 9 (3×3 nested loops)
- 既存 lowering で fallback 動作確認

**Phase 2: 選択ロジック (SSOT)**
- LoopPatternContext に step_tree_max_loop_depth フィールド追加
- choose_pattern_kind() に Pattern6 選択ロジック実装:
  1. Cheap check (has_inner_loop)
  2. StepTree 構築 (max_loop_depth 取得)
  3. AST validation (is_pattern6_lowerable)
- pattern6_nested_minimal.rs モジュール作成 (stub)
- LOOP_PATTERNS に Pattern6 entry 追加
- **検証**: Pattern6 が正しく選択される 

### 設計原則 (確認済み)

1. **Fail-Fast**: Pattern6 選択後は Ok(None) で逃げない
2. **outer 変数 write-back 検出 → validation false** (Phase 188.4+)
3. **最小実装**: inner local だけ、Pattern1 モデル二重化
4. **cfg! 依存なし**: production で動作

### 検証結果

```
[choose_pattern_kind] has_inner_loop=true
[choose_pattern_kind] max_loop_depth=2
[choose_pattern_kind] is_pattern6_lowerable=true
 Pattern6 SELECTED!
```

Stub からの期待エラー:
```
[ERROR]  [Pattern6] Nested loop lowering not yet implemented
```

### 次: Phase 3 (Lowering 実装 - 推定4時間)

残りタスク:
- Phase 3-1: AST 抽出ヘルパー
- Phase 3-2: Validation ヘルパー
- Phase 3-3: Continuation 生成 (outer_step, inner_step, k_inner_exit)
- Phase 3-4: fixture が exit=9 を返すことを検証

### 変更ファイル

**新規**:
- apps/tests/phase1883_nested_minimal.hako
- src/mir/builder/control_flow/joinir/patterns/pattern6_nested_minimal.rs
- docs/development/current/main/phases/phase-188.{1,2,3}/README.md

**変更**:
- src/mir/builder/control_flow/joinir/routing.rs (Pattern6 選択)
- src/mir/builder/control_flow/joinir/patterns/router.rs (Context 拡張)
- src/mir/builder/control_flow/joinir/patterns/mod.rs (module 宣言)

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-27 05:45:12 +09:00
parent 229553f7a4
commit a2c5fd90fe
32 changed files with 1780 additions and 42 deletions

View File

@ -58,6 +58,13 @@ pub enum LoopPatternKind {
/// - Minimal carrier (1 counter-like variable)
InfiniteEarlyExit,
/// Pattern 6: Nested Loop (1-level, minimal) - Phase 188.1
/// - Outer loop: Pattern 1 (simple while)
/// - Inner loop: Pattern 1 (simple while)
/// - max_loop_depth == 2 exactly
/// - No break/continue in either loop
Pattern6NestedLoopMinimal,
/// Pattern not recognized
Unknown,
}
@ -73,6 +80,7 @@ impl LoopPatternKind {
LoopPatternKind::Pattern3IfPhi => "Pattern 3: Loop with If-Else PHI",
LoopPatternKind::Pattern4Continue => "Pattern 4: Loop with Continue",
LoopPatternKind::InfiniteEarlyExit => "Pattern 5: Infinite Loop with Early Exit",
LoopPatternKind::Pattern6NestedLoopMinimal => "Pattern 6: Nested Loop (1-level minimal)",
LoopPatternKind::Unknown => "Unknown Pattern",
}
}
@ -88,6 +96,7 @@ impl LoopPatternKind {
LoopPatternKind::Pattern3IfPhi => 3,
LoopPatternKind::Pattern4Continue => 4,
LoopPatternKind::InfiniteEarlyExit => 5,
LoopPatternKind::Pattern6NestedLoopMinimal => 6,
LoopPatternKind::Unknown => 0,
}
}
@ -150,6 +159,12 @@ pub struct LoopFeatures {
/// Phase 131-11: Is this an infinite loop? (condition == true)
pub is_infinite_loop: bool,
/// Phase 188.1: Nesting depth (1 = single loop, 2 = 1-level nested, etc.)
pub max_loop_depth: u32,
/// Phase 188.1: Has inner loops?
pub has_inner_loops: bool,
/// Phase 170-C-2b: Carrier update pattern summary
///
/// Contains UpdateKind (CounterLike/AccumulationLike/Other) for each carrier.
@ -159,13 +174,31 @@ pub struct LoopFeatures {
Option<crate::mir::join_ir::lowering::loop_update_summary::LoopUpdateSummary>,
}
impl Default for LoopFeatures {
fn default() -> Self {
Self {
has_break: false,
has_continue: false,
has_if: false,
has_if_else_phi: false,
carrier_count: 0,
break_count: 0,
continue_count: 0,
is_infinite_loop: false,
max_loop_depth: 1, // Phase 188.1: Default (no nesting)
has_inner_loops: false, // Phase 188.1: Default (no nesting)
update_summary: None,
}
}
}
impl LoopFeatures {
/// Phase 193-3: Get debug statistics string
///
/// Returns a formatted string showing all feature values for debugging.
pub fn debug_stats(&self) -> String {
format!(
"LoopFeatures {{ break: {}, continue: {}, if: {}, if_else_phi: {}, carriers: {}, break_count: {}, continue_count: {}, infinite: {} }}",
"LoopFeatures {{ break: {}, continue: {}, if: {}, if_else_phi: {}, carriers: {}, break_count: {}, continue_count: {}, infinite: {}, depth: {}, inner: {} }}",
self.has_break,
self.has_continue,
self.has_if,
@ -173,7 +206,9 @@ impl LoopFeatures {
self.carrier_count,
self.break_count,
self.continue_count,
self.is_infinite_loop
self.is_infinite_loop,
self.max_loop_depth,
self.has_inner_loops
)
}
@ -271,6 +306,12 @@ pub(crate) fn extract_features(
)
});
// Phase 188.1: Nesting detection
// TODO: Detect from LoopForm structure (nested LoopForm presence)
// For now, default to no nesting (will be detected in lowering phase)
let max_loop_depth = 1;
let has_inner_loops = false;
LoopFeatures {
has_break,
has_continue,
@ -280,6 +321,8 @@ pub(crate) fn extract_features(
break_count,
continue_count,
is_infinite_loop: false, // Phase 131-11: LoopForm doesn't have condition info, default to false
max_loop_depth,
has_inner_loops,
update_summary,
}
}
@ -323,6 +366,22 @@ pub(crate) fn extract_features(
/// Both routers (`router.rs` and `loop_pattern_router.rs`) use this
/// function to avoid duplicate detection logic.
pub fn classify(features: &LoopFeatures) -> LoopPatternKind {
// Phase 188.1: Pattern 6: NestedLoop (1-level only, check first after depth validation)
// Reject 2+ level nesting (explicit error) BEFORE any pattern matching
if features.max_loop_depth > 2 {
// Return Unknown to trigger explicit error in router
return LoopPatternKind::Unknown;
}
// Pattern 6: NestedLoop Minimal (1-level nested, simple while inside simple while)
if features.max_loop_depth == 2
&& features.has_inner_loops
&& !features.has_break
&& !features.has_continue
{
return LoopPatternKind::Pattern6NestedLoopMinimal;
}
// Phase 131-11: Pattern 5: InfiniteEarlyExit (highest priority - most specific)
// MUST check before Pattern 4 to avoid misrouting break+continue cases
if features.is_infinite_loop && features.has_break && features.has_continue {
@ -391,6 +450,12 @@ pub fn classify_with_diagnosis(features: &LoopFeatures) -> (LoopPatternKind, Str
LoopPatternKind::Pattern1SimpleWhile => {
"Simple while loop with no special control flow".to_string()
}
LoopPatternKind::Pattern6NestedLoopMinimal => {
format!(
"Nested loop (1-level, max_loop_depth={}) with no break/continue",
features.max_loop_depth
)
}
LoopPatternKind::InfiniteEarlyExit => {
format!(
"Infinite loop (loop(true)) with both break and continue (break_count={}, continue_count={})",