//! Phase 33-3: If/Else → Select lowering integration tests //! //! Tests the pattern matching and lowering of if/else to JoinIR Select instruction. #[cfg(test)] mod tests { use crate::mir::join_ir::lowering::try_lower_if_to_joinir; use crate::mir::join_ir::JoinInst; use crate::mir::{ BasicBlock, BasicBlockId, MirFunction, MirInstruction, MirModule, ValueId, }; use std::collections::BTreeMap; /// Helper to create a simple if/else function matching the "simple" pattern fn create_simple_pattern_mir() -> MirFunction { let mut blocks = BTreeMap::new(); // Entry block (bb0): branch on cond let mut entry = BasicBlock::new(BasicBlockId::new(0)); entry.terminator = Some(MirInstruction::Branch { condition: ValueId(0), // cond parameter then_bb: BasicBlockId::new(1), else_bb: BasicBlockId::new(2), }); blocks.insert(BasicBlockId::new(0), entry); // Then block (bb1): return 10 // NOTE: Pattern matcher expects empty blocks (Return only) let mut then_block = BasicBlock::new(BasicBlockId::new(1)); then_block.terminator = Some(MirInstruction::Return { value: Some(ValueId(1)), // Assumes ValueId(1) is const 10 }); blocks.insert(BasicBlockId::new(1), then_block); // Else block (bb2): return 20 // NOTE: Pattern matcher expects empty blocks (Return only) let mut else_block = BasicBlock::new(BasicBlockId::new(2)); else_block.terminator = Some(MirInstruction::Return { value: Some(ValueId(2)), // Assumes ValueId(2) is const 20 }); blocks.insert(BasicBlockId::new(2), else_block); use crate::mir::{EffectMask, MirType}; use crate::mir::function::FunctionMetadata; use std::collections::HashMap; MirFunction { signature: crate::mir::FunctionSignature { name: "IfSelectTest.test/1".to_string(), params: vec![MirType::Unknown], return_type: MirType::Integer, effects: EffectMask::PURE, }, entry_block: BasicBlockId::new(0), blocks: blocks.into_iter().collect(), locals: vec![], params: vec![ValueId(0)], next_value_id: 3, metadata: FunctionMetadata::default(), } } /// Helper to create a local pattern function fn create_local_pattern_mir() -> MirFunction { let mut blocks = BTreeMap::new(); // Entry block (bb0): branch on cond let mut entry = BasicBlock::new(BasicBlockId::new(0)); entry.terminator = Some(MirInstruction::Branch { condition: ValueId(0), // cond then_bb: BasicBlockId::new(1), else_bb: BasicBlockId::new(2), }); blocks.insert(BasicBlockId::new(0), entry); // Then block (bb1): x = 100; jump merge // NOTE: Pattern matcher expects exactly 1 Copy instruction let mut then_block = BasicBlock::new(BasicBlockId::new(1)); then_block.instructions.push(MirInstruction::Copy { dst: ValueId(3), // x src: ValueId(10), // Assumes ValueId(10) is const 100 }); then_block.terminator = Some(MirInstruction::Jump { target: BasicBlockId::new(3), }); blocks.insert(BasicBlockId::new(1), then_block); // Else block (bb2): x = 200; jump merge // NOTE: Pattern matcher expects exactly 1 Copy instruction let mut else_block = BasicBlock::new(BasicBlockId::new(2)); else_block.instructions.push(MirInstruction::Copy { dst: ValueId(3), // x src: ValueId(20), // Assumes ValueId(20) is const 200 }); else_block.terminator = Some(MirInstruction::Jump { target: BasicBlockId::new(3), }); blocks.insert(BasicBlockId::new(2), else_block); // Merge block (bb3): return x let mut merge_block = BasicBlock::new(BasicBlockId::new(3)); merge_block.terminator = Some(MirInstruction::Return { value: Some(ValueId(3)), }); blocks.insert(BasicBlockId::new(3), merge_block); use crate::mir::{EffectMask, MirType}; use crate::mir::function::FunctionMetadata; use std::collections::HashMap; MirFunction { signature: crate::mir::FunctionSignature { name: "IfSelectTest.main/0".to_string(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE, }, entry_block: BasicBlockId::new(0), blocks: blocks.into_iter().collect(), locals: vec![], params: vec![], next_value_id: 21, metadata: FunctionMetadata::default(), } } /// Phase 33-3: 統合パターンマッチングテスト(env 競合回避) /// /// env 変数を触る4つのテストを1つにまとめて、並列実行でのレースを防ぐ。 /// 順番に: simple/local/disabled/wrong_name を確認する。 #[test] fn test_if_select_pattern_matching() { // ==== 1. Simple pattern (env ON) ==== std::env::set_var("NYASH_JOINIR_IF_SELECT", "1"); let func = create_simple_pattern_mir(); let entry_block = func.entry_block; let result = try_lower_if_to_joinir(&func, entry_block, true); assert!( result.is_some(), "Expected simple pattern to be lowered to Select" ); if let Some(JoinInst::Select { dst, cond, then_val, else_val, }) = result { eprintln!("✅ Simple pattern successfully lowered to Select"); eprintln!(" dst: {:?}, cond: {:?}, then: {:?}, else: {:?}", dst, cond, then_val, else_val); } else { panic!("Expected JoinInst::Select, got {:?}", result); } // ==== 2. Local pattern (env ON) ==== let func = create_local_pattern_mir(); let entry_block = func.entry_block; let result = try_lower_if_to_joinir(&func, entry_block, true); assert!( result.is_some(), "Expected local pattern to be lowered to Select" ); if let Some(JoinInst::Select { dst, cond, then_val, else_val, }) = result { eprintln!("✅ Local pattern successfully lowered to Select"); eprintln!(" dst: {:?}, cond: {:?}, then: {:?}, else: {:?}", dst, cond, then_val, else_val); } else { panic!("Expected JoinInst::Select, got {:?}", result); } // ==== 3. Disabled by default (env OFF) ==== std::env::remove_var("NYASH_JOINIR_IF_SELECT"); let func = create_simple_pattern_mir(); let entry_block = func.entry_block; let result = try_lower_if_to_joinir(&func, entry_block, false); assert!( result.is_none(), "Expected None when NYASH_JOINIR_IF_SELECT is not set" ); eprintln!("✅ If/Select lowering correctly disabled by default"); // ==== 4. Wrong function name (env ON) ==== std::env::set_var("NYASH_JOINIR_IF_SELECT", "1"); let mut func = create_simple_pattern_mir(); func.signature.name = "WrongName.test/1".to_string(); let entry_block = func.entry_block; let result = try_lower_if_to_joinir(&func, entry_block, true); assert!( result.is_none(), "Expected None for non-IfSelectTest functions" ); eprintln!("✅ Function name filter working correctly"); // Clean up std::env::remove_var("NYASH_JOINIR_IF_SELECT"); } // ============================================================================ // Phase 33-3.2: Select verification tests // ============================================================================ /// Helper to create a JoinFunction with a valid Select instruction fn create_select_joinir() -> crate::mir::join_ir::JoinFunction { use crate::mir::join_ir::{ConstValue, JoinFuncId, JoinFunction, JoinInst, MirLikeInst}; let func_id = JoinFuncId::new(0); let mut join_func = JoinFunction::new( func_id, "IfSelectTest.test/1".to_string(), vec![ValueId(0)], // cond parameter ); // Create constants for then/else values join_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: ValueId(1), value: ConstValue::Integer(10), })); join_func.body.push(JoinInst::Compute(MirLikeInst::Const { dst: ValueId(2), value: ConstValue::Integer(20), })); // Select instruction join_func.body.push(JoinInst::Select { dst: ValueId(3), cond: ValueId(0), then_val: ValueId(1), else_val: ValueId(2), }); // Return result join_func.body.push(JoinInst::Ret { value: Some(ValueId(3)), }); join_func } /// Helper to create a JoinFunction with multiple Select instructions (invalid) fn create_double_select_joinir() -> crate::mir::join_ir::JoinFunction { use crate::mir::join_ir::{ConstValue, JoinFuncId, JoinFunction, JoinInst, MirLikeInst}; let func_id = JoinFuncId::new(0); let mut join_func = JoinFunction::new( func_id, "IfSelectTest.test/1".to_string(), vec![ValueId(0)], ); // First Select join_func.body.push(JoinInst::Select { dst: ValueId(1), cond: ValueId(0), then_val: ValueId(10), else_val: ValueId(20), }); // Second Select (violates single PHI invariant) join_func.body.push(JoinInst::Select { dst: ValueId(2), cond: ValueId(0), then_val: ValueId(30), else_val: ValueId(40), }); join_func.body.push(JoinInst::Ret { value: Some(ValueId(1)), }); join_func } #[test] fn test_if_select_simple_with_verify() { use crate::mir::join_ir::verify::verify_select_minimal; // Create simple pattern JoinIR let join_func = create_select_joinir(); // Verifier should pass let result = verify_select_minimal(&join_func, true); assert!( result.is_ok(), "Verify should pass for simple pattern: {:?}", result ); eprintln!("✅ verify_select_minimal passed for simple pattern"); } #[test] fn test_if_select_local_with_verify() { use crate::mir::join_ir::verify::verify_select_minimal; // Create local pattern JoinIR let join_func = create_select_joinir(); // Verifier should pass let result = verify_select_minimal(&join_func, true); assert!( result.is_ok(), "Verify should pass for local pattern: {:?}", result ); eprintln!("✅ verify_select_minimal passed for local pattern"); } #[test] fn test_if_select_verify_rejects_multiple_selects() { use crate::mir::join_ir::verify::verify_select_minimal; // Create JoinIR with 2 Select instructions (invalid) let join_func = create_double_select_joinir(); // Verifier should reject let result = verify_select_minimal(&join_func, true); assert!( result.is_err(), "Verify should reject multiple Selects" ); match result { Err(e) => { let msg = e.to_string(); assert!( msg.contains("expected exactly 1 Select, found 2"), "Error message should mention multiple Selects: {}", msg ); assert!( msg.contains("single PHI"), "Error message should reference single PHI invariant: {}", msg ); eprintln!("✅ verify_select_minimal correctly rejected multiple Selects"); } Ok(_) => panic!("Expected Err, got Ok"), } } #[test] fn test_if_select_verify_checks_invariants() { use crate::mir::join_ir::verify::verify_select_minimal; // Create valid JoinIR let join_func = create_select_joinir(); // Capture debug output let result = verify_select_minimal(&join_func, true); assert!(result.is_ok(), "Verification should pass"); // The debug output (visible with --nocapture) should mention: // - "Invariants verified" // - "single PHI (from conservative.rs)" // - "completeness (from phi_invariants.rs)" eprintln!("✅ verify_select_minimal properly checks invariants from phi_invariants.rs and conservative.rs"); } }