From da7c331d2c9d923b9a79f21e55aa09c6c427645d Mon Sep 17 00:00:00 2001 From: Adam Joseph Date: Mon, 28 Nov 2022 00:52:25 -0800 Subject: feat(tvix/eval): add thunks with suspended native Rust code 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 Tested-by: BuildkiteCI --- tvix/eval/src/value/thunk.rs | 46 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs index 9f14159893..420c5fe039 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 Result>>, + ) -> Self { + let span = codemap::CodeMap::new() + .add_file("".to_owned(), "".to_owned()) + .span; + let builtin = Builtin::new( + "Thunk::new_suspended_native()", + &[crate::value::builtin::BuiltinArgument { + strict: true, + name: "fake", + }], + None, + move |v: Vec, 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. /// -- cgit 1.4.1