about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAdam Joseph <adam@westernsemico.com>2022-11-28T08·18-0800
committertazjin <tazjin@tvl.su>2022-12-21T14·50+0000
commit922bf7aca9f4d4f4834ba5de7841ff58015c8791 (patch)
treefea67c12705be543fb569860573a8149208868c2
parente04b1697e4f4e8236418571c1f5938f0a9717bb7 (diff)
feat(tvix/eval): remove `derive(Copy)` from Upvalues r/5453
Change-Id: I0fa069fbeff6718a765ece948c2c1bce285496f7
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7449
Reviewed-by: grfn <grfn@gws.fyi>
Tested-by: BuildkiteCI
-rw-r--r--tvix/eval/src/value/function.rs22
-rw-r--r--tvix/eval/src/value/thunk.rs8
-rw-r--r--tvix/eval/src/vm.rs8
3 files changed, 26 insertions, 12 deletions
diff --git a/tvix/eval/src/value/function.rs b/tvix/eval/src/value/function.rs
index 4c2f3514f0..e31bb92b79 100644
--- a/tvix/eval/src/value/function.rs
+++ b/tvix/eval/src/value/function.rs
@@ -39,7 +39,14 @@ impl Formals {
 /// OpThunkSuspended referencing it.  At runtime `Lambda` is usually wrapped
 /// in `Rc` to avoid copying the `Chunk` it holds (which can be
 /// quite large).
-#[derive(Debug, Default)]
+///
+/// In order to correctly reproduce cppnix's "pointer equality"
+/// semantics it is important that we never clone a Lambda --
+/// use Rc<Lambda>::clone() instead.  This struct deliberately
+/// does not `derive(Clone)` in order to prevent this from being
+/// done accidentally.
+///
+#[derive(/* do not add Clone here */ Debug, Default)]
 pub struct Lambda {
     pub(crate) chunk: Chunk,
 
@@ -62,7 +69,14 @@ impl Lambda {
     }
 }
 
-#[derive(Clone, Debug)]
+///
+/// In order to correctly reproduce cppnix's "pointer equality"
+/// semantics it is important that we never clone a Lambda --
+/// use Rc<Lambda>::clone() instead.  This struct deliberately
+/// does not `derive(Clone)` in order to prevent this from being
+/// done accidentally.
+///
+#[derive(/* do not add Clone here */ Debug)]
 pub struct Closure {
     pub lambda: Rc<Lambda>,
     pub upvalues: Rc<Upvalues>,
@@ -88,7 +102,7 @@ impl Closure {
         self.lambda.clone()
     }
 
-    pub fn upvalues(&self) -> &Upvalues {
-        &self.upvalues
+    pub fn upvalues(&self) -> Rc<Upvalues> {
+        self.upvalues.clone()
     }
 }
diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs
index 54d2b31a2b..9f14159893 100644
--- a/tvix/eval/src/value/thunk.rs
+++ b/tvix/eval/src/value/thunk.rs
@@ -47,7 +47,7 @@ enum ThunkRepr {
     /// execution.
     Suspended {
         lambda: Rc<Lambda>,
-        upvalues: Upvalues,
+        upvalues: Rc<Upvalues>,
         span: Span,
     },
 
@@ -78,7 +78,7 @@ impl Thunk {
 
     pub fn new_suspended(lambda: Rc<Lambda>, span: Span) -> Self {
         Thunk(Rc::new(RefCell::new(ThunkRepr::Suspended {
-            upvalues: Upvalues::with_capacity(lambda.upvalue_count),
+            upvalues: Rc::new(Upvalues::with_capacity(lambda.upvalue_count)),
             lambda: lambda.clone(),
             span,
         })))
@@ -144,7 +144,7 @@ impl Thunk {
 
     pub fn upvalues(&self) -> Ref<'_, Upvalues> {
         Ref::map(self.0.borrow(), |thunk| match thunk {
-            ThunkRepr::Suspended { upvalues, .. } => upvalues,
+            ThunkRepr::Suspended { upvalues, .. } => upvalues.as_ref(),
             ThunkRepr::Evaluated(Value::Closure(c)) => &c.upvalues,
             _ => panic!("upvalues() on non-suspended thunk"),
         })
@@ -152,7 +152,7 @@ impl Thunk {
 
     pub fn upvalues_mut(&self) -> RefMut<'_, Upvalues> {
         RefMut::map(self.0.borrow_mut(), |thunk| match thunk {
-            ThunkRepr::Suspended { upvalues, .. } => upvalues,
+            ThunkRepr::Suspended { upvalues, .. } => Rc::get_mut(upvalues).unwrap(),
             ThunkRepr::Evaluated(Value::Closure(c)) => Rc::get_mut(
                 &mut Rc::get_mut(c).unwrap().upvalues,
             )
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs
index 4ea155f5a3..f0764c314f 100644
--- a/tvix/eval/src/vm.rs
+++ b/tvix/eval/src/vm.rs
@@ -22,7 +22,7 @@ struct CallFrame {
 
     /// Optional captured upvalues of this frame (if a thunk or
     /// closure if being evaluated).
-    upvalues: Upvalues,
+    upvalues: Rc<Upvalues>,
 
     /// Instruction pointer to the instruction currently being
     /// executed.
@@ -242,7 +242,7 @@ impl<'o> VM<'o> {
     /// been forced.
     pub fn call_value(&mut self, callable: &Value) -> EvalResult<()> {
         match callable {
-            Value::Closure(c) => self.enter_frame(c.lambda(), c.upvalues().clone(), 1),
+            Value::Closure(c) => self.enter_frame(c.lambda(), c.upvalues(), 1),
 
             Value::Builtin(b) => self.call_builtin(b.clone()),
 
@@ -347,7 +347,7 @@ impl<'o> VM<'o> {
     pub fn enter_frame(
         &mut self,
         lambda: Rc<Lambda>,
-        upvalues: Upvalues,
+        upvalues: Rc<Upvalues>,
         arg_count: usize,
     ) -> EvalResult<()> {
         self.observer
@@ -1090,7 +1090,7 @@ pub fn run_lambda(
     // with the span of the entire file for top-level expressions.
     let root_span = lambda.chunk.get_span(CodeIdx(lambda.chunk.code.len() - 1));
 
-    vm.enter_frame(lambda, Upvalues::with_capacity(0), 0)?;
+    vm.enter_frame(lambda, Rc::new(Upvalues::with_capacity(0)), 0)?;
     let value = vm.pop();
 
     value