From 95daf230c432f1aecf873b2503b9191299491332 Mon Sep 17 00:00:00 2001 From: nyash-codex Date: Fri, 19 Dec 2025 00:55:51 +0900 Subject: [PATCH] refactor(normalized_shadow): Phase 138 - extract ReturnValueLowererBox (no behavior change) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../normalized_shadow/common/mod.rs | 3 + .../common/return_value_lowerer_box.rs | 316 ++++++++++++++++++ .../normalized_shadow/loop_true_break_once.rs | 137 +------- src/mir/control_tree/normalized_shadow/mod.rs | 1 + 4 files changed, 331 insertions(+), 126 deletions(-) create mode 100644 src/mir/control_tree/normalized_shadow/common/mod.rs create mode 100644 src/mir/control_tree/normalized_shadow/common/return_value_lowerer_box.rs diff --git a/src/mir/control_tree/normalized_shadow/common/mod.rs b/src/mir/control_tree/normalized_shadow/common/mod.rs new file mode 100644 index 00000000..1b736fd6 --- /dev/null +++ b/src/mir/control_tree/normalized_shadow/common/mod.rs @@ -0,0 +1,3 @@ +//! Common utilities for Normalized shadow (Phase 138+) + +pub mod return_value_lowerer_box; diff --git a/src/mir/control_tree/normalized_shadow/common/return_value_lowerer_box.rs b/src/mir/control_tree/normalized_shadow/common/return_value_lowerer_box.rs new file mode 100644 index 00000000..762bdf4c --- /dev/null +++ b/src/mir/control_tree/normalized_shadow/common/return_value_lowerer_box.rs @@ -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, + body: &mut Vec, + next_value_id: &mut u32, + env: &BTreeMap, + ) -> Result, 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, + next_value_id: &mut u32, + env: &BTreeMap, + ) -> Result, 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 + } +} diff --git a/src/mir/control_tree/normalized_shadow/loop_true_break_once.rs b/src/mir/control_tree/normalized_shadow/loop_true_break_once.rs index 6d25a737..7fd5e70b 100644 --- a/src/mir/control_tree/normalized_shadow/loop_true_break_once.rs +++ b/src/mir/control_tree/normalized_shadow/loop_true_break_once.rs @@ -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, - body: &mut Vec, - next_value_id: &mut u32, - env: &BTreeMap, - ) -> Result, 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)] diff --git a/src/mir/control_tree/normalized_shadow/mod.rs b/src/mir/control_tree/normalized_shadow/mod.rs index ff91c657..d2909837 100644 --- a/src/mir/control_tree/normalized_shadow/mod.rs +++ b/src/mir/control_tree/normalized_shadow/mod.rs @@ -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};