refactor(normalized_shadow): Phase 138 - extract ReturnValueLowererBox (no behavior change)
Extract return value lowering logic to shared Box for SSOT: New Files: - common/return_value_lowerer_box.rs (~300 lines) - ReturnValueLowererBox::lower_to_value_id() - Supports: Variable, Integer literal, Add expression - 5 comprehensive unit tests - common/mod.rs (module export) Modified Files: - loop_true_break_once.rs - Removed lower_return_value_to_vid() method (~115 lines) - Added import: use super::common::return_value_lowerer_box::ReturnValueLowererBox - Updated 2 call sites (post_k, k_exit) - Updated SSOT documentation - mod.rs - Added pub mod common; Code Reduction: ~115 lines removed from loop_true_break_once.rs Tests: - cargo test --lib: 1194 tests PASS (+5 new unit tests) - Phase 137 regression: 6/6 PASS - Phase 97 regression: 2/2 PASS - Phase 131/135/136 regression: 3/3 PASS Behavior: Unchanged (all Phase 136/137 fixtures/smokes PASS) SSOT: common/return_value_lowerer_box.rs Next: Phase 139 P0 - Migrate post_if_post_k.rs to ReturnValueLowererBox 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
3
src/mir/control_tree/normalized_shadow/common/mod.rs
Normal file
3
src/mir/control_tree/normalized_shadow/common/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
//! Common utilities for Normalized shadow (Phase 138+)
|
||||
|
||||
pub mod return_value_lowerer_box;
|
||||
@ -0,0 +1,316 @@
|
||||
//! ReturnValueLowererBox: Return value lowering SSOT (Phase 138 P0)
|
||||
//!
|
||||
//! ## Responsibility
|
||||
//!
|
||||
//! Lower return values (variable, literal, expr) to ValueId for Normalized shadow paths
|
||||
//!
|
||||
//! ## Supported Patterns (Phase 136-137)
|
||||
//!
|
||||
//! - Variable: env lookup → ValueId
|
||||
//! - Integer literal: Const generation → ValueId
|
||||
//! - Add expr: (var|int) + int → BinOp(Add, lhs, rhs) → ValueId
|
||||
//!
|
||||
//! ## Fallback
|
||||
//!
|
||||
//! Out-of-scope patterns return `Ok(None)` for legacy routing
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! - Phase 138 P0: loop_true_break_once.rs
|
||||
//! - Phase 139 P0: post_if_post_k.rs (planned)
|
||||
|
||||
use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
|
||||
use crate::mir::control_tree::step_tree::AstNodeHandle;
|
||||
use crate::mir::join_ir::{BinOpKind, ConstValue, JoinInst, MirLikeInst};
|
||||
use crate::mir::ValueId;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Box-First: Return value lowering for Normalized shadow
|
||||
pub struct ReturnValueLowererBox;
|
||||
|
||||
impl ReturnValueLowererBox {
|
||||
/// Lower return value to ValueId
|
||||
///
|
||||
/// Phase 136-137: Support variable, integer literal, and add expression
|
||||
///
|
||||
/// Returns:
|
||||
/// - Ok(Some(vid)): Successfully lowered to ValueId
|
||||
/// - Ok(None): Out of scope (fallback to legacy routing)
|
||||
/// - Err(_): Internal error (should not happen for valid AST)
|
||||
///
|
||||
/// Note: Does NOT return Err for unsupported patterns - returns Ok(None) instead
|
||||
pub fn lower_to_value_id(
|
||||
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 136)
|
||||
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: BinaryOp (x + 2) support
|
||||
ASTNode::BinaryOp { operator, left, right, .. } => {
|
||||
Self::lower_binary_op(operator, left, right, body, next_value_id, env)
|
||||
}
|
||||
_ => {
|
||||
// Other return value types - out of scope
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lower binary operation to ValueId (Phase 137)
|
||||
fn lower_binary_op(
|
||||
operator: &BinaryOperator,
|
||||
left: &ASTNode,
|
||||
right: &ASTNode,
|
||||
body: &mut Vec<JoinInst>,
|
||||
next_value_id: &mut u32,
|
||||
env: &BTreeMap<String, ValueId>,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
// 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 {
|
||||
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 {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
|
||||
use crate::mir::control_tree::step_tree::AstNodeHandle;
|
||||
|
||||
fn make_span() -> Span {
|
||||
Span::unknown()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lower_variable() {
|
||||
let mut body = vec![];
|
||||
let mut next_value_id = 100;
|
||||
let mut env = BTreeMap::new();
|
||||
env.insert("x".to_string(), ValueId(42));
|
||||
|
||||
let var_ast = AstNodeHandle(Box::new(ASTNode::Variable {
|
||||
name: "x".to_string(),
|
||||
span: make_span(),
|
||||
}));
|
||||
|
||||
let result = ReturnValueLowererBox::lower_to_value_id(
|
||||
&Some(var_ast),
|
||||
&mut body,
|
||||
&mut next_value_id,
|
||||
&env,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result, Some(ValueId(42)));
|
||||
assert_eq!(body.len(), 0); // No instructions emitted
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lower_integer_literal() {
|
||||
let mut body = vec![];
|
||||
let mut next_value_id = 100;
|
||||
let env = BTreeMap::new();
|
||||
|
||||
let int_ast = AstNodeHandle(Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(7),
|
||||
span: make_span(),
|
||||
}));
|
||||
|
||||
let result = ReturnValueLowererBox::lower_to_value_id(
|
||||
&Some(int_ast),
|
||||
&mut body,
|
||||
&mut next_value_id,
|
||||
&env,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result, Some(ValueId(100)));
|
||||
assert_eq!(body.len(), 1); // Const instruction emitted
|
||||
assert_eq!(next_value_id, 101);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lower_add_var_plus_int() {
|
||||
let mut body = vec![];
|
||||
let mut next_value_id = 100;
|
||||
let mut env = BTreeMap::new();
|
||||
env.insert("x".to_string(), ValueId(1));
|
||||
|
||||
let add_ast = AstNodeHandle(Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "x".to_string(),
|
||||
span: make_span(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(2),
|
||||
span: make_span(),
|
||||
}),
|
||||
span: make_span(),
|
||||
}));
|
||||
|
||||
let result = ReturnValueLowererBox::lower_to_value_id(
|
||||
&Some(add_ast),
|
||||
&mut body,
|
||||
&mut next_value_id,
|
||||
&env,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result, Some(ValueId(101))); // BinOp result
|
||||
assert_eq!(body.len(), 2); // Const(2) + BinOp
|
||||
assert_eq!(next_value_id, 102);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lower_add_int_plus_int() {
|
||||
let mut body = vec![];
|
||||
let mut next_value_id = 100;
|
||||
let env = BTreeMap::new();
|
||||
|
||||
let add_ast = AstNodeHandle(Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(5),
|
||||
span: make_span(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(3),
|
||||
span: make_span(),
|
||||
}),
|
||||
span: make_span(),
|
||||
}));
|
||||
|
||||
let result = ReturnValueLowererBox::lower_to_value_id(
|
||||
&Some(add_ast),
|
||||
&mut body,
|
||||
&mut next_value_id,
|
||||
&env,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result, Some(ValueId(102))); // BinOp result
|
||||
assert_eq!(body.len(), 3); // Const(5) + Const(3) + BinOp
|
||||
assert_eq!(next_value_id, 103);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_out_of_scope_subtract() {
|
||||
let mut body = vec![];
|
||||
let mut next_value_id = 100;
|
||||
let mut env = BTreeMap::new();
|
||||
env.insert("x".to_string(), ValueId(1));
|
||||
|
||||
let sub_ast = AstNodeHandle(Box::new(ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Subtract,
|
||||
left: Box::new(ASTNode::Variable {
|
||||
name: "x".to_string(),
|
||||
span: make_span(),
|
||||
}),
|
||||
right: Box::new(ASTNode::Literal {
|
||||
value: LiteralValue::Integer(2),
|
||||
span: make_span(),
|
||||
}),
|
||||
span: make_span(),
|
||||
}));
|
||||
|
||||
let result = ReturnValueLowererBox::lower_to_value_id(
|
||||
&Some(sub_ast),
|
||||
&mut body,
|
||||
&mut next_value_id,
|
||||
&env,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result, None); // Out of scope
|
||||
assert_eq!(body.len(), 0); // No instructions emitted
|
||||
}
|
||||
}
|
||||
@ -26,9 +26,10 @@
|
||||
//! - 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+)
|
||||
//! ## Return Value Lowering SSOT (Phase 138+)
|
||||
//!
|
||||
//! - Function: `lower_return_value_to_vid()`
|
||||
//! - **SSOT Location**: `common/return_value_lowerer_box.rs`
|
||||
//! - Function: `ReturnValueLowererBox::lower_to_value_id()`
|
||||
//! - Responsibility: Lower return values (variable, literal, expr) to ValueId
|
||||
//! - Supported patterns:
|
||||
//! - Variable: env lookup
|
||||
@ -36,21 +37,21 @@
|
||||
//! - 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
|
||||
//! ### Usage
|
||||
//!
|
||||
//! If 2+ files need identical return lowering logic, promote to:
|
||||
//! - `normalized_shadow/common/return_value_lowerer_box.rs`
|
||||
//! - Single responsibility: return value → ValueId conversion
|
||||
//! - Phase 138 P0: loop_true_break_once.rs
|
||||
//! - Phase 139 P0: post_if_post_k.rs (planned)
|
||||
//!
|
||||
//! ## Fail-Fast
|
||||
//!
|
||||
//! - Out of scope → Ok(None) (fallback to legacy)
|
||||
//! - In scope but conversion failed → Err (with freeze_with_hint in strict mode)
|
||||
|
||||
use super::common::return_value_lowerer_box::ReturnValueLowererBox;
|
||||
use super::env_layout::EnvLayout;
|
||||
use super::legacy::LegacyLowerer;
|
||||
use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
|
||||
use crate::mir::control_tree::step_tree::{AstNodeHandle, StepNode, StepStmtKind, StepTree};
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
use crate::mir::control_tree::step_tree::{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::{BinOpKind, ConstValue, JoinFunction, JoinFuncId, JoinInst, JoinModule, MirLikeInst};
|
||||
@ -413,7 +414,7 @@ impl LoopTrueBreakOnceBuilderBox {
|
||||
// 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)? {
|
||||
match ReturnValueLowererBox::lower_to_value_id(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) });
|
||||
}
|
||||
@ -477,7 +478,7 @@ impl LoopTrueBreakOnceBuilderBox {
|
||||
StepStmtKind::Return { value_ast } => {
|
||||
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)? {
|
||||
match ReturnValueLowererBox::lower_to_value_id(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) });
|
||||
}
|
||||
@ -625,122 +626,6 @@ impl LoopTrueBreakOnceBuilderBox {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -41,6 +41,7 @@ pub mod dev_pipeline;
|
||||
pub mod parity_contract;
|
||||
pub mod available_inputs_collector; // Phase 126: available_inputs SSOT
|
||||
pub mod exit_reconnector; // Phase 131 P1.5: Direct variable_map reconnection (Option B)
|
||||
pub mod common; // Phase 138: Common utilities (ReturnValueLowererBox)
|
||||
|
||||
pub use builder::StepTreeNormalizedShadowLowererBox;
|
||||
pub use contracts::{CapabilityCheckResult, UnsupportedCapability};
|
||||
|
||||
Reference in New Issue
Block a user