feat(joinir): Phase 188.3-P3.1,3.2 - AST extraction & validation helpers
Phase 3-1 完了: extract_inner_loop_ast() 実装 - 外側 loop body から内側 loop を抽出 - 正確に 1 つの inner loop を検証 - 0 個 or 2+ 個の場合は明示エラー (Fail-Fast) Phase 3-2 完了: validate_strict_mode() プレースホルダー - 現在は大半の検証を is_pattern6_lowerable() で実施 - 将来の strict mode チェック用の足場 ドキュメント追加: - P1-INSTRUCTIONS.md: Phase 3-3 実装指示書 (4関数モデル詳細) - README.md: max_loop_depth 定義の明確化 - 10-Now.md: Phase 188.3 方針の整合性維持 次タスク: Phase 3-3 (Continuation 生成) - P1-INSTRUCTIONS.md 参照 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
- StepTreeの `max_loop_depth` を SSOT に採用(Option A)
|
||||
- strict mode で depth > 2 を明示エラー化(Fail-Fast)
|
||||
- quick 154/154 PASS、integration selfhost FAIL=0 維持
|
||||
- 次: `docs/development/current/main/phases/phase-188.3/README.md`(depth=2 を JoinIR lowering で通す / 変数はread-only capture→write-backは次フェーズ)
|
||||
- 次: `docs/development/current/main/phases/phase-188.3/README.md`(depth=2 を JoinIR lowering で通す / Phase 188.3は“最小のwrite-back”をcarrierとして明示、一般化は次フェーズ)
|
||||
|
||||
**2025-12-27: Phase S0.1 完了** ✅
|
||||
- integration selfhost を「落ちない状態」に収束(FAIL=0)
|
||||
|
||||
@ -0,0 +1,137 @@
|
||||
# Phase 188.3 P1: Pattern6(NestedLoopMinimal)lowering を “実装済み” にする
|
||||
|
||||
**Date**: 2025-12-27
|
||||
**Scope**: Pattern6 の lowering 実装(fixture を PASS)
|
||||
**Non-goals**: 汎用 nested loop / break/continue / 多段ネスト / 多重 inner loop
|
||||
|
||||
---
|
||||
|
||||
## ✅ Success Criteria
|
||||
|
||||
- `apps/tests/phase1883_nested_minimal.hako` が `--backend vm` で **exit=9**
|
||||
- `./tools/smokes/v2/run.sh --profile quick` が **154/154 PASS**
|
||||
- integration selfhost が **FAIL=0 維持**
|
||||
- Pattern6 を選んだ後に **silent fallback しない**(Fail-Fast)
|
||||
|
||||
---
|
||||
|
||||
## SSOT / Constraints
|
||||
|
||||
- ネスト深さ SSOT: `StepTreeFeatures.max_loop_depth`
|
||||
- Pattern6 選択 SSOT: `src/mir/builder/control_flow/joinir/routing.rs::choose_pattern_kind()`
|
||||
- Phase 188.2: strict では depth > 2 を明示エラー
|
||||
- 本タスクで触るのは “lowering stub を実装” のみ
|
||||
|
||||
---
|
||||
|
||||
## Fixture(目標)
|
||||
|
||||
`apps/tests/phase1883_nested_minimal.hako` を SSOT とする(Add/Compare のみ)。
|
||||
|
||||
---
|
||||
|
||||
## 実装方針(重要)
|
||||
|
||||
### 1) `sum` は carrier として渡す(グローバル禁止)
|
||||
|
||||
inner loop が outer の `sum` を更新しているので、**JoinIR では `sum` を引数で運び、`k_inner_exit` で outer に戻す**。
|
||||
|
||||
- `sum` を “グローバル変数” 扱いにしてはいけない(箱理論と Fail-Fast 的にも事故る)
|
||||
|
||||
### 2) merge の「loop_step 選定」を壊さない
|
||||
|
||||
JoinIR merge は「main でも continuation でもない関数」を 1つ選んで “loop header” とみなす。
|
||||
nested loop では `inner_step` が混ざるので、**`inner_step` / `k_inner_exit` を boundary の `continuation_func_ids` に入れて除外**する。
|
||||
|
||||
これをしないと、merge が誤って `inner_step` を loop header として選び、PHI/exit binding が壊れる。
|
||||
|
||||
---
|
||||
|
||||
## 実装タスク(順番)
|
||||
|
||||
### Task A: lowering を実装する
|
||||
|
||||
対象: `src/mir/builder/control_flow/joinir/patterns/pattern6_nested_minimal.rs`
|
||||
|
||||
やること:
|
||||
- `lower()` で `Err` している stub を置き換えて、JoinIR pipeline を呼ぶ
|
||||
- 最小形だけ対応し、外れた形は **Pattern6 選択前に落とす**(`is_pattern6_lowerable()` 側を強化)か、ここで明示エラー
|
||||
|
||||
推奨構成(Pattern1 と同じ流儀):
|
||||
- JoinIR 生成は `src/mir/join_ir/lowering/nested_loop_minimal.rs`(新規)に切り出す
|
||||
- builder 側は「context 作成 → JoinModule → boundary → conversion pipeline」のみ
|
||||
|
||||
(ただし PoC なので builder 側に直書きでも可。差分は最小に。)
|
||||
|
||||
### Task B: JoinIR(nested minimal)を生成する
|
||||
|
||||
参考: `src/mir/join_ir/lowering/simple_while_minimal.rs`
|
||||
|
||||
最小形の JoinIR 関数構成(推奨):
|
||||
|
||||
- `main(i0, sum0)`:
|
||||
- `Call(loop_step, [i0, sum0])`
|
||||
- `Ret 0`(statement-position)
|
||||
- `loop_step(i, sum)`(outer):
|
||||
- `exit_cond = !(i < N_outer)`
|
||||
- `Jump(k_exit, [sum], cond=exit_cond)`
|
||||
- `Call(inner_step, [j0, i, sum])`
|
||||
- `inner_step(j, i_outer, sum)`:
|
||||
- `exit_cond = !(j < N_inner)`
|
||||
- `Jump(k_inner_exit, [i_outer, sum], cond=exit_cond)`
|
||||
- `sum_next = sum + 1`
|
||||
- `j_next = j + 1`
|
||||
- `Call(inner_step, [j_next, i_outer, sum_next])`
|
||||
- `k_inner_exit(i, sum)`:
|
||||
- `i_next = i + 1`
|
||||
- `Call(loop_step, [i_next, sum])`
|
||||
- `k_exit(sum)`:
|
||||
- `Ret sum`
|
||||
|
||||
命名:
|
||||
- `k_exit` は canonical name: `src/mir/join_ir/lowering/canonical_names.rs::K_EXIT`
|
||||
- `loop_step` は canonical name: `...::LOOP_STEP`
|
||||
- `inner_step` / `k_inner_exit` は Phase 188.3 で追加(文字列でよいが、可能なら canonical_names に追加)
|
||||
|
||||
### Task C: boundary(exit binding + continuation funcs)を正しく構築する
|
||||
|
||||
参考: `src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs`
|
||||
|
||||
必須:
|
||||
- join_inputs/host_inputs は **JoinModule.entry.params と同順・同数**
|
||||
- `exit_bindings` は `sum` を host slot に reconnect
|
||||
- join_exit_value は `k_exit` の param(Pattern1 と同じく `join_module.require_function("k_exit").params[0]` から取得)
|
||||
- `continuation_func_ids` に以下を含める:
|
||||
- `k_exit`
|
||||
- `k_inner_exit`
|
||||
- `inner_step`
|
||||
|
||||
### Task D: integration smoke を追加する(exit code SSOT)
|
||||
|
||||
新規:
|
||||
- `tools/smokes/v2/profiles/integration/joinir/phase1883_nested_minimal_vm.sh`
|
||||
- `apps/tests/phase1883_nested_minimal.hako` を実行し、exit code == 9 で PASS
|
||||
- stdout 比較はしない
|
||||
|
||||
注意:
|
||||
- `tools/smokes/v2` は `manifest.txt` 方式ではない(`find` ベース)
|
||||
- 既存の helper を使う: `source tools/smokes/v2/lib/test_runner.sh`
|
||||
|
||||
---
|
||||
|
||||
## 検証手順
|
||||
|
||||
1. `cargo build --release`
|
||||
2. `./target/release/hakorune --backend vm apps/tests/phase1883_nested_minimal.hako`(exit=9)
|
||||
3. `./tools/smokes/v2/run.sh --profile integration --filter "phase1883_nested_minimal"`
|
||||
4. `./tools/smokes/v2/run.sh --profile quick`(154/154 PASS)
|
||||
5. `./tools/smokes/v2/run.sh --profile integration --filter "selfhost_"`(FAIL=0)
|
||||
|
||||
---
|
||||
|
||||
## 追加の注意(Fail-Fast)
|
||||
|
||||
- Pattern6 が選ばれたあとに `Ok(None)` で他パターンに流すのは禁止(silent fallback)
|
||||
- “選ぶ前に落とす” が最も安全:
|
||||
- `is_pattern6_lowerable()` を「lowering が確実に通る形だけ true」に強化する
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Phase 188.3: Nested loop lowering (1-level) — make Pattern 6 real
|
||||
|
||||
**Date**: TBD
|
||||
**Status**: Planning (docs-first)
|
||||
**Date**: 2025-12-27
|
||||
**Status**: In progress (Phase 2 done: selection / Phase 3: lowering)
|
||||
**Prereq**: Phase 188.2 Option A is complete (StepTree depth SSOT + strict Fail-Fast)
|
||||
|
||||
---
|
||||
@ -15,6 +15,20 @@
|
||||
|
||||
---
|
||||
|
||||
## Definition: `max_loop_depth` (SSOT)
|
||||
|
||||
`StepTreeFeatures.max_loop_depth` は「その関数(or 評価対象ブロック)の中で、最も深い `loop{}` ネストの深さ」を表す。
|
||||
|
||||
- loop が無い: `0`(※StepTree実装に依存。現状の運用では “loopがある前提”で扱う)
|
||||
- loop が 1 段: `1`
|
||||
- loop の中に loop が 1 つ: `2`
|
||||
- `loop{ loop{ loop{ ... } } }`: `3`
|
||||
|
||||
Phase 188.2 の strict ガードは「depth > 2 を明示エラー」にしている。
|
||||
これは言語仕様の制限ではなく、「JoinIR lowering の実装範囲(compiler capability)」の制限。
|
||||
|
||||
---
|
||||
|
||||
## Scope (minimal)
|
||||
|
||||
対応するのは “NestedLoop Minimal” の 1形だけに限定する。
|
||||
@ -28,6 +42,20 @@
|
||||
|
||||
---
|
||||
|
||||
## Current Code Status (reality)
|
||||
|
||||
Phase 188.3 は “選択ロジック(Pattern6選定)” まで実装済みで、lowering が未実装(stub)な状態。
|
||||
|
||||
- Selection SSOT: `src/mir/builder/control_flow/joinir/routing.rs` の `choose_pattern_kind()`
|
||||
- cheap check → StepTree → AST validation
|
||||
- `max_loop_depth == 2` かつ “Pattern6 lowerable” のときだけ `Pattern6NestedLoopMinimal` を返す
|
||||
- Lowering stub: `src/mir/builder/control_flow/joinir/patterns/pattern6_nested_minimal.rs`
|
||||
- 現在は `Err("[Pattern6] ... not yet implemented")` で Fail-Fast
|
||||
|
||||
この README のゴールは、stub を “実装済み” にして fixture を通すこと。
|
||||
|
||||
---
|
||||
|
||||
## SSOT (what to rely on)
|
||||
|
||||
- nesting depth SSOT: `StepTreeFeatures.max_loop_depth`
|
||||
@ -48,10 +76,12 @@ Nyash の変数スコープ方針に合わせて、nested loop lowering でも
|
||||
|
||||
Phase 188.3 では段階的に進める:
|
||||
|
||||
- **P188.3 (minimal)**: outer 変数の **read-only capture** を許す(inner から outer を読む)
|
||||
- **P188.4+ (generalize)**: outer 変数の **write-back** を対応する(inner で outer に代入した値を `k_inner_exit(...)` で戻す)
|
||||
- **P188.3 (minimal)**: “最小の write-back” を **明示的な carrier** として許す
|
||||
- inner loop が outer 変数を更新する場合、その変数は **carrier として引数で受けて、`k_inner_exit(...)` で返す**
|
||||
- 対応範囲は最小(fixtureが要求する 1 carrier 程度)に限定する
|
||||
- **P188.4+ (generalize)**: write-back を一般化(複数 carrier / 代入形 / break/continue を含む形)
|
||||
|
||||
この分離により、PHI/exit binding の複雑さを Phase 188.3 に持ち込まずに済む。
|
||||
この分離により、write-back の複雑さ(複数 carrier / break / continue / PHI の再接続)を Phase 188.3 に持ち込まずに済む。
|
||||
|
||||
---
|
||||
|
||||
@ -65,6 +95,35 @@ Nested loop は JoinIR の「tail recursion + continuation」を再帰的に合
|
||||
|
||||
この `k_inner_exit` がスコープ境界として働くので、将来の write-back もここに集約できる。
|
||||
|
||||
### Minimal function graph (recommended)
|
||||
|
||||
Phase 188.3 の最小形は「outer の loop_step の中で inner の loop_step を呼び、inner が終わったら k_inner_exit で outer に戻る」。
|
||||
|
||||
推奨の JoinIR 関数群(概念):
|
||||
|
||||
- `main(i0, sum0)`:
|
||||
- `Call(loop_step, [i0, sum0])`
|
||||
- `Ret 0`(statement-position loop の場合)
|
||||
- `loop_step(i, sum)`(outer の step。canonical name は `loop_step` に寄せる):
|
||||
- `exit_cond = !(i < N_outer)`
|
||||
- `Jump(k_exit, [sum], cond=exit_cond)`
|
||||
- `Call(inner_step, [j0, i, sum])`
|
||||
- `inner_step(j, i_outer, sum)`:
|
||||
- `exit_cond = !(j < N_inner)`
|
||||
- `Jump(k_inner_exit, [i_outer, sum], cond=exit_cond)`
|
||||
- `sum_next = sum + 1`(fixture の最小形)
|
||||
- `j_next = j + 1`
|
||||
- `Call(inner_step, [j_next, i_outer, sum_next])`
|
||||
- `k_inner_exit(i, sum)`:
|
||||
- `i_next = i + 1`
|
||||
- `Call(loop_step, [i_next, sum])`
|
||||
- `k_exit(sum)`(canonical name `k_exit`):
|
||||
- `Ret sum`
|
||||
|
||||
重要:
|
||||
- **carrier はグローバル扱いしない**。`sum` は引数で運んで戻す。
|
||||
- merge 側の “loop_step 関数の選定” を壊さないため、`inner_step` と `k_inner_exit` は boundary の `continuation_func_ids` に含めて除外する(詳細は指示書)。
|
||||
|
||||
---
|
||||
|
||||
## Deliverables
|
||||
@ -91,6 +150,12 @@ Nested loop は JoinIR の「tail recursion + continuation」を再帰的に合
|
||||
|
||||
---
|
||||
|
||||
## Instructions (handoff)
|
||||
|
||||
実装者(Claude Code)向けの手順書は `docs/development/current/main/phases/phase-188.3/P1-INSTRUCTIONS.md` を参照。
|
||||
|
||||
---
|
||||
|
||||
## Next (schedule)
|
||||
|
||||
- **Phase 188.3**: depth=2 の最小形を “確実に通す” + PoC fixture を smoke 固定
|
||||
|
||||
@ -22,11 +22,74 @@
|
||||
//! - Generate k_inner_exit (bridges to outer continuation)
|
||||
//! - Wire continuations
|
||||
|
||||
use crate::ast::ASTNode;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::LoopPatternContext;
|
||||
use crate::mir::builder::MirBuilder;
|
||||
use crate::mir::loop_pattern_detection::LoopPatternKind;
|
||||
use crate::mir::ValueId;
|
||||
|
||||
/// Phase 188.3: Validate strict mode constraints
|
||||
///
|
||||
/// Requirements (most already checked in is_pattern6_lowerable):
|
||||
/// - Inner loop has no break (checked in routing.rs)
|
||||
/// - Inner loop has no continue (checked in routing.rs)
|
||||
/// - Outer loop has no break (checked in routing.rs)
|
||||
/// - Outer loop has no continue (checked in routing.rs)
|
||||
///
|
||||
/// Additional checks (Phase 188.4+):
|
||||
/// - Outer variable write-back detection (TODO: add to is_pattern6_lowerable)
|
||||
///
|
||||
/// This function serves as final safety check before lowering.
|
||||
/// If validation fails → Fail-Fast (not silent fallback).
|
||||
fn validate_strict_mode(_inner_ast: &ASTNode, _ctx: &LoopPatternContext) -> Result<(), String> {
|
||||
// Phase 188.3: Most validation already done in is_pattern6_lowerable()
|
||||
// This is a placeholder for future strict mode checks
|
||||
|
||||
// Future Phase 188.4+ checks:
|
||||
// - Detect outer variable write-back in inner loop body
|
||||
// - Validate carrier compatibility
|
||||
// - Check for unsupported AST patterns
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Phase 188.3: Extract inner loop AST from outer loop body
|
||||
///
|
||||
/// Requirements:
|
||||
/// - Outer body must contain exactly 1 Loop node
|
||||
/// - 0 loops → Error (shouldn't happen after Pattern6 selection)
|
||||
/// - 2+ loops → Error (multiple inner loops not supported)
|
||||
///
|
||||
/// Returns reference to inner loop ASTNode
|
||||
fn extract_inner_loop_ast<'a>(ctx: &'a LoopPatternContext) -> Result<&'a ASTNode, String> {
|
||||
// Find all Loop nodes in outer body
|
||||
let mut inner_loop: Option<&ASTNode> = None;
|
||||
let mut loop_count = 0;
|
||||
|
||||
for stmt in ctx.body.iter() {
|
||||
if matches!(stmt, ASTNode::Loop { .. }) {
|
||||
loop_count += 1;
|
||||
if inner_loop.is_none() {
|
||||
inner_loop = Some(stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate exactly 1 inner loop
|
||||
match loop_count {
|
||||
0 => Err(
|
||||
"[Pattern6/extract] No inner loop found (should not happen after Pattern6 selection)"
|
||||
.to_string(),
|
||||
),
|
||||
1 => Ok(inner_loop.unwrap()), // Safe: loop_count == 1 guarantees Some
|
||||
_ => Err(format!(
|
||||
"[Pattern6/extract] Multiple inner loops ({}) not supported. \
|
||||
Phase 188.3 supports exactly 1 inner loop only.",
|
||||
loop_count
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect if this context can be lowered as Pattern6 (NestedLoopMinimal)
|
||||
///
|
||||
/// Pattern selection happens in choose_pattern_kind() (SSOT).
|
||||
@ -40,9 +103,15 @@ pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &LoopPatternContext) -> bool
|
||||
/// Phase 188.3: Full implementation with continuation generation
|
||||
pub(crate) fn lower(
|
||||
_builder: &mut MirBuilder,
|
||||
_ctx: &LoopPatternContext,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
// Phase 188.3 stub - full implementation in Phase 3-3
|
||||
// Phase 3-1: Extract inner loop AST (validate exactly 1 inner loop)
|
||||
let inner_ast = extract_inner_loop_ast(ctx)?;
|
||||
|
||||
// Phase 3-2: Validate strict mode constraints (Fail-Fast)
|
||||
validate_strict_mode(inner_ast, ctx)?;
|
||||
|
||||
// Phase 3-3 stub - full implementation next
|
||||
// TODO: Implement continuation generation (outer_step, inner_step, k_inner_exit)
|
||||
Err("[Pattern6] Nested loop lowering not yet implemented (Phase 188.3 stub)".to_string())
|
||||
Err("[Pattern6] Nested loop lowering not yet implemented (Phase 3-3 pending)".to_string())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user