//! Phase 121: StepTree → JoinModule shadow lowering (if-only) //! //! ## Responsibility //! //! - Convert StepTree to JoinModule (Normalized dialect) //! - Only for if-only patterns (no loops) //! - Returns None for out-of-scope patterns //! - Returns Err for patterns that should be supported but conversion failed //! //! ## Design //! //! - Input: `&StepTree` with pre-computed contract //! - No AST re-analysis (contract-only decisions) //! - Single responsibility: structure → JoinIR conversion use crate::mir::control_tree::step_tree::StepTree; use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta}; use crate::mir::join_ir::JoinModule; use super::contracts::{check_if_only, CapabilityCheckResult}; /// Box-First: StepTree → Normalized shadow lowering pub struct StepTreeNormalizedShadowLowererBox; impl StepTreeNormalizedShadowLowererBox { /// Try to lower an if-only StepTree to normalized form /// /// ## Returns /// /// - `Ok(None)`: Out of scope (e.g., contains loops) /// - `Ok(Some(...))`: Shadow generation succeeded /// - `Err(...)`: Should be supported but conversion failed (internal error) /// /// ## Contract /// /// - Only processes if-only patterns (no loops/breaks/continues) /// - Uses contract information only (no AST re-analysis) /// - Dev-only: caller must check `joinir_dev_enabled()` before calling /// /// ## Phase 122 Implementation /// /// - Generates Normalized JoinIR (env + continuation) /// - env layout: writes only (SSOT) /// - merge = join_k(env) tail-call (no PHI) /// - Minimal node support: If/Return/Assign(Const/Variable/BinOp(Add)) pub fn try_lower_if_only( step_tree: &StepTree, ) -> Result, String> { // Phase 121 P1: Capability check (if-only scope) let capability = check_if_only(step_tree); match capability { CapabilityCheckResult::Supported => { // Phase 122 P1: Generate Normalized JoinModule Self::lower_if_only_to_normalized(step_tree) .map(Some) } CapabilityCheckResult::Unsupported(_reason) => { // Out of scope for Phase 121/122 Ok(None) } } } /// Lower if-only StepTree to Normalized JoinModule (Phase 122-123) /// /// ## Design /// /// - env レイアウト: `writes` に含まれる変数だけ(決定的順序) /// - merge 形式: `join_k(env)` への tail-call(PHI 禁止) /// - 対応ノード: If/Return/Assign(最小セット) /// /// ## Phase 123 Node Support /// /// - Return(Integer literal): `Const + Ret(Some(vid))` /// - Return(Variable): Fail-Fast (needs reads fact) /// - Return(void): `Ret(None)` /// /// ## Returns /// /// - `Ok((module, meta))`: Normalized JoinModule生成成功 /// - `Err(msg)`: 生成できるはずなのに失敗(内部エラー) fn lower_if_only_to_normalized( step_tree: &StepTree, ) -> Result<(JoinModule, JoinFragmentMeta), String> { use crate::mir::join_ir::{JoinFunction, JoinFuncId, JoinInst}; use crate::mir::ValueId; use std::collections::BTreeMap; // Phase 122: env レイアウト let env_fields: Vec = step_tree.contract.writes.iter().cloned().collect(); let main_func_id = JoinFuncId::new(0); // env フィールドに対応する引数ValueIdを生成 let mut next_value_id = 1; let env_params: Vec = env_fields .iter() .map(|_| { let vid = ValueId(next_value_id); next_value_id += 1; vid }) .collect(); // main 関数生成 let mut main_func = JoinFunction::new( main_func_id, "main".to_string(), env_params.clone(), ); // Phase 123: Return node lowering Self::lower_return_from_tree( &step_tree.root, &mut main_func.body, &mut next_value_id, )?; // JoinModule 構築 let mut module = JoinModule::new(); module.add_function(main_func); module.entry = Some(main_func_id); module.mark_normalized(); // JoinFragmentMeta 生成(最小) let meta = JoinFragmentMeta::empty(); Ok((module, meta)) } /// Phase 123 P1: Lower Return node from StepTree /// /// ## Support (Phase 123) /// /// - Return(Integer literal): Generate Const + Ret(Some(vid)) /// - Return(void): Ret(None) /// - Return(other): Fail-Fast with structured error fn lower_return_from_tree( node: &crate::mir::control_tree::step_tree::StepNode, body: &mut Vec, next_value_id: &mut u32, ) -> Result<(), String> { use crate::ast::{ASTNode, LiteralValue}; use crate::mir::control_tree::step_tree::{StepNode, StepStmtKind}; use crate::mir::join_ir::JoinInst; use crate::mir::ValueId; match node { StepNode::Block(nodes) => { // Find first Return in block (Phase 123 minimal: single return only) for n in nodes { if let StepNode::Stmt { kind: StepStmtKind::Return { value_ast }, .. } = n { return Self::lower_return_value(value_ast, body, next_value_id); } } // No return found - default to void body.push(JoinInst::Ret { value: None }); Ok(()) } StepNode::Stmt { kind: StepStmtKind::Return { value_ast }, .. } => Self::lower_return_value(value_ast, body, next_value_id), _ => { // No return in tree - default to void body.push(JoinInst::Ret { value: None }); Ok(()) } } } /// Phase 123 P1-P2: Lower return value /// /// ## Support /// /// - Integer literal: Generate Const + Ret(Some(vid)) /// - None: Ret(None) /// - Variable: Fail-Fast (needs reads fact - Phase 124) /// - Other: Fail-Fast (out of scope) fn lower_return_value( value_ast: &Option, body: &mut Vec, next_value_id: &mut u32, ) -> Result<(), String> { use crate::ast::{ASTNode, LiteralValue}; use crate::mir::join_ir::{ConstValue, JoinInst, MirLikeInst}; use crate::mir::ValueId; match value_ast { None => { body.push(JoinInst::Ret { value: None }); Ok(()) } Some(ast_handle) => { let ast = &ast_handle.0; match &**ast { ASTNode::Literal { value, .. } => match value { LiteralValue::Integer(i) => { // Phase 123 P1: Integer literal → Const + Ret(Some(vid)) let const_vid = ValueId(*next_value_id); *next_value_id += 1; // Generate Const instruction (wrapped in Compute) body.push(JoinInst::Compute(MirLikeInst::Const { dst: const_vid, value: ConstValue::Integer(*i), })); // Generate Ret instruction body.push(JoinInst::Ret { value: Some(const_vid), }); Ok(()) } _ => { // Phase 123: Other literals not supported Err(format!( "[phase123/return/literal_unsupported] Phase 123 only supports integer literals. Hint: Use integer literal or wait for Phase 124" )) } }, ASTNode::Variable { name, .. } => { // Phase 123 P2: Variable not supported (needs reads fact) Err(format!( "[phase123/return/var_unsupported] Phase 123 only supports return with integer literals (found variable: {}). Hint: Add reads fact (Phase 124) or return literal only", name )) } _ => { // Phase 123: Other expressions not supported Err(format!( "[phase123/return/expr_unsupported] Phase 123 only supports integer literals. Hint: Simplify to literal or wait for Phase 124" )) } } } } } /// Get shadow lowering status string for dev logging /// /// ## Contract /// /// - Returns 1-line summary: "shadow_lowered=true/false reason=..." /// - Does not perform actual lowering (use `try_lower_if_only` for that) pub fn get_status_string(step_tree: &StepTree) -> String { let capability = check_if_only(step_tree); match capability { CapabilityCheckResult::Supported => { format!( "shadow_lowered=true step_tree_sig={} exits={:?} writes={:?}", step_tree.signature_basis_string(), step_tree.contract.exits, step_tree.contract.writes ) } CapabilityCheckResult::Unsupported(reason) => { format!( "shadow_lowered=false reason=\"{}\" step_tree_sig={}", reason.reason(), step_tree.signature_basis_string() ) } } } } #[cfg(test)] mod tests { use super::*; use crate::mir::control_tree::step_tree::{ StepNode, StepStmtKind, StepTreeFeatures, StepTreeSignature, }; use crate::mir::control_tree::step_tree_contract_box::StepTreeContract; fn make_if_only_tree() -> StepTree { StepTree { root: StepNode::Block(vec![]), features: StepTreeFeatures { has_if: true, has_loop: false, has_break: false, has_continue: false, has_return: false, max_if_depth: 1, max_loop_depth: 0, }, contract: StepTreeContract { exits: Default::default(), writes: Default::default(), required_caps: Default::default(), cond_sig: Default::default(), }, signature: StepTreeSignature(0), } } fn make_loop_tree() -> StepTree { StepTree { root: StepNode::Block(vec![]), features: StepTreeFeatures { has_if: false, has_loop: true, has_break: false, has_continue: false, has_return: false, max_if_depth: 0, max_loop_depth: 1, }, contract: StepTreeContract { exits: Default::default(), writes: Default::default(), required_caps: Default::default(), cond_sig: Default::default(), }, signature: StepTreeSignature(0), } } #[test] fn test_if_only_supported() { let tree = make_if_only_tree(); let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree); assert!(result.is_ok()); assert!(result.unwrap().is_some()); } #[test] fn test_loop_rejected() { let tree = make_loop_tree(); let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree); assert!(result.is_ok()); assert!(result.unwrap().is_none()); } #[test] fn test_status_string_if_only() { let tree = make_if_only_tree(); let status = StepTreeNormalizedShadowLowererBox::get_status_string(&tree); assert!(status.contains("shadow_lowered=true")); assert!(status.contains("step_tree_sig=")); } #[test] fn test_status_string_loop() { let tree = make_loop_tree(); let status = StepTreeNormalizedShadowLowererBox::get_status_string(&tree); assert!(status.contains("shadow_lowered=false")); assert!(status.contains("reason=\"contains loop")); } #[test] fn test_return_integer_literal() { use crate::ast::{ASTNode, LiteralValue, Span}; use crate::mir::control_tree::step_tree::AstNodeHandle; // Create StepTree with "return 7" let return_ast = Box::new(ASTNode::Literal { value: LiteralValue::Integer(7), span: Span::unknown(), }); let mut tree = make_if_only_tree(); tree.root = StepNode::Stmt { kind: StepStmtKind::Return { value_ast: Some(AstNodeHandle(return_ast)), }, span: Span::unknown(), }; // Lower to JoinModule let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree); assert!(result.is_ok()); let (module, _meta) = result.unwrap().expect("Should generate JoinModule"); // Verify Const + Ret instructions assert_eq!(module.functions.len(), 1); let func = &module.functions.values().next().unwrap(); assert_eq!(func.body.len(), 2, "Should have Const + Ret"); // Check Const instruction use crate::mir::join_ir::{ConstValue, JoinInst, MirLikeInst}; use crate::mir::ValueId; if let JoinInst::Compute(MirLikeInst::Const { dst, value }) = &func.body[0] { assert_eq!(*dst, ValueId(1)); if let ConstValue::Integer(i) = value { assert_eq!(*i, 7); } else { panic!("Expected Integer const"); } } else { panic!("Expected Const instruction"); } // Check Ret instruction if let JoinInst::Ret { value } = &func.body[1] { assert_eq!(value, &Some(ValueId(1))); } else { panic!("Expected Ret instruction"); } } #[test] fn test_return_void() { use crate::ast::Span; // Create StepTree with "return" (no value) let mut tree = make_if_only_tree(); tree.root = StepNode::Stmt { kind: StepStmtKind::Return { value_ast: None }, span: Span::unknown(), }; // Lower to JoinModule let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree); assert!(result.is_ok()); let (module, _meta) = result.unwrap().expect("Should generate JoinModule"); // Verify Ret(None) instruction assert_eq!(module.functions.len(), 1); let func = &module.functions.values().next().unwrap(); assert_eq!(func.body.len(), 1, "Should have only Ret"); // Check Ret instruction use crate::mir::join_ir::JoinInst; if let JoinInst::Ret { value } = &func.body[0] { assert_eq!(value, &None); } else { panic!("Expected Ret instruction"); } } #[test] fn test_return_variable_fails() { use crate::ast::{ASTNode, Span}; use crate::mir::control_tree::step_tree::AstNodeHandle; // Create StepTree with "return x" (variable) let return_ast = Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown(), }); let mut tree = make_if_only_tree(); tree.root = StepNode::Stmt { kind: StepStmtKind::Return { value_ast: Some(AstNodeHandle(return_ast)), }, span: Span::unknown(), }; // Lower to JoinModule - should fail let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree); assert!(result.is_err()); let err = result.unwrap_err(); assert!(err.contains("phase123/return/var_unsupported")); assert!(err.contains("Phase 124")); } }