about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-10-05T14·01+0300
committertazjin <tazjin@tvl.su>2022-10-07T14·24+0000
commit07e03498f26afbf647102d656c90d447f8586820 (patch)
tree0f891cb5f2601e12bbbe89d10961f4f9b2d16fc1
parent4b9178fa2ae4cab718225f6136791df1d11814ee (diff)
feat(tvix/eval): add method for emitting runtime warnings r/5049
This lets the VM emit warnings when it encounters situations that
should only be warned about at runtime.

For starters, this is used to pass through compilation warnings that
come up when `import` is used.

Change-Id: I0c4bc8c534d699999887c430d93629fadfa662c4
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6868
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
-rw-r--r--corp/tvixbolt/src/main.rs13
-rw-r--r--tvix/eval/src/builtins/impure.rs8
-rw-r--r--tvix/eval/src/eval.rs8
-rw-r--r--tvix/eval/src/vm.rs36
4 files changed, 57 insertions, 8 deletions
diff --git a/corp/tvixbolt/src/main.rs b/corp/tvixbolt/src/main.rs
index f81ae070a2..8099a5238d 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 7c98fcb4e1..d73e5ccfa8 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<Value>, _: &mut VM| {
+        move |mut args: Vec<Value>, 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 4e50b7279c..5af5f9d758 100644
--- a/tvix/eval/src/eval.rs
+++ b/tvix/eval/src/eval.rs
@@ -115,5 +115,11 @@ pub fn interpret(code: &str, location: Option<PathBuf>, 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 41f51a9874..d616b9ce1c 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<usize>,
 
+    /// Runtime warnings collected during evaluation.
+    warnings: Vec<EvalWarning>,
+
     observer: &'o mut dyn RuntimeObserver,
 }
 
+/// The result of a VM's runtime evaluation.
+pub struct RuntimeResult {
+    pub value: Value,
+    pub warnings: Vec<EvalWarning>,
+}
+
 /// 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<T: Clone>(rc: Rc<T>) -> T {
     Rc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone())
 }
 
-pub fn run_lambda(observer: &mut dyn RuntimeObserver, lambda: Rc<Lambda>) -> EvalResult<Value> {
+pub fn run_lambda(
+    observer: &mut dyn RuntimeObserver,
+    lambda: Rc<Lambda>,
+) -> EvalResult<RuntimeResult> {
     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,
+    })
 }