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 ;
2025-12-18 06:45:23 +09:00
use crate ::mir ::ValueId ; // Phase 126
use std ::collections ::BTreeMap ; // Phase 126
2025-12-18 04:31:41 +09:00
use super ::contracts ::{ check_if_only , CapabilityCheckResult } ;
2025-12-18 06:30:55 +09:00
/// Phase 125: Normalized env layout (writes + inputs)
///
/// ## Design
///
/// - writes: Variables written in the function (generate ValueId)
/// - inputs: Variables read from outer scope (reference ValueId, don't generate)
///
/// ## SSOT
///
/// - writes: From StepTreeContract.writes
/// - inputs: From (StepTreeContract.reads ∩ available_inputs)
#[ derive(Debug, Clone) ]
pub struct EnvLayout {
/// Variables written (generate ValueId for these)
pub writes : Vec < String > ,
/// Variables read from outer scope (reference ValueId from available_inputs)
pub inputs : Vec < String > ,
}
impl EnvLayout {
/// Create env layout from contract and available_inputs (Phase 125)
///
/// ## Contract
///
/// - writes: All variables from contract.writes (deterministic order)
/// - inputs: Variables in contract.reads that are available from outer scope
///
/// ## SSOT
///
/// - available_inputs source: function params + CapturedEnv (pinned/captured)
/// - No AST inference (don't capture from AST)
pub fn from_contract (
contract : & crate ::mir ::control_tree ::step_tree_contract_box ::StepTreeContract ,
available_inputs : & std ::collections ::BTreeMap < String , crate ::mir ::ValueId > ,
) -> Self {
use std ::collections ::BTreeSet ;
// Phase 125 P2: writes from contract
let writes : Vec < String > = contract . writes . iter ( ) . cloned ( ) . collect ( ) ;
// Phase 125 P2: inputs = reads ∩ available_inputs (deterministic order)
let inputs : Vec < String > = contract
. reads
. iter ( )
. filter ( | name | available_inputs . contains_key ( * name ) )
. cloned ( )
. collect ( ) ;
EnvLayout { writes , inputs }
}
}
2025-12-18 04:31:41 +09:00
/// 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
///
2025-12-18 06:45:23 +09:00
/// ## Phase 122-126 Implementation
2025-12-18 04:50:32 +09:00
///
/// - Generates Normalized JoinIR (env + continuation)
2025-12-18 06:45:23 +09:00
/// - env layout: writes + inputs (Phase 125-126)
2025-12-18 04:50:32 +09:00
/// - merge = join_k(env) tail-call (no PHI)
/// - Minimal node support: If/Return/Assign(Const/Variable/BinOp(Add))
2025-12-18 06:45:23 +09:00
///
/// ## Phase 126: available_inputs
///
/// - available_inputs: function params + CapturedEnv (SSOT)
/// - inputs = reads ∩ available_inputs (deterministic order)
2025-12-18 04:31:41 +09:00
pub fn try_lower_if_only (
step_tree : & StepTree ,
2025-12-18 06:45:23 +09:00
available_inputs : & BTreeMap < String , ValueId > ,
2025-12-18 04:31:41 +09:00
) -> 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 06:45:23 +09:00
// Phase 122-126: Generate Normalized JoinModule
Self ::lower_if_only_to_normalized ( step_tree , available_inputs )
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 06:45:23 +09:00
/// Lower if-only StepTree to Normalized JoinModule (Phase 122-126)
2025-12-18 04:50:32 +09:00
///
/// ## Design
///
2025-12-18 06:30:55 +09:00
/// - env レイアウト: writes + inputs( 決定的順序)
2025-12-18 04:50:32 +09:00
/// - merge 形式: `join_k(env)` への tail-call( PHI 禁止)
/// - 対応ノード: If/Return/Assign(最小セット)
///
2025-12-18 06:45:23 +09:00
/// ## Phase 123-126 Node Support
2025-12-18 05:37:13 +09:00
///
/// - Return(Integer literal): `Const + Ret(Some(vid))`
2025-12-18 06:30:55 +09:00
/// - Return(Variable from writes): Phase 124
2025-12-18 06:45:23 +09:00
/// - Return(Variable from inputs): Phase 125-126 (reads-only)
2025-12-18 05:37:13 +09:00
/// - Return(void): `Ret(None)`
2025-12-18 05:50:09 +09:00
/// - If(minimal compare): Compare with Integer literal only
2025-12-18 05:37:13 +09:00
///
2025-12-18 06:45:23 +09:00
/// ## Phase 126: available_inputs
///
/// - available_inputs: function params + CapturedEnv (SSOT)
/// - inputs = reads ∩ available_inputs (deterministic order)
///
2025-12-18 04:50:32 +09:00
/// ## Returns
///
2025-12-18 05:50:09 +09:00
/// - `Ok(Some((module, meta)))`: Normalized JoinModule生成成功
2025-12-18 06:45:23 +09:00
/// - `Ok(None)`: Out of scope for Phase 123-126 (unsupported patterns)
2025-12-18 04:50:32 +09:00
/// - `Err(msg)`: 生成できるはずなのに失敗(内部エラー)
fn lower_if_only_to_normalized (
step_tree : & StepTree ,
2025-12-18 06:45:23 +09:00
available_inputs : & BTreeMap < String , ValueId > ,
2025-12-18 05:50:09 +09:00
) -> Result < Option < ( JoinModule , JoinFragmentMeta ) > , String > {
2025-12-18 04:50:32 +09:00
use crate ::mir ::join_ir ::{ JoinFunction , JoinFuncId , JoinInst } ;
use crate ::mir ::ValueId ;
2025-12-18 06:30:55 +09:00
2025-12-18 06:45:23 +09:00
// Phase 126: EnvLayout 生成( available_inputs を使用)
let env_layout = EnvLayout ::from_contract ( & step_tree . contract , available_inputs ) ;
2025-12-18 06:30:55 +09:00
2025-12-18 04:50:32 +09:00
let main_func_id = JoinFuncId ::new ( 0 ) ;
2025-12-18 06:30:55 +09:00
// Phase 125 P2: writes 用の ValueId 生成
2025-12-18 04:50:32 +09:00
let mut next_value_id = 1 ;
2025-12-18 06:30:55 +09:00
let writes_params : Vec < ValueId > = env_layout
. writes
2025-12-18 04:50:32 +09:00
. iter ( )
. map ( | _ | {
let vid = ValueId ( next_value_id ) ;
next_value_id + = 1 ;
vid
} )
. collect ( ) ;
2025-12-18 06:30:55 +09:00
// Phase 125 P2: inputs 用の ValueId 生成( 今は空だが、P3 で available_inputs から参照)
let inputs_params : Vec < ValueId > = env_layout
. inputs
2025-12-18 06:05:18 +09:00
. iter ( )
2025-12-18 06:30:55 +09:00
. map ( | name | {
// Phase 125 P3: available_inputs から取得
// 今は空なので到達しないはずだが、念のため placeholder
available_inputs
. get ( name )
. copied ( )
. unwrap_or_else ( | | {
// Should not reach here (inputs は available_inputs にある前提)
ValueId ( 0 ) // placeholder
} )
} )
2025-12-18 06:05:18 +09:00
. collect ( ) ;
2025-12-18 06:30:55 +09:00
// Phase 125 P2: env マッピング( writes + inputs)
let mut env : BTreeMap < String , ValueId > = BTreeMap ::new ( ) ;
for ( name , vid ) in env_layout . writes . iter ( ) . zip ( writes_params . iter ( ) ) {
env . insert ( name . clone ( ) , * vid ) ;
}
for ( name , vid ) in env_layout . inputs . iter ( ) . zip ( inputs_params . iter ( ) ) {
env . insert ( name . clone ( ) , * vid ) ;
}
// Phase 125 P2: 関数パラメータは writes のみ( inputs は外側から来る前提)
// TODO P3: inputs も params に含める必要があるか検討
let env_params = writes_params ;
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 06:30:55 +09:00
// Phase 123-125: Return node lowering
// If Phase 123-125 patterns are not supported, return Ok(None)
2025-12-18 05:50:09 +09:00
match Self ::lower_return_from_tree (
2025-12-18 05:37:13 +09:00
& step_tree . root ,
& mut main_func . body ,
& mut next_value_id ,
2025-12-18 06:05:18 +09:00
& env ,
& step_tree . contract ,
2025-12-18 05:50:09 +09:00
) {
Ok ( ( ) ) = > {
// Success - continue
}
2025-12-18 06:30:55 +09:00
Err ( msg )
if msg . starts_with ( " [phase123/ " )
| | msg . starts_with ( " [phase124/ " )
| | msg . starts_with ( " [phase125/ " ) = >
{
// Phase 123-125 limitation - out of scope
2025-12-18 05:50:09 +09:00
return Ok ( None ) ;
}
Err ( msg ) = > {
// Real error - propagate
return Err ( msg ) ;
}
}
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 ( ) ;
2025-12-18 05:50:09 +09:00
Ok ( Some ( ( module , meta ) ) )
2025-12-18 04:50:32 +09:00
}
2025-12-18 06:05:18 +09:00
/// Phase 123-124 P1-P3: Lower node from StepTree
2025-12-18 05:37:13 +09:00
///
2025-12-18 06:05:18 +09:00
/// ## Support (Phase 123-124)
2025-12-18 05:37:13 +09:00
///
/// - Return(Integer literal): Generate Const + Ret(Some(vid))
/// - Return(void): Ret(None)
2025-12-18 06:05:18 +09:00
/// - Return(Variable): Phase 124 - lookup from env (dev-only)
2025-12-18 05:37:13 +09:00
/// - Return(other): Fail-Fast with structured error
2025-12-18 05:50:09 +09:00
/// - If(minimal compare): Generate Compare + Branch + Ret (P3)
2025-12-18 05:37:13 +09:00
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 ,
2025-12-18 06:05:18 +09:00
env : & std ::collections ::BTreeMap < String , crate ::mir ::ValueId > ,
contract : & crate ::mir ::control_tree ::step_tree_contract_box ::StepTreeContract ,
2025-12-18 05:37:13 +09:00
) -> 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 ) = > {
2025-12-18 05:50:09 +09:00
// Process nodes in order
2025-12-18 05:37:13 +09:00
for n in nodes {
2025-12-18 05:50:09 +09:00
match n {
StepNode ::Stmt {
kind : StepStmtKind ::Return { value_ast } ,
..
} = > {
2025-12-18 06:05:18 +09:00
return Self ::lower_return_value ( value_ast , body , next_value_id , env , contract ) ;
2025-12-18 05:50:09 +09:00
}
StepNode ::If { .. } = > {
// Phase 123 P3: Lower If node
2025-12-18 06:05:18 +09:00
return Self ::lower_if_node ( n , body , next_value_id , env , contract ) ;
2025-12-18 05:50:09 +09:00
}
_ = > {
// Other nodes not yet supported
}
2025-12-18 05:37:13 +09:00
}
}
// No return found - default to void
body . push ( JoinInst ::Ret { value : None } ) ;
Ok ( ( ) )
}
StepNode ::Stmt {
kind : StepStmtKind ::Return { value_ast } ,
..
2025-12-18 06:05:18 +09:00
} = > Self ::lower_return_value ( value_ast , body , next_value_id , env , contract ) ,
2025-12-18 05:50:09 +09:00
StepNode ::If { .. } = > {
// Phase 123 P3: Lower If node
2025-12-18 06:05:18 +09:00
Self ::lower_if_node ( node , body , next_value_id , env , contract )
2025-12-18 05:50:09 +09:00
}
2025-12-18 05:37:13 +09:00
_ = > {
// No return in tree - default to void
body . push ( JoinInst ::Ret { value : None } ) ;
Ok ( ( ) )
}
}
}
2025-12-18 06:05:18 +09:00
/// Phase 123-124 P3: Lower If node with minimal compare
2025-12-18 05:50:09 +09:00
///
/// ## Support
///
/// - Minimal binary comparison: Variable vs Integer literal
2025-12-18 06:05:18 +09:00
/// - then/else: Return(Integer literal) or Return(Variable) (Phase 124)
2025-12-18 05:50:09 +09:00
/// - Merge: Not yet implemented (will use join_k tail-call in future)
///
/// ## Not Supported (Fail-Fast)
///
/// - Compound expressions (&&, ||)
/// - Method calls
/// - Complex expressions
/// - Non-return statements in branches
fn lower_if_node (
node : & crate ::mir ::control_tree ::step_tree ::StepNode ,
body : & mut Vec < crate ::mir ::join_ir ::JoinInst > ,
next_value_id : & mut u32 ,
2025-12-18 06:05:18 +09:00
env : & std ::collections ::BTreeMap < String , crate ::mir ::ValueId > ,
contract : & crate ::mir ::control_tree ::step_tree_contract_box ::StepTreeContract ,
2025-12-18 05:50:09 +09:00
) -> Result < ( ) , String > {
use crate ::ast ::{ ASTNode , BinaryOperator } ;
use crate ::mir ::control_tree ::step_tree ::StepNode ;
use crate ::mir ::join_ir ::{ CompareOp , ConstValue , JoinInst , MirLikeInst } ;
use crate ::mir ::ValueId ;
if let StepNode ::If {
cond_ast ,
then_branch ,
else_branch ,
..
} = node
{
let ast = & cond_ast . 0 ;
// Phase 123 P3: Parse minimal binary comparison only
let ( _lhs_var , op , rhs_literal ) = Self ::parse_minimal_compare ( ast ) ? ;
// Generate Compare instruction
// 1. Load/create lhs variable (for now, assume it's a parameter)
// For Phase 123 minimal: we'll just create a load instruction placeholder
// This is a simplification - real implementation would need variable resolution
let lhs_vid = ValueId ( * next_value_id ) ;
* next_value_id + = 1 ;
// For now, emit a const for the variable (placeholder)
// Real implementation in Phase 124 will use reads facts
body . push ( JoinInst ::Compute ( MirLikeInst ::Const {
dst : lhs_vid ,
value : ConstValue ::Integer ( 0 ) , // Placeholder
} ) ) ;
// 2. Create constant for rhs literal
let rhs_vid = ValueId ( * next_value_id ) ;
* next_value_id + = 1 ;
body . push ( JoinInst ::Compute ( MirLikeInst ::Const {
dst : rhs_vid ,
value : ConstValue ::Integer ( rhs_literal ) ,
} ) ) ;
// 3. Generate Compare instruction
let cond_vid = ValueId ( * next_value_id ) ;
* next_value_id + = 1 ;
body . push ( JoinInst ::Compute ( MirLikeInst ::Compare {
dst : cond_vid ,
op ,
lhs : lhs_vid ,
rhs : rhs_vid ,
} ) ) ;
// Phase 123 P3: Verify then/else branches contain only Return(Integer literal)
Self ::verify_branch_is_return_literal ( then_branch ) ? ;
if let Some ( else_br ) = else_branch {
Self ::verify_branch_is_return_literal ( else_br ) ? ;
}
2025-12-18 06:05:18 +09:00
// For Phase 123-124, we generate a simplified structure:
2025-12-18 05:50:09 +09:00
// The actual branching logic will be added in future phases
// For now, just emit the then branch return
2025-12-18 06:05:18 +09:00
Self ::lower_return_from_tree ( then_branch , body , next_value_id , env , contract ) ? ;
2025-12-18 05:50:09 +09:00
Ok ( ( ) )
} else {
Err ( " [phase123/if/internal] Expected If node " . to_string ( ) )
}
}
/// Parse minimal binary comparison: Variable op Integer
///
/// Returns: (variable_name, compare_op, integer_value)
fn parse_minimal_compare (
ast : & crate ::ast ::ASTNode ,
) -> Result < ( String , crate ::mir ::join_ir ::CompareOp , i64 ) , String > {
use crate ::ast ::{ ASTNode , BinaryOperator , LiteralValue } ;
use crate ::mir ::join_ir ::CompareOp ;
match ast {
ASTNode ::BinaryOp {
operator ,
left ,
right ,
..
} = > {
// Phase 123: Only support Variable on left, Integer literal on right
let var_name = match & * * left {
ASTNode ::Variable { name , .. } = > name . clone ( ) ,
_ = > {
return Err ( format! (
" [phase123/if/compare_lhs_unsupported] Phase 123 only supports Variable on left side of comparison. Hint: Use simple variable comparison or wait for Phase 124 "
) ) ;
}
} ;
let int_value = match & * * right {
ASTNode ::Literal {
value : LiteralValue ::Integer ( i ) ,
..
} = > * i ,
_ = > {
return Err ( format! (
" [phase123/if/compare_rhs_unsupported] Phase 123 only supports Integer literal on right side of comparison. Hint: Use integer literal or wait for Phase 124 "
) ) ;
}
} ;
let compare_op = match operator {
BinaryOperator ::Equal = > CompareOp ::Eq ,
BinaryOperator ::NotEqual = > CompareOp ::Ne ,
BinaryOperator ::Less = > CompareOp ::Lt ,
BinaryOperator ::LessEqual = > CompareOp ::Le ,
BinaryOperator ::Greater = > CompareOp ::Gt ,
BinaryOperator ::GreaterEqual = > CompareOp ::Ge ,
_ = > {
return Err ( format! (
" [phase123/if/compare_op_unsupported] Phase 123 only supports comparison operators (==, !=, <, <=, >, >=). Hint: Use comparison operator or wait for Phase 124 "
) ) ;
}
} ;
Ok ( ( var_name , compare_op , int_value ) )
}
_ = > Err ( format! (
" [phase123/if/cond_unsupported] Phase 123 only supports binary comparisons. Hint: Use simple comparison (var == literal) or wait for Phase 124 "
) ) ,
}
}
/// Verify branch contains only Return(Integer literal)
fn verify_branch_is_return_literal (
branch : & crate ::mir ::control_tree ::step_tree ::StepNode ,
) -> Result < ( ) , String > {
use crate ::ast ::{ ASTNode , LiteralValue } ;
use crate ::mir ::control_tree ::step_tree ::{ StepNode , StepStmtKind } ;
match branch {
StepNode ::Stmt {
kind : StepStmtKind ::Return { value_ast } ,
..
} = > {
if let Some ( ast_handle ) = value_ast {
let ast = & ast_handle . 0 ;
if let ASTNode ::Literal {
value : LiteralValue ::Integer ( _ ) ,
..
} = & * * ast
{
Ok ( ( ) )
} else {
Err ( format! (
" [phase123/if/branch_return_not_int_literal] Phase 123 only supports Return(Integer literal) in then/else branches. Hint: Return integer literal only or wait for Phase 124 "
) )
}
} else {
Err ( format! (
" [phase123/if/branch_return_void] Phase 123 requires Return(Integer literal) in branches, not void return. Hint: Return integer literal or wait for Phase 124 "
) )
}
}
StepNode ::Block ( nodes ) = > {
// Check first node only
if nodes . is_empty ( ) {
return Err ( format! (
" [phase123/if/branch_empty] Phase 123 requires Return(Integer literal) in branches. Hint: Add return statement "
) ) ;
}
Self ::verify_branch_is_return_literal ( & nodes [ 0 ] )
}
_ = > Err ( format! (
" [phase123/if/branch_not_return] Phase 123 only supports Return(Integer literal) in then/else branches. Hint: Use return statement with integer literal "
) ) ,
}
}
2025-12-18 06:30:55 +09:00
/// Phase 123-125 P1-P2-P3-P4: Lower return value
2025-12-18 05:37:13 +09:00
///
/// ## Support
///
/// - Integer literal: Generate Const + Ret(Some(vid))
/// - None: Ret(None)
2025-12-18 06:30:55 +09:00
/// - Variable (Phase 124-125): lookup from env (writes + inputs)
/// - Phase 124: writes (written variables)
/// - Phase 125: inputs (reads-only from outer scope)
/// - Fail-Fast if not in env (structured error with hint)
2025-12-18 05:37:13 +09:00
/// - 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 ,
2025-12-18 06:05:18 +09:00
env : & std ::collections ::BTreeMap < String , crate ::mir ::ValueId > ,
contract : & crate ::mir ::control_tree ::step_tree_contract_box ::StepTreeContract ,
2025-12-18 05:37:13 +09:00
) -> 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 , .. } = > {
2025-12-18 06:30:55 +09:00
// Phase 124-125 P3-P4: Variable return support (dev-only)
// Check if variable is in env (writes + inputs)
2025-12-18 06:05:18 +09:00
if let Some ( & vid ) = env . get ( name ) {
2025-12-18 06:30:55 +09:00
// Phase 125 P4: Variable found in env (writes or inputs) - return it
2025-12-18 06:05:18 +09:00
body . push ( JoinInst ::Ret { value : Some ( vid ) } ) ;
Ok ( ( ) )
} else {
2025-12-18 06:30:55 +09:00
// Phase 125 P4: Variable not in env - Fail-Fast with hint
// Check if variable is in reads (potential input)
let in_reads = contract . reads . contains ( name ) ;
let in_writes = contract . writes . contains ( name ) ;
let hint = if in_reads & & ! in_writes {
// Variable is read but not available as input
format! (
" Variable '{}' is read but not available from outer scope. \
Hint : Pass as function parameter , add to pinned capture , or define before if " ,
name
)
} else if ! in_reads & & ! in_writes {
// Variable is neither read nor written (undefined)
format! (
" Variable '{}' is undefined. \
Hint : Define variable before return or check spelling " ,
name
)
} else {
// In writes but not in env (internal error)
format! (
" Variable '{}' in writes but not in env (internal error) " ,
name
)
} ;
Err ( format! ( " [phase125/return/var_not_in_env] {} " , hint ) )
2025-12-18 06:05:18 +09:00
}
2025-12-18 05:37:13 +09:00
}
_ = > {
// 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 ( ) ,
2025-12-18 06:00:21 +09:00
reads : Default ::default ( ) , // Phase 124
2025-12-18 04:31:41 +09:00
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 ( ) ,
2025-12-18 06:00:21 +09:00
reads : Default ::default ( ) , // Phase 124
2025-12-18 04:31:41 +09:00
required_caps : Default ::default ( ) ,
cond_sig : Default ::default ( ) ,
} ,
signature : StepTreeSignature ( 0 ) ,
}
}
#[ test ]
fn test_if_only_supported ( ) {
let tree = make_if_only_tree ( ) ;
2025-12-18 06:45:23 +09:00
let available_inputs = BTreeMap ::new ( ) ; // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox ::try_lower_if_only ( & tree , & available_inputs ) ;
2025-12-18 04:31:41 +09:00
assert! ( result . is_ok ( ) ) ;
assert! ( result . unwrap ( ) . is_some ( ) ) ;
}
#[ test ]
fn test_loop_rejected ( ) {
let tree = make_loop_tree ( ) ;
2025-12-18 06:45:23 +09:00
let available_inputs = BTreeMap ::new ( ) ; // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox ::try_lower_if_only ( & tree , & available_inputs ) ;
2025-12-18 04:31:41 +09:00
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
2025-12-18 06:45:23 +09:00
let available_inputs = BTreeMap ::new ( ) ; // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox ::try_lower_if_only ( & tree , & available_inputs ) ;
2025-12-18 05:37:13 +09:00
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
2025-12-18 06:45:23 +09:00
let available_inputs = BTreeMap ::new ( ) ; // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox ::try_lower_if_only ( & tree , & available_inputs ) ;
2025-12-18 05:37:13 +09:00
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 ]
2025-12-18 05:50:09 +09:00
fn test_return_variable_out_of_scope ( ) {
2025-12-18 06:30:55 +09:00
// Phase 125 P4: Test Return(Variable) when variable is not in env (neither writes nor inputs)
// Expected: Ok(None) because variable is not available
2025-12-18 05:37:13 +09:00
use crate ::ast ::{ ASTNode , Span } ;
use crate ::mir ::control_tree ::step_tree ::AstNodeHandle ;
2025-12-18 06:30:55 +09:00
// Create StepTree with "return x" (variable not in env)
2025-12-18 05:37:13 +09:00
let return_ast = Box ::new ( ASTNode ::Variable {
name : " x " . to_string ( ) ,
span : Span ::unknown ( ) ,
} ) ;
let mut tree = make_if_only_tree ( ) ;
2025-12-18 06:30:55 +09:00
// Phase 125: Add x to reads (to simulate variable reference)
tree . contract . reads . insert ( " x " . to_string ( ) ) ;
2025-12-18 05:37:13 +09:00
tree . root = StepNode ::Stmt {
kind : StepStmtKind ::Return {
value_ast : Some ( AstNodeHandle ( return_ast ) ) ,
} ,
span : Span ::unknown ( ) ,
} ;
2025-12-18 06:30:55 +09:00
// Phase 125 P4: Lower to JoinModule - should return Ok(None)
// because x is in reads but not in available_inputs (not in env)
2025-12-18 06:45:23 +09:00
let available_inputs = BTreeMap ::new ( ) ; // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox ::try_lower_if_only ( & tree , & available_inputs ) ;
2025-12-18 05:50:09 +09:00
assert! ( result . is_ok ( ) ) ;
2025-12-18 06:30:55 +09:00
let option = result . unwrap ( ) ;
assert! ( option . is_none ( ) , " Should return None when variable is in reads but not available as input " ) ;
2025-12-18 05:50:09 +09:00
}
#[ test ]
fn test_if_minimal_compare ( ) {
use crate ::ast ::{ ASTNode , BinaryOperator , LiteralValue , Span } ;
use crate ::mir ::control_tree ::step_tree ::AstNodeHandle ;
// Create condition: flag == 1
let cond_ast = Box ::new ( ASTNode ::BinaryOp {
operator : BinaryOperator ::Equal ,
left : Box ::new ( ASTNode ::Variable {
name : " flag " . to_string ( ) ,
span : Span ::unknown ( ) ,
} ) ,
right : Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Integer ( 1 ) ,
span : Span ::unknown ( ) ,
} ) ,
span : Span ::unknown ( ) ,
} ) ;
// Create then branch: return 2
let then_branch = Box ::new ( StepNode ::Stmt {
kind : StepStmtKind ::Return {
value_ast : Some ( AstNodeHandle ( Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Integer ( 2 ) ,
span : Span ::unknown ( ) ,
} ) ) ) ,
} ,
span : Span ::unknown ( ) ,
} ) ;
// Create else branch: return 3
let else_branch = Some ( Box ::new ( StepNode ::Stmt {
kind : StepStmtKind ::Return {
value_ast : Some ( AstNodeHandle ( Box ::new ( ASTNode ::Literal {
value : LiteralValue ::Integer ( 3 ) ,
span : Span ::unknown ( ) ,
} ) ) ) ,
} ,
span : Span ::unknown ( ) ,
} ) ) ;
// Create If node
let mut tree = make_if_only_tree ( ) ;
tree . root = StepNode ::If {
cond : crate ::mir ::control_tree ::step_tree ::AstSummary ::Other ( " test " ) ,
cond_ast : AstNodeHandle ( cond_ast ) ,
then_branch ,
else_branch ,
span : Span ::unknown ( ) ,
} ;
// Lower to JoinModule
2025-12-18 06:45:23 +09:00
let available_inputs = BTreeMap ::new ( ) ; // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox ::try_lower_if_only ( & tree , & available_inputs ) ;
2025-12-18 05:50:09 +09:00
assert! ( result . is_ok ( ) ) ;
let ( module , _meta ) = result . unwrap ( ) . expect ( " Should generate JoinModule " ) ;
// Verify structure
assert_eq! ( module . functions . len ( ) , 1 ) ;
let func = & module . functions . values ( ) . next ( ) . unwrap ( ) ;
// Should have: Const (lhs placeholder), Const (rhs), Compare, Const (return), Ret
assert! ( func . body . len ( ) > = 4 , " Should have at least 4 instructions " ) ;
2025-12-18 05:37:13 +09:00
2025-12-18 05:50:09 +09:00
// Verify Compare instruction exists
use crate ::mir ::join_ir ::{ JoinInst , MirLikeInst } ;
let has_compare = func . body . iter ( ) . any ( | inst | {
matches! (
inst ,
JoinInst ::Compute ( MirLikeInst ::Compare { .. } )
)
} ) ;
assert! ( has_compare , " Should have Compare instruction " ) ;
2025-12-18 05:37:13 +09:00
}
2025-12-18 06:05:18 +09:00
#[ 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
2025-12-18 06:45:23 +09:00
let available_inputs = BTreeMap ::new ( ) ; // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox ::try_lower_if_only ( & tree , & available_inputs ) ;
2025-12-18 06:05:18 +09:00
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 " ) ;
}
}
2025-12-18 06:30:55 +09:00
#[ test ]
fn test_return_variable_from_inputs_stub ( ) {
// Phase 125 P2-P4: Test Return(Variable) when variable is in inputs (reads-only)
// Note: P3 (available_inputs wiring) is not implemented yet, so this test
// demonstrates the structure but won't actually provide inputs
use crate ::ast ::{ ASTNode , Span } ;
use crate ::mir ::control_tree ::step_tree ::AstNodeHandle ;
// Create StepTree with "return x" where x is read-only input
let mut tree = make_if_only_tree ( ) ;
// Phase 125 P2: Add x to reads (simulating outer scope variable read)
tree . contract . reads . 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 ( ) ,
} ;
// Phase 125 P2-P4: Lower to JoinModule
// Because available_inputs is empty (P3 not wired), x won't be in inputs
// So this should return Ok(None) (out of scope)
2025-12-18 06:45:23 +09:00
let available_inputs = BTreeMap ::new ( ) ; // Phase 126: empty for unit tests
let result = StepTreeNormalizedShadowLowererBox ::try_lower_if_only ( & tree , & available_inputs ) ;
2025-12-18 06:30:55 +09:00
assert! ( result . is_ok ( ) ) ;
let option = result . unwrap ( ) ;
// Phase 125 P2: EnvLayout.inputs will be empty (no available_inputs)
// So x is not in env, and we get Ok(None)
assert! ( option . is_none ( ) , " Should return None when x is in reads but not in available_inputs (P3 not wired yet) " ) ;
}
2025-12-18 04:31:41 +09:00
}