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:
@ -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={})",
|
||||
|
||||
Reference in New Issue
Block a user