diff --git a/src/mir/control_tree/normalized_shadow/builder.rs b/src/mir/control_tree/normalized_shadow/builder.rs index 2d734cda..708c5878 100644 --- a/src/mir/control_tree/normalized_shadow/builder.rs +++ b/src/mir/control_tree/normalized_shadow/builder.rs @@ -61,7 +61,7 @@ impl StepTreeNormalizedShadowLowererBox { } } - /// Lower if-only StepTree to Normalized JoinModule (Phase 122) + /// Lower if-only StepTree to Normalized JoinModule (Phase 122-123) /// /// ## Design /// @@ -69,6 +69,12 @@ impl StepTreeNormalizedShadowLowererBox { /// - 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生成成功 @@ -80,11 +86,8 @@ impl StepTreeNormalizedShadowLowererBox { use crate::mir::ValueId; use std::collections::BTreeMap; - // Phase 122 P1: 最小実装 - main関数1つ + Ret のみ - // env レイアウト: writes から決定的に決める + // Phase 122: env レイアウト let env_fields: Vec = step_tree.contract.writes.iter().cloned().collect(); - - // 関数ID生成(Phase 122では固定ID使用) let main_func_id = JoinFuncId::new(0); // env フィールドに対応する引数ValueIdを生成 @@ -98,16 +101,19 @@ impl StepTreeNormalizedShadowLowererBox { }) .collect(); - // main 関数生成(最小: Ret のみ) + // main 関数生成 let mut main_func = JoinFunction::new( main_func_id, "main".to_string(), env_params.clone(), ); - // Phase 122 P1: 最小実装 - Return void のみ - // TODO Phase 122 P2-P4: If/Assign/条件式の lowering 実装 - main_func.body.push(JoinInst::Ret { value: None }); + // 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(); @@ -121,6 +127,120 @@ impl StepTreeNormalizedShadowLowererBox { 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 @@ -152,7 +272,9 @@ impl StepTreeNormalizedShadowLowererBox { #[cfg(test)] mod tests { use super::*; - use crate::mir::control_tree::step_tree::{StepNode, StepTreeFeatures, StepTreeSignature}; + 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 { @@ -230,4 +352,115 @@ mod tests { 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")); + } } diff --git a/src/mir/control_tree/step_tree.rs b/src/mir/control_tree/step_tree.rs index b2797e95..4f7036b3 100644 --- a/src/mir/control_tree/step_tree.rs +++ b/src/mir/control_tree/step_tree.rs @@ -57,7 +57,10 @@ pub enum StepStmtKind { LocalDecl { vars: Vec }, Assign { target: Option }, Print, - Return, + Return { + /// Phase 123: return value AST (for Normalized lowering) + value_ast: Option, + }, Break, Continue, Other(&'static str), @@ -190,7 +193,13 @@ impl StepStmtKind { None => "assign(?)".to_string(), }, StepStmtKind::Print => "print".to_string(), - StepStmtKind::Return => "return".to_string(), + StepStmtKind::Return { value_ast } => { + if value_ast.is_some() { + "return(value)".to_string() + } else { + "return(void)".to_string() + } + } StepStmtKind::Break => "break".to_string(), StepStmtKind::Continue => "continue".to_string(), StepStmtKind::Other(name) => format!("other:{name}"), @@ -322,9 +331,11 @@ impl StepTreeBuilderBox { let (node, features) = Self::build_block_node(body, if_depth, loop_depth); (node.with_span(span.clone()), features) } - ASTNode::Return { span, .. } => ( + ASTNode::Return { value, span } => ( StepNode::Stmt { - kind: StepStmtKind::Return, + kind: StepStmtKind::Return { + value_ast: value.as_ref().map(|v| AstNodeHandle(v.clone())), + }, span: span.clone(), }, StepTreeFeatures { @@ -497,7 +508,7 @@ fn walk_for_facts(node: &StepNode, facts: &mut StepTreeFacts) { } } StepStmtKind::Print => {} - StepStmtKind::Return => { + StepStmtKind::Return { .. } => { facts.add_exit(ExitKind::Return); } StepStmtKind::Break => {