about summary refs log tree commit diff
path: root/tvix/eval/src/value
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2023-03-12T21·30+0300
committerclbot <clbot@tvl.fyi>2023-03-17T19·31+0000
commit3fa6b13c1e8cbd7a007365dbac0ffc30d03d8472 (patch)
tree5aa4b1692bc71730408dae780b172b777e4bf815 /tvix/eval/src/value
parent5095e4f2696ef85ee7c6ae0515eb8d1586459f8c (diff)
feat(tvix/eval): track span of first force in a thunk blackhole r/6025
This is step 1 towards being able to use all 4 spans that we know when
dealing with infinite recursion. It tracks the span at which the
force of a thunk was first requested when constructing a blackhole, so
that we can highlight the spans of the first and second forces.

These are actually the least relevant spans, but the easiest to put in
place, more coming soon.

Change-Id: I4c7e82f6211b98756439d4148a4191457cc46807
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8269
Autosubmit: tazjin <tazjin@tvl.su>
Tested-by: BuildkiteCI
Reviewed-by: flokli <flokli@flokli.de>
Diffstat (limited to 'tvix/eval/src/value')
-rw-r--r--tvix/eval/src/value/mod.rs5
-rw-r--r--tvix/eval/src/value/thunk.rs14
2 files changed, 11 insertions, 8 deletions
diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs
index f57cfda5ee..77d40b0e39 100644
--- a/tvix/eval/src/value/mod.rs
+++ b/tvix/eval/src/value/mod.rs
@@ -22,6 +22,7 @@ mod thunk;
 
 use crate::errors::ErrorKind;
 use crate::opcode::StackIdx;
+use crate::spans::LightSpan;
 use crate::vm::generators::{self, GenCo};
 use crate::AddContext;
 pub use attrs::NixAttrs;
@@ -611,9 +612,9 @@ impl Value {
     }
 
     // TODO(amjoseph): de-asyncify this (when called directly by the VM)
-    pub async fn force(self, co: GenCo) -> Result<Value, ErrorKind> {
+    pub async fn force(self, co: GenCo, span: LightSpan) -> Result<Value, ErrorKind> {
         if let Value::Thunk(thunk) = self {
-            return thunk.force(co).await;
+            return thunk.force(co, span).await;
         }
 
         Ok(self)
diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs
index f2959ba15c..0290e73ebb 100644
--- a/tvix/eval/src/value/thunk.rs
+++ b/tvix/eval/src/value/thunk.rs
@@ -65,7 +65,7 @@ enum ThunkRepr {
 
     /// Thunk currently under-evaluation; encountering a blackhole
     /// value means that infinite recursion has occured.
-    Blackhole,
+    Blackhole { forced_at: LightSpan },
 
     /// Fully evaluated thunk.
     Evaluated(Value),
@@ -75,7 +75,7 @@ impl ThunkRepr {
     fn debug_repr(&self) -> String {
         match self {
             ThunkRepr::Evaluated(v) => format!("thunk(val|{})", v),
-            ThunkRepr::Blackhole => "thunk(blackhole)".to_string(),
+            ThunkRepr::Blackhole { .. } => "thunk(blackhole)".to_string(),
             ThunkRepr::Native(_) => "thunk(native)".to_string(),
             ThunkRepr::Suspended { lambda, .. } => format!("thunk({:p})", *lambda),
         }
@@ -114,7 +114,7 @@ impl Thunk {
     }
 
     // TODO(amjoseph): de-asyncify this
-    pub async fn force(self, co: GenCo) -> Result<Value, ErrorKind> {
+    pub async fn force(self, co: GenCo, span: LightSpan) -> Result<Value, ErrorKind> {
         // 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() {
@@ -124,12 +124,14 @@ impl Thunk {
         // Begin evaluation of this thunk by marking it as a blackhole, meaning
         // that any other forcing frame encountering this thunk before its
         // evaluation is completed detected an evaluation cycle.
-        let inner = self.0.replace(ThunkRepr::Blackhole);
+        let inner = self.0.replace(ThunkRepr::Blackhole { forced_at: span });
 
         match inner {
             // If there was already a blackhole in the thunk, this is an
             // evaluation cycle.
-            ThunkRepr::Blackhole => Err(ErrorKind::InfiniteRecursion),
+            ThunkRepr::Blackhole { forced_at } => Err(ErrorKind::InfiniteRecursion {
+                first_force: forced_at.span(),
+            }),
 
             // If there is a native function stored in the thunk, evaluate it
             // and replace this thunk's representation with the result.
@@ -206,7 +208,7 @@ impl Thunk {
     pub fn value(&self) -> Ref<Value> {
         Ref::map(self.0.borrow(), |thunk| match thunk {
             ThunkRepr::Evaluated(value) => value,
-            ThunkRepr::Blackhole => panic!("Thunk::value called on a black-holed thunk"),
+            ThunkRepr::Blackhole { .. } => panic!("Thunk::value called on a black-holed thunk"),
             ThunkRepr::Suspended { .. } | ThunkRepr::Native(_) => {
                 panic!("Thunk::value called on a suspended thunk")
             }