about summary refs log tree commit diff
diff options
context:
space:
mode:
-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 f81ae070a2ce..8099a5238d02 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 7c98fcb4e192..d73e5ccfa8f2 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 4e50b7279c38..5af5f9d7588e 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 41f51a987459..d616b9ce1c5c 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,
+    })
 }