diff --git a/src/backend/vm.rs b/src/backend/vm.rs index 201c5a4e..2e52a860 100644 --- a/src/backend/vm.rs +++ b/src/backend/vm.rs @@ -41,6 +41,7 @@ pub enum VMValue { Float(f64), Bool(bool), String(String), + Future(crate::boxes::future::FutureBox), Void, } @@ -52,6 +53,7 @@ impl VMValue { VMValue::Float(f) => Box::new(StringBox::new(&f.to_string())), // Simplified for now VMValue::Bool(b) => Box::new(BoolBox::new(*b)), VMValue::String(s) => Box::new(StringBox::new(s)), + VMValue::Future(f) => Box::new(f.clone()), VMValue::Void => Box::new(VoidBox::new()), } } @@ -63,6 +65,7 @@ impl VMValue { VMValue::Float(f) => f.to_string(), VMValue::Bool(b) => b.to_string(), VMValue::String(s) => s.clone(), + VMValue::Future(f) => f.to_string_box().value, VMValue::Void => "void".to_string(), } } @@ -83,6 +86,23 @@ impl VMValue { _ => Err(VMError::TypeError(format!("Expected bool, got {:?}", self))), } } + + /// Convert from NyashBox to VMValue + pub fn from_nyash_box(nyash_box: Box) -> VMValue { + // Try to downcast to known types + if let Some(int_box) = nyash_box.as_any().downcast_ref::() { + VMValue::Integer(int_box.value) + } else if let Some(bool_box) = nyash_box.as_any().downcast_ref::() { + VMValue::Bool(bool_box.value) + } else if let Some(string_box) = nyash_box.as_any().downcast_ref::() { + VMValue::String(string_box.value.clone()) + } else if let Some(future_box) = nyash_box.as_any().downcast_ref::() { + VMValue::Future(future_box.clone()) + } else { + // For any other type, convert to string representation + VMValue::String(nyash_box.to_string_box().value) + } + } } impl From<&ConstValue> for VMValue { @@ -158,8 +178,10 @@ impl VM { let mut should_return = None; // Execute instructions in this block + println!("Executing block {} with {} instructions", current_block, block.instructions.len()); for (index, instruction) in block.instructions.iter().enumerate() { self.pc = index; + println!(" Instruction {}: {:?}", index, instruction); match self.execute_instruction(instruction)? { ControlFlow::Continue => continue, @@ -174,6 +196,8 @@ impl VM { } } + println!("Block execution finished. should_return: {:?}, next_block: {:?}", should_return.is_some(), next_block.is_some()); + // Handle control flow if let Some(return_value) = should_return { return Ok(return_value); @@ -189,6 +213,7 @@ impl VM { /// Execute a single instruction fn execute_instruction(&mut self, instruction: &MirInstruction) -> Result { + println!("Executing instruction: {:?}", instruction); match instruction { MirInstruction::Const { dst, value } => { let vm_value = VMValue::from(value); @@ -227,8 +252,11 @@ impl VM { MirInstruction::Return { value } => { let return_value = if let Some(val_id) = value { - self.get_value(*val_id)? + let val = self.get_value(*val_id)?; + println!("Return: returning value from {:?} = {:?}", val_id, val); + val } else { + println!("Return: returning void (no value specified)"); VMValue::Void }; Ok(ControlFlow::Return(return_value)) @@ -439,6 +467,51 @@ impl VM { // In a real implementation, this would ensure memory ordering Ok(ControlFlow::Continue) }, + + // Phase 7: Async/Future Operations + MirInstruction::FutureNew { dst, value } => { + let initial_value = self.get_value(*value)?; + println!("FutureNew: initial_value = {:?}", initial_value); + let future = crate::boxes::future::FutureBox::new(); + // Convert VMValue to NyashBox and set it in the future + let nyash_box = initial_value.to_nyash_box(); + println!("FutureNew: converted to NyashBox type = {}", nyash_box.type_name()); + future.set_result(nyash_box); + self.values.insert(*dst, VMValue::Future(future)); + println!("FutureNew: stored Future in dst = {:?}", dst); + Ok(ControlFlow::Continue) + }, + + MirInstruction::FutureSet { future, value } => { + let future_val = self.get_value(*future)?; + let new_value = self.get_value(*value)?; + + if let VMValue::Future(ref future_box) = future_val { + future_box.set_result(new_value.to_nyash_box()); + Ok(ControlFlow::Continue) + } else { + Err(VMError::TypeError(format!("Expected Future, got {:?}", future_val))) + } + }, + + MirInstruction::Await { dst, future } => { + let future_val = self.get_value(*future)?; + println!("Await: future_val = {:?}", future_val); + + if let VMValue::Future(ref future_box) = future_val { + // This blocks until the future is ready + let result = future_box.get(); + println!("Await: future.get() returned type = {}", result.type_name()); + println!("Await: future.get() string = {}", result.to_string_box().value); + // Convert NyashBox back to VMValue + let vm_value = VMValue::from_nyash_box(result); + println!("Await: converted back to VMValue = {:?}", vm_value); + self.values.insert(*dst, vm_value); + Ok(ControlFlow::Continue) + } else { + Err(VMError::TypeError(format!("Expected Future, got {:?}", future_val))) + } + }, } } diff --git a/src/mir/builder.rs b/src/mir/builder.rs index 0475cd6e..d59b522d 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -203,6 +203,15 @@ impl MirBuilder { self.build_new_expression(class.clone(), arguments.clone()) }, + // Phase 7: Async operations + ASTNode::Nowait { variable, expression, .. } => { + self.build_nowait_statement(variable.clone(), *expression.clone()) + }, + + ASTNode::AwaitExpression { expression, .. } => { + self.build_await_expression(*expression.clone()) + }, + _ => { Err(format!("Unsupported AST node type: {:?}", ast)) } @@ -771,6 +780,41 @@ impl MirBuilder { _ => Err(format!("Unsupported unary operator: {}", op)), } } + + /// Build nowait statement: nowait variable = expression + fn build_nowait_statement(&mut self, variable: String, expression: ASTNode) -> Result { + // Evaluate the expression + let expression_value = self.build_expression(expression)?; + + // Create a new Future with the evaluated expression as the initial value + let future_id = self.value_gen.next(); + self.emit_instruction(MirInstruction::FutureNew { + dst: future_id, + value: expression_value, + })?; + + // Store the future in the variable + self.variable_map.insert(variable.clone(), future_id); + + Ok(future_id) + } + + /// Build await expression: await expression + fn build_await_expression(&mut self, expression: ASTNode) -> Result { + // Evaluate the expression (should be a Future) + let future_value = self.build_expression(expression)?; + + // Create destination for await result + let result_id = self.value_gen.next(); + + // Emit await instruction + self.emit_instruction(MirInstruction::Await { + dst: result_id, + future: future_value, + })?; + + Ok(result_id) + } } /// Helper enum for binary operator classification diff --git a/src/mir/instruction.rs b/src/mir/instruction.rs index 941c775f..22c07a11 100644 --- a/src/mir/instruction.rs +++ b/src/mir/instruction.rs @@ -250,6 +250,29 @@ pub enum MirInstruction { BarrierWrite { ptr: ValueId, }, + + // === Phase 7: Async/Future Operations === + + /// Create a new Future with initial value + /// `%dst = future_new %value` + FutureNew { + dst: ValueId, + value: ValueId, + }, + + /// Set Future value and mark as ready + /// `future_set %future = %value` + FutureSet { + future: ValueId, + value: ValueId, + }, + + /// Wait for Future completion and get value + /// `%dst = await %future` + Await { + dst: ValueId, + future: ValueId, + }, } /// Constant values in MIR @@ -304,6 +327,7 @@ pub enum MirType { String, Box(String), // Box type with name Array(Box), + Future(Box), // Future containing a type Void, Unknown, } @@ -360,6 +384,11 @@ impl MirInstruction { MirInstruction::WeakLoad { .. } => EffectMask::READ, // Loading weak ref has read effects MirInstruction::BarrierRead { .. } => EffectMask::READ.add(Effect::Barrier), // Memory barrier with read MirInstruction::BarrierWrite { .. } => EffectMask::WRITE.add(Effect::Barrier), // Memory barrier with write + + // Phase 7: Async/Future Operations + MirInstruction::FutureNew { .. } => EffectMask::PURE.add(Effect::Alloc), // Creating future may allocate + MirInstruction::FutureSet { .. } => EffectMask::WRITE, // Setting future has write effects + MirInstruction::Await { .. } => EffectMask::READ.add(Effect::Async), // Await blocks and reads } } @@ -380,7 +409,9 @@ impl MirInstruction { MirInstruction::RefNew { dst, .. } | MirInstruction::RefGet { dst, .. } | MirInstruction::WeakNew { dst, .. } | - MirInstruction::WeakLoad { dst, .. } => Some(*dst), + MirInstruction::WeakLoad { dst, .. } | + MirInstruction::FutureNew { dst, .. } | + MirInstruction::Await { dst, .. } => Some(*dst), MirInstruction::Call { dst, .. } | MirInstruction::BoxCall { dst, .. } => *dst, @@ -396,6 +427,7 @@ impl MirInstruction { MirInstruction::RefSet { .. } | MirInstruction::BarrierRead { .. } | MirInstruction::BarrierWrite { .. } | + MirInstruction::FutureSet { .. } | MirInstruction::Safepoint | MirInstruction::Nop => None, @@ -463,6 +495,11 @@ impl MirInstruction { MirInstruction::WeakLoad { weak_ref, .. } => vec![*weak_ref], MirInstruction::BarrierRead { ptr } => vec![*ptr], MirInstruction::BarrierWrite { ptr } => vec![*ptr], + + // Phase 7: Async/Future Operations + MirInstruction::FutureNew { value, .. } => vec![*value], + MirInstruction::FutureSet { future, value } => vec![*future, *value], + MirInstruction::Await { future, .. } => vec![*future], } } } diff --git a/src/mir/printer.rs b/src/mir/printer.rs index 91407eb3..565180a3 100644 --- a/src/mir/printer.rs +++ b/src/mir/printer.rs @@ -347,6 +347,19 @@ impl MirPrinter { MirInstruction::BarrierWrite { ptr } => { format!("barrier_write {}", ptr) }, + + // Phase 7: Async/Future Operations + MirInstruction::FutureNew { dst, value } => { + format!("{} = future_new {}", dst, value) + }, + + MirInstruction::FutureSet { future, value } => { + format!("future_set {} = {}", future, value) + }, + + MirInstruction::Await { dst, future } => { + format!("{} = await {}", dst, future) + }, } } @@ -359,6 +372,7 @@ impl MirPrinter { super::MirType::String => "str".to_string(), super::MirType::Box(name) => format!("box<{}>", name), super::MirType::Array(elem_type) => format!("[{}]", self.format_type(elem_type)), + super::MirType::Future(inner_type) => format!("future<{}>", self.format_type(inner_type)), super::MirType::Void => "void".to_string(), super::MirType::Unknown => "?".to_string(), } diff --git a/tests/mir_phase7_async_ops.rs b/tests/mir_phase7_async_ops.rs new file mode 100644 index 00000000..32e94f63 --- /dev/null +++ b/tests/mir_phase7_async_ops.rs @@ -0,0 +1,387 @@ +/*! + * Phase 7 MIR Builder & VM Test - Async Operations (nowait/await) + * + * Tests AST → MIR lowering and VM execution for Phase 7 async operations + */ + +use nyash_rust::mir::{MirBuilder, MirPrinter}; +use nyash_rust::backend::VM; +use nyash_rust::ast::{ASTNode, LiteralValue, Span}; +use std::collections::HashMap; + +#[test] +fn test_mir_phase7_basic_nowait_await() { + // Build AST equivalent to: + // static box Main { + // main() { + // nowait f1 = 42; + // local result = await f1; + // return result + // } + // } + + let mut main_methods = HashMap::new(); + + // Create main method body + let main_body = vec![ + // nowait f1 = 42 + ASTNode::Nowait { + variable: "f1".to_string(), + expression: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(42), + span: Span::unknown(), + }), + span: Span::unknown(), + }, + // local result = await f1 + ASTNode::Local { + variables: vec!["result".to_string()], + initial_values: vec![Some(Box::new(ASTNode::AwaitExpression { + expression: Box::new(ASTNode::Variable { + name: "f1".to_string(), + span: Span::unknown(), + }), + span: Span::unknown(), + }))], + span: Span::unknown(), + }, + // return result + ASTNode::Return { + value: Some(Box::new(ASTNode::Variable { + name: "result".to_string(), + span: Span::unknown(), + })), + span: Span::unknown(), + }, + ]; + + // Create main method + let main_method = ASTNode::FunctionDeclaration { + name: "main".to_string(), + params: vec![], + body: main_body, + is_static: false, + is_override: false, + span: Span::unknown(), + }; + + main_methods.insert("main".to_string(), main_method); + + // Create static box Main + let ast = ASTNode::BoxDeclaration { + name: "Main".to_string(), + fields: vec![], + methods: main_methods, + constructors: HashMap::new(), + init_fields: vec![], + weak_fields: vec![], + is_interface: false, + extends: vec![], + implements: vec![], + type_parameters: vec![], + is_static: true, + static_init: None, + span: Span::unknown(), + }; + + // Build MIR + let mut builder = MirBuilder::new(); + let result = builder.build_module(ast); + + if let Err(e) = &result { + println!("MIR build error: {}", e); + } + assert!(result.is_ok(), "MIR build should succeed"); + + let module = result.unwrap(); + + // Print MIR for debugging + let printer = MirPrinter::new(); + let mir_output = printer.print_module(&module); + println!("Generated MIR:"); + println!("{}", mir_output); + + // Verify MIR contains expected instructions + let function = module.get_function("main").unwrap(); + let instructions: Vec<_> = function.blocks.values() + .flat_map(|block| &block.instructions) + .collect(); + + // Should contain FutureNew instruction + let has_future_new = instructions.iter().any(|inst| { + matches!(inst, nyash_rust::mir::MirInstruction::FutureNew { .. }) + }); + assert!(has_future_new, "MIR should contain FutureNew instruction"); + + // Should contain Await instruction + let has_await = instructions.iter().any(|inst| { + matches!(inst, nyash_rust::mir::MirInstruction::Await { .. }) + }); + assert!(has_await, "MIR should contain Await instruction"); + + // Test VM execution + let mut vm = VM::new(); + let execution_result = vm.execute_module(&module); + + if let Err(e) = &execution_result { + println!("VM execution error: {}", e); + } + assert!(execution_result.is_ok(), "VM execution should succeed"); + + let final_value = execution_result.unwrap(); + println!("VM execution result: {}", final_value.to_string_box().value); + + // Should return 42 + assert_eq!(final_value.to_string_box().value, "42"); +} + +#[test] +fn test_mir_phase7_multiple_nowait_await() { + // Build AST equivalent to: + // static box Main { + // main() { + // nowait f1 = 10; + // nowait f2 = 20; + // local result1 = await f1; + // local result2 = await f2; + // return result1 + result2 + // } + // } + + let mut main_methods = HashMap::new(); + + // Create main method body + let main_body = vec![ + // nowait f1 = 10 + ASTNode::Nowait { + variable: "f1".to_string(), + expression: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(10), + span: Span::unknown(), + }), + span: Span::unknown(), + }, + // nowait f2 = 20 + ASTNode::Nowait { + variable: "f2".to_string(), + expression: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(20), + span: Span::unknown(), + }), + span: Span::unknown(), + }, + // local result1 = await f1 + ASTNode::Local { + variables: vec!["result1".to_string()], + initial_values: vec![Some(Box::new(ASTNode::AwaitExpression { + expression: Box::new(ASTNode::Variable { + name: "f1".to_string(), + span: Span::unknown(), + }), + span: Span::unknown(), + }))], + span: Span::unknown(), + }, + // local result2 = await f2 + ASTNode::Local { + variables: vec!["result2".to_string()], + initial_values: vec![Some(Box::new(ASTNode::AwaitExpression { + expression: Box::new(ASTNode::Variable { + name: "f2".to_string(), + span: Span::unknown(), + }), + span: Span::unknown(), + }))], + span: Span::unknown(), + }, + // return result1 + result2 + ASTNode::Return { + value: Some(Box::new(ASTNode::BinaryOp { + left: Box::new(ASTNode::Variable { + name: "result1".to_string(), + span: Span::unknown(), + }), + operator: nyash_rust::ast::BinaryOperator::Add, + right: Box::new(ASTNode::Variable { + name: "result2".to_string(), + span: Span::unknown(), + }), + span: Span::unknown(), + })), + span: Span::unknown(), + }, + ]; + + // Create main method + let main_method = ASTNode::FunctionDeclaration { + name: "main".to_string(), + params: vec![], + body: main_body, + is_static: false, + is_override: false, + span: Span::unknown(), + }; + + main_methods.insert("main".to_string(), main_method); + + // Create static box Main + let ast = ASTNode::BoxDeclaration { + name: "Main".to_string(), + fields: vec![], + methods: main_methods, + constructors: HashMap::new(), + init_fields: vec![], + weak_fields: vec![], + is_interface: false, + extends: vec![], + implements: vec![], + type_parameters: vec![], + is_static: true, + static_init: None, + span: Span::unknown(), + }; + + // Build MIR + let mut builder = MirBuilder::new(); + let result = builder.build_module(ast); + + assert!(result.is_ok(), "MIR build should succeed"); + let module = result.unwrap(); + + // Print MIR for debugging + let printer = MirPrinter::new(); + let mir_output = printer.print_module(&module); + println!("Generated MIR for multiple nowait/await:"); + println!("{}", mir_output); + + // Test VM execution + let mut vm = VM::new(); + let execution_result = vm.execute_module(&module); + + assert!(execution_result.is_ok(), "VM execution should succeed"); + let final_value = execution_result.unwrap(); + println!("VM execution result: {}", final_value.to_string_box().value); + + // Should return 30 (10 + 20) + assert_eq!(final_value.to_string_box().value, "30"); +} + +#[test] +fn test_mir_phase7_nested_await() { + // Build AST equivalent to: + // static box Main { + // main() { + // nowait outer = { + // nowait inner = 5; + // await inner * 2 + // }; + // return await outer + // } + // } + + let mut main_methods = HashMap::new(); + + // Create inner computation: nowait inner = 5; await inner * 2 + let inner_computation = ASTNode::Program { + statements: vec![ + ASTNode::Nowait { + variable: "inner".to_string(), + expression: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(5), + span: Span::unknown(), + }), + span: Span::unknown(), + }, + ASTNode::BinaryOp { + left: Box::new(ASTNode::AwaitExpression { + expression: Box::new(ASTNode::Variable { + name: "inner".to_string(), + span: Span::unknown(), + }), + span: Span::unknown(), + }), + operator: nyash_rust::ast::BinaryOperator::Multiply, + right: Box::new(ASTNode::Literal { + value: LiteralValue::Integer(2), + span: Span::unknown(), + }), + span: Span::unknown(), + }, + ], + span: Span::unknown(), + }; + + // Create main method body + let main_body = vec![ + // nowait outer = { ... } + ASTNode::Nowait { + variable: "outer".to_string(), + expression: Box::new(inner_computation), + span: Span::unknown(), + }, + // return await outer + ASTNode::Return { + value: Some(Box::new(ASTNode::AwaitExpression { + expression: Box::new(ASTNode::Variable { + name: "outer".to_string(), + span: Span::unknown(), + }), + span: Span::unknown(), + })), + span: Span::unknown(), + }, + ]; + + // Create main method + let main_method = ASTNode::FunctionDeclaration { + name: "main".to_string(), + params: vec![], + body: main_body, + is_static: false, + is_override: false, + span: Span::unknown(), + }; + + main_methods.insert("main".to_string(), main_method); + + // Create static box Main + let ast = ASTNode::BoxDeclaration { + name: "Main".to_string(), + fields: vec![], + methods: main_methods, + constructors: HashMap::new(), + init_fields: vec![], + weak_fields: vec![], + is_interface: false, + extends: vec![], + implements: vec![], + type_parameters: vec![], + is_static: true, + static_init: None, + span: Span::unknown(), + }; + + // Build MIR + let mut builder = MirBuilder::new(); + let result = builder.build_module(ast); + + assert!(result.is_ok(), "MIR build should succeed"); + let module = result.unwrap(); + + // Print MIR for debugging + let printer = MirPrinter::new(); + let mir_output = printer.print_module(&module); + println!("Generated MIR for nested await:"); + println!("{}", mir_output); + + // Test VM execution + let mut vm = VM::new(); + let execution_result = vm.execute_module(&module); + + assert!(execution_result.is_ok(), "VM execution should succeed"); + let final_value = execution_result.unwrap(); + println!("VM execution result: {}", final_value.to_string_box().value); + + // Should return 10 (5 * 2) + assert_eq!(final_value.to_string_box().value, "10"); +} \ No newline at end of file