Files
hakorune/docs/development/current/main/phases/phase-133/README.md
nyash-codex ef71ea955c feat(control_tree): Phase 133 P0 - Multiple post-loop assigns support
Extend Phase 132's loop(true) + post-loop to accept multiple assignments:

Goal: `x=0; loop(true){ x=1; break }; x=x+2; x=x+3; return x` → exit code 6

Implementation:
- Extended loop_true_break_once.rs pattern detection (len() == 2 → len() >= 2)
- Added iterative assignment lowering (for loop over post_nodes)
- Reused Phase 130's lower_assign_stmt for each assignment
- Maintained ExitMeta DirectValue mode (PHI-free)

Changes:
- apps/tests/phase133_loop_true_break_once_post_multi_add_min.hako (new fixture)
- tools/smokes/v2/profiles/integration/apps/phase133_*_multi_add_*.sh (new smokes)
- src/mir/control_tree/normalized_shadow/loop_true_break_once.rs (+30 lines)
- docs/development/current/main/phases/phase-133/README.md (new documentation)
- docs/development/current/main/10-Now.md (Phase 133 entry added)

Scope (Phase 130 baseline):
-  x = <int literal>
-  x = y (variable copy)
-  x = x + <int literal> (increment)
-  Function calls / general expressions (future phases)

Design principles:
- Minimal change: ~30 lines added
- SSOT preservation: env_post_k remains single source of truth
- Reuse: Leveraged existing lower_assign_stmt
- Fail-Fast: Contract violations trigger freeze_with_hint

Test results:
- cargo test --lib: 1176 PASS
- Phase 133 VM: PASS (exit code 6)
- Phase 133 LLVM EXE: PASS (exit code 6)
- Phase 132 regression: PASS (exit code 3)
- Phase 131 regression: PASS (exit code 1)
- Phase 97 regression: PASS

Architecture maintained:
- 5-function structure unchanged (main/loop_step/loop_body/k_exit/post_k)
- PHI-free DirectValue mode
- Zero changes to ExitMeta, merge logic, or JoinIR contracts

Related: Phase 133 loop(true) + multiple post-loop assignments

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-18 22:11:08 +09:00

6.9 KiB

Phase 133: loop(true) break-once Multiple Post-Loop Assignments

Status: Implemented & Verified Date: 2025-12-18 Scope: P0 (Minimal)

Overview

Phase 133-P0 extends Phase 132-P4's loop(true) break-once pattern to accept multiple post-loop assignments before the final return statement. This enables more complex post-loop computation while maintaining the PHI-free Normalized shadow architecture.

Pattern

x = 0              // pre-loop init
loop(true) {       // condition is Bool literal true
    x = 1          // body assignment
    break          // break at end
}
x = x + 2          // first post-loop assignment
x = x + 3          // second post-loop assignment (NEW in P133)
return x           // return updated value (1 + 2 + 3 = 6)

Implementation Changes

1. Pattern Detection (loop_true_break_once.rs)

Extended from Phase 132:

  • Phase 132: post_nodes == [Assign, Return] (exactly 1 assign)
  • Phase 133: post_nodes == [Assign+, Return] (1+ assigns)
// Phase 133-P0: Detect multi-assign + return pattern (generalize Phase 132-P4)
let has_post_computation = if post_nodes.is_empty() {
    false
} else if post_nodes.len() >= 2 {
    // Check if all nodes except the last are Assign statements
    let all_assigns = post_nodes[..post_nodes.len() - 1]
        .iter()
        .all(|n| matches!(n, StepNode::Stmt { kind: StepStmtKind::Assign { .. }, .. }));

    // Check if the last node is a Return statement
    let ends_with_return = matches!(
        post_nodes.last(),
        Some(StepNode::Stmt { kind: StepStmtKind::Return { .. }, .. })
    );

    all_assigns && ends_with_return
} else {
    false
};

2. Post-Loop Lowering (loop_true_break_once.rs)

Iterative Assignment Processing:

// Phase 133-P0: Lower multiple post-loop assignments
// Split post_nodes into assigns and return (last element is return)
let assign_nodes = &post_nodes[..post_nodes.len() - 1];
let return_node = post_nodes.last().unwrap();

// Lower all assignment statements
for node in assign_nodes {
    let StepNode::Stmt { kind: StepStmtKind::Assign { target, value_ast }, .. } = node else {
        return Ok(None);
    };
    if LegacyLowerer::lower_assign_stmt(
        target,
        value_ast,
        &mut post_k_func.body,
        &mut next_value_id,
        &mut env_post_k,
    )
    .is_err()
    {
        return Ok(None);
    }
}

3. SSOT Maintenance

ExitMeta Contract:

  • Unchanged: ExitMeta still uses env_post_k's final values as SSOT
  • Iterative Updates: Each assignment updates env_post_k map
  • Final State: Last assignment's result becomes the exit value

JoinIR Structure (Unchanged from Phase 132)

5-Function Module:

  1. main(env) → TailCall(loop_step, env)
  2. loop_step(env) → TailCall(loop_body, env)
  3. loop_body(env) → → TailCall(k_exit, env)
  4. k_exit(env) → TailCall(post_k, env)
  5. post_k(env)* → Ret(env[x]) ← NEW: Multiple assigns

Contract:

  • PHI-free: All state passing via env arguments + continuations
  • DirectValue mode: ExitMeta uses post_k's final env values

Scope Limitations (Phase 130 Baseline)

Allowed Assignments:

  • x = <int literal> (e.g., x = 0)
  • x = y (variable copy)
  • x = x + <int literal> (increment)

Not Allowed:

  • x = call(...) (function calls)
  • x = a + b (general binary ops)
  • if, loop, print in post-loop

Test Coverage

Fixture

  • apps/tests/phase133_loop_true_break_once_post_multi_add_min.hako
  • Expected exit code: 6 (1 + 2 + 3)

Smoke Tests

  1. VM: tools/smokes/v2/profiles/integration/apps/phase133_loop_true_break_once_post_multi_add_vm.sh
  2. LLVM EXE: tools/smokes/v2/profiles/integration/apps/phase133_loop_true_break_once_post_multi_add_llvm_exe.sh

Regression Tests

  • Phase 132 (exit code 3)
  • Phase 131 (exit code 1)
  • Phase 97 (numeric output 2, -1, 3)

Verification Results

# Unit tests
cargo test --lib
# Result: 1176 passed; 0 failed

# Phase 133 smokes
bash tools/smokes/v2/profiles/integration/apps/phase133_loop_true_break_once_post_multi_add_vm.sh
# Result: PASS (exit code 6)

bash tools/smokes/v2/profiles/integration/apps/phase133_loop_true_break_once_post_multi_add_llvm_exe.sh
# Result: PASS (exit code 6)

# Regression smokes
bash tools/smokes/v2/profiles/integration/apps/phase132_loop_true_break_once_post_add_llvm_exe.sh
# Result: PASS (exit code 3)

bash tools/smokes/v2/profiles/integration/apps/phase131_loop_true_break_once_llvm_exe.sh
# Result: PASS (exit code 1)

bash tools/smokes/v2/profiles/integration/apps/phase97_next_non_ws_llvm_exe.sh
# Result: PASS (numeric output)

Design Principles

1. Minimal Change

  • Extends Phase 132's detection logic from len() == 2 to len() >= 2
  • Replaces single assignment lowering with iterative loop
  • Zero changes to JoinIR structure, ExitMeta, or merge logic

2. SSOT Preservation

  • env_post_k remains the single source of truth for exit values
  • Each assignment updates env_post_k map in order
  • ExitMeta collection happens after all assignments

3. Reuse

  • Uses LegacyLowerer::lower_assign_stmt for each assignment
  • Leverages Phase 130's proven assignment handling
  • No new lowering code required

4. Fail-Fast

  • Contract violations trigger freeze_with_hint in strict mode
  • Missing env fields → explicit error with hint
  • Invalid assignment → fallback to legacy (Ok(None))

Dev Toggle

Required: NYASH_JOINIR_DEV=1 and HAKO_JOINIR_STRICT=1

This is a dev-only Normalized shadow feature. With toggle OFF, the pattern falls back to legacy lowering with zero impact on production code.

Files Modified

  1. src/mir/control_tree/normalized_shadow/loop_true_break_once.rs (3 edits)

    • Extended pattern detection
    • Iterative assignment lowering
    • Updated debug logging
  2. apps/tests/phase133_loop_true_break_once_post_multi_add_min.hako (new)

  3. tools/smokes/v2/profiles/integration/apps/phase133_loop_true_break_once_post_multi_add_vm.sh (new)

  4. tools/smokes/v2/profiles/integration/apps/phase133_loop_true_break_once_post_multi_add_llvm_exe.sh (new)

Acceptance Criteria

  • cargo test --lib PASS (1176 tests)
  • Phase 133 VM smoke: PASS (exit code 6)
  • Phase 133 LLVM EXE smoke: PASS (exit code 6)
  • Phase 132 regression: PASS (exit code 3)
  • Phase 131 regression: PASS (exit code 1)
  • Phase 97 regression: PASS (numeric output)
  • Dev toggle OFF: Zero impact on production

Summary

Phase 133-P0 successfully generalizes Phase 132-P4's single post-loop assignment to support multiple post-loop assignments, enabling more complex post-loop computation patterns. The implementation maintains all architectural invariants (PHI-free, DirectValue mode, SSOT) while adding only ~30 lines of code.

Key Achievement: Transformed fixed-length pattern detection (len() == 2) to flexible pattern detection (len() >= 2) with zero impact on existing contracts.