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:
360
docs/development/current/main/phases/phase-188.1/README.md
Normal file
360
docs/development/current/main/phases/phase-188.1/README.md
Normal file
@ -0,0 +1,360 @@
|
||||
# Phase 188.1: cap_missing/NestedLoop 解除(strict gate unblock)
|
||||
|
||||
**Date**: 2025-12-27
|
||||
**Goal**: `cap_missing/NestedLoop` を解除して、`selfhost_minimal` を integration で PASS させる(Fail-Fastを崩さない)
|
||||
**Status**: ✅ Capability gate unblock 完了 / ❌ Pattern 6(検出・lowering)は Phase 188.2+ に deferred
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Implementation Reality (Phase 188.1 Scope)
|
||||
|
||||
### What Phase 188.1 actually did
|
||||
|
||||
- ✅ **StepTree capability gate**: `StepCapability::NestedLoop` を strict allowlist に追加して、`cap_missing/NestedLoop` を解除した
|
||||
- ✅ **Integration導線**: `selfhost_minimal` の SKIP を撤去しても integration selfhost が FAIL=0 になることを確認した
|
||||
|
||||
### What Phase 188.1 did NOT do
|
||||
|
||||
- ❌ **Nested loop の自動検出(LoopFormベース)**は未実装
|
||||
`loop_pattern_detection::extract_features()` は `max_loop_depth=1` / `has_inner_loops=false` の固定値(TODO)で、Pattern 6 の分岐は到達しない
|
||||
- ❌ **Pattern 6 lowering** は未実装
|
||||
`src/mir/join_ir/lowering/loop_patterns/nested_minimal.rs` はインフラ(stub)で、現状 `None` を返す
|
||||
|
||||
### Why LoopForm-based nesting detection is impossible (current architecture)
|
||||
|
||||
Nesting depth は **StepTree(AST側)**にはあるが、**LoopForm(MIR側)**には存在しない。
|
||||
|
||||
- ✅ StepTree: `StepTreeFeatures.max_loop_depth`(AST → StepTree 変換時に計算)
|
||||
- ❌ LoopForm: `LoopForm = LoopShape` は CFG ブロック参照だけを持ち、親子/深さ情報が無い
|
||||
|
||||
従って、ネスト深さの検査や Pattern 6 の自動検出を **LoopFormレイヤーだけで完結させることはできない**。
|
||||
次の実装は Phase 188.2 で「StepTree側を使うか / LoopRegion(親子構造)を実装するか」を docs-first で決める。
|
||||
|
||||
## Pattern 6 Specification: NestedLoop Minimal
|
||||
|
||||
**Note**: このセクションは「目標仕様(design)」であり、Phase 188.1 では gate unblock のみ完了。実装は Phase 188.2+。
|
||||
|
||||
### Supported Forms (ONLY)
|
||||
|
||||
**Pattern**: Outer Pattern 1 + Inner Pattern 1
|
||||
|
||||
```nyash
|
||||
// Outer loop: Pattern 1 (simple while, no break/continue)
|
||||
loop(outer_cond) {
|
||||
// ... outer loop body before inner ...
|
||||
|
||||
// Inner loop: Pattern 1 ONLY (simple while, no break/continue)
|
||||
loop(inner_cond) {
|
||||
// ... inner loop body ...
|
||||
}
|
||||
|
||||
// ... outer loop body after inner ...
|
||||
}
|
||||
```
|
||||
|
||||
**Requirements**:
|
||||
- **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)
|
||||
|
||||
**Rejected with明示エラー** (no silent fallback):
|
||||
|
||||
1. **Deeper nesting** (`max_loop_depth > 2`):
|
||||
```
|
||||
[joinir/nested_loop/depth_exceeded] max_loop_depth=3 exceeds limit (max=2)
|
||||
Hint: Refactor to avoid 3+ level nesting, or split into separate functions
|
||||
```
|
||||
|
||||
2. **Inner loop with break/continue**:
|
||||
```
|
||||
[joinir/nested_loop/inner_control_flow] Inner loop has break/continue (not supported)
|
||||
Hint: Only simple while loops (Pattern 1) supported as inner loops
|
||||
```
|
||||
|
||||
3. **Outer loop with break/continue**:
|
||||
```
|
||||
[joinir/nested_loop/outer_control_flow] Outer loop has break/continue (not supported)
|
||||
Hint: Only simple while loops (Pattern 1) supported as outer loops
|
||||
```
|
||||
|
||||
4. **Multiple inner loops** (siblings):
|
||||
```
|
||||
[joinir/nested_loop/multiple_inner] Multiple inner loops detected (not supported)
|
||||
Hint: Only one inner loop per outer loop supported
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## JoinIR Lowering Strategy
|
||||
|
||||
### Example Input (Nyash)
|
||||
|
||||
```nyash
|
||||
static box Main {
|
||||
main() {
|
||||
local outer_i = 0
|
||||
loop(outer_i < 3) {
|
||||
local inner_j = 0
|
||||
loop(inner_j < 2) {
|
||||
print(inner_j)
|
||||
inner_j = inner_j + 1
|
||||
}
|
||||
outer_i = outer_i + 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Expected Output (JoinIR Pseudocode)
|
||||
|
||||
```text
|
||||
fn main():
|
||||
Call(outer_step, [0, k_main_exit])
|
||||
|
||||
fn outer_step(outer_i, k_outer_exit):
|
||||
// Exit condition check
|
||||
exit_cond = !(outer_i < 3)
|
||||
Jump(k_outer_exit, [], cond=exit_cond) // Early exit if condition false
|
||||
|
||||
// Initialize inner loop variables
|
||||
inner_j = 0
|
||||
|
||||
// Inner loop step function (nested inside outer_step)
|
||||
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 body after inner completes)
|
||||
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])
|
||||
|
||||
fn k_main_exit():
|
||||
return 0
|
||||
```
|
||||
|
||||
**Key Points**:
|
||||
- **Nested functions**: Inner step function is defined inside outer step function
|
||||
- **Continuation wiring**: `k_inner_exit` resumes outer loop body after inner completes
|
||||
- **Carrier isolation**: Outer carriers (`outer_i`) and inner carriers (`inner_j`) are separate
|
||||
- **Same pattern as Pattern 1**: Both loops use tail-recursive step function pattern
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Implementation Reality (Phase 188.1 Scope)
|
||||
|
||||
### What Was Actually Implemented
|
||||
|
||||
**Phase 188.1 delivered**:
|
||||
1. ✅ **StepTree capability gate**: `StepCapability::NestedLoop` added to allowlist
|
||||
2. ✅ **Pattern 6 enum**: `Pattern6NestedLoopMinimal` classification added
|
||||
3. ✅ **Lowering stub**: `nested_minimal.rs` module created (infrastructure only)
|
||||
4. ✅ **Test pass**: selfhost_minimal conditional SKIP removed, 154/154 PASS maintained
|
||||
|
||||
**Phase 188.1 did NOT implement**:
|
||||
- ❌ **Automatic nesting detection from LoopForm**: LoopForm (= LoopShape) has NO nesting information
|
||||
- ❌ **Pattern 6 lowering logic**: `lower_nested_loop_minimal_to_joinir()` returns `None` (stub)
|
||||
- ❌ **LoopRegion integration**: LoopRegion parent/child structure exists but is NOT instantiated
|
||||
|
||||
### Why Automatic Detection Is NOT Possible (Current Architecture)
|
||||
|
||||
**LoopForm Limitation**:
|
||||
```rust
|
||||
// src/mir/loop_form.rs
|
||||
pub type LoopForm = crate::mir::control_form::LoopShape;
|
||||
|
||||
// LoopShape structure (src/mir/control_form.rs):
|
||||
pub struct LoopShape {
|
||||
pub preheader: BasicBlockId,
|
||||
pub header: BasicBlockId,
|
||||
pub body: Vec<BasicBlockId>,
|
||||
pub latch: BasicBlockId,
|
||||
pub exit_blocks: Vec<BasicBlockId>,
|
||||
// ❌ NO parent field
|
||||
// ❌ NO children field
|
||||
// ❌ NO depth field
|
||||
}
|
||||
```
|
||||
|
||||
**Nesting information exists ONLY in StepTree** (AST level):
|
||||
```rust
|
||||
// src/mir/control_tree/step_tree.rs (line 17-25)
|
||||
pub struct StepTreeFeatures {
|
||||
pub max_loop_depth: u32, // ← Detected during AST → StepTree conversion
|
||||
// ...
|
||||
}
|
||||
```
|
||||
- ✅ AST parsing time: Nesting depth calculated
|
||||
- ❌ MIR lowering time: LoopForm has NO access to this information
|
||||
|
||||
**LoopRegion infrastructure exists but is NOT integrated**:
|
||||
```rust
|
||||
// src/mir/control_form.rs (line 40-62) - Phase 32 definition
|
||||
pub struct LoopRegion {
|
||||
pub parent: Option<LoopId>, // ← Structure defined
|
||||
pub children: Vec<LoopId>, // ← But NOT instantiated anywhere
|
||||
}
|
||||
```
|
||||
|
||||
### Where Nesting Depth Checking MUST Happen
|
||||
|
||||
**✅ Possible locations**:
|
||||
1. **StepTree level** (before LoopForm creation):
|
||||
- Use `StepTreeFeatures.max_loop_depth` during control flow analysis
|
||||
- Reject `max_loop_depth > 2` at StepTree → LoopForm conversion time
|
||||
|
||||
2. **LoopRegion level** (if integrated in Phase 188.2+):
|
||||
- Use `LoopRegion.parent` to compute actual nesting depth
|
||||
- Build LoopRegion tree and check depth constraints
|
||||
|
||||
**❌ Impossible location**:
|
||||
- **LoopForm level**: No nesting information available (by design)
|
||||
|
||||
### Phase 188.2 Design Decision Required
|
||||
|
||||
**Choice A: StepTreeFeatures Integration**
|
||||
- Pass `StepTreeFeatures` (AST-level) to JoinIR lowering
|
||||
- Use `max_loop_depth` from StepTree for Pattern 6 detection
|
||||
- Pro: Nesting info already exists
|
||||
- Con: AST-level info may not match MIR structure (optimizations)
|
||||
|
||||
**Choice B: LoopRegion Integration**
|
||||
- Instantiate `LoopRegion` with parent/child relationships
|
||||
- Compute nesting depth from MIR control flow graph
|
||||
- Pro: MIR-level truth (accurate after optimizations)
|
||||
- Con: Requires implementing LoopRegion builder (Phase 32 infrastructure not yet wired)
|
||||
|
||||
**Decision timeline**: Phase 188.2 planning session (docs-first approach)
|
||||
|
||||
---
|
||||
|
||||
## Implementation Files
|
||||
|
||||
### New Files Created
|
||||
|
||||
1. **`src/mir/join_ir/lowering/loop_patterns/nested_minimal.rs`**
|
||||
- Phase 188.1: インフラ(stub)。現状 `lower_nested_loop_minimal_to_joinir()` は `None` を返す
|
||||
|
||||
### Modified Files
|
||||
|
||||
1. **`src/mir/loop_pattern_detection/mod.rs`** (~15 lines)
|
||||
- Add `Pattern6NestedLoopMinimal` enum variant
|
||||
- Add `max_loop_depth`, `has_inner_loops` to `LoopFeatures`
|
||||
- Update `extract_features()`, `classify()`
|
||||
|
||||
2. **`src/mir/join_ir/lowering/loop_patterns/mod.rs`** (~2 lines)
|
||||
- Export `nested_minimal` module
|
||||
|
||||
3. **`src/mir/join_ir/lowering/loop_pattern_router.rs`** (~10 lines)
|
||||
- Add Pattern 6 routing case
|
||||
- Add explicit error for `max_loop_depth > 2`
|
||||
|
||||
4. **`src/mir/builder/control_flow/joinir/control_tree_capability_guard.rs`** (~1 line)
|
||||
- Add `StepCapability::NestedLoop` to allowlist
|
||||
|
||||
5. **`tools/smokes/v2/profiles/integration/selfhost/selfhost_minimal.sh`** (~6 lines removed)
|
||||
- Remove conditional SKIP for `cap_missing/NestedLoop`
|
||||
|
||||
---
|
||||
|
||||
## Integration Points
|
||||
|
||||
### Detection Pipeline
|
||||
|
||||
1. **AST → StepTree** (`step_tree.rs` lines 461-463):
|
||||
```rust
|
||||
if features.max_loop_depth > 1 {
|
||||
facts.add_capability(StepCapability::NestedLoop);
|
||||
}
|
||||
```
|
||||
|
||||
2. **StepTree → Contract** (automatic capability contract check)
|
||||
|
||||
3. **Contract → Guard** (`control_tree_capability_guard.rs` line 44):
|
||||
```rust
|
||||
StepCapability::NestedLoop, // Phase 188.1: Now in allowlist
|
||||
```
|
||||
|
||||
4. **LoopForm → Pattern Detection** (`loop_pattern_detection::classify()`):
|
||||
```rust
|
||||
// Phase 188.1: Pattern 6 enum defined, but detection logic is STUB
|
||||
// Currently always returns max_loop_depth = 1 (default)
|
||||
// because LoopForm has NO nesting information.
|
||||
//
|
||||
// TODO (Phase 188.2): Integrate StepTreeFeatures or LoopRegion
|
||||
// to enable actual nesting detection.
|
||||
if features.max_loop_depth == 2
|
||||
&& features.has_inner_loops
|
||||
&& !features.has_break
|
||||
&& !features.has_continue
|
||||
{
|
||||
return LoopPatternKind::Pattern6NestedLoopMinimal; // Never reached (stub)
|
||||
}
|
||||
```
|
||||
|
||||
5. **Pattern → Lowering** (`loop_pattern_router.rs`):
|
||||
```rust
|
||||
LoopPatternKind::Pattern6NestedLoopMinimal => {
|
||||
super::loop_patterns::lower_nested_loop_minimal_to_joinir(loop_form, lowerer)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- ✅ `selfhost_minimal.sh`: Remove conditional SKIP, test PASS (or explicit error)
|
||||
- ✅ `integration --filter "selfhost_"`: FAIL=0 maintained
|
||||
- ✅ `quick`: 154/154 PASS unchanged
|
||||
|
||||
### Quality Requirements
|
||||
|
||||
- ✅ Explicit errors for unsupported forms (no silent `Ok(None)`)
|
||||
- ✅ Phase 286 Fail-Fast principle maintained (no silent fallback)
|
||||
- ✅ Minimal diff (~180 lines total)
|
||||
|
||||
---
|
||||
|
||||
## Out of Scope (Future Work)
|
||||
|
||||
**Deferred to Phase 188.2+**:
|
||||
- Pattern 6 の実装(ネスト検出のSSOT決定 + lowering 実装)
|
||||
- break/continue in nested loops (Pattern 2/4 inner/outer combinations)
|
||||
- Multiple inner loops (siblings)
|
||||
- 2+ level nesting (3+ loop depth)
|
||||
- Nested loops with PHI (Pattern 3 inner/outer combinations)
|
||||
- Shared carriers between outer/inner loops
|
||||
|
||||
**Rationale**: Phase 188.1 is minimal PoC to unblock selfhost_minimal. Complex patterns deferred to avoid scope creep.
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **Phase 188 Overview**: `docs/private/roadmap2/phases/phase-188-joinir-loop-pattern-expansion/README.md`
|
||||
- **Pattern Classification**: `docs/private/roadmap2/phases/phase-188-joinir-loop-pattern-expansion/pattern-classification.md`
|
||||
- **JoinIR Architecture**: `docs/development/current/main/joinir-architecture-overview.md`
|
||||
- **Selfhost Integration Limitations**: `docs/development/current/main/investigations/selfhost-integration-limitations.md`
|
||||
|
||||
---
|
||||
|
||||
## End of Phase 188.1 Documentation
|
||||
|
||||
**Status**: Infrastructure complete, detection logic deferred to Phase 188.2
|
||||
**Reality**: Pattern 6 enum exists, but automatic detection is NOT implemented (LoopForm limitation)
|
||||
**Total Estimated Effort**: 4-5 hours (infrastructure only)
|
||||
**Date Completed**: 2025-12-27
|
||||
Reference in New Issue
Block a user