feat: Add tail call optimization

Introduce a TCO pass to the compiler and modify the VM to handle tail
calls.
This allows for deep recursion without stack overflow.
This commit is contained in:
Michael Schimmel
2026-02-17 23:10:55 +01:00
parent e6eaf70836
commit 98d3344912
7 changed files with 205 additions and 27 deletions
+51 -24
View File
@@ -114,40 +114,67 @@ impl VM {
Ok(Value::Object(Rc::new(closure)))
},
BoundKind::TailCall { callee, args } => {
let func_val = self.eval(callee)?;
let mut arg_vals = Vec::with_capacity(args.len());
for arg in args {
arg_vals.push(self.eval(arg)?);
}
match func_val {
Value::Object(obj) => Ok(Value::TailCallRequest(obj, arg_vals)),
// Native functions can't be TCO'd this way (they return value directly),
// so we just execute them and return Value.
Value::Function(f) => Ok(f(arg_vals)),
_ => Err(format!("Tail call target is not a function: {}", func_val)),
}
},
BoundKind::Call { callee, args } => {
let func_val = self.eval(callee)?;
let mut func_val = self.eval(callee)?;
let mut arg_vals = Vec::with_capacity(args.len());
for arg in args {
arg_vals.push(self.eval(arg)?);
}
match func_val {
Value::Function(f) => Ok(f(arg_vals)),
Value::Object(obj) => {
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
let old_stack_top = self.stack.len();
let closure_rc = Rc::new(closure.clone());
self.stack.extend(arg_vals);
self.frames.push(CallFrame {
stack_base: old_stack_top,
closure: Some(closure_rc.clone()),
});
// Trampoline Loop
loop {
match func_val {
Value::Function(f) => return Ok(f(arg_vals)),
Value::Object(obj) => {
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
let old_stack_top = self.stack.len();
let closure_rc = Rc::new(closure.clone());
self.stack.extend(arg_vals); // Moves arg_vals into stack
self.frames.push(CallFrame {
stack_base: old_stack_top,
closure: Some(closure_rc.clone()),
});
let result = self.eval(&closure.function_node);
let result = self.eval(&closure.function_node);
self.frames.pop();
self.stack.truncate(old_stack_top);
result
} else {
Err(format!("Object is not a closure: {}", obj.type_name()))
}
},
_ => Err(format!("Attempt to call non-function: {}", func_val)),
self.frames.pop();
self.stack.truncate(old_stack_top);
match result {
Ok(Value::TailCallRequest(next_obj, next_args)) => {
func_val = Value::Object(next_obj);
arg_vals = next_args;
continue;
},
Ok(val) => return Ok(val),
Err(e) => return Err(e),
}
} else {
return Err(format!("Object is not a closure: {}", obj.type_name()));
}
},
_ => return Err(format!("Attempt to call non-function: {}", func_val)),
}
}
},