diff --git a/docs/development/current/main/phases/phase-143-loopvocab/README.md b/docs/development/current/main/phases/phase-143-loopvocab/README.md index cbde5cfb..cc0ef564 100644 --- a/docs/development/current/main/phases/phase-143-loopvocab/README.md +++ b/docs/development/current/main/phases/phase-143-loopvocab/README.md @@ -1,96 +1,177 @@ -# Phase 143-loopvocab: StepTree Vocabulary Expansion (loop → if/break/continue) +# Phase 143-loopvocab: Loop Vocabulary Extension -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` +Status: ✅ **P0 Complete** +Date: 2025-12-19 --- -## Goal +## 目的(Why) -`loop(true){ if(cond) break/continue }` を「新パターン追加」ではなく、**StepTree/ControlTree の語彙(vocabulary)拡張**として表現し、同じ Normalized lowering(env + continuation)に流す。 +Phase 131で `loop(true) break` を実装したが、Phase 143では「条件付きbreak」パターンを吸収する。 -つまり、suffix/fixture の形を増やすのではなく: +**Phase 143-loopvocab** では、`loop(true) { if(cond_pure) break }` を **Normalized shadow lowering** で段階的に拡張するための語彙追加を行う。 -- **制御は構造(StepTree)** -- **値は一般化(ExprLowererBox)** +### パターン進化 -で吸収する。 - -## Non-Goals - -- `loop()`(条件付き 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 } -} +**Phase 131**: +```nyash +loop(true) { break } // 無条件break ``` -2) continue: -```hako -loop(true) { - if (cond) { continue } -} +**Phase 143 P0**: +```nyash +loop(true) { if(cond_pure) break } // 条件付きbreak ``` -### 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 +## P0: Minimal Vocabulary Extension (COMPLETE ✅) -- P1: `if(cond) { break } else { continue }`(else branch を入れる)を vocabulary として追加 -- P2+: nested if / multi-if は capability guard で段階解禁(Phase 切りで SSOT 化) +### Summary + +`loop(true) { if(cond_pure) break }` パターンを Normalized shadow で実装完了。 + +### Scope (P0 - Conservative) + +**Supported**: +- Loop condition: `true` literal only +- Loop body: Single `if` statement only +- If then branch: `break` only (no continue, no nested if) +- Condition expression: Pure only (variables, literals, arith, compare) + +**Out of Scope** (Ok(None) fallback): +- Loop condition not `true`: `loop(x > 0) { ... }` +- Multiple statements in loop body +- If/else statements (no else branch) +- Impure conditions: `loop(true) { if(s.length() > 0) break }` +- Nested structures within if body + +### Design: 6-Function JoinModule + +``` +main(env) → loop_step(env) → loop_cond_check(env) + [condition lowering + Jump] + → k_exit(env) on break [Ret] + → loop_step(env) on continue [Call] + +k_then(unused in P0) +k_else(unused in P0) +``` + +**Jump Semantics**: +- `Jump { cont: k_exit, args: [...], cond: Some(vid) }` +- If `vid` is true: jump to k_exit (break) +- If `vid` is false: fall through to Call(loop_step) + +### Implementation Steps (Complete ✅) + +#### Step 1-2: Pattern Detection + Routing +- Extract loop(true) + if + break pattern +- Validate loop condition = Bool(true) literal +- Validate if body = single Break statement +- Add module declaration and routing in builder.rs + +#### Step 3: Condition Lowering Integration +- Validate condition with NormalizedExprLowererBox +- Use ExprLoweringScope::PureOnly +- Return Ok(None) for impure conditions (graceful fallback) + +#### Step 4: Branch Instruction Emission +- Allocate 6 JoinFuncIds (stable IDs 0-5) +- Build env parameter passing (deterministic BTreeMap) +- Emit Jump instruction in loop_cond_check +- Create Call fallthrough for loop_step + +#### Step 5: Exit Action Discrimination +- Complete k_exit() function +- Build exit values from env_layout.writes +- Extract final ValueIds from environment + +#### Step 6: ExitMeta Construction +- Create ExitMeta::multiple() from exit values +- Use JoinFragmentMeta::carrier_only() +- Return Ok(Some((module, meta))) + +#### Step 7: Routing Integration +- Already integrated in builder.rs (Step 1) +- Phase 131 (simpler) → Phase 143 (conditional) priority + +#### Step 8: Fixtures + Smoke Tests +**Fixture**: `apps/tests/phase143_loop_true_if_break_min.hako` + +**Tests**: +- `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` + +Expected exit code: 1 + +#### Step 9: Documentation (THIS FILE) + +### Acceptance Criteria (All Met ✅) + +- [x] Pattern detection correctly identifies `loop(true) { if(cond) break }` +- [x] Condition lowering validates pure scope +- [x] Out-of-scope patterns always return `Ok(None)` +- [x] JoinModule built with 6 functions +- [x] Jump instruction with conditional emission +- [x] k_exit returns exit values +- [x] ExitMeta constructed correctly +- [x] Fixture executes correctly (VM) +- [x] LLVM EXE parity test ready +- [x] No regressions in Phase 131-142 +- [x] cargo check passes (no errors) +- [x] All commits created + +### Verification Results + +**Compilation**: `cargo check -p nyash-rust --lib` ✅ (0 errors) + +**Commits**: +1. `f55f6cc65` - Step 3: Condition lowering integration +2. `434c891a1` - Step 4: Branch instruction emission +3. `d1d59dc82` - Steps 5-6: Exit handling + ExitMeta +4. `e28d59101` - Step 8: Fixtures + Smoke tests + +--- + +## Design Principles + +### Out-of-Scope Handling + +All out-of-scope patterns return `Ok(None)` for graceful fallback to legacy routing. + +### Fail-Fast Policy + +- **Out-of-scope**: Always `Ok(None)` (no change to existing behavior) +- **In-scope errors**: Return `Err(msg)` (internal errors, strict mode freeze) + +--- + +## Files + +### Core Implementation + +1. **src/mir/control_tree/normalized_shadow/loop_true_if_break_continue.rs** (~400 lines) + +### Fixtures + Tests + +2. **apps/tests/phase143_loop_true_if_break_min.hako** +3. **tools/smokes/v2/profiles/integration/apps/phase143_loop_true_if_break_vm.sh** +4. **tools/smokes/v2/profiles/integration/apps/phase143_loop_true_if_break_llvm_exe.sh** + +--- + +## Related Documentation + +- [Phase 131 (loop break-once)](../phase-131/README.md) +- [Phase 142 (statement-level normalization)](../phase-142-loopstmt/README.md) +- [JoinIR architecture](../../joinir-architecture-overview.md) + +--- + +## Commits + +1. `f55f6cc65` - feat(phase143): Step 3 - Condition lowering integration +2. `434c891a1` - feat(phase143): Step 4 - Branch instruction emission +3. `d1d59dc82` - feat(phase143): Steps 5-6 - Exit handling + ExitMeta +4. `e28d59101` - feat(phase143): Step 8 - Fixtures + Smoke tests