Remove unused TailCallRequest variant

The `TailCallRequest` enum variant and its associated logic have been
removed. Instead, the VM now uses an `Option<(Rc<dyn Object>,
Vec<Value>)>` field named `tail_call` to manage tail-call requests. This
change simplifies the `Value` enum and centralizes tail-call handling
within the `VM` struct.
This commit is contained in:
2026-03-22 19:20:18 +01:00
parent f6c963cb41
commit 37d7ed3e75
2 changed files with 68 additions and 78 deletions
-6
View File
@@ -259,7 +259,6 @@ pub enum Value {
Function(Rc<NativeFunction>), Function(Rc<NativeFunction>),
Object(Rc<dyn Object>), // For compiled Closures and other opaque types Object(Rc<dyn Object>), // For compiled Closures and other opaque types
Cell(Rc<RefCell<Value>>), // Boxed value for captures Cell(Rc<RefCell<Value>>), // Boxed value for captures
TailCallRequest(Box<(Rc<dyn Object>, Vec<Value>)>), // Internal: For TCO (Boxed to keep Value small)
} }
impl PartialEq for Value { impl PartialEq for Value {
@@ -280,9 +279,6 @@ impl PartialEq for Value {
(Value::Function(a), Value::Function(b)) => Rc::ptr_eq(a, b), (Value::Function(a), Value::Function(b)) => Rc::ptr_eq(a, b),
(Value::Object(a), Value::Object(b)) => Rc::ptr_eq(a, b), (Value::Object(a), Value::Object(b)) => Rc::ptr_eq(a, b),
(Value::Cell(a), Value::Cell(b)) => Rc::ptr_eq(a, b), (Value::Cell(a), Value::Cell(b)) => Rc::ptr_eq(a, b),
(Value::TailCallRequest(a), Value::TailCallRequest(b)) => {
Rc::ptr_eq(&a.0, &b.0) && a.1 == b.1
}
_ => false, _ => false,
} }
} }
@@ -623,7 +619,6 @@ impl Value {
Value::Function(_) => StaticType::Any, // Dynamic function Value::Function(_) => StaticType::Any, // Dynamic function
Value::Object(o) => StaticType::Object(o.type_name()), Value::Object(o) => StaticType::Object(o.type_name()),
Value::Cell(c) => c.borrow().static_type(), Value::Cell(c) => c.borrow().static_type(),
Value::TailCallRequest(_) => StaticType::Any, // Internal state, but typable as Any
} }
} }
} }
@@ -667,7 +662,6 @@ impl fmt::Display for Value {
Value::Function(f_meta) => write!(f, "<native fn, {:?}>", f_meta.purity), Value::Function(f_meta) => write!(f, "<native fn, {:?}>", f_meta.purity),
Value::Object(o) => write!(f, "<{:?}>", o), Value::Object(o) => write!(f, "<{:?}>", o),
Value::Cell(c) => write!(f, "{}", c.borrow()), Value::Cell(c) => write!(f, "{}", c.borrow()),
Value::TailCallRequest(_) => write!(f, "<tail call request>"),
} }
} }
} }
+14 -18
View File
@@ -133,6 +133,9 @@ pub struct VM {
stack: Vec<Value>, stack: Vec<Value>,
globals: Rc<RefCell<Vec<Value>>>, globals: Rc<RefCell<Vec<Value>>>,
frames: Vec<CallFrame>, frames: Vec<CallFrame>,
/// Side-channel for tail-call signaling. Set by `eval_internal` when a tail-call
/// is requested; consumed by `resolve_tail_calls` and the inline TCO loop.
tail_call: Option<(Rc<dyn Object>, Vec<Value>)>,
} }
impl VM { impl VM {
@@ -141,6 +144,7 @@ impl VM {
stack: Vec::new(), stack: Vec::new(),
globals, globals,
frames: Vec::new(), frames: Vec::new(),
tail_call: None,
} }
} }
@@ -154,9 +158,7 @@ impl VM {
mut result: Result<Value, String>, mut result: Result<Value, String>,
) -> Result<Value, String> { ) -> Result<Value, String> {
loop { loop {
match result { if let Some((next_obj, next_args)) = self.tail_call.take() {
Ok(Value::TailCallRequest(payload)) => {
let (next_obj, next_args) = *payload;
if let Ok(closure_rc) = next_obj.clone().into_rc_any().downcast::<Closure>() { if let Ok(closure_rc) = next_obj.clone().into_rc_any().downcast::<Closure>() {
self.stack.clear(); self.stack.clear();
// frames should be empty here since we popped before entering the loop // frames should be empty here since we popped before entering the loop
@@ -211,8 +213,8 @@ impl VM {
next_obj.type_name() next_obj.type_name()
)); ));
} }
} } else {
_ => return result, return result;
} }
} }
} }
@@ -527,7 +529,8 @@ impl VM {
match func_val { match func_val {
Value::Object(obj) => { Value::Object(obj) => {
return Ok(Value::TailCallRequest(Box::new((obj, arg_vals)))); self.tail_call = Some((obj, arg_vals));
return Ok(Value::Void);
} }
Value::Function(f) => return Ok((f.func)(&arg_vals)), Value::Function(f) => return Ok((f.func)(&arg_vals)),
Value::FieldAccessor(k) => { Value::FieldAccessor(k) => {
@@ -753,24 +756,19 @@ impl VM {
Value::Function(_) => "Function", Value::Function(_) => "Function",
Value::Object(_) => "Object", Value::Object(_) => "Object",
Value::Cell(_) => "Cell", Value::Cell(_) => "Cell",
Value::TailCallRequest(_) => "TailCallRequest",
}; };
return Err(format!("Attempt to call non-function: {} (Variant: {})", current_func, variant_name)); return Err(format!("Attempt to call non-function: {} (Variant: {})", current_func, variant_name));
} }
}; };
match result { if let Some((next_obj, next_args)) = self.tail_call.take() {
Ok(Value::TailCallRequest(payload)) => {
let (next_obj, next_args) = *payload;
current_func = Value::Object(next_obj); current_func = Value::Object(next_obj);
self.stack.truncate(base); self.stack.truncate(base);
self.stack.extend(next_args); self.stack.extend(next_args);
continue; continue;
} } else {
res => {
self.stack.truncate(base); self.stack.truncate(base);
return res; return result;
}
} }
} }
} }
@@ -785,10 +783,8 @@ impl VM {
let frame = self.frames.last().ok_or("No call frame for 'again'")?; let frame = self.frames.last().ok_or("No call frame for 'again'")?;
if let Some(closure_obj) = &frame.closure { if let Some(closure_obj) = &frame.closure {
Ok(Value::TailCallRequest(Box::new(( self.tail_call = Some((closure_obj.clone() as Rc<dyn Object>, arg_vals));
closure_obj.clone(), Ok(Value::Void)
arg_vals,
))))
} else { } else {
Err("'again' called outside of a closure".to_string()) Err("'again' called outside of a closure".to_string())
} }