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

@ -132,6 +132,19 @@ pub fn try_lower_loop_pattern_to_joinir(
// Step 3: Route to appropriate lowerer based on pattern
match pattern {
LoopPatternKind::Pattern6NestedLoopMinimal => {
// Phase 188.2: Pattern 6 lowering stub (infrastructure only)
// Currently unreachable: LoopForm has no nesting info, so classify() never returns Pattern6
#[cfg(debug_assertions)]
eprintln!("[try_lower_loop_pattern] Pattern 6 (NestedLoop) reached (should be unreachable until Phase 188.3)");
if let Some(inst) =
super::loop_patterns::lower_nested_loop_minimal_to_joinir(loop_form, lowerer)
{
return Some(inst);
}
// Stub returns None - fallback to existing lowering
}
LoopPatternKind::Pattern4Continue => {
if let Some(inst) =
super::loop_patterns::lower_loop_with_continue_to_joinir(loop_form, lowerer)
@ -169,7 +182,19 @@ pub fn try_lower_loop_pattern_to_joinir(
eprintln!("[try_lower_loop_pattern] ⚠️ Pattern 5 (InfiniteEarlyExit) not implemented in LoopForm router");
}
LoopPatternKind::Unknown => {
eprintln!("[try_lower_loop_pattern] ❌ Unknown pattern, fallback to existing lowering");
// Phase 188.1: Check for explicit rejection reasons (depth > 2)
if features.max_loop_depth > 2 {
eprintln!(
"[try_lower_loop_pattern] ❌ EXPLICIT ERROR: max_loop_depth={} exceeds limit (max=2)",
features.max_loop_depth
);
eprintln!(
"[try_lower_loop_pattern] Hint: Nested loops with depth > 2 not supported in Phase 188.1"
);
// Fallback will trigger error (no silent Ok(None))
} else {
eprintln!("[try_lower_loop_pattern] ❌ Unknown pattern, fallback to existing lowering");
}
}
}

View File

@ -50,11 +50,13 @@ pub mod simple_while;
pub mod with_break;
pub mod with_continue;
pub mod with_if_phi;
pub mod nested_minimal; // Phase 188.1
pub use simple_while::lower_simple_while_to_joinir;
pub use with_break::lower_loop_with_break_to_joinir;
pub use with_continue::lower_loop_with_continue_to_joinir;
pub use with_if_phi::lower_loop_with_conditional_phi_to_joinir;
pub use nested_minimal::lower_nested_loop_minimal_to_joinir; // Phase 188.1
// ============================================================================
// Helper Functions (Shared Utilities)

View File

@ -0,0 +1,369 @@
//! Pattern 6: Nested Loop Minimal Lowering (Phase 188.1)
//!
//! Target: 1-level nested simple while loops
//! Example: `loop(i < 3) { loop(j < 2) { ... } }`
//!
//! # Transformation
//!
//! ```text
//! // Outer loop step function
//! fn outer_step(outer_i, k_outer_exit):
//! exit_cond = !(outer_i < 3)
//! Jump(k_outer_exit, [], cond=exit_cond) // exit if condition false
//!
//! // Initialize inner loop variables
//! inner_j = 0
//!
//! // Inner loop step function (nested)
//! fn inner_step(inner_j, k_inner_exit):
//! exit_cond = !(inner_j < 2)
//! Jump(k_inner_exit, [], cond=exit_cond)
//!
//! print(inner_j)
//! inner_j_next = inner_j + 1
//! Call(inner_step, [inner_j_next, k_inner_exit]) // tail recursion
//!
//! // k_inner_exit continuation (resume outer loop)
//! fn k_inner_exit():
//! outer_i_next = outer_i + 1
//! Call(outer_step, [outer_i_next, k_outer_exit]) // outer tail recursion
//!
//! // Entry: call inner loop
//! Call(inner_step, [inner_j, k_inner_exit])
//!
//! // Main entry
//! Call(outer_step, [0, k_main_exit])
//! ```
//!
//! # Key Design Points
//!
//! - Outer step function contains inner step function (nested structure)
//! - `k_inner_exit` resumes outer loop body after inner completes
//! - Outer/inner carriers are isolated (no shared carriers)
//! - Both loops use same tail-recursive pattern as Pattern 1
//!
//! # Supported Forms (Phase 188.1 Scope)
//!
//! - **Outer loop**: Pattern 1 (simple while, no break/continue)
//! - **Inner loop**: Pattern 1 (simple while, no break/continue)
//! - **Nesting depth**: EXACTLY 1 level (`max_loop_depth == 2`)
//! - **No control flow**: No break/continue in either loop
//! - **Sequential execution**: Inner loop completes before outer continues
//!
//! # Unsupported Forms (Explicit Error)
//!
//! - Deeper nesting (2+ levels): `max_loop_depth > 2`
//! - Inner loop with break/continue
//! - Outer loop with break/continue
//! - Multiple inner loops (siblings)
//!
//! # Reference
//!
//! See `docs/development/current/main/phases/phase-188.1/README.md` for complete specification.
use crate::mir::join_ir::lowering::loop_to_join::LoopToJoinLowerer;
use crate::mir::join_ir::JoinInst;
use crate::mir::loop_form::LoopForm;
/// Lower 1-level nested simple while loops to JoinIR
///
/// # Pattern 6 Transformation Steps
///
/// 1. **Detect Outer + Inner Loops**
/// - Validate outer loop is LoopForm
/// - Find inner loop within outer body
/// - Validate both are Pattern 1 (no break/continue)
///
/// 2. **Extract Outer Loop Variables**
/// - Analyze outer header PHI nodes
/// - Identify outer carriers (e.g., `outer_i`)
///
/// 3. **Extract Inner Loop Variables**
/// - Analyze inner header PHI nodes
/// - Identify inner carriers (e.g., `inner_j`)
///
/// 4. **Create Outer Step Function**
/// - Signature: `fn outer_step(outer_i, k_outer_exit)`
/// - Exit condition check: `!(outer_i < 3)`
/// - Contains inner loop initialization
///
/// 5. **Create Inner Step Function (Nested)**
/// - Signature: `fn inner_step(inner_j, k_inner_exit)`
/// - Exit condition check: `!(inner_j < 2)`
/// - Tail recursion: `Call(inner_step, [inner_j_next])`
///
/// 6. **Create k_inner_exit Continuation**
/// - Resumes outer loop after inner completes
/// - Updates outer carriers: `outer_i_next = outer_i + 1`
/// - Tail call to outer: `Call(outer_step, [outer_i_next])`
///
/// 7. **Wire Continuations**
/// - Inner exit → k_inner_exit
/// - Outer exit → k_outer_exit (parent continuation)
///
/// # Arguments
///
/// * `loop_form` - The outer loop structure (must contain inner loop)
/// * `lowerer` - The LoopToJoinLowerer builder (provides ValueId allocation)
///
/// # Returns
///
/// * `Some(JoinInst)` - Lowering succeeded, returns generated JoinIR instruction
/// * `None` - Lowering failed (pattern not matched or unsupported)
///
/// # Errors
///
/// Returns `None` if:
/// - Outer loop has break/continue (not Pattern 1)
/// - Inner loop has break/continue (not Pattern 1)
/// - Nesting depth > 2 (more than 1 level)
/// - Multiple inner loops detected (siblings)
/// - Inner loop not found in outer body
///
/// # Example Usage
///
/// ```rust,ignore
/// use crate::mir::loop_pattern_detection::LoopPatternKind;
///
/// if pattern == LoopPatternKind::Pattern6NestedLoopMinimal {
/// lower_nested_loop_minimal_to_joinir(&loop_form, &mut lowerer)?;
/// }
/// ```
pub fn lower_nested_loop_minimal_to_joinir(
_loop_form: &LoopForm,
_lowerer: &mut LoopToJoinLowerer,
) -> Option<JoinInst> {
// TODO: Implement Pattern 6 lowering (Phase 188.1 Task 4)
//
// Step 1: Detect Outer + Inner Loops
// ===================================
// Validate loop_form is outer loop, find inner loop in body
//
// ```rust
// let inner_loop = find_inner_loop_in_body(loop_form)?;
// validate_nested_structure(loop_form, inner_loop)?;
// ```
//
// Step 2: Extract Outer Loop Variables
// =====================================
// From outer header PHI: %2 = phi [%1, bb1], [%6, bb_outer_latch]
//
// ```rust
// let outer_carriers = extract_carriers_from_header_phi(loop_form)?;
// ```
//
// Step 3: Extract Inner Loop Variables
// =====================================
// From inner header PHI: %10 = phi [%9, bb_inner_preheader], [%14, bb_inner_latch]
//
// ```rust
// let inner_carriers = extract_carriers_from_header_phi(inner_loop)?;
// ```
//
// Step 4: Create Outer Step Function
// ===================================
// Signature: fn outer_step(outer_i: ValueId, k_outer_exit: JoinContId)
//
// ```rust
// let outer_step_id = lowerer.allocate_join_func_id();
// let k_outer_exit_id = lowerer.allocate_join_func_id();
// let k_inner_exit_id = lowerer.allocate_join_func_id();
//
// // Outer step function body:
// // 1. Exit condition check: exit_cond = !(outer_i < 3)
// // 2. Jump(k_outer_exit, [], cond=exit_cond)
// // 3. Initialize inner loop: inner_j = 0
// // 4. Call(inner_step, [inner_j, k_inner_exit])
// ```
//
// Step 5: Create Inner Step Function (Nested)
// ============================================
// Signature: fn inner_step(inner_j: ValueId, k_inner_exit: JoinContId)
//
// ```rust
// let inner_step_id = lowerer.allocate_join_func_id();
//
// // Inner step function body:
// // 1. Exit condition check: exit_cond = !(inner_j < 2)
// // 2. Jump(k_inner_exit, [], cond=exit_cond)
// // 3. Translate inner body instructions
// // 4. Update inner carrier: inner_j_next = inner_j + 1
// // 5. Tail call: Call(inner_step, [inner_j_next, k_inner_exit])
// ```
//
// Step 6: Create k_inner_exit Continuation
// =========================================
// Resumes outer loop after inner completes
//
// ```rust
// let k_inner_exit_func = JoinFunction {
// id: k_inner_exit_id,
// name: "k_inner_exit".to_string(),
// params: vec![], // No values passed from inner to outer
// body: vec![
// // Update outer carrier: outer_i_next = outer_i + 1
// // Tail call to outer: Call(outer_step, [outer_i_next, k_outer_exit])
// ],
// exit_cont: Some(k_outer_exit_id),
// };
// lowerer.register_join_function(k_inner_exit_func);
// ```
//
// Step 7: Wire Continuations
// ===========================
// Connect inner/outer exits to appropriate continuations
//
// ```rust
// // Inner step exit → k_inner_exit
// // Outer step exit → k_outer_exit (parent continuation)
// ```
// For now, return None (stub implementation)
// Actual implementation will be added incrementally
None
}
/// Validate nested loop structure meets Pattern 6 requirements
///
/// # Validation Rules (Phase 188.1)
///
/// 1. **Outer loop must be Pattern 1**
/// - No break statements
/// - No continue statements
/// - Simple while condition
///
/// 2. **Inner loop must be Pattern 1**
/// - No break statements
/// - No continue statements
/// - Simple while condition
///
/// 3. **Nesting depth must be exactly 2**
/// - Only 1 level of nesting (max_loop_depth == 2)
/// - Deeper nesting (3+ levels) is out-of-scope
///
/// 4. **No multiple inner loops**
/// - Only one inner loop allowed (no siblings)
///
/// # Arguments
///
/// * `outer_loop` - The outer loop structure
/// * `inner_loop` - The inner loop structure
///
/// # Returns
///
/// * `Ok(())` - Validation passed
/// * `Err(String)` - Validation failed with error message
///
/// # Errors
///
/// Uses `error_tags::freeze_with_hint()` format for explicit errors:
///
/// - `[joinir/nested_loop/outer_control_flow]` - Outer loop has break/continue
/// - `[joinir/nested_loop/inner_control_flow]` - Inner loop has break/continue
/// - `[joinir/nested_loop/depth_exceeded]` - Nesting depth > 2
/// - `[joinir/nested_loop/multiple_inner]` - Multiple inner loops detected
#[allow(dead_code)]
fn validate_nested_structure(
_outer_loop: &LoopForm,
_inner_loop: &LoopForm,
) -> Result<(), String> {
// TODO: Implement validation (Phase 188.1 Task 4)
//
// Check 1: Outer loop must be Pattern 1
// ======================================
// if has_break_or_continue(outer_loop) {
// return Err(error_tags::freeze_with_hint(
// "nested_loop/outer_control_flow",
// "Outer loop has break/continue (not supported in Pattern 6)",
// "Only simple while loops supported as outer loops (Pattern 1 only)",
// ));
// }
//
// Check 2: Inner loop must be Pattern 1
// ======================================
// if has_break_or_continue(inner_loop) {
// return Err(error_tags::freeze_with_hint(
// "nested_loop/inner_control_flow",
// "Inner loop has break/continue (not supported in Pattern 6)",
// "Only simple while loops supported as inner loops (Pattern 1 only)",
// ));
// }
//
// Check 3: Nesting depth must be exactly 2
// =========================================
// (This is validated earlier in classify(), but double-check here)
//
// Check 4: No multiple inner loops
// =================================
// let inner_loop_count = count_inner_loops(outer_loop);
// if inner_loop_count > 1 {
// return Err(error_tags::freeze_with_hint(
// "nested_loop/multiple_inner",
// &format!("Multiple inner loops detected ({} loops)", inner_loop_count),
// "Only one inner loop supported in Pattern 6 (Phase 188.1 scope)",
// ));
// }
// For now, return Ok (stub implementation)
Ok(())
}
/// Check if loop has break or continue statements
///
/// # Arguments
///
/// * `loop_form` - The loop structure to check
///
/// # Returns
///
/// * `true` - Loop has break or continue
/// * `false` - Loop is Pattern 1 (no break/continue)
#[allow(dead_code)]
fn has_break_or_continue(_loop_form: &LoopForm) -> bool {
// TODO: Check loop_form.break_targets, loop_form.continue_targets
// For now, assume no break/continue (conservative)
false
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore] // TODO: Implement test after lowering logic is complete
fn test_pattern6_lowering_success() {
// TODO: Add integration test for nested loop pattern lowering
// Step 1: Create mock LoopForm for nested loop pattern
// Step 2: Create mock LoopToJoinLowerer
// Step 3: Call lower_nested_loop_minimal_to_joinir()
// Step 4: Assert returns Some(JoinInst)
// Step 5: Verify generated JoinIR structure (nested functions)
}
#[test]
#[ignore] // TODO: Implement test after lowering logic is complete
fn test_pattern6_rejects_outer_break() {
// TODO: Add test that rejects outer loop with break
// Step 1: Create mock LoopForm with break in outer loop
// Step 2: Call lower_nested_loop_minimal_to_joinir()
// Step 3: Assert returns None (unsupported pattern)
}
#[test]
#[ignore] // TODO: Implement test after lowering logic is complete
fn test_pattern6_rejects_inner_continue() {
// TODO: Add test that rejects inner loop with continue
// Step 1: Create mock LoopForm with continue in inner loop
// Step 2: Call lower_nested_loop_minimal_to_joinir()
// Step 3: Assert returns None (unsupported pattern)
}
#[test]
#[ignore] // TODO: Implement test after lowering logic is complete
fn test_pattern6_rejects_2level_nesting() {
// TODO: Add test that rejects 2+ level nesting
// Step 1: Create mock LoopForm with loop { loop { loop {} } }
// Step 2: Call lower_nested_loop_minimal_to_joinir()
// Step 3: Assert returns None (depth exceeded)
}
}

View File

@ -307,15 +307,9 @@ impl CaseALoweringShape {
// Create stub features (Phase 170-B will use real LoopFeatures)
let stub_features = crate::mir::loop_pattern_detection::LoopFeatures {
has_break: false, // Unknown from LoopScopeShape alone
has_continue: false, // Unknown from LoopScopeShape alone
has_if: false,
has_if_else_phi: false,
carrier_count,
break_count: 0,
continue_count: 0,
is_infinite_loop: false, // Phase 131-11: Unknown from LoopScopeShape alone
update_summary: Some(update_summary),
..Default::default() // Phase 188.1: Use Default for nesting fields
};
Self::detect_with_updates(&stub_features, carrier_count, has_progress_carrier)

View File

@ -75,15 +75,9 @@ impl LoopViewBuilder {
let update_summary = loop_update_summary::analyze_loop_updates_by_name(&carrier_names);
let stub_features = crate::mir::loop_pattern_detection::LoopFeatures {
has_break: false,
has_continue: false,
has_if: false,
has_if_else_phi: false,
carrier_count: scope.carriers.len(),
break_count: 0,
continue_count: 0,
is_infinite_loop: false, // Phase 131-11: Unknown from LoopScopeShape alone
update_summary: Some(update_summary),
..Default::default() // Phase 188.1: Use Default for nesting fields
};
let has_progress_carrier = scope.progress_carrier.is_some();