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

@ -43,6 +43,43 @@ pub(in crate::mir::builder) fn choose_pattern_kind(
let has_break = ast_features::detect_break_in_body(body);
let has_return = ast_features::detect_return_in_body(body);
// Phase 188.3: Pattern6 selection for 1-level nested loops
// SSOT: All Pattern6 detection happens here (no dev dependency)
//
// Strategy: Cheap check → StepTree → Full AST validation
// Only select Pattern6 if lowering is guaranteed to work
// Step 1: Cheap check - does body contain any Loop node?
let has_inner_loop = body.iter().any(|stmt| matches!(stmt, ASTNode::Loop { .. }));
if has_inner_loop {
// Step 2: Build StepTree to get nesting depth (cost: acceptable for nested loops only)
use crate::ast::Span;
use crate::mir::control_tree::StepTreeBuilderBox;
let loop_ast = ASTNode::Loop {
condition: Box::new(condition.clone()),
body: body.to_vec(),
span: Span::unknown(),
};
let tree = StepTreeBuilderBox::build_from_ast(&loop_ast);
// Step 3: Check if exactly 1-level nesting (depth == 2)
if tree.features.max_loop_depth == 2 {
// Step 4: Full AST validation (Pattern1-compatible requirements)
if is_pattern6_lowerable(&tree, body) {
// Pattern6 selected - lowering MUST succeed
trace::trace().dev(
"choose_pattern_kind",
"[routing] Pattern6 selected: 1-level nested loop validated"
);
return loop_pattern_detection::LoopPatternKind::Pattern6NestedLoopMinimal;
}
// Validation failed - not Pattern6, fall through to router_choice
}
}
// Phase 110: StepTree parity check (structure-only SSOT).
//
// This is dev-only; strict mode turns mismatch into a fail-fast.
@ -145,6 +182,56 @@ pub(in crate::mir::builder) fn choose_pattern_kind(
router_choice
}
/// Phase 188.3: Validate nested loop meets ALL Pattern6 requirements
///
/// Returns true ONLY if Pattern6 lowering is guaranteed to succeed.
/// False → fall through to other patterns (NOT an error)
fn is_pattern6_lowerable(tree: &crate::mir::control_tree::StepTree, body: &[crate::ast::ASTNode]) -> bool {
use crate::ast::ASTNode;
// Requirement 1: Outer loop has no break (Pattern1 requirement)
if tree.features.has_break {
return false;
}
// Requirement 2: Outer loop has no continue (Pattern1 requirement)
if tree.features.has_continue {
return false;
}
// Requirement 3: Extract inner loop(s) - must have exactly 1
let mut inner_loop: Option<&ASTNode> = None;
for stmt in body.iter() {
if matches!(stmt, ASTNode::Loop { .. }) {
if inner_loop.is_some() {
// Multiple inner loops - not supported
return false;
}
inner_loop = Some(stmt);
}
}
let inner_loop = match inner_loop {
Some(l) => l,
None => return false, // No inner loop found (shouldn't happen, but defensive)
};
// Requirement 4: Inner loop has no break (Pattern1 requirement)
use crate::mir::control_tree::StepTreeBuilderBox;
let inner_tree = StepTreeBuilderBox::build_from_ast(inner_loop);
if inner_tree.features.has_break {
return false;
}
// Requirement 5: Inner loop has no continue (Pattern1 requirement)
if inner_tree.features.has_continue {
return false;
}
// All requirements met - Pattern6 lowering will succeed
true
}
impl MirBuilder {
/// Phase 49: Try JoinIR Frontend for mainline integration
///