# 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()`(条件付き 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 化)