about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/eval/src/builtins/impure.rs3
-rw-r--r--tvix/eval/src/compiler/mod.rs3
-rw-r--r--tvix/eval/src/spans.rs37
-rw-r--r--tvix/eval/src/value/thunk.rs21
-rw-r--r--tvix/eval/src/vm.rs9
5 files changed, 61 insertions, 12 deletions
diff --git a/tvix/eval/src/builtins/impure.rs b/tvix/eval/src/builtins/impure.rs
index ee6cb2afdee4..b086df2f843d 100644
--- a/tvix/eval/src/builtins/impure.rs
+++ b/tvix/eval/src/builtins/impure.rs
@@ -12,6 +12,7 @@ use crate::{
     compiler::GlobalsMap,
     errors::ErrorKind,
     observer::NoOpObserver,
+    spans::LightSpan,
     value::{Builtin, BuiltinArgument, NixAttrs, Thunk},
     vm::VM,
     SourceCode, Value,
@@ -176,7 +177,7 @@ pub fn builtins_import(globals: &Weak<GlobalsMap>, source: SourceCode) -> Builti
             let res = entry
                 .insert(Value::Thunk(Thunk::new_suspended(
                     result.lambda,
-                    current_span,
+                    LightSpan::new_actual(current_span),
                 )))
                 .clone();
 
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
index b8d8fb11a247..ae31d2714513 100644
--- a/tvix/eval/src/compiler/mod.rs
+++ b/tvix/eval/src/compiler/mod.rs
@@ -28,6 +28,7 @@ use crate::chunk::Chunk;
 use crate::errors::{Error, ErrorKind, EvalResult};
 use crate::observer::CompilerObserver;
 use crate::opcode::{CodeIdx, Count, JumpOffset, OpCode, UpvalueIdx};
+use crate::spans::LightSpan;
 use crate::spans::ToSpan;
 use crate::value::{Closure, Formals, Lambda, Thunk, Value};
 use crate::warnings::{EvalWarning, WarningKind};
@@ -946,7 +947,7 @@ impl Compiler<'_> {
         if lambda.upvalue_count == 0 {
             self.emit_constant(
                 if is_suspended_thunk {
-                    Value::Thunk(Thunk::new_suspended(lambda, span))
+                    Value::Thunk(Thunk::new_suspended(lambda, LightSpan::new_actual(span)))
                 } else {
                     Value::Closure(Rc::new(Closure::new(lambda)))
                 },
diff --git a/tvix/eval/src/spans.rs b/tvix/eval/src/spans.rs
index 9998e438b220..d2a8e9badf86 100644
--- a/tvix/eval/src/spans.rs
+++ b/tvix/eval/src/spans.rs
@@ -1,9 +1,46 @@
 //! Utilities for dealing with span tracking in the compiler and in
 //! error reporting.
 
+use crate::opcode::CodeIdx;
+use crate::value::Lambda;
 use codemap::{File, Span};
 use rnix::ast;
 use rowan::ast::AstNode;
+use std::rc::Rc;
+
+/// Helper struct to carry information required for making a span, but
+/// without actually performing the (expensive) span lookup.
+///
+/// This is used for tracking spans across thunk boundaries, as they
+/// are frequently instantiated but spans are only used in error or
+/// warning cases.
+#[derive(Clone, Debug)]
+pub enum LightSpan {
+    /// The span has already been computed and can just be used right
+    /// away.
+    Actual { span: Span },
+
+    /// The span needs to be computed from the provided data, but only
+    /// when it is required.
+    Delayed { lambda: Rc<Lambda>, offset: CodeIdx },
+}
+
+impl LightSpan {
+    pub fn new_delayed(lambda: Rc<Lambda>, offset: CodeIdx) -> Self {
+        Self::Delayed { lambda, offset }
+    }
+
+    pub fn new_actual(span: Span) -> Self {
+        Self::Actual { span }
+    }
+
+    pub fn span(&self) -> Span {
+        match self {
+            LightSpan::Actual { span } => *span,
+            LightSpan::Delayed { lambda, offset } => lambda.chunk.get_span(*offset),
+        }
+    }
+}
 
 /// Trait implemented by all types from which we can retrieve a span.
 pub trait ToSpan {
diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs
index 420c5fe039aa..680cc9b1fbb1 100644
--- a/tvix/eval/src/value/thunk.rs
+++ b/tvix/eval/src/value/thunk.rs
@@ -24,11 +24,10 @@ use std::{
     rc::Rc,
 };
 
-use codemap::Span;
-
 use crate::{
     chunk::Chunk,
     errors::{Error, ErrorKind},
+    spans::LightSpan,
     upvalues::Upvalues,
     value::{Builtin, Closure},
     vm::VM,
@@ -49,7 +48,7 @@ enum ThunkRepr {
     Suspended {
         lambda: Rc<Lambda>,
         upvalues: Rc<Upvalues>,
-        span: Span,
+        light_span: LightSpan,
     },
 
     /// Thunk currently under-evaluation; encountering a blackhole
@@ -77,11 +76,11 @@ impl Thunk {
         )))))
     }
 
-    pub fn new_suspended(lambda: Rc<Lambda>, span: Span) -> Self {
+    pub fn new_suspended(lambda: Rc<Lambda>, light_span: LightSpan) -> Self {
         Thunk(Rc::new(RefCell::new(ThunkRepr::Suspended {
             upvalues: Rc::new(Upvalues::with_capacity(lambda.upvalue_count)),
             lambda: lambda.clone(),
-            span,
+            light_span,
         })))
     }
 
@@ -124,7 +123,7 @@ impl Thunk {
         Thunk(Rc::new(RefCell::new(ThunkRepr::Suspended {
             lambda: Rc::new(lambda),
             upvalues: Rc::new(Upvalues::with_capacity(0)),
-            span: span,
+            light_span: LightSpan::new_actual(span),
         })))
     }
 
@@ -151,12 +150,16 @@ impl Thunk {
                     if let ThunkRepr::Suspended {
                         lambda,
                         upvalues,
-                        span,
+                        light_span,
                     } = std::mem::replace(&mut *thunk_mut, ThunkRepr::Blackhole)
                     {
                         drop(thunk_mut);
-                        vm.enter_frame(lambda, upvalues, 0)
-                            .map_err(|e| ErrorKind::ThunkForce(Box::new(Error { span, ..e })))?;
+                        vm.enter_frame(lambda, upvalues, 0).map_err(|e| {
+                            ErrorKind::ThunkForce(Box::new(Error {
+                                span: light_span.span(),
+                                ..e
+                            }))
+                        })?;
                         (*self.0.borrow_mut()) = ThunkRepr::Evaluated(vm.pop())
                     }
                 }
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs
index f0764c314fb3..37d30a6c0178 100644
--- a/tvix/eval/src/vm.rs
+++ b/tvix/eval/src/vm.rs
@@ -10,6 +10,7 @@ use crate::{
     nix_search_path::NixSearchPath,
     observer::RuntimeObserver,
     opcode::{CodeIdx, Count, JumpOffset, OpCode, StackIdx, UpvalueIdx},
+    spans::LightSpan,
     unwrap_or_clone_rc,
     upvalues::Upvalues,
     value::{Builtin, Closure, CoercionKind, Lambda, NixAttrs, NixList, Thunk, Value},
@@ -211,6 +212,12 @@ impl<'o> VM<'o> {
         self.chunk().get_span(self.frame().ip - 1)
     }
 
+    /// Returns the information needed to calculate the current span,
+    /// but without performing that calculation.
+    fn current_light_span(&self) -> LightSpan {
+        LightSpan::new_delayed(self.frame().lambda.clone(), self.frame().ip - 1)
+    }
+
     /// Construct an error from the given ErrorKind and the source
     /// span of the current instruction.
     pub fn error(&self, kind: ErrorKind) -> Error {
@@ -884,7 +891,7 @@ impl<'o> VM<'o> {
                     );
                     Thunk::new_closure(blueprint)
                 } else {
-                    Thunk::new_suspended(blueprint, self.current_span())
+                    Thunk::new_suspended(blueprint, self.current_light_span())
                 };
                 let upvalues = thunk.upvalues_mut();
                 self.push(Value::Thunk(thunk.clone()));