Implement Local and TryCatch lowering in MirBuilder

Co-authored-by: moe-charm <217100418+moe-charm@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-08-13 07:13:53 +00:00
parent 3b040f1587
commit ba568f7dfb
3 changed files with 110 additions and 28 deletions

View File

@ -103,7 +103,8 @@ impl BasicBlock {
matches!(instruction,
MirInstruction::Branch { .. } |
MirInstruction::Jump { .. } |
MirInstruction::Return { .. }
MirInstruction::Return { .. } |
MirInstruction::Throw { .. }
)
}
@ -123,6 +124,10 @@ impl BasicBlock {
MirInstruction::Return { .. } => {
// No successors for return
},
MirInstruction::Throw { .. } => {
// No normal successors for throw - control goes to exception handlers
// Exception edges are handled separately from normal control flow
},
_ => unreachable!("Non-terminator instruction in terminator position"),
}
}

View File

@ -175,6 +175,10 @@ impl MirBuilder {
self.build_throw_statement(*expression.clone())
},
ASTNode::Local { variables, initial_values, .. } => {
self.build_local_statement(variables.clone(), initial_values.clone())
},
_ => {
Err(format!("Unsupported AST node type: {:?}", ast))
}
@ -467,6 +471,18 @@ impl MirBuilder {
let finally_block = if finally_body.is_some() { Some(self.block_gen.next()) } else { None };
let exit_block = self.block_gen.next();
// Set up exception handler for the try block (before we enter it)
if let Some(catch_clause) = catch_clauses.first() {
let exception_value = self.value_gen.next();
// Register catch handler for exceptions that may occur in try block
self.emit_instruction(MirInstruction::Catch {
exception_type: catch_clause.exception_type.clone(),
exception_value,
handler_bb: catch_block,
})?;
}
// Jump to try block
self.emit_instruction(MirInstruction::Jump { target: try_block })?;
@ -477,26 +493,19 @@ impl MirBuilder {
statements: try_body,
span: crate::ast::Span::unknown(),
};
let try_result = self.build_expression(try_ast)?;
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 })?;
// Normal completion of try block - jump to finally or exit (if not already terminated)
if !self.is_current_block_terminated() {
let next_target = finally_block.unwrap_or(exit_block);
self.emit_instruction(MirInstruction::Jump { target: next_target })?;
}
// Build catch block
// Build catch block (reachable via exception handling)
self.start_new_block(catch_block)?;
// For now, handle first catch clause only (simplified)
// Handle catch clause
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(),
@ -505,9 +514,11 @@ impl MirBuilder {
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 })?;
// Catch completion - jump to finally or exit (if not already terminated)
if !self.is_current_block_terminated() {
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) {
@ -525,28 +536,65 @@ impl MirBuilder {
// Create exit block
self.start_new_block(exit_block)?;
// Return the try result (simplified - in real implementation would need phi node)
Ok(try_result)
// Return void for now (in a complete implementation, would use phi for try/catch values)
let result = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: result,
value: ConstValue::Void,
})?;
Ok(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
// Emit throw instruction with PANIC effect (this is a terminator)
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,
})?;
// We can't add more instructions after throw, so just return the exception value
Ok(exception_value)
}
/// Build local variable declarations with optional initial values
fn build_local_statement(&mut self, variables: Vec<String>, initial_values: Vec<Option<Box<ASTNode>>>) -> Result<ValueId, String> {
let mut last_value = None;
Ok(void_dst)
// Process each variable declaration
for (i, var_name) in variables.iter().enumerate() {
let value_id = if i < initial_values.len() && initial_values[i].is_some() {
// Variable has initial value - evaluate it
let init_expr = initial_values[i].as_ref().unwrap();
self.build_expression(*init_expr.clone())?
} else {
// No initial value - assign void (uninitialized)
let void_dst = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: void_dst,
value: ConstValue::Void,
})?;
void_dst
};
// Register variable in SSA form
self.variable_map.insert(var_name.clone(), value_id);
last_value = Some(value_id);
}
// Return the last assigned value, or void if no variables
Ok(last_value.unwrap_or_else(|| {
let void_val = self.value_gen.next();
self.emit_instruction(MirInstruction::Const {
dst: void_val,
value: ConstValue::Void,
}).unwrap();
void_val
}))
}
/// Start a new basic block
@ -560,6 +608,16 @@ impl MirBuilder {
}
}
/// Check if the current basic block is terminated
fn is_current_block_terminated(&self) -> bool {
if let (Some(block_id), Some(ref function)) = (self.current_block, &self.current_function) {
if let Some(block) = function.get_block(block_id) {
return block.is_terminated();
}
}
false
}
/// Convert AST binary operator to MIR operator
fn convert_binary_operator(&self, op: BinaryOperator) -> Result<BinaryOpType, String> {
match op {

View File

@ -228,11 +228,30 @@ impl MirVerifier {
while let Some(current) = worklist.pop() {
if reachable.insert(current) {
if let Some(block) = function.blocks.get(&current) {
// Add normal successors
for successor in &block.successors {
if !reachable.contains(successor) {
worklist.push(*successor);
}
}
// Add exception handler blocks as reachable
for instruction in &block.instructions {
if let super::MirInstruction::Catch { handler_bb, .. } = instruction {
if !reachable.contains(handler_bb) {
worklist.push(*handler_bb);
}
}
}
// Also check terminator for exception handlers
if let Some(ref terminator) = block.terminator {
if let super::MirInstruction::Catch { handler_bb, .. } = terminator {
if !reachable.contains(handler_bb) {
worklist.push(*handler_bb);
}
}
}
}
}
}