diff options
author | Adam Joseph <adam@westernsemico.com> | 2022-11-28T08·52-0800 |
---|---|---|
committer | tazjin <tazjin@tvl.su> | 2022-12-21T21·48+0000 |
commit | da7c331d2c9d923b9a79f21e55aa09c6c427645d (patch) | |
tree | 2c962360dfe5a98ceae3a3c8ea2b1ef3a0ce24f1 /tvix/eval/src/value/thunk.rs | |
parent | 87995ed35575e31ee881c796a901fdf4005a6ccb (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/src/value/thunk.rs')
-rw-r--r-- | tvix/eval/src/value/thunk.rs | 46 |
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. /// |