/*! * Nyash MIR (Mid-level Intermediate Representation) - Stage 1 Implementation * * ChatGPT5-designed MIR infrastructure for native compilation support * Based on SSA form with effect tracking and Box-aware optimizations */ pub mod instruction; pub mod instruction_v2; // New 25-instruction specification pub mod instruction_introspection; // Introspection helpers for tests (core instruction names) pub mod basic_block; pub mod function; pub mod builder; pub mod loop_builder; // SSA loop construction with phi nodes pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready) pub mod verification; pub mod ownership_verifier_simple; // Simple ownership forest verification for current MIR pub mod printer; pub mod value_id; pub mod effect; pub mod optimizer; pub mod slot_registry; // Phase 9.79b.1: method slot resolution (IDs) pub mod passes; // Optimization subpasses (e.g., type_hints) // Re-export main types for easy access pub use instruction::{MirInstruction, BinaryOp, CompareOp, UnaryOp, ConstValue, MirType, TypeOpKind, WeakRefOp, BarrierOp}; pub use instruction_v2::{MirInstructionV2, AtomicOrdering}; // New 25-instruction set pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator}; pub use function::{MirFunction, MirModule, FunctionSignature}; pub use builder::MirBuilder; pub use verification::{MirVerifier, VerificationError}; pub use ownership_verifier_simple::{OwnershipVerifier, OwnershipError, OwnershipStats}; // Simple ownership forest verification pub use printer::MirPrinter; pub use value_id::{ValueId, LocalId, ValueIdGenerator}; pub use effect::{EffectMask, Effect}; pub use optimizer::MirOptimizer; pub use slot_registry::{BoxTypeId, MethodSlot}; /// MIR compilation result #[derive(Debug, Clone)] pub struct MirCompileResult { pub module: MirModule, pub verification_result: Result<(), Vec>, } /// MIR compiler - converts AST to MIR/SSA form pub struct MirCompiler { builder: MirBuilder, verifier: MirVerifier, optimize: bool, } impl MirCompiler { /// Create a new MIR compiler pub fn new() -> Self { Self { builder: MirBuilder::new(), verifier: MirVerifier::new(), optimize: true, } } /// Create with options pub fn with_options(optimize: bool) -> Self { Self { builder: MirBuilder::new(), verifier: MirVerifier::new(), optimize, } } /// Compile AST to MIR module with verification pub fn compile(&mut self, ast: crate::ast::ASTNode) -> Result { // Convert AST to MIR using builder let mut module = self.builder.build_module(ast)?; if self.optimize { let mut optimizer = MirOptimizer::new(); let stats = optimizer.optimize_module(&mut module); if (crate::config::env::opt_diag_fail() || crate::config::env::opt_diag_forbid_legacy()) && stats.diagnostics_reported > 0 { return Err(format!("Diagnostic failure: {} issues detected (unlowered/legacy)", stats.diagnostics_reported)); } } // Verify the generated MIR let verification_result = self.verifier.verify_module(&module); Ok(MirCompileResult { module, verification_result, }) } /// Dump MIR to string for debugging pub fn dump_mir(&self, module: &MirModule) -> String { MirPrinter::new().print_module(module) } } impl Default for MirCompiler { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; use crate::ast::{ASTNode, LiteralValue}; #[test] fn test_basic_mir_compilation() { let mut compiler = MirCompiler::new(); // Create a simple literal AST node let ast = ASTNode::Literal { value: LiteralValue::Integer(42), span: crate::ast::Span::unknown() }; // Compile to MIR let result = compiler.compile(ast); assert!(result.is_ok(), "Basic MIR compilation should succeed"); let compile_result = result.unwrap(); assert!(!compile_result.module.functions.is_empty(), "Module should contain at least one function"); } #[test] fn test_mir_dump() { let mut compiler = MirCompiler::new(); let ast = ASTNode::Literal { value: LiteralValue::Integer(42), span: crate::ast::Span::unknown() }; let result = compiler.compile(ast).unwrap(); let mir_dump = compiler.dump_mir(&result.module); assert!(!mir_dump.is_empty(), "MIR dump should not be empty"); assert!(mir_dump.contains("define"), "MIR dump should contain function definition"); } #[test] fn test_lowering_is_type_function_call_in_print() { // Build AST: print(isType(42, "Integer")) let ast = ASTNode::Print { expression: Box::new(ASTNode::FunctionCall { name: "isType".to_string(), arguments: vec![ ASTNode::Literal { value: LiteralValue::Integer(42), span: crate::ast::Span::unknown() }, ASTNode::Literal { value: LiteralValue::String("Integer".to_string()), span: crate::ast::Span::unknown() }, ], span: crate::ast::Span::unknown(), }), span: crate::ast::Span::unknown(), }; let mut compiler = MirCompiler::new(); let result = compiler.compile(ast).expect("compile should succeed"); // Ensure TypeOp exists in the resulting MIR let has_typeop = result.module.functions.values().any(|f| { f.blocks.values().any(|b| b.all_instructions().any(|i| matches!(i, MirInstruction::TypeOp { .. }))) }); assert!(has_typeop, "Expected TypeOp lowering for print(isType(...))"); } #[test] fn test_lowering_is_method_call_in_print() { // Build AST: print( (42).is("Integer") ) let ast = ASTNode::Print { expression: Box::new(ASTNode::MethodCall { object: Box::new(ASTNode::Literal { value: LiteralValue::Integer(42), span: crate::ast::Span::unknown() }), method: "is".to_string(), arguments: vec![ ASTNode::Literal { value: LiteralValue::String("Integer".to_string()), span: crate::ast::Span::unknown() }, ], span: crate::ast::Span::unknown(), }), span: crate::ast::Span::unknown(), }; let mut compiler = MirCompiler::new(); let result = compiler.compile(ast).expect("compile should succeed"); // Ensure TypeOp exists in the resulting MIR let has_typeop = result.module.functions.values().any(|f| { f.blocks.values().any(|b| b.all_instructions().any(|i| matches!(i, MirInstruction::TypeOp { .. }))) }); assert!(has_typeop, "Expected TypeOp lowering for print(obj.is(...))"); } #[test] fn test_lowering_extern_console_log() { // Build AST: console.log("hi") → ExternCall env.console.log let ast = ASTNode::MethodCall { object: Box::new(ASTNode::Variable { name: "console".to_string(), span: crate::ast::Span::unknown() }), method: "log".to_string(), arguments: vec![ ASTNode::Literal { value: LiteralValue::String("hi".to_string()), span: crate::ast::Span::unknown() } ], span: crate::ast::Span::unknown(), }; let mut compiler = MirCompiler::new(); let result = compiler.compile(ast).expect("compile should succeed"); let dump = MirPrinter::verbose().print_module(&result.module); assert!(dump.contains("extern_call env.console.log"), "Expected extern_call env.console.log in MIR dump. Got:\n{}", dump); } #[test] fn test_lowering_boxcall_array_push() { // Build AST: (new ArrayBox()).push(1) let ast = ASTNode::MethodCall { object: Box::new(ASTNode::New { class: "ArrayBox".to_string(), arguments: vec![], type_arguments: vec![], span: crate::ast::Span::unknown() }), method: "push".to_string(), arguments: vec![ ASTNode::Literal { value: LiteralValue::Integer(1), span: crate::ast::Span::unknown() } ], span: crate::ast::Span::unknown(), }; let mut compiler = MirCompiler::new(); let result = compiler.compile(ast).expect("compile should succeed"); let dump = MirPrinter::new().print_module(&result.module); // Expect a BoxCall to push (printer formats as `call .(...)`) assert!(dump.contains(".push("), "Expected BoxCall to .push(...). Got:\n{}", dump); } #[test] fn test_boxcall_method_id_on_universal_slot() { // Build AST: (new ArrayBox()).toString() let ast = ASTNode::MethodCall { object: Box::new(ASTNode::New { class: "ArrayBox".to_string(), arguments: vec![], type_arguments: vec![], span: crate::ast::Span::unknown() }), method: "toString".to_string(), arguments: vec![], span: crate::ast::Span::unknown(), }; let mut compiler = MirCompiler::new(); let result = compiler.compile(ast).expect("compile should succeed"); let dump = MirPrinter::new().print_module(&result.module); // Expect a BoxCall with numeric method id [#0] for toString universal slot assert!(dump.contains("toString[#0]"), "Expected method_id #0 for toString. Dump:\n{}", dump); } #[test] fn test_lowering_await_expression() { // Build AST: await 1 (semantic is nonsensical but should emit Await) let ast = ASTNode::AwaitExpression { expression: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: crate::ast::Span::unknown() }), span: crate::ast::Span::unknown() }; let mut compiler = MirCompiler::new(); let result = compiler.compile(ast).expect("compile should succeed"); let dump = MirPrinter::new().print_module(&result.module); assert!(dump.contains("await"), "Expected await in MIR dump. Got:\n{}", dump); } #[test] fn test_await_has_checkpoints() { use crate::ast::{LiteralValue, Span}; // Build: await 1 let ast = ASTNode::AwaitExpression { expression: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), span: Span::unknown() }; let mut compiler = MirCompiler::new(); let result = compiler.compile(ast).expect("compile"); // Verifier should pass (await flanked by safepoints) assert!(result.verification_result.is_ok(), "Verifier failed for await checkpoints: {:?}", result.verification_result); let dump = compiler.dump_mir(&result.module); // Expect at least two safepoints in the function (before/after await) let sp_count = dump.matches("safepoint").count(); assert!(sp_count >= 2, "Expected >=2 safepoints around await, got {}. Dump:\n{}", sp_count, dump); } #[test] fn test_rewritten_await_still_checkpoints() { use crate::ast::{LiteralValue, Span}; // Enable rewrite so Await → ExternCall(env.future.await) std::env::set_var("NYASH_REWRITE_FUTURE", "1"); let ast = ASTNode::AwaitExpression { expression: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), span: Span::unknown() }; let mut compiler = MirCompiler::new(); let result = compiler.compile(ast).expect("compile"); // Verifier should still pass (checkpoint verification includes ExternCall await) assert!(result.verification_result.is_ok(), "Verifier failed for rewritten await checkpoints: {:?}", result.verification_result); let dump = compiler.dump_mir(&result.module); assert!(dump.contains("env.future.await"), "Expected rewritten await extern call. Dump:\n{}", dump); let sp_count = dump.matches("safepoint").count(); assert!(sp_count >= 2, "Expected >=2 safepoints around rewritten await, got {}. Dump:\n{}", sp_count, dump); // Cleanup env std::env::remove_var("NYASH_REWRITE_FUTURE"); } #[test] fn test_throw_compilation() { let mut compiler = MirCompiler::new(); let throw_ast = ASTNode::Throw { expression: Box::new(ASTNode::Literal { value: LiteralValue::String("Test exception".to_string()), span: crate::ast::Span::unknown(), }), span: crate::ast::Span::unknown(), }; let result = compiler.compile(throw_ast); assert!(result.is_ok(), "Throw compilation should succeed"); let compile_result = result.unwrap(); let mir_dump = compiler.dump_mir(&compile_result.module); assert!(mir_dump.contains("throw"), "MIR should contain throw instruction"); assert!(mir_dump.contains("safepoint"), "MIR should contain safepoint instruction"); } #[test] fn test_loop_compilation() { let mut compiler = MirCompiler::new(); let loop_ast = ASTNode::Loop { condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(true), span: crate::ast::Span::unknown(), }), body: vec![ ASTNode::Print { expression: Box::new(ASTNode::Literal { value: LiteralValue::String("Loop body".to_string()), span: crate::ast::Span::unknown(), }), span: crate::ast::Span::unknown(), } ], span: crate::ast::Span::unknown(), }; let result = compiler.compile(loop_ast); assert!(result.is_ok(), "Loop compilation should succeed"); let compile_result = result.unwrap(); let mir_dump = compiler.dump_mir(&compile_result.module); assert!(mir_dump.contains("br"), "MIR should contain branch instructions"); assert!(mir_dump.contains("safepoint"), "MIR should contain safepoint instructions"); } #[test] fn test_try_catch_compilation() { let mut compiler = MirCompiler::new(); let try_catch_ast = ASTNode::TryCatch { try_body: vec![ ASTNode::Print { expression: Box::new(ASTNode::Literal { value: LiteralValue::String("Try block".to_string()), span: crate::ast::Span::unknown(), }), span: crate::ast::Span::unknown(), } ], catch_clauses: vec![ crate::ast::CatchClause { exception_type: Some("Exception".to_string()), variable_name: Some("e".to_string()), body: vec![ ASTNode::Print { expression: Box::new(ASTNode::Literal { value: LiteralValue::String("Catch block".to_string()), span: crate::ast::Span::unknown(), }), span: crate::ast::Span::unknown(), } ], span: crate::ast::Span::unknown(), } ], finally_body: None, span: crate::ast::Span::unknown(), }; let result = compiler.compile(try_catch_ast); assert!(result.is_ok(), "TryCatch compilation should succeed"); let compile_result = result.unwrap(); let mir_dump = compiler.dump_mir(&compile_result.module); assert!(mir_dump.contains("catch"), "MIR should contain catch instruction"); } }