tvl-depot/tvix/eval/src/value/function.rs
Vincent Ambo 776120de05 fix(tvix/eval): instantiate *new* closures from blueprints each time
The previous closure refactoring introduced a bug in which the same
closure object would get mutated constantly for each instance of a
closure, which is incorrect behaviour.

This commit instead introduces an explicit new Value variant for the
internal "blueprint" that the compiler generates (essentially just the
lambda) and uses this variant to construct the closure at runtime.

If the blueprint ever leaks out to a user somehow that is a critical
bug and tvix-eval will panic.

As a ~treat~ test for this, the fibonacci function is being used as it
is a self-recursive closure (i.e. different instantiations of the same
"blueprint") getting called with different values and it's good to
have it around.

Change-Id: I485de675e9bb0c599ed7d5dc0f001eb34ab4c15f
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6323
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
2022-09-06 07:45:43 +00:00

63 lines
1.4 KiB
Rust

//! This module implements the runtime representation of functions.
use std::{
cell::{Ref, RefCell},
rc::Rc,
};
use crate::{chunk::Chunk, opcode::UpvalueIdx, Value};
#[derive(Clone, Debug)]
pub struct Lambda {
// name: Option<NixString>,
pub(crate) chunk: Chunk,
pub(crate) upvalue_count: usize,
}
impl Lambda {
pub fn new_anonymous() -> Self {
Lambda {
// name: None,
chunk: Default::default(),
upvalue_count: 0,
}
}
pub fn chunk(&mut self) -> &mut Chunk {
&mut self.chunk
}
}
#[derive(Clone, Debug)]
pub struct InnerClosure {
pub lambda: Rc<Lambda>,
pub upvalues: Vec<Value>,
}
#[repr(transparent)]
#[derive(Clone, Debug)]
pub struct Closure(Rc<RefCell<InnerClosure>>);
impl Closure {
pub fn new(lambda: Rc<Lambda>) -> Self {
Closure(Rc::new(RefCell::new(InnerClosure {
upvalues: Vec::with_capacity(lambda.upvalue_count),
lambda,
})))
}
pub fn chunk(&self) -> Ref<'_, Chunk> {
Ref::map(self.0.borrow(), |c| &c.lambda.chunk)
}
pub fn upvalue(&self, idx: UpvalueIdx) -> Ref<'_, Value> {
Ref::map(self.0.borrow(), |c| &c.upvalues[idx.0])
}
pub fn upvalue_count(&self) -> usize {
self.0.borrow().lambda.upvalue_count
}
pub fn push_upvalue(&self, value: Value) {
self.0.borrow_mut().upvalues.push(value)
}
}