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:
@ -102,6 +102,13 @@ impl StepTreeNormalizedShadowLowererBox {
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Phase 124: env マッピング(変数名 → ValueId)
|
||||
let env: BTreeMap<String, ValueId> = env_fields
|
||||
.iter()
|
||||
.cloned()
|
||||
.zip(env_params.iter().cloned())
|
||||
.collect();
|
||||
|
||||
// main 関数生成
|
||||
let mut main_func = JoinFunction::new(
|
||||
main_func_id,
|
||||
@ -109,18 +116,20 @@ impl StepTreeNormalizedShadowLowererBox {
|
||||
env_params.clone(),
|
||||
);
|
||||
|
||||
// Phase 123: Return node lowering
|
||||
// If Phase 123 patterns are not supported, return Ok(None)
|
||||
// Phase 123-124: Return node lowering
|
||||
// If Phase 123-124 patterns are not supported, return Ok(None)
|
||||
match Self::lower_return_from_tree(
|
||||
&step_tree.root,
|
||||
&mut main_func.body,
|
||||
&mut next_value_id,
|
||||
&env,
|
||||
&step_tree.contract,
|
||||
) {
|
||||
Ok(()) => {
|
||||
// Success - continue
|
||||
}
|
||||
Err(msg) if msg.starts_with("[phase123/") => {
|
||||
// Phase 123 limitation - out of scope
|
||||
Err(msg) if msg.starts_with("[phase123/") || msg.starts_with("[phase124/") => {
|
||||
// Phase 123-124 limitation - out of scope
|
||||
return Ok(None);
|
||||
}
|
||||
Err(msg) => {
|
||||
@ -141,18 +150,21 @@ impl StepTreeNormalizedShadowLowererBox {
|
||||
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(void): Ret(None)
|
||||
/// - Return(Variable): Phase 124 - lookup from env (dev-only)
|
||||
/// - Return(other): Fail-Fast with structured error
|
||||
/// - If(minimal compare): Generate Compare + Branch + Ret (P3)
|
||||
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,
|
||||
env: &std::collections::BTreeMap<String, crate::mir::ValueId>,
|
||||
contract: &crate::mir::control_tree::step_tree_contract_box::StepTreeContract,
|
||||
) -> Result<(), String> {
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
use crate::mir::control_tree::step_tree::{StepNode, StepStmtKind};
|
||||
@ -168,11 +180,11 @@ impl StepTreeNormalizedShadowLowererBox {
|
||||
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 { .. } => {
|
||||
// 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
|
||||
@ -186,10 +198,10 @@ impl StepTreeNormalizedShadowLowererBox {
|
||||
StepNode::Stmt {
|
||||
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 { .. } => {
|
||||
// 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
|
||||
@ -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
|
||||
///
|
||||
/// - 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)
|
||||
///
|
||||
/// ## Not Supported (Fail-Fast)
|
||||
@ -217,6 +229,8 @@ impl StepTreeNormalizedShadowLowererBox {
|
||||
node: &crate::mir::control_tree::step_tree::StepNode,
|
||||
body: &mut Vec<crate::mir::join_ir::JoinInst>,
|
||||
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> {
|
||||
use crate::ast::{ASTNode, BinaryOperator};
|
||||
use crate::mir::control_tree::step_tree::StepNode;
|
||||
@ -273,10 +287,10 @@ impl StepTreeNormalizedShadowLowererBox {
|
||||
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
|
||||
// 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(())
|
||||
} else {
|
||||
@ -390,18 +404,20 @@ impl StepTreeNormalizedShadowLowererBox {
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 123 P1-P2: Lower return value
|
||||
/// Phase 123-124 P1-P2-P3: Lower return value
|
||||
///
|
||||
/// ## Support
|
||||
///
|
||||
/// - Integer literal: Generate Const + Ret(Some(vid))
|
||||
/// - 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)
|
||||
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,
|
||||
env: &std::collections::BTreeMap<String, crate::mir::ValueId>,
|
||||
contract: &crate::mir::control_tree::step_tree_contract_box::StepTreeContract,
|
||||
) -> Result<(), String> {
|
||||
use crate::ast::{ASTNode, LiteralValue};
|
||||
use crate::mir::join_ir::{ConstValue, JoinInst, MirLikeInst};
|
||||
@ -442,11 +458,21 @@ impl StepTreeNormalizedShadowLowererBox {
|
||||
}
|
||||
},
|
||||
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 124 P3: Variable return support (dev-only)
|
||||
// Check if variable is in env (writes-derived)
|
||||
if let Some(&vid) = env.get(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
|
||||
@ -755,4 +781,57 @@ mod tests {
|
||||
});
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user