feat(control_tree): Phase 124 return variable from env (dev-only)

Phase 124-P3:
- Add env: BTreeMap<String, ValueId> from writes in lower_if_only_to_normalized
- Pass env and contract to lower_return_from_tree, lower_if_node, lower_return_value
- Implement Return(Variable) support:
  - If variable in env (writes): Ret(Some(vid))
  - If variable not in env: Err (out of scope, phase124 error)
- Add phase124 errors to Ok(None) conversion (dev-only compatibility)
- Add unit test: test_return_variable_from_env (verifies Ret(Some(ValueId(1))))
- All 1159 tests PASS
This commit is contained in:
nyash-codex
2025-12-18 06:05:18 +09:00
parent 320a23e3d1
commit c40971dc74

View File

@ -102,6 +102,13 @@ impl StepTreeNormalizedShadowLowererBox {
}) })
.collect(); .collect();
// Phase 124: env マッピング(変数名 → ValueId
let env: BTreeMap<String, ValueId> = env_fields
.iter()
.cloned()
.zip(env_params.iter().cloned())
.collect();
// main 関数生成 // main 関数生成
let mut main_func = JoinFunction::new( let mut main_func = JoinFunction::new(
main_func_id, main_func_id,
@ -109,18 +116,20 @@ impl StepTreeNormalizedShadowLowererBox {
env_params.clone(), env_params.clone(),
); );
// Phase 123: Return node lowering // Phase 123-124: Return node lowering
// If Phase 123 patterns are not supported, return Ok(None) // If Phase 123-124 patterns are not supported, return Ok(None)
match Self::lower_return_from_tree( match Self::lower_return_from_tree(
&step_tree.root, &step_tree.root,
&mut main_func.body, &mut main_func.body,
&mut next_value_id, &mut next_value_id,
&env,
&step_tree.contract,
) { ) {
Ok(()) => { Ok(()) => {
// Success - continue // Success - continue
} }
Err(msg) if msg.starts_with("[phase123/") => { Err(msg) if msg.starts_with("[phase123/") || msg.starts_with("[phase124/") => {
// Phase 123 limitation - out of scope // Phase 123-124 limitation - out of scope
return Ok(None); return Ok(None);
} }
Err(msg) => { Err(msg) => {
@ -141,18 +150,21 @@ impl StepTreeNormalizedShadowLowererBox {
Ok(Some((module, meta))) Ok(Some((module, meta)))
} }
/// Phase 123 P1-P3: Lower node from StepTree /// Phase 123-124 P1-P3: Lower node from StepTree
/// ///
/// ## Support (Phase 123) /// ## Support (Phase 123-124)
/// ///
/// - Return(Integer literal): Generate Const + Ret(Some(vid)) /// - Return(Integer literal): Generate Const + Ret(Some(vid))
/// - Return(void): Ret(None) /// - Return(void): Ret(None)
/// - Return(Variable): Phase 124 - lookup from env (dev-only)
/// - Return(other): Fail-Fast with structured error /// - Return(other): Fail-Fast with structured error
/// - If(minimal compare): Generate Compare + Branch + Ret (P3) /// - If(minimal compare): Generate Compare + Branch + Ret (P3)
fn lower_return_from_tree( fn lower_return_from_tree(
node: &crate::mir::control_tree::step_tree::StepNode, node: &crate::mir::control_tree::step_tree::StepNode,
body: &mut Vec<crate::mir::join_ir::JoinInst>, body: &mut Vec<crate::mir::join_ir::JoinInst>,
next_value_id: &mut u32, next_value_id: &mut u32,
env: &std::collections::BTreeMap<String, crate::mir::ValueId>,
contract: &crate::mir::control_tree::step_tree_contract_box::StepTreeContract,
) -> Result<(), String> { ) -> Result<(), String> {
use crate::ast::{ASTNode, LiteralValue}; use crate::ast::{ASTNode, LiteralValue};
use crate::mir::control_tree::step_tree::{StepNode, StepStmtKind}; use crate::mir::control_tree::step_tree::{StepNode, StepStmtKind};
@ -168,11 +180,11 @@ impl StepTreeNormalizedShadowLowererBox {
kind: StepStmtKind::Return { value_ast }, kind: StepStmtKind::Return { value_ast },
.. ..
} => { } => {
return Self::lower_return_value(value_ast, body, next_value_id); return Self::lower_return_value(value_ast, body, next_value_id, env, contract);
} }
StepNode::If { .. } => { StepNode::If { .. } => {
// Phase 123 P3: Lower If node // Phase 123 P3: Lower If node
return Self::lower_if_node(n, body, next_value_id); return Self::lower_if_node(n, body, next_value_id, env, contract);
} }
_ => { _ => {
// Other nodes not yet supported // Other nodes not yet supported
@ -186,10 +198,10 @@ impl StepTreeNormalizedShadowLowererBox {
StepNode::Stmt { StepNode::Stmt {
kind: StepStmtKind::Return { value_ast }, kind: StepStmtKind::Return { value_ast },
.. ..
} => Self::lower_return_value(value_ast, body, next_value_id), } => Self::lower_return_value(value_ast, body, next_value_id, env, contract),
StepNode::If { .. } => { StepNode::If { .. } => {
// Phase 123 P3: Lower If node // Phase 123 P3: Lower If node
Self::lower_if_node(node, body, next_value_id) Self::lower_if_node(node, body, next_value_id, env, contract)
} }
_ => { _ => {
// No return in tree - default to void // No return in tree - default to void
@ -199,12 +211,12 @@ impl StepTreeNormalizedShadowLowererBox {
} }
} }
/// Phase 123 P3: Lower If node with minimal compare /// Phase 123-124 P3: Lower If node with minimal compare
/// ///
/// ## Support /// ## Support
/// ///
/// - Minimal binary comparison: Variable vs Integer literal /// - Minimal binary comparison: Variable vs Integer literal
/// - then/else: Return(Integer literal) only /// - then/else: Return(Integer literal) or Return(Variable) (Phase 124)
/// - Merge: Not yet implemented (will use join_k tail-call in future) /// - Merge: Not yet implemented (will use join_k tail-call in future)
/// ///
/// ## Not Supported (Fail-Fast) /// ## Not Supported (Fail-Fast)
@ -217,6 +229,8 @@ impl StepTreeNormalizedShadowLowererBox {
node: &crate::mir::control_tree::step_tree::StepNode, node: &crate::mir::control_tree::step_tree::StepNode,
body: &mut Vec<crate::mir::join_ir::JoinInst>, body: &mut Vec<crate::mir::join_ir::JoinInst>,
next_value_id: &mut u32, next_value_id: &mut u32,
env: &std::collections::BTreeMap<String, crate::mir::ValueId>,
contract: &crate::mir::control_tree::step_tree_contract_box::StepTreeContract,
) -> Result<(), String> { ) -> Result<(), String> {
use crate::ast::{ASTNode, BinaryOperator}; use crate::ast::{ASTNode, BinaryOperator};
use crate::mir::control_tree::step_tree::StepNode; use crate::mir::control_tree::step_tree::StepNode;
@ -273,10 +287,10 @@ impl StepTreeNormalizedShadowLowererBox {
Self::verify_branch_is_return_literal(else_br)?; Self::verify_branch_is_return_literal(else_br)?;
} }
// For Phase 123, we generate a simplified structure: // For Phase 123-124, we generate a simplified structure:
// The actual branching logic will be added in future phases // The actual branching logic will be added in future phases
// For now, just emit the then branch return // For now, just emit the then branch return
Self::lower_return_from_tree(then_branch, body, next_value_id)?; Self::lower_return_from_tree(then_branch, body, next_value_id, env, contract)?;
Ok(()) Ok(())
} else { } else {
@ -390,18 +404,20 @@ impl StepTreeNormalizedShadowLowererBox {
} }
} }
/// Phase 123 P1-P2: Lower return value /// Phase 123-124 P1-P2-P3: Lower return value
/// ///
/// ## Support /// ## Support
/// ///
/// - Integer literal: Generate Const + Ret(Some(vid)) /// - Integer literal: Generate Const + Ret(Some(vid))
/// - None: Ret(None) /// - None: Ret(None)
/// - Variable: Fail-Fast (needs reads fact - Phase 124) /// - Variable: Phase 124 - lookup from env (dev-only, Fail-Fast if not in env)
/// - Other: Fail-Fast (out of scope) /// - Other: Fail-Fast (out of scope)
fn lower_return_value( fn lower_return_value(
value_ast: &Option<crate::mir::control_tree::step_tree::AstNodeHandle>, value_ast: &Option<crate::mir::control_tree::step_tree::AstNodeHandle>,
body: &mut Vec<crate::mir::join_ir::JoinInst>, body: &mut Vec<crate::mir::join_ir::JoinInst>,
next_value_id: &mut u32, next_value_id: &mut u32,
env: &std::collections::BTreeMap<String, crate::mir::ValueId>,
contract: &crate::mir::control_tree::step_tree_contract_box::StepTreeContract,
) -> Result<(), String> { ) -> Result<(), String> {
use crate::ast::{ASTNode, LiteralValue}; use crate::ast::{ASTNode, LiteralValue};
use crate::mir::join_ir::{ConstValue, JoinInst, MirLikeInst}; use crate::mir::join_ir::{ConstValue, JoinInst, MirLikeInst};
@ -442,11 +458,21 @@ impl StepTreeNormalizedShadowLowererBox {
} }
}, },
ASTNode::Variable { name, .. } => { ASTNode::Variable { name, .. } => {
// Phase 123 P2: Variable not supported (needs reads fact) // Phase 124 P3: Variable return support (dev-only)
Err(format!( // Check if variable is in env (writes-derived)
"[phase123/return/var_unsupported] Phase 123 only supports return with integer literals (found variable: {}). Hint: Add reads fact (Phase 124) or return literal only", if let Some(&vid) = env.get(name) {
name // Variable found in env - return it
)) body.push(JoinInst::Ret { value: Some(vid) });
Ok(())
} else {
// Variable not in env - out of scope for Phase 124
// Phase 124 only supports Return(Variable) when variable is in env (writes)
// Variables in reads-only or undefined are not supported
Err(format!(
"[phase124/return/var_not_in_env] Variable '{}' not in env (writes). Phase 124 only supports return of written variables. Hint: Assign variable before return",
name
))
}
} }
_ => { _ => {
// Phase 123: Other expressions not supported // Phase 123: Other expressions not supported
@ -755,4 +781,57 @@ mod tests {
}); });
assert!(has_compare, "Should have Compare instruction"); assert!(has_compare, "Should have Compare instruction");
} }
#[test]
fn test_return_variable_from_env() {
// Phase 124 P3: Test Return(Variable) when variable is in env (writes)
use crate::ast::{ASTNode, Span};
use crate::mir::control_tree::step_tree::AstNodeHandle;
use std::collections::BTreeSet;
// Create StepTree with "local x; return x"
let mut tree = make_if_only_tree();
// Add x to writes (simulating "local x" or assignment)
tree.contract.writes.insert("x".to_string());
// Create return x node
tree.root = StepNode::Stmt {
kind: StepStmtKind::Return {
value_ast: Some(AstNodeHandle(Box::new(ASTNode::Variable {
name: "x".to_string(),
span: Span::unknown(),
}))),
},
span: Span::unknown(),
};
// Lower to JoinModule - should succeed
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree);
assert!(result.is_ok());
let option = result.unwrap();
assert!(option.is_some(), "Should generate JoinModule for return x (x in writes)");
let (module, _meta) = option.unwrap();
// Verify structure
assert_eq!(module.functions.len(), 1);
let func = &module.functions.values().next().unwrap();
// Should have exactly 1 parameter (x from writes/env)
assert_eq!(func.params.len(), 1, "Should have 1 parameter (x)");
// Should have exactly 1 instruction: Ret(Some(x))
assert_eq!(func.body.len(), 1, "Should have 1 instruction (Ret)");
// Verify Ret instruction returns the parameter (ValueId(1))
use crate::mir::join_ir::JoinInst;
use crate::mir::ValueId;
if let JoinInst::Ret { value } = &func.body[0] {
assert_eq!(value, &Some(ValueId(1)), "Should return ValueId(1) (parameter x)");
} else {
panic!("Expected Ret instruction");
}
}
} }