about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-09-02T01·38+0300
committertazjin <tazjin@tvl.su>2022-09-08T07·59+0000
commit0d7ad5e6d1992d4f80f0ea08fee636b7e34eec59 (patch)
tree28474ff042e31b55d3c8698bbfa1d43e117c0282
parent5ee89bcf5ca3f0b0d3b809b01ac04bf38f51d7e4 (diff)
refactor(tvix/eval): pass a VM reference to builtins r/4744
This makes it possible for builtins to force values on their own,
without the VM having to apply a strictness mask to the arguments
first.

Change-Id: Ib49a94e56ca2a8d515c39647381ab55a727766e3
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6411
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
-rw-r--r--tvix/eval/src/builtins/mod.rs38
-rw-r--r--tvix/eval/src/value/builtin.rs18
-rw-r--r--tvix/eval/src/vm.rs2
3 files changed, 34 insertions, 24 deletions
diff --git a/tvix/eval/src/builtins/mod.rs b/tvix/eval/src/builtins/mod.rs
index 3e1f49d6995a..16ac418f6bd8 100644
--- a/tvix/eval/src/builtins/mod.rs
+++ b/tvix/eval/src/builtins/mod.rs
@@ -17,17 +17,17 @@ use crate::arithmetic_op;
 
 fn pure_builtins() -> Vec<Builtin> {
     vec![
-        Builtin::new("add", 2, |mut args| {
+        Builtin::new("add", 2, |mut args, _| {
             let b = args.pop().unwrap();
             let a = args.pop().unwrap();
             arithmetic_op!(a, b, +)
         }),
-        Builtin::new("abort", 1, |mut args| {
+        Builtin::new("abort", 1, |mut args, _| {
             return Err(ErrorKind::Abort(
                 args.pop().unwrap().to_string()?.as_str().to_owned(),
             ));
         }),
-        Builtin::new("catAttrs", 2, |mut args| {
+        Builtin::new("catAttrs", 2, |mut args, _| {
             let list = args.pop().unwrap().to_list()?;
             let key = args.pop().unwrap().to_string()?;
             let mut output = vec![];
@@ -40,64 +40,64 @@ fn pure_builtins() -> Vec<Builtin> {
 
             Ok(Value::List(NixList::construct(output.len(), output)))
         }),
-        Builtin::new("div", 2, |mut args| {
+        Builtin::new("div", 2, |mut args, _| {
             let b = args.pop().unwrap();
             let a = args.pop().unwrap();
             arithmetic_op!(a, b, /)
         }),
-        Builtin::new("length", 1, |args| {
+        Builtin::new("length", 1, |args, _| {
             Ok(Value::Integer(args[0].as_list()?.len() as i64))
         }),
-        Builtin::new("isAttrs", 1, |args| {
+        Builtin::new("isAttrs", 1, |args, _| {
             Ok(Value::Bool(matches!(args[0], Value::Attrs(_))))
         }),
-        Builtin::new("isBool", 1, |args| {
+        Builtin::new("isBool", 1, |args, _| {
             Ok(Value::Bool(matches!(args[0], Value::Bool(_))))
         }),
-        Builtin::new("isFloat", 1, |args| {
+        Builtin::new("isFloat", 1, |args, _| {
             Ok(Value::Bool(matches!(args[0], Value::Float(_))))
         }),
-        Builtin::new("isFunction", 1, |args| {
+        Builtin::new("isFunction", 1, |args, _| {
             Ok(Value::Bool(matches!(
                 args[0],
                 Value::Closure(_) | Value::Builtin(_)
             )))
         }),
-        Builtin::new("isInt", 1, |args| {
+        Builtin::new("isInt", 1, |args, _| {
             Ok(Value::Bool(matches!(args[0], Value::Integer(_))))
         }),
-        Builtin::new("isList", 1, |args| {
+        Builtin::new("isList", 1, |args, _| {
             Ok(Value::Bool(matches!(args[0], Value::List(_))))
         }),
-        Builtin::new("isNull", 1, |args| {
+        Builtin::new("isNull", 1, |args, _| {
             Ok(Value::Bool(matches!(args[0], Value::Null)))
         }),
-        Builtin::new("isPath", 1, |args| {
+        Builtin::new("isPath", 1, |args, _| {
             Ok(Value::Bool(matches!(args[0], Value::Path(_))))
         }),
-        Builtin::new("isString", 1, |args| {
+        Builtin::new("isString", 1, |args, _| {
             Ok(Value::Bool(matches!(args[0], Value::String(_))))
         }),
-        Builtin::new("mul", 2, |mut args| {
+        Builtin::new("mul", 2, |mut args, _| {
             let b = args.pop().unwrap();
             let a = args.pop().unwrap();
             arithmetic_op!(a, b, *)
         }),
-        Builtin::new("sub", 2, |mut args| {
+        Builtin::new("sub", 2, |mut args, _| {
             let b = args.pop().unwrap();
             let a = args.pop().unwrap();
             arithmetic_op!(a, b, -)
         }),
-        Builtin::new("throw", 1, |mut args| {
+        Builtin::new("throw", 1, |mut args, _| {
             return Err(ErrorKind::Throw(
                 args.pop().unwrap().to_string()?.as_str().to_owned(),
             ));
         }),
-        Builtin::new("toString", 1, |args| {
+        Builtin::new("toString", 1, |args, _| {
             // TODO: toString is actually not the same as Display
             Ok(Value::String(format!("{}", args[0]).into()))
         }),
-        Builtin::new("typeOf", 1, |args| {
+        Builtin::new("typeOf", 1, |args, _| {
             Ok(Value::String(args[0].type_of().into()))
         }),
     ]
diff --git a/tvix/eval/src/value/builtin.rs b/tvix/eval/src/value/builtin.rs
index d1248b1ec219..7572103ec96e 100644
--- a/tvix/eval/src/value/builtin.rs
+++ b/tvix/eval/src/value/builtin.rs
@@ -3,13 +3,23 @@
 //!
 //! Builtins are directly backed by Rust code operating on Nix values.
 
-use crate::errors::ErrorKind;
+use crate::{errors::ErrorKind, vm::VM};
 
 use super::Value;
 
 use std::fmt::{Debug, Display};
 
-pub type BuiltinFn = fn(arg: Vec<Value>) -> Result<Value, ErrorKind>;
+/// Function pointer type for builtins implemented directly by backing
+/// Rust code.
+///
+/// Builtins declare their arity and are passed a vector with the
+/// right number of arguments. Additionally, as they might have to
+/// force the evaluation of thunks, they are passed a reference to the
+/// current VM which they can use for forcing a value.
+///
+/// Errors returned from a builtin will be annotated with the location
+/// of the call to the builtin.
+pub type BuiltinFn = fn(arg: Vec<Value>, vm: &mut VM) -> Result<Value, ErrorKind>;
 
 /// Represents a single built-in function which directly executes Rust
 /// code that operates on a Nix value.
@@ -50,11 +60,11 @@ impl Builtin {
     /// Apply an additional argument to the builtin, which will either
     /// lead to execution of the function or to returning a partial
     /// builtin.
-    pub fn apply(mut self, arg: Value) -> Result<Value, ErrorKind> {
+    pub fn apply(mut self, vm: &mut VM, arg: Value) -> Result<Value, ErrorKind> {
         self.partials.push(arg);
 
         if self.partials.len() == self.arity {
-            return (self.func)(self.partials);
+            return (self.func)(self.partials, vm);
         }
 
         // Function is not yet ready to be called.
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs
index 5fabdb491e1f..318fc726abd7 100644
--- a/tvix/eval/src/vm.rs
+++ b/tvix/eval/src/vm.rs
@@ -418,7 +418,7 @@ impl VM {
 
                         Value::Builtin(builtin) => {
                             let arg = self.pop();
-                            let result = fallible!(self, builtin.apply(arg));
+                            let result = fallible!(self, builtin.apply(self, arg));
                             self.push(result);
                         }
                         _ => return Err(self.error(ErrorKind::NotCallable)),