diff --git a/corp/tvixbolt/src/main.rs b/corp/tvixbolt/src/main.rs index f81ae070a..8099a5238 100644 --- a/corp/tvixbolt/src/main.rs +++ b/corp/tvixbolt/src/main.rs @@ -292,7 +292,18 @@ fn eval(trace: bool, code: &str) -> Output { }; match result { - Ok(value) => writeln!(&mut out.output, "{}", value).unwrap(), + Ok(result) => { + for warning in result.warnings { + writeln!( + &mut out.warnings, + "{}\n", + warning.fancy_format_str(&source).trim(), + ) + .unwrap(); + } + + writeln!(&mut out.output, "{}", result.value).unwrap() + } Err(err) => writeln!( &mut out.runtime_errors, "{}", diff --git a/tvix/eval/src/builtins/impure.rs b/tvix/eval/src/builtins/impure.rs index 7c98fcb4e..d73e5ccfa 100644 --- a/tvix/eval/src/builtins/impure.rs +++ b/tvix/eval/src/builtins/impure.rs @@ -51,7 +51,7 @@ pub fn builtins_import( Builtin::new( "import", &[true], - move |mut args: Vec, _: &mut VM| { + move |mut args: Vec, vm: &mut VM| { let path = match args.pop().unwrap() { Value::Path(path) => path, Value::String(_) => { @@ -102,9 +102,9 @@ pub fn builtins_import( }); } - // TODO: deal with runtime *warnings* (most likely through an - // emit_warning function on the VM that might return it together with - // the result) + for warning in result.warnings { + vm.push_warning(warning); + } // Compilation succeeded, we can construct a thunk from whatever it spat // out and return that. diff --git a/tvix/eval/src/eval.rs b/tvix/eval/src/eval.rs index 4e50b7279..5af5f9d75 100644 --- a/tvix/eval/src/eval.rs +++ b/tvix/eval/src/eval.rs @@ -115,5 +115,11 @@ pub fn interpret(code: &str, location: Option, options: Options) -> Eva err.fancy_format_stderr(&source); } - result + result.map(|r| { + for warning in r.warnings { + warning.fancy_format_stderr(&source); + } + + r.value + }) } diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs index 41f51a987..d616b9ce1 100644 --- a/tvix/eval/src/vm.rs +++ b/tvix/eval/src/vm.rs @@ -10,6 +10,7 @@ use crate::{ opcode::{CodeIdx, Count, JumpOffset, OpCode, StackIdx, UpvalueIdx}, upvalues::{UpvalueCarrier, Upvalues}, value::{Builtin, Closure, CoercionKind, Lambda, NixAttrs, NixList, Thunk, Value}, + warnings::{EvalWarning, WarningKind}, }; struct CallFrame { @@ -43,9 +44,18 @@ pub struct VM<'o> { /// dynamically resolved (`with`). with_stack: Vec, + /// Runtime warnings collected during evaluation. + warnings: Vec, + observer: &'o mut dyn RuntimeObserver, } +/// The result of a VM's runtime evaluation. +pub struct RuntimeResult { + pub value: Value, + pub warnings: Vec, +} + /// This macro wraps a computation that returns an ErrorKind or a /// result, and wraps the ErrorKind in an Error struct if present. /// @@ -133,6 +143,7 @@ impl<'o> VM<'o> { frames: vec![], stack: vec![], with_stack: vec![], + warnings: vec![], } } @@ -182,6 +193,20 @@ impl<'o> VM<'o> { } } + /// Push an already constructed warning. + pub fn push_warning(&mut self, warning: EvalWarning) { + self.warnings.push(warning); + } + + /// Emit a warning with the given WarningKind and the source span + /// of the current instruction. + pub fn emit_warning(&mut self, kind: WarningKind) { + self.push_warning(EvalWarning { + kind, + span: self.current_span(), + }); + } + /// Execute the given value in this VM's context, if it is a /// callable. /// @@ -780,9 +805,16 @@ fn unwrap_or_clone_rc(rc: Rc) -> T { Rc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()) } -pub fn run_lambda(observer: &mut dyn RuntimeObserver, lambda: Rc) -> EvalResult { +pub fn run_lambda( + observer: &mut dyn RuntimeObserver, + lambda: Rc, +) -> EvalResult { let mut vm = VM::new(observer); let value = vm.call(lambda, Upvalues::with_capacity(0), 0)?; vm.force_for_output(&value)?; - Ok(value) + + Ok(RuntimeResult { + value, + warnings: vm.warnings, + }) }