feat(normalization): Phase 142 P0 - Loop statement-level normalization
Phase 142-loopstmt P0: Statement-level normalization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -4,12 +4,15 @@
|
||||
|
||||
## 現在の Phase
|
||||
|
||||
- **Phase 132**: Exit Values Parity (VM == LLVM)
|
||||
- **Phase 133**: Promoted carrier join_id(Trim)修正
|
||||
- **Phase 134**: Plugin loader best-effort loading
|
||||
- **Phase 135**: ConditionLoweringBox allocator SSOT(ValueId 衝突の根治)
|
||||
- **Phase 136**: MirBuilder Context SSOT 化(+ ValueId allocator 掃討)
|
||||
- **Phase 137–141**: Loop Canonicalizer(前処理 SSOT)導入(Phase 137 フォルダに統合して記録)
|
||||
- **Phase 139(DONE)**: post-if `post_k` の return lowering を `ReturnValueLowererBox` に統一(出口 SSOT 完成)
|
||||
- **Phase 140(DONE)**: `NormalizedExprLowererBox` 初版(pure expression のみ)
|
||||
- **Phase 141 P0(DONE)**: impure 拡張点(contract)を SSOT 化(Call/MethodCall はまだ out-of-scope)
|
||||
- **Phase 141 P1(DONE)**: “既知 intrinsic だけ” を許可して段階投入(length0)
|
||||
- **Phase 141 P1.5(DONE)**: known intrinsic registry + available_inputs 3-source merge + diagnostics
|
||||
- **Phase 141 P2+(planned)**: Call/MethodCall 対応(effects + typing の段階投入)
|
||||
- **Phase 142-loopstmt P0(DONE)**: 正規化単位を statement(loop 1個)へ寄せる(パターン爆発を止める)
|
||||
- **Phase 142-loopstmt P1(planned)**: LLVM EXE smoke(同 fixture)を追加
|
||||
- **Phase 143-loopvocab(planned)**: StepTree の語彙拡張(loop 内 if/break/continue を「語彙追加」で吸収)
|
||||
- **Phase 91–92**: Selfhost depth‑2 coverage(P5b escape recognition → lowering)
|
||||
- **Phase 94–100**: P5b escape E2E / Trim policy / pinned + accumulator(VM/LLVM EXE parity)
|
||||
- **Phase 102**: real-app read_quoted loop regression(VM + LLVM EXE)
|
||||
@ -52,4 +55,4 @@ phases/phase-131/
|
||||
|
||||
---
|
||||
|
||||
**最終更新**: 2025-12-18
|
||||
**最終更新**: 2025-12-19
|
||||
|
||||
46
docs/development/current/main/phases/phase-139/README.md
Normal file
46
docs/development/current/main/phases/phase-139/README.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Phase 139: post-if `post_k` Return Lowering Unification
|
||||
|
||||
Status: DONE ✅
|
||||
Scope: if-only `post_if_post_k.rs` の return lowering を `ReturnValueLowererBox` に統一し、loop/if の出口 SSOT を完成させる。
|
||||
Related:
|
||||
- `docs/development/current/main/10-Now.md`
|
||||
- `docs/development/current/main/30-Backlog.md`
|
||||
- `docs/development/current/main/phases/phase-138/README.md`
|
||||
- `src/mir/control_tree/normalized_shadow/post_if_post_k.rs`
|
||||
- `src/mir/control_tree/normalized_shadow/common/return_value_lowerer_box.rs`
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
- if-only `post_k` 内の return を `ReturnValueLowererBox` に委譲し、return の仕様を 1 箇所へ集約する。
|
||||
- out-of-scope は `Ok(None)` で既存経路へフォールバック(既定挙動不変)。
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Assign lowering の共通化(return のみ対象)
|
||||
- ExprLowerer の一般化(Phase 140)
|
||||
- Call/MethodCall(Phase 141+)
|
||||
|
||||
## Plan
|
||||
|
||||
1. `post_if_post_k.rs` の return lowering を `ReturnValueLowererBox::lower_to_value_id()` 呼び出しへ置換(DONE)
|
||||
2. return の重複ロジック削除(変数 lookup / const emission / add emission)(DONE)
|
||||
3. fixture + smoke を追加して VM/LLVM EXE parity で固定(DONE)
|
||||
|
||||
## Tests
|
||||
|
||||
- Fixture:
|
||||
- `apps/tests/phase139_if_only_post_k_return_add_min.hako`(expected exit code 4)
|
||||
- Smokes:
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase139_if_only_post_k_return_add_vm.sh`
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase139_if_only_post_k_return_add_llvm_exe.sh`
|
||||
- regressions:
|
||||
- Phase 136/137/138(return 系)
|
||||
- Phase 97(loop(i<n) フォールバック確認)
|
||||
|
||||
## Acceptance
|
||||
|
||||
- `post_if_post_k.rs` の return lowering が `ReturnValueLowererBox` に一本化されている
|
||||
- 既存の挙動を壊さない(dev-only、既定挙動不変)
|
||||
- VM/LLVM EXE parity を fixture/smoke で固定
|
||||
62
docs/development/current/main/phases/phase-140/README.md
Normal file
62
docs/development/current/main/phases/phase-140/README.md
Normal file
@ -0,0 +1,62 @@
|
||||
# Phase 140: NormalizedExprLowererBox (pure expressions)
|
||||
|
||||
Status: DONE ✅
|
||||
Scope: Normalized shadow の expression lowering を AST walker で一般化し、パターン爆発を避ける(pure のみ)。
|
||||
Related:
|
||||
- `docs/development/current/main/design/normalized-expr-lowering.md`
|
||||
- `docs/development/current/main/30-Backlog.md`
|
||||
- `docs/development/current/main/phases/phase-139/README.md`
|
||||
- `src/mir/control_tree/normalized_shadow/common/expr_lowerer_box.rs`
|
||||
- `src/mir/control_tree/normalized_shadow/common/return_value_lowerer_box.rs`
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
- `NormalizedExprLowererBox` を導入し、pure expression を JoinIR(Normalized dialect)へ lowering できるようにする。
|
||||
- return lowering は `ReturnValueLowererBox` → `ExprLowererBox` 委譲へ寄せ、return の形追加を “総当たり” にしない。
|
||||
|
||||
## Scope (Phase 140 P0)
|
||||
|
||||
### ✅ In scope: pure expression
|
||||
|
||||
- `Variable`
|
||||
- `Literal`(Integer/Bool から開始)
|
||||
- `UnaryOp`(not, -)
|
||||
- `BinaryOp`(+ - * /)
|
||||
- `Compare`(==, <, <=, >, >=)
|
||||
|
||||
### ❌ Out of scope
|
||||
|
||||
- Call/MethodCall(Phase 141+)
|
||||
- Short-circuit(&&/||)の制御フローを伴う lowering
|
||||
- 例外/throw/try/catch
|
||||
- NormalizationPlan の粒度変更(suffix→statement などの再設計は Phase 141+ の検討に回す)
|
||||
|
||||
## Contract
|
||||
|
||||
`NormalizedExprLowererBox::lower_expr(...) -> Result<Option<ValueId>, String>`
|
||||
|
||||
- `Ok(Some(vid))`: lowering 成功
|
||||
- `Ok(None)`: out-of-scope(既存経路へフォールバック、既定挙動不変)
|
||||
- `Err(_)`: 内部不整合のみ(strict では fail-fast)
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- 既存の `ReturnValueLowererBox` は “return 構文” の薄い箱に縮退し、expr lowering の実体は `ExprLowererBox` に置く。
|
||||
- 型は過剰に推論しない。Const/Compare/BinOp の最小ヒントのみ(必要なら段階投入)。
|
||||
|
||||
## Tests
|
||||
|
||||
- Unit tests:
|
||||
- `cargo test -p nyash-rust --lib mir::control_tree::normalized_shadow::common`
|
||||
- Smokes (regressions):
|
||||
- Phase 139(VM/LLVM EXE)
|
||||
- Phase 136/137/138(return 系)
|
||||
- Phase 97(フォールバック)
|
||||
|
||||
## Implementation (DONE)
|
||||
|
||||
- Added pure expression SSOT: `src/mir/control_tree/normalized_shadow/common/expr_lowerer_box.rs`
|
||||
- `ReturnValueLowererBox` shrunk to return-syntax only and delegates to `NormalizedExprLowererBox`
|
||||
- Explicitly keeps out-of-scope = `Ok(None)` (fallback) and avoids fail-fast expansion
|
||||
89
docs/development/current/main/phases/phase-141/README.md
Normal file
89
docs/development/current/main/phases/phase-141/README.md
Normal file
@ -0,0 +1,89 @@
|
||||
# Phase 141: Impure Extension Contract → P1 Known Intrinsic (incremental)
|
||||
|
||||
Status: DONE ✅
|
||||
Scope: `NormalizedExprLowererBox` の pure/impure 境界を contract(SSOT)として型で固定し、Call/MethodCall を段階投入できる形へ収束させる。
|
||||
Related:
|
||||
- `docs/development/current/main/design/normalized-expr-lowering.md`
|
||||
- `docs/development/current/main/phases/phase-140/README.md`
|
||||
- `src/mir/control_tree/normalized_shadow/common/expr_lowering_contract.rs`
|
||||
- `src/mir/control_tree/normalized_shadow/common/expr_lowerer_box.rs`
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
- ExprLowerer が “pure only” のままでも、impure(Call/MethodCall)導入の拡張点(契約)を SSOT 化する。
|
||||
- P0 は contract のみ(Call/MethodCall を lowering しない、既定挙動不変)。
|
||||
- P1 は “既知 intrinsic だけ” を許可し、impure 導入の第一歩を作る(それ以外は既定挙動不変)。
|
||||
|
||||
## Non-Goals (P0)
|
||||
|
||||
- Call/MethodCall の lowering(ValueId生成、effects、receiver materialization)
|
||||
- impure式の順序付け(effects ordering)
|
||||
- 型解決(dispatch/overload)
|
||||
|
||||
## SSOT
|
||||
|
||||
- Contract: `src/mir/control_tree/normalized_shadow/common/expr_lowering_contract.rs`
|
||||
- `ExprLoweringScope::{PureOnly, WithImpure(..)}`
|
||||
- Phase 141 P1: `ImpurePolicy::KnownIntrinsicOnly`, `KnownIntrinsic::{Length0}`
|
||||
- `OutOfScopeReason`(Call/MethodCall 等の最小理由)
|
||||
- ExprLowerer API: `src/mir/control_tree/normalized_shadow/common/expr_lowerer_box.rs`
|
||||
- `lower_expr_with_scope(scope, ...)` を追加
|
||||
- 既存 `lower_expr(...)` は `PureOnly` の thin wrapper(挙動不変)
|
||||
- Return lowering entry: `src/mir/control_tree/normalized_shadow/common/return_value_lowerer_box.rs`
|
||||
- Phase 141 P1: return lowering は `WithImpure(KnownIntrinsicOnly)` を使う(既知 intrinsic のみ許可)
|
||||
|
||||
## Tests
|
||||
|
||||
- Unit: `cargo test -p nyash-rust --lib mir::control_tree::normalized_shadow::common`
|
||||
- Added minimal contract tests:
|
||||
- `call_is_out_of_scope_in_pure_only`
|
||||
- `methodcall_is_out_of_scope_in_pure_only`
|
||||
- `methodcall_length0_is_in_scope_with_known_intrinsic_only`
|
||||
- `lower_methodcall_length0_emits_method_call_inst`
|
||||
|
||||
## Phase 141 P1: KnownIntrinsicOnly (Length0)
|
||||
|
||||
### Scope
|
||||
|
||||
- `ExprLoweringScope::WithImpure(ImpurePolicy::KnownIntrinsicOnly)` のときのみ、以下を lowering する:
|
||||
- `receiver.length()`(引数 0、receiver は env に存在する Variable のみ)
|
||||
- それ以外の Call/MethodCall は out-of-scope (`Ok(None)`) のまま(既定挙動不変)。
|
||||
|
||||
### Fixture / Smoke
|
||||
|
||||
- Fixture: `apps/tests/phase141_p1_if_only_post_k_return_length_min.hako`(expected exit code 3)
|
||||
- Smoke tests:
|
||||
- VM: `tools/smokes/v2/profiles/integration/apps/phase141_p1_if_only_post_k_return_length_vm.sh`
|
||||
- LLVM EXE: `tools/smokes/v2/profiles/integration/apps/phase141_p1_if_only_post_k_return_length_llvm_exe.sh`
|
||||
|
||||
## Phase 141 P1.5: SSOT Reinforcement (Registry + available_inputs + diagnostics)
|
||||
|
||||
### Task A: KnownIntrinsic SSOT (Registry)
|
||||
|
||||
- 目的: intrinsic の metadata(method 名/arity/type_hint)を 1 箇所に集約し、文字列直書きの散らばりを止める。
|
||||
- SSOT:
|
||||
- `src/mir/control_tree/normalized_shadow/common/known_intrinsics.rs`
|
||||
- `KnownIntrinsicRegistryBox::{lookup,get_spec}`
|
||||
- 効果:
|
||||
- `expr_lowerer_box.rs` の by-name 判定が “registry lookup” に収束
|
||||
|
||||
### Task B: available_inputs 3-source merge (Bug fix)
|
||||
|
||||
- 目的: suffix 正規化が prefix 側で生成された locals(`local s; s="..."`)を見失わないようにする。
|
||||
- 変更:
|
||||
- `AvailableInputsCollectorBox::collect(builder, captured_env, prefix_variables)` を追加
|
||||
- 優先順位: Function params > Prefix variables > CapturedEnv
|
||||
- Call sites:
|
||||
- `src/mir/builder/control_flow/normalization/execute_box.rs`
|
||||
- `src/mir/builder/control_flow/joinir/patterns/policies/normalized_shadow_suffix_router_box.rs`
|
||||
|
||||
### Task C: Diagnostics
|
||||
|
||||
- 追加: `OutOfScopeReason::IntrinsicNotWhitelisted`
|
||||
- “methodcall だが known intrinsic allowlist 外” を区別できるようにする
|
||||
|
||||
## Next
|
||||
|
||||
- Phase 141 P2+: 一般 Call/MethodCall(effects + typing を分離して拡張)
|
||||
@ -0,0 +1,351 @@
|
||||
# Phase 142-loopstmt: Statement-Level Loop Normalization
|
||||
|
||||
Status: ✅ P0 Complete
|
||||
Date: 2025-12-19
|
||||
|
||||
---
|
||||
|
||||
## 目的(Why)
|
||||
|
||||
Phase 131-141 で loop(true) 系の Normalized shadow を段階的に拡張したが、「block suffix (loop + post assigns + return)」の直積パターン増殖を止める必要があった。
|
||||
|
||||
**Phase 142-loopstmt** では、正規化単位を **"block suffix" から "statement (loop 1個)" へ寄せる** ことで、パターン爆発を防ぐ。
|
||||
|
||||
### Non-Goal
|
||||
|
||||
- ⚠️ **Phase 142 (Canonicalizer Pattern Extension) とは別物**
|
||||
- Phase 142 = trim leading/trailing, continue pattern (Canonicalizer)
|
||||
- Phase 142-loopstmt = Statement-level normalization (Normalized shadow)
|
||||
- SSOT 衝突回避のため phase-142-loopstmt として独立管理
|
||||
|
||||
---
|
||||
|
||||
## P0: Statement-Level Normalization (COMPLETE ✅)
|
||||
|
||||
### Summary
|
||||
|
||||
Normalization unit changed from "block suffix (loop + post + return)" to "statement (loop only)".
|
||||
|
||||
### Implementation
|
||||
|
||||
#### 1. PlanBox の挙動変更
|
||||
|
||||
**File**: `src/mir/builder/control_flow/normalization/plan_box.rs`
|
||||
|
||||
**Before**:
|
||||
- `loop(true)` の後ろに return が無いと plan が None になりやすい
|
||||
- LoopWithPost pattern を返す(loop + post assigns + return を一括消費)
|
||||
|
||||
**After**:
|
||||
- `remaining[0]` が `loop(true)` なら 常に `NormalizationPlan::loop_only()` を返す
|
||||
- **consumed = 1** (loop のみ)
|
||||
- 後続文(return/assign 等)は通常 MIR lowering へ戻す
|
||||
|
||||
**Code changes**:
|
||||
```rust
|
||||
// Phase 142-loopstmt P0: Always return loop_only for loop(true)
|
||||
// Normalization unit is now "statement (loop 1個)" not "block suffix"
|
||||
// Subsequent statements handled by normal MIR lowering
|
||||
if debug {
|
||||
trace.routing(
|
||||
"normalization/plan",
|
||||
func_name,
|
||||
"Detected loop(true) - Phase 142-loopstmt P0: returning loop_only (consumed=1)",
|
||||
);
|
||||
}
|
||||
Ok(Some(NormalizationPlan::loop_only()))
|
||||
```
|
||||
|
||||
**Impact**: ~70 lines reduced
|
||||
|
||||
#### 2. SuffixRouter の LoopOnly 対応
|
||||
|
||||
**File**: `src/mir/builder/control_flow/joinir/patterns/policies/normalized_shadow_suffix_router_box.rs`
|
||||
|
||||
**Before**: Lines 64-75 で PlanKind::LoopOnly を reject ("not a suffix")
|
||||
|
||||
**After**: LoopOnly も受け入れて execute_box に渡す
|
||||
|
||||
**Code changes**:
|
||||
```rust
|
||||
// Phase 142-loopstmt P0: Accept both LoopOnly and LoopWithPost
|
||||
// Normalization unit is now "statement (loop 1個)", not "block suffix"
|
||||
if debug {
|
||||
let description = match &plan.kind {
|
||||
PlanKind::LoopOnly => "Loop-only pattern".to_string(),
|
||||
PlanKind::LoopWithPost { post_assign_count } => {
|
||||
format!("Loop+post pattern: {} post assigns", post_assign_count)
|
||||
}
|
||||
};
|
||||
trace.routing("suffix_router", func_name, &description);
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**: ~12 lines reduced
|
||||
|
||||
#### 3. build_block で consumed 後の break 削除
|
||||
|
||||
**File**: `src/mir/builder/stmts.rs`
|
||||
|
||||
**Before**:
|
||||
- suffix_router が Some(consumed) を返したら break
|
||||
- return statement が消費されたと仮定
|
||||
|
||||
**After**:
|
||||
- consumed 後も `idx += consumed` でスキップし、後続文を通常処理
|
||||
- Phase 142-loopstmt では loop 正規化後も `return s.length()` 等が残る
|
||||
|
||||
**Code changes**:
|
||||
```rust
|
||||
Some(consumed) => {
|
||||
trace.emit_if(
|
||||
"debug",
|
||||
"build_block/suffix_router",
|
||||
&format!("Phase 142-loopstmt P0: Suffix router consumed {} statement(s), continuing to process subsequent statements", consumed),
|
||||
debug,
|
||||
);
|
||||
// Phase 142-loopstmt P0: Normalization unit is now "statement (loop 1個)"
|
||||
// Loop normalization returns consumed=1, and subsequent statements
|
||||
// (return, assignments, etc.) are handled by normal MIR lowering
|
||||
idx += consumed;
|
||||
// No break - continue processing subsequent statements
|
||||
}
|
||||
```
|
||||
|
||||
### Unit Tests
|
||||
|
||||
**File**: `src/mir/builder/control_flow/normalization/plan_box.rs`
|
||||
|
||||
**Updated tests** (7 tests total):
|
||||
- `test_plan_block_suffix_phase131_loop_only()` - unchanged
|
||||
- `test_plan_block_suffix_phase142_loop_with_subsequent_stmts()` - was phase132, now expects LoopOnly
|
||||
- `test_plan_block_suffix_phase142_loop_only_always()` - was phase133, now expects LoopOnly
|
||||
- `test_plan_block_suffix_phase142_loop_with_trailing_stmt()` - new, no return but still LoopOnly
|
||||
- `test_plan_block_suffix_no_match_empty()` - unchanged
|
||||
- `test_plan_block_suffix_no_match_not_loop()` - unchanged
|
||||
- `test_plan_block_suffix_no_match_loop_not_true()` - unchanged
|
||||
|
||||
**Results**: ✅ 7/7 passed
|
||||
|
||||
### E2E Tests
|
||||
|
||||
#### Fixture
|
||||
|
||||
**File**: `apps/tests/phase142_loop_stmt_only_then_return_length_min.hako`
|
||||
|
||||
```hako
|
||||
static box Main {
|
||||
main() {
|
||||
local s
|
||||
s = "abc"
|
||||
loop(true) {
|
||||
break
|
||||
}
|
||||
return s.length()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Expected**: exit code 3 (s="abc" → s.length() → 3)
|
||||
|
||||
#### VM Smoke Test
|
||||
|
||||
**File**: `tools/smokes/v2/profiles/integration/apps/phase142_loop_stmt_only_then_return_length_min_vm.sh`
|
||||
|
||||
**Command**:
|
||||
```bash
|
||||
bash tools/smokes/v2/profiles/integration/apps/phase142_loop_stmt_only_then_return_length_min_vm.sh
|
||||
```
|
||||
|
||||
**Result**: ✅ PASS (exit code 3)
|
||||
|
||||
#### LLVM EXE Smoke Test
|
||||
|
||||
**File**: `tools/smokes/v2/profiles/integration/apps/phase142_loop_stmt_only_then_return_length_min_llvm_exe.sh`
|
||||
|
||||
**Status**: ⚠️ TODO in P1 (requires Phase 130 LLVM EXE gate complete)
|
||||
|
||||
---
|
||||
|
||||
## Refactoring (COMPLETE ✅)
|
||||
|
||||
### Task 1: suffix_router コメント更新
|
||||
|
||||
**Commit**: `21a3c6b5d` - "docs(normalization): Update suffix_router comments for Phase 142-loopstmt P0"
|
||||
|
||||
**Changes**:
|
||||
- Updated header comments to reflect post statements no longer required
|
||||
- Documented LoopOnly pattern acceptance
|
||||
|
||||
### Task 2: LoopWithPost Deprecation
|
||||
|
||||
**Commit**: `aaba27d31` - "refactor(normalization): Deprecate LoopWithPost variant"
|
||||
|
||||
**Changes**:
|
||||
- Added `#[deprecated]` to `PlanKind::LoopWithPost` enum variant
|
||||
- Added deprecation to `loop_with_post()` constructor function
|
||||
- Documented migration path to LoopOnly
|
||||
- Kept for backward compatibility
|
||||
|
||||
**Deprecation warnings** (4 locations):
|
||||
- `normalized_shadow_suffix_router_box.rs:69`
|
||||
- `routing.rs:457`
|
||||
- `plan.rs:74`
|
||||
- `execute_box.rs:66`
|
||||
|
||||
### Task 3: README 更新
|
||||
|
||||
**Commit**: `3ef929df5` - "docs(normalization): Update README for Phase 142-loopstmt P0"
|
||||
|
||||
**Changes**:
|
||||
- Added Phase 142-loopstmt P0 section
|
||||
- Marked Phase 132-135 as LEGACY
|
||||
- Updated suffix_router description
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria (All Met ✅)
|
||||
|
||||
- [x] PlanBox が loop(true) に対して常に loop_only を返す(consumed=1)
|
||||
- [x] SuffixRouter が LoopOnly を受け入れて実行する
|
||||
- [x] build_block が consumed 後も後続文を処理する
|
||||
- [x] Fixture が exit code 3 を返す(VM)
|
||||
- [x] 既存 smokes が緑(Phase 97/131/136/137/139/141)
|
||||
- Phase 131 VM: ✅ PASS
|
||||
- Normalization unit tests: ✅ 10 passed
|
||||
- [x] Out-of-scope は常に Ok(None) でフォールバック(既定挙動不変)
|
||||
- [x] Documentation 作成
|
||||
- [x] Refactoring tasks 完了(3 commits)
|
||||
- [x] Main implementation commit
|
||||
|
||||
---
|
||||
|
||||
## Design Principles
|
||||
|
||||
### Pattern Explosion Prevention
|
||||
|
||||
- **制御フローの骨格(loop/if/post_k/continuation)**: 正規化(段階投入)で固める
|
||||
- **式(return value を含む)**: 一般化(AST walker)で受ける(Phase 140+)
|
||||
|
||||
### Normalization Unit Evolution
|
||||
|
||||
**Phase 141 以前**:
|
||||
```
|
||||
NormalizationPlan::loop_with_post(n) → consumed = 1 + n + 1 (loop + assigns + return)
|
||||
```
|
||||
|
||||
**Phase 142-loopstmt P0**:
|
||||
```
|
||||
NormalizationPlan::loop_only() → consumed = 1 (loop のみ)
|
||||
後続文は通常 MIR lowering で処理
|
||||
```
|
||||
|
||||
### Fail-Fast Policy
|
||||
|
||||
- **Out-of-scope**: 常に `Ok(None)` でフォールバック(既定挙動不変)
|
||||
- **Fail-Fast**: "in-scope のはずなのに壊れた" ケースのみ(internal error)
|
||||
|
||||
---
|
||||
|
||||
## Files Modified
|
||||
|
||||
### Core Implementation
|
||||
|
||||
1. `src/mir/builder/control_flow/normalization/plan_box.rs`
|
||||
- Always return loop_only for loop(true)
|
||||
- 7 unit tests updated
|
||||
|
||||
2. `src/mir/builder/control_flow/joinir/patterns/policies/normalized_shadow_suffix_router_box.rs`
|
||||
- Accept LoopOnly patterns
|
||||
- Remove rejection logic
|
||||
|
||||
3. `src/mir/builder/stmts.rs`
|
||||
- Remove break after consumed
|
||||
- Continue processing subsequent statements
|
||||
|
||||
### Tests
|
||||
|
||||
4. `apps/tests/phase142_loop_stmt_only_then_return_length_min.hako` (NEW)
|
||||
5. `tools/smokes/v2/profiles/integration/apps/phase142_loop_stmt_only_then_return_length_min_vm.sh` (NEW)
|
||||
|
||||
### Documentation
|
||||
|
||||
6. `src/mir/builder/control_flow/normalization/plan.rs` (deprecation)
|
||||
7. `src/mir/builder/control_flow/normalization/README.md` (updated)
|
||||
|
||||
### Statistics
|
||||
|
||||
- **Total commits**: 4
|
||||
- **Files changed**: 7
|
||||
- **Net change**: -38 lines (削減成功!)
|
||||
- **Code reduction**: ~82 lines deleted (pattern detection logic)
|
||||
|
||||
---
|
||||
|
||||
## Verification Commands
|
||||
|
||||
### Unit Tests
|
||||
```bash
|
||||
cargo test -p nyash-rust --lib mir::builder::control_flow::normalization
|
||||
```
|
||||
|
||||
### Regression Tests
|
||||
```bash
|
||||
# Phase 131 regression
|
||||
bash tools/smokes/v2/profiles/integration/apps/phase131_loop_true_break_once_vm.sh
|
||||
|
||||
# Phase 141 regression
|
||||
bash tools/smokes/v2/profiles/integration/apps/phase141_p1_if_only_post_k_return_length_vm.sh
|
||||
|
||||
# Phase 142-loopstmt P0
|
||||
bash tools/smokes/v2/profiles/integration/apps/phase142_loop_stmt_only_then_return_length_min_vm.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
### P1: LLVM EXE Smoke Test (TODO)
|
||||
|
||||
**File**: `tools/smokes/v2/profiles/integration/apps/phase142_loop_stmt_only_then_return_length_min_llvm_exe.sh`
|
||||
|
||||
**Prerequisites**: Phase 130 LLVM EXE gate complete
|
||||
|
||||
**Expected**: exit code 3 parity with VM
|
||||
|
||||
### P2: Code Contract Enforcement (Planned)
|
||||
|
||||
**Goal**: Prevent future regression to pattern explosion
|
||||
|
||||
**Options**:
|
||||
- **Option A (Recommended)**: Remove LoopWithPost creation path entirely
|
||||
- **Option B**: Introduce Outcome { consumed, stop_block } SSOT
|
||||
|
||||
### Phase 143-loopvocab (Planned)
|
||||
|
||||
**Goal**: 「語彙追加」で `loop(true){ if(cond) break/continue }` を吸収
|
||||
|
||||
**Approach**:
|
||||
- Extend StepTree/ControlTree vocabulary (not new patterns)
|
||||
- Use NormalizedExprLowererBox for pure conditions
|
||||
- JoinIR Jump { cond: Some(vid) } for conditional exits
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [10-Now.md](../../10-Now.md) - Current progress
|
||||
- [30-Backlog.md](../../30-Backlog.md) - Future phases
|
||||
- [phase-143-loopvocab/README.md](../phase-143-loopvocab/README.md) - Next planned vocabulary expansion
|
||||
- [normalized-expr-lowering.md](../../design/normalized-expr-lowering.md) - Normalized design SSOT
|
||||
- [control-tree.md](../../design/control-tree.md) - ControlTree design
|
||||
- [normalization/README.md](../../../src/mir/builder/control_flow/normalization/README.md) - Architecture
|
||||
|
||||
---
|
||||
|
||||
## Commits
|
||||
|
||||
1. `21a3c6b5d` - docs(normalization): Update suffix_router comments for Phase 142-loopstmt P0
|
||||
2. `aaba27d31` - refactor(normalization): Deprecate LoopWithPost variant
|
||||
3. `3ef929df5` - docs(normalization): Update README for Phase 142-loopstmt P0
|
||||
4. `275fe45ba` - feat(normalization): Phase 142-loopstmt P0 - Statement-level normalization
|
||||
@ -0,0 +1,96 @@
|
||||
# Phase 143-loopvocab: StepTree Vocabulary Expansion (loop → if/break/continue)
|
||||
|
||||
Status: planned
|
||||
Scope: Normalized shadow / JoinIR(dev-only, default behavior unchanged)
|
||||
|
||||
Related (SSOT):
|
||||
- `docs/development/current/main/design/control-tree.md`
|
||||
- `docs/development/current/main/design/normalized-expr-lowering.md`
|
||||
- `docs/development/current/main/phases/phase-142-loopstmt/README.md`
|
||||
|
||||
---
|
||||
|
||||
## Goal
|
||||
|
||||
`loop(true){ if(cond) break/continue }` を「新パターン追加」ではなく、**StepTree/ControlTree の語彙(vocabulary)拡張**として表現し、同じ Normalized lowering(env + continuation)に流す。
|
||||
|
||||
つまり、suffix/fixture の形を増やすのではなく:
|
||||
|
||||
- **制御は構造(StepTree)**
|
||||
- **値は一般化(ExprLowererBox)**
|
||||
|
||||
で吸収する。
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- `loop(<cond>)`(条件付き loop)を Normalized に入れる(既存経路へフォールバック維持)
|
||||
- impure expression(Call/MethodCall general case)を増やす(Phase 141 P2+ で扱う)
|
||||
- 既定挙動変更(out-of-scope は `Ok(None)` でフォールバック)
|
||||
|
||||
---
|
||||
|
||||
## P0 (planned): If-in-loop (single if, break/continue only)
|
||||
|
||||
### Accepted surface syntax (fixtures)
|
||||
|
||||
1) break:
|
||||
```hako
|
||||
loop(true) {
|
||||
if (cond) { break }
|
||||
}
|
||||
```
|
||||
|
||||
2) continue:
|
||||
```hako
|
||||
loop(true) {
|
||||
if (cond) { continue }
|
||||
}
|
||||
```
|
||||
|
||||
### StepTree vocabulary (existing)
|
||||
|
||||
StepTree は既に `StepNode::Loop` / `StepNode::If` / `StepStmtKind::{Break,Continue}` を持っている。
|
||||
|
||||
P0 の焦点は **Normalized lowering が Loop-body 内の If を受けられるようにする**こと。
|
||||
|
||||
### Capability / contract (Fail-Fast boundary)
|
||||
|
||||
- P0 で許可するのは「Loop-body の先頭/末尾に 1 個の If(else無しでも可)で、その then/else が Break/Continue のみ」まで。
|
||||
- それ以外(ネスト if、複数 if、then に Assign など)は out-of-scope (`Ok(None)`) にして既存経路へフォールバック。
|
||||
- strict/dev では `OutOfScopeReason` を必ず出す(silent accept 禁止)。
|
||||
|
||||
### Implementation sketch (boxes)
|
||||
|
||||
- `NormalizationPlanBox`:
|
||||
- `loop(true)` を見たら `loop_only(consumed=1)`(Phase 142-loopstmt と同じ)
|
||||
- 追加で「Loop-body が If を含むか」を feature として観測(plan の枝分かれは最小)
|
||||
- `NormalizationExecuteBox`:
|
||||
- `execute_loop_only` の内部で StepTree を作る
|
||||
- `StepTreeContractBox` で capability 判定(許可形なら Normalized lowering、不可なら `Ok(None)`)
|
||||
- Normalized lowering:
|
||||
- `if(cond){break}`: `cond_vid` を `ExprLowererBox`(pure only)で生成し、branch to `k_exit` / `loop_step` に落とす
|
||||
- `if(cond){continue}`: then -> tailcall `loop_step`, else -> fallthrough
|
||||
|
||||
### Fixtures / smokes
|
||||
|
||||
- VM + LLVM EXE parity を 1 本ずつ増やす(最小2本)
|
||||
- `apps/tests/phase143_loop_true_if_break_min.hako`
|
||||
- `apps/tests/phase143_loop_true_if_continue_min.hako`
|
||||
- Integration smokes:
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase143_loop_true_if_break_vm.sh`
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase143_loop_true_if_break_llvm_exe.sh`
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase143_loop_true_if_continue_vm.sh`
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase143_loop_true_if_continue_llvm_exe.sh`
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Phase 142-loopstmt の「statement-level normalization(consumed=1)」を維持し、suffix 形の増殖をさせない
|
||||
- out-of-scope は `Ok(None)` でフォールバック(既定挙動不変)
|
||||
- Unit tests が「許可形/不許可形」を contract として固定している(箱の責務が明確)
|
||||
|
||||
---
|
||||
|
||||
## Next
|
||||
|
||||
- P1: `if(cond) { break } else { continue }`(else branch を入れる)を vocabulary として追加
|
||||
- P2+: nested if / multi-if は capability guard で段階解禁(Phase 切りで SSOT 化)
|
||||
Reference in New Issue
Block a user