2025-12-19 00:16:06 +09:00
|
|
|
# Phase 137: loop(true) break-once with return add expression
|
2025-12-16 05:10:29 +09:00
|
|
|
|
2025-12-19 00:16:06 +09:00
|
|
|
**Date**: 2025-12-18
|
|
|
|
|
**Status**: DONE ✅
|
|
|
|
|
**Scope**: return add expression (x + 2) support in Normalized shadow (dev-only)
|
|
|
|
|
|
|
|
|
|
---
|
2025-12-16 05:10:29 +09:00
|
|
|
|
|
|
|
|
## Goal
|
2025-12-16 06:17:03 +09:00
|
|
|
|
2025-12-19 00:16:06 +09:00
|
|
|
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`.
|