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();
// 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");
}
}
}