feat(control_tree): Phase 123 P1 return integer literal in Normalized if-only
- Add value_ast to StepStmtKind::Return for payload tracking - Implement lower_return_value for Integer literal → Compute(Const) + Ret - Add 3 unit tests: integer literal, void, variable fail-fast - All tests passing (7 passed)
This commit is contained in:
@ -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<String> = 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<crate::mir::join_ir::JoinInst>,
|
||||
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<crate::mir::control_tree::step_tree::AstNodeHandle>,
|
||||
body: &mut Vec<crate::mir::join_ir::JoinInst>,
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +57,10 @@ pub enum StepStmtKind {
|
||||
LocalDecl { vars: Vec<String> },
|
||||
Assign { target: Option<String> },
|
||||
Print,
|
||||
Return,
|
||||
Return {
|
||||
/// Phase 123: return value AST (for Normalized lowering)
|
||||
value_ast: Option<AstNodeHandle>,
|
||||
},
|
||||
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 => {
|
||||
|
||||
Reference in New Issue
Block a user