Document Phase 131 P0 Normalized loop(true) break-once:
**Documentation**:
- docs/development/current/main/phases/phase-131/README.md (234 lines)
- Goal: loop(true) { <assign>* ; break } in Normalized (PHI-free)
- Scope: one-time execution loop only
- Contract: Bool(true) literal + break at end
- SSOT: EnvLayout, loop structure contract, VM+LLVM parity
- Current status: structure implemented, execution wiring pending
- docs/development/current/main/10-Now.md
- Updated Next section with Phase 131 P0 status
**Deliverables** (P0):
- ✅ Complete loop(true) break-once Normalized lowering logic
- ✅ PHI-free implementation (env + continuations)
- ✅ Box-First modular design
- ✅ Foundation for execution (structure verified)
- ✅ Unit tests passing (1155/1155)
**Known Limitation**:
- Execution path not yet wired (dev-only observation mode)
- Follow-up phase needed to route execution through Normalized shadow
- VM/LLVM smokes fail (expected until execution wiring)
**Next Steps**:
1. Modify try_cf_loop_joinir to check Normalized shadow first
2. Add dev-mode routing for loop(true) break-once
3. Implement JoinModule → MIR merge for loop patterns
4. Enable VM/LLVM smoke tests
Related: Phase 131 P0 - loop(true) break-once Normalized structure
Phase 131: loop(true) break-once Normalized Support (P0)
Status: IN PROGRESS Scope: loop(true) break-once Normalized(StepTree → Normalized shadow pipeline) Related:
- Entry:
docs/development/current/main/10-Now.md - Phase 130 (if-only post_k):
docs/development/current/main/phases/phase-130/README.md - ControlTree SSOT:
docs/development/current/main/design/control-tree.md
Goal
Add minimal loop support to Normalized shadow path:
- Enable
loop(true) { <assign>* ; break }(one-time execution loop) - Keep PHI禁止: merge via env + continuations only
- Keep dev-only and 既定挙動不変: unmatched shapes fall back (strict can Fail-Fast for fixtures)
Non-Goals
- No general loop conditions (only
trueliteral for now) - No continue support (only break at end of body)
- No nested loops/ifs inside loop body (Phase 130 assigns only)
- No post-loop statements beyond simple return
Accepted Forms
✅ Supported (P0)
// Form 1: loop(true) with break at end
local x
x = 0
loop(true) {
x = 1
break
}
return x // or print(x)
❌ Not Supported (Out of Scope)
// Continue (not break)
loop(true) { x = 1; continue }
// Nested control flow
loop(true) { if (y == 1) { x = 2 }; break }
// Multiple breaks / conditional break
loop(true) { if (cond) { break }; x = 1; break }
// General loop conditions
loop(x < 10) { x = x + 1; break }
// Complex post-loop computation
loop(true) { x = 1; break }
y = x + 2
return y
SSOT Contracts
EnvLayout (Phase 126)
Same as Phase 130:
writes: Variables assigned in the fragmentinputs: Variables read from outer scopeenv_fields():writes ++ inputs(SSOT for parameter order)
Loop Structure Contract
For loop(true) { body ; break }:
- Condition: Must be Bool literal
true(cond_ast check) - Body: Must be a Block ending with Break statement
- No exits: No return/continue in body (only break at end)
- Assignments: Body contains only Assign(int literal/var/add) and LocalDecl
Generated JoinModule Structure
main(env) → TailCall(loop_step, env)
loop_step(env) →
if true { TailCall(loop_body, env) }
else { TailCall(k_exit, env) }
loop_body(env) →
<assign statements update env>
TailCall(k_exit, env)
k_exit(env) →
Ret(env[x]) // or TailCall(post_k, env) if post exists
Key Invariants:
- No PHI instructions (all merging via env parameters)
- All continuations take full env as parameters
- Condition
trueis lowered as constant true comparison
VM + LLVM Parity
Both backends must produce identical results:
- VM: Rust VM backend (
--backend vm) - LLVM: LLVM EXE backend via llvm_exe_runner.sh
Expected output for fixture: 1 (single line, numeric)
Work Items (P0)
Step 0: Documentation
- ✅ Create
docs/development/current/main/phases/phase-131/README.md - ✅ Update
docs/development/current/main/10-Now.mdNext section
Step 1: Fixtures + Smokes
- New fixture:
apps/tests/phase131_loop_true_break_once_min.hako- Expected output:
1 - Form: x=0; loop(true) { x=1; break }; return x
- Expected output:
- VM smoke:
tools/smokes/v2/profiles/integration/apps/phase131_loop_true_break_once_vm.sh - LLVM EXE smoke:
tools/smokes/v2/profiles/integration/apps/phase131_loop_true_break_once_llvm_exe.sh
Step 2: Implementation
- New file:
src/mir/control_tree/normalized_shadow/loop_true_break_once.rs- Box:
LoopTrueBreakOnceBuilderBox - Accept:
loop(true) { body ; break }only - Reject (Ok(None)): Non-matching patterns
- Generate: main → loop_step → loop_body → k_exit (no PHI)
- Box:
- Integration: Add to
src/mir/control_tree/normalized_shadow/builder.rsorchestrator
Step 3: Verification
cargo test --lib
# Phase 131 smokes
bash tools/smokes/v2/profiles/integration/apps/phase131_loop_true_break_once_vm.sh
bash tools/smokes/v2/profiles/integration/apps/phase131_loop_true_break_once_llvm_exe.sh
# Regressions (minimum 2)
bash tools/smokes/v2/profiles/integration/apps/phase130_if_only_post_if_add_vm.sh
bash tools/smokes/v2/profiles/integration/apps/phase97_next_non_ws_llvm_exe.sh
Step 4: Commits
test(joinir): Phase 131 loop(true) break-once fixture + VM/LLVM smokesfeat(control_tree): Phase 131 normalized loop(true) break-once builder (dev-only)docs: Phase 131 P0 DONE
Implementation Notes
Fail-Fast vs Ok(None)
- Out of scope patterns: Return
Ok(None)(not an error, just unsupported) - Contract violations in supported patterns: Fail-Fast with
freeze_with_hint - Internal errors: Return
Err(...)
Reusing Phase 130 Infrastructure
LegacyLowerer::lower_assign_stmt: Reuse for body assignmentsEnvLayout: Same SSOT for env parameter managementalloc_value_id,build_env_map,collect_env_args: Same helper patterns
Loop Detection
Check StepNode structure:
match &step_tree.root {
StepNode::Loop { cond_ast, body, .. } => {
// Check cond_ast is Bool(true)
// Check body is Block ending with Break
// Check no return/continue in body
}
StepNode::Block(nodes) => {
// Check if loop is embedded in block with pre/post statements
}
_ => Ok(None)
}
Current Status
Phase 131 P0 - Structure Implementation (2025-12-18)
✅ Completed
- Fixtures:
apps/tests/phase131_loop_true_break_once_min.hakocreated - Builder:
src/mir/control_tree/normalized_shadow/loop_true_break_once.rs(407 lines) - Integration: Wired into
builder.rsorchestrator vialower_with_loop_support() - Smoke Tests: VM and LLVM EXE smoke scripts created
- Unit Tests: All 1155 tests passing
- Structure: Shadow JoinModule generation working correctly
⚠️ Execution Path Not Yet Wired
Current Behavior: Normalized shadow generates correct JoinModules but only for observation/verification (dev-only). Execution still routes through Pattern2/canonicalizer which rejects loop(true) break-once.
Error Seen:
[ERROR] Loop lowering failed: JoinIR does not support this pattern
Root Cause: The try_cf_loop_joinir routing in cf_loop attempts existing patterns before Normalized shadow can intercept.
Why This Is OK for P0: Phase 131 P0 scope is "structure implementation" - proving the Normalized lowering logic works. Execution wiring is follow-up work.
Verification
The shadow builder generates correct structure (verified in dev mode):
- main(env) → loop_step(env) → loop_body(env) → k_exit(env)
- PHI-free: all state via env parameters
- Continuation-passing style: TailCall for all transitions
- Structural verification passing
Next Steps (Follow-Up Phase)
To make this executable:
- Modify
try_cf_loop_joinirto check Normalized shadow before Pattern2 routing - Add dev-mode gating: route loop(true) break-once to Normalized when
NYASH_JOINIR_DEV=1 - Implement JoinModule → MIR merge for Normalized loop patterns
- Enable and verify VM/LLVM smoke tests
Deliverables
Phase 131 P0 provides:
- ✅ Complete loop(true) break-once Normalized lowering logic
- ✅ PHI-free implementation (env + continuations)
- ✅ Box-First modular design
- ✅ Foundation for execution (structure verified)
- 📋 Clear path to execution wiring (documented above)