# Phase 137: loop(true) break-once with return add expression **Date**: 2025-12-18 **Status**: DONE ✅ **Scope**: return add expression (x + 2) support in Normalized shadow (dev-only) --- ## Goal Extend Phase 136 return literal to support minimal add expressions: - Enable `return x + 2` (variable + integer literal) - Enable `return 5 + 3` (integer literal + integer literal) - Keep **PHI禁止**: merge via env + continuations only - Keep **dev-only** and **既定挙動不変**: unmatched shapes fall back ## Supported Forms ### ✅ Supported (Phase 137 P0) ```nyash // Form 1: return variable + integer literal local x x = 1 loop(true) { break } return x + 2 // Expected: 3 (1 + 2) ``` ```nyash // Form 2: return integer literal + integer literal loop(true) { break } return 5 + 3 // Expected: 8 ``` ```nyash // Form 3: loop + post assigns + return add local x x = 0 loop(true) { x = 1 break } x = x + 10 return x + 2 // Expected: 13 (0 → 1 → 11 → 13) ``` ### ❌ Not Supported (Out of Scope) ```nyash // Variable + variable (Phase 138+) return x + y // Other operators (Phase 138+) return x - 2 return x * 2 // Nested expressions return (x + 2) + 3 // Function calls return f() ``` ## Implementation ### Core Changes **File**: `src/mir/control_tree/normalized_shadow/loop_true_break_once.rs` **Method**: `lower_return_value_to_vid()` (行 638-743, BinaryOp Add at 行 673) **Added Pattern**: BinaryOp Add ```rust ASTNode::BinaryOp { operator, left, right, .. } => { // Phase 137 contract: Add only if !matches!(operator, BinaryOperator::Add) { return Ok(None); // out of scope } // Lower LHS (Variable or Integer literal) let lhs_vid = match left.0.as_ref() { ASTNode::Variable { name, .. } => { match env.get(name).copied() { Some(vid) => vid, None => return Ok(None), // out of scope } } ASTNode::Literal { value: LiteralValue::Integer(i), .. } => { // Generate Const for LHS let vid = ValueId(*next_value_id); *next_value_id += 1; body.push(JoinInst::Compute(MirLikeInst::Const { dst: vid, value: ConstValue::Integer(*i), })); vid } _ => return Ok(None), }; // Lower RHS (Integer literal only) let rhs_vid = match right.0.as_ref() { ASTNode::Literal { value: LiteralValue::Integer(i), .. } => { let vid = ValueId(*next_value_id); *next_value_id += 1; body.push(JoinInst::Compute(MirLikeInst::Const { dst: vid, value: ConstValue::Integer(*i), })); vid } _ => return Ok(None), // e.g., return x + y }; // Generate BinOp Add let result_vid = ValueId(*next_value_id); *next_value_id += 1; body.push(JoinInst::Compute(MirLikeInst::BinOp { dst: result_vid, op: BinOpKind::Add, lhs: lhs_vid, rhs: rhs_vid, })); Ok(Some(result_vid)) } ``` ### Return Value Lowering SSOT **Location**: `src/mir/control_tree/normalized_shadow/loop_true_break_once.rs` (行 29-43) **SSOT Documentation**: ```rust //! ## Return Value Lowering SSOT (Phase 137+) //! //! - Function: `lower_return_value_to_vid()` //! - Responsibility: Lower return values (variable, literal, expr) to ValueId //! - Supported patterns: //! - Variable: env lookup //! - Integer literal: Const generation //! - Add expr (Phase 137): x + 2 → BinOp(Add, env[x], Const(2)) //! - Fallback: Out-of-scope patterns return `Ok(None)` for legacy routing //! //! ### Boxification Trigger //! //! If 2+ files need identical return lowering logic, promote to: //! - `normalized_shadow/common/return_value_lowerer_box.rs` //! - Single responsibility: return value → ValueId conversion ``` ### Fixtures 1. **phase137_loop_true_break_once_return_add_min.hako** - Pattern: `x=1; loop(true){break}; return x+2` - Expected: exit code 3 2. **phase137_loop_true_break_once_return_add_const_min.hako** - Pattern: `loop(true){break}; return 5+3` - Expected: exit code 8 3. **phase137_loop_true_break_once_post_return_add_min.hako** - Pattern: `x=0; loop(true){x=1;break}; x=x+10; return x+2` - Expected: exit code 13 ### Smoke Tests **VM**: - `tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_vm.sh` - `tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_vm.sh` - `tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_vm.sh` **LLVM EXE**: - `tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_llvm_exe.sh` - `tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_llvm_exe.sh` - `tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_llvm_exe.sh` ## Verification ```bash # Build cargo build --release -p nyash-rust --features llvm # Phase 137 smokes (6 tests) bash tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_vm.sh bash tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_vm.sh bash tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_vm.sh bash tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_llvm_exe.sh bash tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_return_add_const_llvm_exe.sh bash tools/smokes/v2/profiles/integration/apps/phase137_loop_true_break_once_post_return_add_llvm_exe.sh # Regressions bash tools/smokes/v2/profiles/integration/apps/phase97_next_non_ws_llvm_exe.sh bash tools/smokes/v2/profiles/integration/apps/phase97_json_loader_escape_llvm_exe.sh bash tools/smokes/v2/profiles/integration/apps/phase131_loop_true_break_once_vm.sh bash tools/smokes/v2/profiles/integration/apps/phase135_loop_true_break_once_post_empty_return_vm.sh bash tools/smokes/v2/profiles/integration/apps/phase136_loop_true_break_once_return_literal_vm.sh ``` ## Acceptance Criteria - ✅ Phase 137 VM: 3/3 PASS - ✅ Phase 137 LLVM EXE: 3/3 PASS - ✅ Phase 97 regression: 2/2 PASS - ✅ Phase 131/135/136 regression: 3/3 PASS - ✅ Dev toggle OFF → no impact (Ok(None) fallback) ## Key Design Points ### Ok(None) for Fallback - Unsupported patterns (e.g., `return x + y`, `return x - 2`) return `Ok(None)` - Fallback to existing JoinIR routing - No hard errors for out-of-scope patterns ### BinOp Generation Pattern - LHS: Variable (env lookup) or Integer literal (Const generation) - RHS: Integer literal only (Const generation) - Result: `BinOp(Add, lhs_vid, rhs_vid)` ### Phase 137 P0 Scope Decision - **Approach A (adopted)**: Direct extension in `loop_true_break_once.rs` - **Reasoning**: Small change scope, no boxification needed yet - **Boxification Trigger**: When 2+ files need identical return lowering logic ### post_if_post_k.rs Not Modified - Different responsibility (if-with-post normalization) - Unification planned for Phase 138-139 when needed ## Current Status Phase 137 - DONE ✅ (2025-12-18) VM/LLVM EXE parity achieved (exit codes 3, 8, 13). Return Value Lowering SSOT documented in `loop_true_break_once.rs`.