about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2023-03-20T22·13+0300
committertazjin <tazjin@tvl.su>2023-03-27T09·02+0000
commit2d305fd5b37fa7bf5a0512e8992b4557a1745296 (patch)
tree5f554ecb08ce097864f7d2f420bf96d08bbc7c96
parent367a5e9922264b787667fd5e750c8eadf8a7796f (diff)
refactor(tvix/eval): retain call frames when entering calls r/6045
This grows the frame stack as the call stack grows, which yields *much*
better user-facing error messages.

I haven't measured the performance impact this has yet, for now I'm
still just trying to add more information to errors and then cut down
again where necessary.

Change-Id: I89f058ef31979edacf4667775d460b60704ce4d7
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8334
Reviewed-by: flokli <flokli@flokli.de>
Tested-by: BuildkiteCI
Autosubmit: tazjin <tazjin@tvl.su>
-rw-r--r--tvix/eval/src/vm/mod.rs19
1 files changed, 13 insertions, 6 deletions
diff --git a/tvix/eval/src/vm/mod.rs b/tvix/eval/src/vm/mod.rs
index 13158619c6..81c9ef39d9 100644
--- a/tvix/eval/src/vm/mod.rs
+++ b/tvix/eval/src/vm/mod.rs
@@ -508,7 +508,7 @@ impl<'o> VM<'o> {
 
                 OpCode::OpCall => {
                     let callable = self.stack_pop();
-                    self.call_value(frame.current_light_span(), Some(frame), callable)?;
+                    self.call_value(frame.current_light_span(), Some((span, frame)), callable)?;
 
                     // exit this loop and let the outer loop enter the new call
                     return Ok(true);
@@ -985,7 +985,7 @@ impl<'o> VM<'o> {
     fn call_value(
         &mut self,
         span: LightSpan,
-        parent: Option<CallFrame>,
+        parent: Option<(LightSpan, CallFrame)>,
         callable: Value,
     ) -> EvalResult<()> {
         match callable {
@@ -1002,6 +1002,13 @@ impl<'o> VM<'o> {
                 // `stack_len - 1`.
                 let stack_offset = self.stack.len() - 1;
 
+                // Reenqueue the parent frame, which should only have
+                // `OpReturn` left. Not throwing it away leads to more
+                // useful error traces.
+                if let Some((parent_span, parent_frame)) = parent {
+                    self.push_call_frame(parent_span, parent_frame);
+                }
+
                 self.push_call_frame(
                     span,
                     CallFrame {
@@ -1017,11 +1024,11 @@ impl<'o> VM<'o> {
 
             // Attribute sets with a __functor attribute are callable.
             val @ Value::Attrs(_) => {
-                let gen_span = parent
-                    .map(|p| p.current_light_span())
-                    .unwrap_or_else(|| self.reasonable_light_span());
+                if let Some((parent_span, parent_frame)) = parent {
+                    self.push_call_frame(parent_span, parent_frame);
+                }
 
-                self.enqueue_generator("__functor call", gen_span, |co| call_functor(co, val));
+                self.enqueue_generator("__functor call", span, |co| call_functor(co, val));
                 Ok(())
             }
             v => Err(ErrorKind::NotCallable(v.type_of())).with_span(&span, self),