feat(control_tree): Phase 136/137 - return literal and add expression (dev-only)
Phase 136 P0: Return literal (Integer) support - Extend loop(true) break-once to support `return 7` - Fixtures: phase136_loop_true_break_once_return_literal_min.hako (exit code 7) - VM/LLVM EXE parity achieved Phase 137 P0: Return add expression support - Extend to support `return x + 2` and `return 5 + 3` - LHS: Variable or Integer literal - RHS: Integer literal only - Fixtures: - phase137_loop_true_break_once_return_add_min.hako (exit code 3) - phase137_loop_true_break_once_return_add_const_min.hako (exit code 8) - phase137_loop_true_break_once_post_return_add_min.hako (exit code 13) - VM/LLVM EXE parity achieved Implementation: - Added lower_return_value_to_vid() method in loop_true_break_once.rs - Replaced extract_variable_name() with unified return value lowering - Supported patterns: Variable, Integer literal, BinaryOp Add - Out-of-scope patterns return Ok(None) for fallback - SSOT documentation added (lines 29-46) Tests: 5 fixtures + 10 smoke tests (5 VM + 5 LLVM EXE), all PASS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -26,6 +26,22 @@
|
||||
//! - Post-loop (Phase 132-P4): One assignment + return (reuse Phase 130's lower_assign_stmt)
|
||||
//! - Post-loop (Phase 133-P0): Multiple assignments + return (extend Phase 132-P4)
|
||||
//!
|
||||
//! ## 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
|
||||
//!
|
||||
//! ## Fail-Fast
|
||||
//!
|
||||
//! - Out of scope → Ok(None) (fallback to legacy)
|
||||
@ -33,11 +49,11 @@
|
||||
|
||||
use super::env_layout::EnvLayout;
|
||||
use super::legacy::LegacyLowerer;
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
use crate::mir::control_tree::step_tree::{StepNode, StepStmtKind, StepTree};
|
||||
use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
|
||||
use crate::mir::control_tree::step_tree::{AstNodeHandle, StepNode, StepStmtKind, StepTree};
|
||||
use crate::mir::join_ir::lowering::carrier_info::JoinFragmentMeta;
|
||||
use crate::mir::join_ir::lowering::error_tags;
|
||||
use crate::mir::join_ir::{ConstValue, JoinFunction, JoinFuncId, JoinInst, JoinModule, MirLikeInst};
|
||||
use crate::mir::join_ir::{BinOpKind, ConstValue, JoinFunction, JoinFuncId, JoinInst, JoinModule, MirLikeInst};
|
||||
use crate::mir::join_ir_vm_bridge::join_func_name;
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap;
|
||||
@ -393,17 +409,21 @@ impl LoopTrueBreakOnceBuilderBox {
|
||||
let StepNode::Stmt { kind: StepStmtKind::Return { value_ast }, .. } = return_node else {
|
||||
return Ok(None);
|
||||
};
|
||||
if let Some(ast_handle) = value_ast {
|
||||
if let Some(var_name) = Self::extract_variable_name(&ast_handle.0) {
|
||||
if let Some(vid) = env_post_k.get(&var_name).copied() {
|
||||
|
||||
// Lower post-loop return (Phase 136: support variable + integer literal)
|
||||
if let Some(_ast_handle) = value_ast {
|
||||
// Return with value
|
||||
match Self::lower_return_value_to_vid(value_ast, &mut post_k_func.body, &mut next_value_id, &env_post_k)? {
|
||||
Some(vid) => {
|
||||
post_k_func.body.push(JoinInst::Ret { value: Some(vid) });
|
||||
} else {
|
||||
return Ok(None); // Variable not in env
|
||||
}
|
||||
} else {
|
||||
return Ok(None); // Return value is not a variable
|
||||
None => {
|
||||
// Out of scope (unsupported return value type)
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Return void
|
||||
post_k_func.body.push(JoinInst::Ret { value: None });
|
||||
}
|
||||
|
||||
@ -455,16 +475,16 @@ impl LoopTrueBreakOnceBuilderBox {
|
||||
match &post_nodes[0] {
|
||||
StepNode::Stmt { kind, .. } => match kind {
|
||||
StepStmtKind::Return { value_ast } => {
|
||||
if let Some(ast_handle) = value_ast {
|
||||
// Return variable from env
|
||||
if let Some(var_name) = Self::extract_variable_name(&ast_handle.0) {
|
||||
if let Some(vid) = env_k_exit.get(&var_name).copied() {
|
||||
if let Some(_ast_handle) = value_ast {
|
||||
// Return with value (Phase 136: variable + integer literal)
|
||||
match Self::lower_return_value_to_vid(value_ast, &mut k_exit_func.body, &mut next_value_id, &env_k_exit)? {
|
||||
Some(vid) => {
|
||||
k_exit_func.body.push(JoinInst::Ret { value: Some(vid) });
|
||||
} else {
|
||||
return Ok(None); // Variable not in env
|
||||
}
|
||||
} else {
|
||||
return Ok(None); // Return value is not a variable
|
||||
None => {
|
||||
// Out of scope
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Return void
|
||||
@ -606,11 +626,119 @@ impl LoopTrueBreakOnceBuilderBox {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract variable name from AST node
|
||||
fn extract_variable_name(ast: &ASTNode) -> Option<String> {
|
||||
match ast {
|
||||
ASTNode::Variable { name, .. } => Some(name.clone()),
|
||||
_ => None,
|
||||
/// Lower return value (variable or integer literal) to ValueId
|
||||
///
|
||||
/// Phase 136 P0: Support return variable and return integer literal
|
||||
///
|
||||
/// Returns:
|
||||
/// - Ok(Some(vid)): variable from env or newly generated const
|
||||
/// - Ok(None): out of scope (fallback to default behavior)
|
||||
///
|
||||
/// Note: Does NOT return Err - unsupported patterns return Ok(None) for fallback
|
||||
fn lower_return_value_to_vid(
|
||||
value_ast: &Option<AstNodeHandle>,
|
||||
body: &mut Vec<JoinInst>,
|
||||
next_value_id: &mut u32,
|
||||
env: &BTreeMap<String, ValueId>,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
match value_ast {
|
||||
None => {
|
||||
// void return
|
||||
Ok(Some(ValueId(0))) // Dummy - caller handles void separately
|
||||
}
|
||||
Some(ast_handle) => {
|
||||
match ast_handle.0.as_ref() {
|
||||
// Variable: lookup in env
|
||||
ASTNode::Variable { name, .. } => {
|
||||
if let Some(&vid) = env.get(name) {
|
||||
Ok(Some(vid))
|
||||
} else {
|
||||
// Variable not in env - out of scope
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
// Integer literal: generate Const instruction (Phase 123 pattern)
|
||||
ASTNode::Literal { value: LiteralValue::Integer(i), .. } => {
|
||||
let const_vid = ValueId(*next_value_id);
|
||||
*next_value_id += 1;
|
||||
|
||||
body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: const_vid,
|
||||
value: ConstValue::Integer(*i),
|
||||
}));
|
||||
|
||||
Ok(Some(const_vid))
|
||||
}
|
||||
// Phase 137 P0: BinaryOp (x + 2) support
|
||||
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.as_ref() {
|
||||
ASTNode::Variable { name, .. } => {
|
||||
// Get from env
|
||||
if let Some(&vid) = env.get(name) {
|
||||
vid
|
||||
} else {
|
||||
// Variable not in env - out of scope
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
ASTNode::Literal { value: LiteralValue::Integer(i), .. } => {
|
||||
// Generate Const for LHS integer literal
|
||||
let vid = ValueId(*next_value_id);
|
||||
*next_value_id += 1;
|
||||
body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: vid,
|
||||
value: ConstValue::Integer(*i),
|
||||
}));
|
||||
vid
|
||||
}
|
||||
_ => {
|
||||
// Other LHS types - out of scope
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
// Lower RHS (Integer literal only)
|
||||
let rhs_vid = match right.as_ref() {
|
||||
ASTNode::Literal { value: LiteralValue::Integer(i), .. } => {
|
||||
// Generate Const for RHS integer literal
|
||||
let vid = ValueId(*next_value_id);
|
||||
*next_value_id += 1;
|
||||
body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: vid,
|
||||
value: ConstValue::Integer(*i),
|
||||
}));
|
||||
vid
|
||||
}
|
||||
_ => {
|
||||
// Other RHS types - out of scope (e.g., return x + y)
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
// 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))
|
||||
}
|
||||
_ => {
|
||||
// Other return value types - out of scope
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user