Add into_rc_any and push_value to Object

This commit introduces the `into_rc_any` and `push_value` methods to the
`Object` trait.

The `into_rc_any` method allows for efficient, zero-cost downcasting of
`Rc<Self>` to `Rc<dyn Any>`. This is a crucial step for enabling more
flexible dynamic dispatch and type manipulation within the VM.

The `push_value` method is implemented for series types (`ScalarSeries`,
`ValueSeries`, `RecordSeries`) to allow pushing values directly onto
them. This simplifies the `push` intrinsic function, removing the need
for manual downcasting within its implementation. The default
implementation for non-series objects panics, enforcing that only
mutable series support this operation.

Additionally, `ScalarValue` now includes a `from_value` method to
facilitate converting `Value` back into concrete scalar types. This
improves type safety and reduces boilerplate code when handling scalar
conversions.
This commit is contained in:
2026-03-22 19:04:32 +01:00
parent db443df932
commit f6c963cb41
5 changed files with 97 additions and 75 deletions
+21 -16
View File
@@ -48,12 +48,15 @@ impl Object for Closure {
fn as_any(&self) -> &dyn Any {
self
}
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
}
#[derive(Debug)]
struct CallFrame {
stack_base: usize,
closure: Option<Rc<dyn Object>>,
closure: Option<Rc<Closure>>,
}
pub trait VMObserver {
@@ -154,13 +157,14 @@ impl VM {
match result {
Ok(Value::TailCallRequest(payload)) => {
let (next_obj, next_args) = *payload;
if let Some(closure) = next_obj.as_any().downcast_ref::<Closure>() {
if let Ok(closure_rc) = next_obj.clone().into_rc_any().downcast::<Closure>() {
self.stack.clear();
// frames should be empty here since we popped before entering the loop
self.frames.push(CallFrame {
stack_base: 0,
closure: Some(next_obj.clone()),
closure: Some(closure_rc.clone()),
});
let closure = closure_rc.as_ref();
if let Some(count) = closure.positional_count
&& next_args.len() == count as usize
{
@@ -227,14 +231,15 @@ impl VM {
closure_obj: Rc<dyn Object>,
args: &[Value],
) -> Result<Value, String> {
let closure = closure_obj
.as_any()
.downcast_ref::<Closure>()
.ok_or_else(|| "Object is not a closure".to_string())?;
let closure_rc = closure_obj
.into_rc_any()
.downcast::<Closure>()
.map_err(|_| "Object is not a closure".to_string())?;
self.stack.clear();
self.frames.clear();
let closure = closure_rc.as_ref();
if let Some(count) = closure.positional_count
&& args.len() == count as usize
{
@@ -251,7 +256,7 @@ impl VM {
self.frames.push(CallFrame {
stack_base: 0,
closure: Some(closure_obj.clone()),
closure: Some(closure_rc.clone()),
});
let result = self.eval_internal(observer, &closure.exec_node);
@@ -652,11 +657,14 @@ impl VM {
return res;
}
Value::Object(obj) => {
if let Some(closure) = obj.as_any().downcast_ref::<Closure>() {
if let Ok(closure_rc) =
obj.clone().into_rc_any().downcast::<Closure>()
{
self.frames.push(CallFrame {
stack_base: base,
closure: Some(obj.clone()),
closure: Some(closure_rc.clone()),
});
let closure = closure_rc.as_ref();
let unpack_res = if let Some(count) = closure.positional_count
&& (self.stack.len() - base) == count as usize
@@ -915,8 +923,7 @@ impl VM {
}
Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?;
if let Some(closure_obj) = &frame.closure {
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
if let Some(closure) = &frame.closure {
let u_idx = idx.0 as usize;
if u_idx < closure.upvalues.len() {
Ok(closure.upvalues[u_idx].clone())
@@ -956,8 +963,7 @@ impl VM {
}
Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?;
if let Some(closure_obj) = &frame.closure {
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
if let Some(closure) = &frame.closure {
let u_idx = idx.0 as usize;
if u_idx < closure.upvalues.len() {
Ok(closure.upvalues[u_idx].borrow().clone())
@@ -1000,8 +1006,7 @@ impl VM {
}
Address::Upvalue(idx) => {
let frame = self.frames.last().ok_or("No call frame")?;
if let Some(closure_obj) = &frame.closure {
let closure = closure_obj.as_any().downcast_ref::<Closure>().unwrap();
if let Some(closure) = &frame.closure {
let u_idx = idx.0 as usize;
if u_idx < closure.upvalues.len() {
*closure.upvalues[u_idx].borrow_mut() = value;