about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAdam Joseph <adam@westernsemico.com>2023-11-12T13·41-0800
committerclbot <clbot@tvl.fyi>2023-11-25T02·55+0000
commitb5a15989cddd8c1eb05ef648b0df1dc631daf9f2 (patch)
tree6a5cda7b048f029ca3140eaff654e4dca503825c
parent875bb26fc314b22f4a6fa2e457cf7ec5a44e7954 (diff)
feat(tvix/eval): add Thunk::unwrap_or_clone() r/7054
This commit adds Thunk::unwrap_or_clone(), which uses
Rc::try_unwrap() to avoid cloning the Value out of a an Rc which has
only one strong reference.

Change-Id: Icacefe0c823dcddf046d90c0c5cd5ed59fe976d4
Reviewed-on: https://cl.tvl.fyi/c/depot/+/10037
Reviewed-by: tazjin <tazjin@tvl.su>
Autosubmit: Adam Joseph <adam@westernsemico.com>
Tested-by: BuildkiteCI
-rw-r--r--tvix/eval/src/value/thunk.rs35
1 files changed, 34 insertions, 1 deletions
diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs
index 9c048d40b499..1ff5beb0d472 100644
--- a/tvix/eval/src/value/thunk.rs
+++ b/tvix/eval/src/value/thunk.rs
@@ -92,6 +92,28 @@ impl ThunkRepr {
             ThunkRepr::Suspended { lambda, .. } => format!("thunk({:p})", *lambda),
         }
     }
+
+    /// Return the Value within a fully-evaluated ThunkRepr; panics
+    /// if the thunk is not fully-evaluated.
+    fn expect(self) -> Value {
+        match self {
+            ThunkRepr::Evaluated(value) => value,
+            ThunkRepr::Blackhole { .. } => panic!("Thunk::expect() called on a black-holed thunk"),
+            ThunkRepr::Suspended { .. } | ThunkRepr::Native(_) => {
+                panic!("Thunk::expect() called on a suspended thunk")
+            }
+        }
+    }
+
+    fn expect_ref(&self) -> &Value {
+        match self {
+            ThunkRepr::Evaluated(value) => value,
+            ThunkRepr::Blackhole { .. } => panic!("Thunk::expect() called on a black-holed thunk"),
+            ThunkRepr::Suspended { .. } | ThunkRepr::Native(_) => {
+                panic!("Thunk::expect() called on a suspended thunk")
+            }
+        }
+    }
 }
 
 /// A thunk is created for any value which requires non-strict
@@ -178,7 +200,7 @@ impl Thunk {
         // If the current thunk is already fully evaluated, return its evaluated
         // value. The VM will continue running the code that landed us here.
         if self.is_forced() {
-            return Ok(self.value().clone());
+            return Ok(self.unwrap_or_clone());
         }
 
         // Begin evaluation of this thunk by marking it as a blackhole, meaning
@@ -280,6 +302,17 @@ impl Thunk {
         })
     }
 
+    /// Returns the inner evaluated value of a thunk, cloning it if
+    /// the Rc has more than one strong reference.  It is an error
+    /// to call this on a thunk that has not been forced, or is not
+    /// otherwise known to be fully evaluated.
+    fn unwrap_or_clone(self) -> Value {
+        match Rc::try_unwrap(self.0) {
+            Ok(refcell) => refcell.into_inner().expect(),
+            Err(rc) => Ref::map(rc.borrow(), |thunkrepr| thunkrepr.expect_ref()).clone(),
+        }
+    }
+
     pub fn upvalues(&self) -> Ref<'_, Upvalues> {
         Ref::map(self.0.borrow(), |thunk| match thunk {
             ThunkRepr::Suspended { upvalues, .. } => upvalues.as_ref(),