From 2d305fd5b37fa7bf5a0512e8992b4557a1745296 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Tue, 21 Mar 2023 01:13:14 +0300 Subject: refactor(tvix/eval): retain call frames when entering calls 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 Tested-by: BuildkiteCI Autosubmit: tazjin --- tvix/eval/src/vm/mod.rs | 19 +++++++++++++------ 1 file 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, + 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), -- cgit 1.4.1