2025-12-18 04:31:41 +09:00
|
|
|
|
//! Phase 121: StepTree → JoinModule shadow lowering (if-only)
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ## Responsibility
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! - Convert StepTree to JoinModule (Normalized dialect)
|
|
|
|
|
|
//! - Only for if-only patterns (no loops)
|
|
|
|
|
|
//! - Returns None for out-of-scope patterns
|
|
|
|
|
|
//! - Returns Err for patterns that should be supported but conversion failed
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ## Design
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! - Input: `&StepTree` with pre-computed contract
|
|
|
|
|
|
//! - No AST re-analysis (contract-only decisions)
|
|
|
|
|
|
//! - Single responsibility: structure → JoinIR conversion
|
|
|
|
|
|
|
|
|
|
|
|
use crate::mir::control_tree::step_tree::StepTree;
|
|
|
|
|
|
use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta};
|
|
|
|
|
|
use crate::mir::join_ir::JoinModule;
|
|
|
|
|
|
|
|
|
|
|
|
use super::contracts::{check_if_only, CapabilityCheckResult};
|
|
|
|
|
|
|
|
|
|
|
|
/// Box-First: StepTree → Normalized shadow lowering
|
|
|
|
|
|
pub struct StepTreeNormalizedShadowLowererBox;
|
|
|
|
|
|
|
|
|
|
|
|
impl StepTreeNormalizedShadowLowererBox {
|
|
|
|
|
|
/// Try to lower an if-only StepTree to normalized form
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ## Returns
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - `Ok(None)`: Out of scope (e.g., contains loops)
|
|
|
|
|
|
/// - `Ok(Some(...))`: Shadow generation succeeded
|
|
|
|
|
|
/// - `Err(...)`: Should be supported but conversion failed (internal error)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ## Contract
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - Only processes if-only patterns (no loops/breaks/continues)
|
|
|
|
|
|
/// - Uses contract information only (no AST re-analysis)
|
|
|
|
|
|
/// - Dev-only: caller must check `joinir_dev_enabled()` before calling
|
2025-12-18 04:50:32 +09:00
|
|
|
|
///
|
|
|
|
|
|
/// ## Phase 122 Implementation
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - Generates Normalized JoinIR (env + continuation)
|
|
|
|
|
|
/// - env layout: writes only (SSOT)
|
|
|
|
|
|
/// - merge = join_k(env) tail-call (no PHI)
|
|
|
|
|
|
/// - Minimal node support: If/Return/Assign(Const/Variable/BinOp(Add))
|
2025-12-18 04:31:41 +09:00
|
|
|
|
pub fn try_lower_if_only(
|
|
|
|
|
|
step_tree: &StepTree,
|
|
|
|
|
|
) -> Result<Option<(JoinModule, JoinFragmentMeta)>, String> {
|
|
|
|
|
|
// Phase 121 P1: Capability check (if-only scope)
|
|
|
|
|
|
let capability = check_if_only(step_tree);
|
|
|
|
|
|
match capability {
|
|
|
|
|
|
CapabilityCheckResult::Supported => {
|
2025-12-18 04:50:32 +09:00
|
|
|
|
// Phase 122 P1: Generate Normalized JoinModule
|
|
|
|
|
|
Self::lower_if_only_to_normalized(step_tree)
|
|
|
|
|
|
.map(Some)
|
2025-12-18 04:31:41 +09:00
|
|
|
|
}
|
|
|
|
|
|
CapabilityCheckResult::Unsupported(_reason) => {
|
2025-12-18 04:50:32 +09:00
|
|
|
|
// Out of scope for Phase 121/122
|
2025-12-18 04:31:41 +09:00
|
|
|
|
Ok(None)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-18 05:37:13 +09:00
|
|
|
|
/// Lower if-only StepTree to Normalized JoinModule (Phase 122-123)
|
2025-12-18 04:50:32 +09:00
|
|
|
|
///
|
|
|
|
|
|
/// ## Design
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - env レイアウト: `writes` に含まれる変数だけ(決定的順序)
|
|
|
|
|
|
/// - merge 形式: `join_k(env)` への tail-call(PHI 禁止)
|
|
|
|
|
|
/// - 対応ノード: If/Return/Assign(最小セット)
|
|
|
|
|
|
///
|
2025-12-18 05:37:13 +09:00
|
|
|
|
/// ## Phase 123 Node Support
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - Return(Integer literal): `Const + Ret(Some(vid))`
|
|
|
|
|
|
/// - Return(Variable): Fail-Fast (needs reads fact)
|
|
|
|
|
|
/// - Return(void): `Ret(None)`
|
|
|
|
|
|
///
|
2025-12-18 04:50:32 +09:00
|
|
|
|
/// ## Returns
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - `Ok((module, meta))`: Normalized JoinModule生成成功
|
|
|
|
|
|
/// - `Err(msg)`: 生成できるはずなのに失敗(内部エラー)
|
|
|
|
|
|
fn lower_if_only_to_normalized(
|
|
|
|
|
|
step_tree: &StepTree,
|
|
|
|
|
|
) -> Result<(JoinModule, JoinFragmentMeta), String> {
|
|
|
|
|
|
use crate::mir::join_ir::{JoinFunction, JoinFuncId, JoinInst};
|
|
|
|
|
|
use crate::mir::ValueId;
|
|
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
|
|
|
2025-12-18 05:37:13 +09:00
|
|
|
|
// Phase 122: env レイアウト
|
2025-12-18 04:50:32 +09:00
|
|
|
|
let env_fields: Vec<String> = step_tree.contract.writes.iter().cloned().collect();
|
|
|
|
|
|
let main_func_id = JoinFuncId::new(0);
|
|
|
|
|
|
|
|
|
|
|
|
// env フィールドに対応する引数ValueIdを生成
|
|
|
|
|
|
let mut next_value_id = 1;
|
|
|
|
|
|
let env_params: Vec<ValueId> = env_fields
|
|
|
|
|
|
.iter()
|
|
|
|
|
|
.map(|_| {
|
|
|
|
|
|
let vid = ValueId(next_value_id);
|
|
|
|
|
|
next_value_id += 1;
|
|
|
|
|
|
vid
|
|
|
|
|
|
})
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
2025-12-18 05:37:13 +09:00
|
|
|
|
// main 関数生成
|
2025-12-18 04:50:32 +09:00
|
|
|
|
let mut main_func = JoinFunction::new(
|
|
|
|
|
|
main_func_id,
|
|
|
|
|
|
"main".to_string(),
|
|
|
|
|
|
env_params.clone(),
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-12-18 05:37:13 +09:00
|
|
|
|
// Phase 123: Return node lowering
|
|
|
|
|
|
Self::lower_return_from_tree(
|
|
|
|
|
|
&step_tree.root,
|
|
|
|
|
|
&mut main_func.body,
|
|
|
|
|
|
&mut next_value_id,
|
|
|
|
|
|
)?;
|
2025-12-18 04:50:32 +09:00
|
|
|
|
|
|
|
|
|
|
// JoinModule 構築
|
|
|
|
|
|
let mut module = JoinModule::new();
|
|
|
|
|
|
module.add_function(main_func);
|
|
|
|
|
|
module.entry = Some(main_func_id);
|
|
|
|
|
|
module.mark_normalized();
|
|
|
|
|
|
|
|
|
|
|
|
// JoinFragmentMeta 生成(最小)
|
|
|
|
|
|
let meta = JoinFragmentMeta::empty();
|
|
|
|
|
|
|
|
|
|
|
|
Ok((module, meta))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-18 05:37:13 +09:00
|
|
|
|
/// 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"
|
|
|
|
|
|
))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-18 04:31:41 +09:00
|
|
|
|
/// Get shadow lowering status string for dev logging
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ## Contract
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - Returns 1-line summary: "shadow_lowered=true/false reason=..."
|
|
|
|
|
|
/// - Does not perform actual lowering (use `try_lower_if_only` for that)
|
|
|
|
|
|
pub fn get_status_string(step_tree: &StepTree) -> String {
|
|
|
|
|
|
let capability = check_if_only(step_tree);
|
|
|
|
|
|
match capability {
|
|
|
|
|
|
CapabilityCheckResult::Supported => {
|
|
|
|
|
|
format!(
|
|
|
|
|
|
"shadow_lowered=true step_tree_sig={} exits={:?} writes={:?}",
|
|
|
|
|
|
step_tree.signature_basis_string(),
|
|
|
|
|
|
step_tree.contract.exits,
|
|
|
|
|
|
step_tree.contract.writes
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
CapabilityCheckResult::Unsupported(reason) => {
|
|
|
|
|
|
format!(
|
|
|
|
|
|
"shadow_lowered=false reason=\"{}\" step_tree_sig={}",
|
|
|
|
|
|
reason.reason(),
|
|
|
|
|
|
step_tree.signature_basis_string()
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::*;
|
2025-12-18 05:37:13 +09:00
|
|
|
|
use crate::mir::control_tree::step_tree::{
|
|
|
|
|
|
StepNode, StepStmtKind, StepTreeFeatures, StepTreeSignature,
|
|
|
|
|
|
};
|
2025-12-18 04:31:41 +09:00
|
|
|
|
use crate::mir::control_tree::step_tree_contract_box::StepTreeContract;
|
|
|
|
|
|
|
|
|
|
|
|
fn make_if_only_tree() -> StepTree {
|
|
|
|
|
|
StepTree {
|
|
|
|
|
|
root: StepNode::Block(vec![]),
|
|
|
|
|
|
features: StepTreeFeatures {
|
|
|
|
|
|
has_if: true,
|
|
|
|
|
|
has_loop: false,
|
|
|
|
|
|
has_break: false,
|
|
|
|
|
|
has_continue: false,
|
|
|
|
|
|
has_return: false,
|
|
|
|
|
|
max_if_depth: 1,
|
|
|
|
|
|
max_loop_depth: 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
contract: StepTreeContract {
|
|
|
|
|
|
exits: Default::default(),
|
|
|
|
|
|
writes: Default::default(),
|
|
|
|
|
|
required_caps: Default::default(),
|
|
|
|
|
|
cond_sig: Default::default(),
|
|
|
|
|
|
},
|
|
|
|
|
|
signature: StepTreeSignature(0),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn make_loop_tree() -> StepTree {
|
|
|
|
|
|
StepTree {
|
|
|
|
|
|
root: StepNode::Block(vec![]),
|
|
|
|
|
|
features: StepTreeFeatures {
|
|
|
|
|
|
has_if: false,
|
|
|
|
|
|
has_loop: true,
|
|
|
|
|
|
has_break: false,
|
|
|
|
|
|
has_continue: false,
|
|
|
|
|
|
has_return: false,
|
|
|
|
|
|
max_if_depth: 0,
|
|
|
|
|
|
max_loop_depth: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
contract: StepTreeContract {
|
|
|
|
|
|
exits: Default::default(),
|
|
|
|
|
|
writes: Default::default(),
|
|
|
|
|
|
required_caps: Default::default(),
|
|
|
|
|
|
cond_sig: Default::default(),
|
|
|
|
|
|
},
|
|
|
|
|
|
signature: StepTreeSignature(0),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_if_only_supported() {
|
|
|
|
|
|
let tree = make_if_only_tree();
|
|
|
|
|
|
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree);
|
|
|
|
|
|
assert!(result.is_ok());
|
|
|
|
|
|
assert!(result.unwrap().is_some());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_loop_rejected() {
|
|
|
|
|
|
let tree = make_loop_tree();
|
|
|
|
|
|
let result = StepTreeNormalizedShadowLowererBox::try_lower_if_only(&tree);
|
|
|
|
|
|
assert!(result.is_ok());
|
|
|
|
|
|
assert!(result.unwrap().is_none());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_status_string_if_only() {
|
|
|
|
|
|
let tree = make_if_only_tree();
|
|
|
|
|
|
let status = StepTreeNormalizedShadowLowererBox::get_status_string(&tree);
|
|
|
|
|
|
assert!(status.contains("shadow_lowered=true"));
|
|
|
|
|
|
assert!(status.contains("step_tree_sig="));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_status_string_loop() {
|
|
|
|
|
|
let tree = make_loop_tree();
|
|
|
|
|
|
let status = StepTreeNormalizedShadowLowererBox::get_status_string(&tree);
|
|
|
|
|
|
assert!(status.contains("shadow_lowered=false"));
|
|
|
|
|
|
assert!(status.contains("reason=\"contains loop"));
|
|
|
|
|
|
}
|
2025-12-18 05:37:13 +09:00
|
|
|
|
|
|
|
|
|
|
#[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"));
|
|
|
|
|
|
}
|
2025-12-18 04:31:41 +09:00
|
|
|
|
}
|