Implement Phase 5: Control flow & exceptions in MIR/VM - Core functionality complete

Co-authored-by: moe-charm <217100418+moe-charm@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-08-13 06:23:28 +00:00
parent 2e9b5daadf
commit d3a85b2305
8 changed files with 468 additions and 0 deletions

View File

@ -72,6 +72,9 @@ impl MirBuilder {
self.current_function = Some(main_function);
self.current_block = Some(entry_block);
// Add safepoint at function entry
self.emit_instruction(MirInstruction::Safepoint)?;
// Convert AST to MIR
let result_value = self.build_expression(ast)?;
@ -160,6 +163,18 @@ impl MirBuilder {
)
},
ASTNode::Loop { condition, body, .. } => {
self.build_loop_statement(*condition.clone(), body.clone())
},
ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => {
self.build_try_catch_statement(try_body.clone(), catch_clauses.clone(), finally_body.clone())
},
ASTNode::Throw { expression, .. } => {
self.build_throw_statement(*expression.clone())
},
_ => {
Err(format!("Unsupported AST node type: {:?}", ast))
}
@ -391,6 +406,160 @@ impl MirBuilder {
}
}
/// Build a loop statement: loop(condition) { body }
fn build_loop_statement(&mut self, condition: ASTNode, body: Vec<ASTNode>) -> Result<ValueId, String> {
// Add safepoint at loop entry
self.emit_instruction(MirInstruction::Safepoint)?;
let loop_header = self.block_gen.next();
let loop_body = self.block_gen.next();
let loop_exit = self.block_gen.next();
// Jump to loop header
self.emit_instruction(MirInstruction::Jump { target: loop_header })?;
// Create loop header block
self.start_new_block(loop_header)?;
// Evaluate condition
let condition_value = self.build_expression(condition)?;
// Branch based on condition
self.emit_instruction(MirInstruction::Branch {
condition: condition_value,
then_bb: loop_body,
else_bb: loop_exit,
})?;
// Create loop body block
self.start_new_block(loop_body)?;
// Add safepoint at loop body start
self.emit_instruction(MirInstruction::Safepoint)?;
// Build loop body
let body_ast = ASTNode::Program {
statements: body,
span: crate::ast::Span::unknown(),
};
self.build_expression(body_ast)?;
// Jump back to loop header
self.emit_instruction(MirInstruction::Jump { target: loop_header })?;
// Create exit block
self.start_new_block(loop_exit)?;
// Return void value
let void_dst = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: void_dst,
value: ConstValue::Void,
})?;
Ok(void_dst)
}
/// Build a try/catch statement
fn build_try_catch_statement(&mut self, try_body: Vec<ASTNode>, catch_clauses: Vec<crate::ast::CatchClause>, finally_body: Option<Vec<ASTNode>>) -> Result<ValueId, String> {
let try_block = self.block_gen.next();
let catch_block = self.block_gen.next();
let finally_block = if finally_body.is_some() { Some(self.block_gen.next()) } else { None };
let exit_block = self.block_gen.next();
// Jump to try block
self.emit_instruction(MirInstruction::Jump { target: try_block })?;
// Build try block
self.start_new_block(try_block)?;
let try_ast = ASTNode::Program {
statements: try_body,
span: crate::ast::Span::unknown(),
};
let try_result = self.build_expression(try_ast)?;
// Jump to finally or exit
let next_target = finally_block.unwrap_or(exit_block);
self.emit_instruction(MirInstruction::Jump { target: next_target })?;
// Build catch block
self.start_new_block(catch_block)?;
// For now, handle first catch clause only (simplified)
if let Some(catch_clause) = catch_clauses.first() {
let exception_value = self.value_gen.next();
// Set up catch handler
self.emit_instruction(MirInstruction::Catch {
exception_type: catch_clause.exception_type.clone(),
exception_value,
handler_bb: catch_block,
})?;
// Build catch body
let catch_ast = ASTNode::Program {
statements: catch_clause.body.clone(),
span: crate::ast::Span::unknown(),
};
self.build_expression(catch_ast)?;
}
// Jump to finally or exit
let next_target = finally_block.unwrap_or(exit_block);
self.emit_instruction(MirInstruction::Jump { target: next_target })?;
// Build finally block if present
if let (Some(finally_block_id), Some(finally_statements)) = (finally_block, finally_body) {
self.start_new_block(finally_block_id)?;
let finally_ast = ASTNode::Program {
statements: finally_statements,
span: crate::ast::Span::unknown(),
};
self.build_expression(finally_ast)?;
self.emit_instruction(MirInstruction::Jump { target: exit_block })?;
}
// Create exit block
self.start_new_block(exit_block)?;
// Return the try result (simplified - in real implementation would need phi node)
Ok(try_result)
}
/// Build a throw statement
fn build_throw_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
let exception_value = self.build_expression(expression)?;
// Emit throw instruction with PANIC effect
self.emit_instruction(MirInstruction::Throw {
exception: exception_value,
effects: EffectMask::PANIC,
})?;
// Throw doesn't return normally, but we need to return a value for the type system
let void_dst = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: void_dst,
value: ConstValue::Void,
})?;
Ok(void_dst)
}
/// Start a new basic block
fn start_new_block(&mut self, block_id: BasicBlockId) -> Result<(), String> {
if let Some(ref mut function) = self.current_function {
function.add_block(BasicBlock::new(block_id));
self.current_block = Some(block_id);
Ok(())
} else {
Err("No current function".to_string())
}
}
/// Convert AST binary operator to MIR operator
fn convert_binary_operator(&self, op: BinaryOperator) -> Result<BinaryOpType, String> {
match op {