about summary refs log tree commit diff
path: root/tvix/eval
diff options
context:
space:
mode:
authorAdam Joseph <adam@westernsemico.com>2022-11-28T08·52-0800
committertazjin <tazjin@tvl.su>2022-12-21T21·48+0000
commitda7c331d2c9d923b9a79f21e55aa09c6c427645d (patch)
tree2c962360dfe5a98ceae3a3c8ea2b1ef3a0ce24f1 /tvix/eval
parent87995ed35575e31ee881c796a901fdf4005a6ccb (diff)
feat(tvix/eval): add thunks with suspended native Rust code r/5455
Having thunks which, when forced, execute native Rust code rather
than interpreted opcodes lets us avoid having to bundle
`src/libexpr/primops/derivation.nix` like cppnix does by implementing
it in Rust instead.

Change-Id: If91d77a6736234321eee87ba4b4777eed5a3fe1c
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7450
Reviewed-by: grfn <grfn@gws.fyi>
Tested-by: BuildkiteCI
Diffstat (limited to 'tvix/eval')
-rw-r--r--tvix/eval/src/value/thunk.rs46
1 files changed, 45 insertions, 1 deletions
diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs
index 9f1415989335..420c5fe039aa 100644
--- a/tvix/eval/src/value/thunk.rs
+++ b/tvix/eval/src/value/thunk.rs
@@ -27,9 +27,10 @@ use std::{
 use codemap::Span;
 
 use crate::{
+    chunk::Chunk,
     errors::{Error, ErrorKind},
     upvalues::Upvalues,
-    value::Closure,
+    value::{Builtin, Closure},
     vm::VM,
     Value,
 };
@@ -84,6 +85,49 @@ impl Thunk {
         })))
     }
 
+    /// Create a new thunk from suspended Rust code.
+    ///
+    /// The suspended code will be executed and expected to return a
+    /// value whenever the thunk is forced like any other thunk.
+    pub fn new_suspended_native(
+        native: Rc<Box<dyn Fn(&mut VM) -> Result<Value, ErrorKind>>>,
+    ) -> Self {
+        let span = codemap::CodeMap::new()
+            .add_file("<internal>".to_owned(), "<internal>".to_owned())
+            .span;
+        let builtin = Builtin::new(
+            "Thunk::new_suspended_native()",
+            &[crate::value::builtin::BuiltinArgument {
+                strict: true,
+                name: "fake",
+            }],
+            None,
+            move |v: Vec<Value>, vm: &mut VM| {
+                // sanity check that only the dummy argument was popped
+                assert_eq!(v.len(), 1);
+                assert!(matches!(v[0], Value::Null));
+                native(vm)
+            },
+        );
+        let mut chunk = Chunk::default();
+        let constant_idx = chunk.push_constant(Value::Builtin(builtin));
+        // Tvix doesn't have "0-ary" builtins, so we have to push a fake argument
+        chunk.push_op(crate::opcode::OpCode::OpNull, span);
+        chunk.push_op(crate::opcode::OpCode::OpConstant(constant_idx), span);
+        chunk.push_op(crate::opcode::OpCode::OpCall, span);
+        let lambda = Lambda {
+            name: None,
+            formals: None,
+            upvalue_count: 0,
+            chunk,
+        };
+        Thunk(Rc::new(RefCell::new(ThunkRepr::Suspended {
+            lambda: Rc::new(lambda),
+            upvalues: Rc::new(Upvalues::with_capacity(0)),
+            span: span,
+        })))
+    }
+
     /// Evaluate the content of a thunk, potentially repeatedly, until a
     /// non-thunk value is returned.
     ///