diff options
-rw-r--r-- | tvix/eval/src/errors.rs | 3 | ||||
-rw-r--r-- | tvix/eval/src/opcode.rs | 1 | ||||
-rw-r--r-- | tvix/eval/src/value/thunk.rs | 32 | ||||
-rw-r--r-- | tvix/eval/src/vm.rs | 12 |
4 files changed, 46 insertions, 2 deletions
diff --git a/tvix/eval/src/errors.rs b/tvix/eval/src/errors.rs index 49a051c16eee..32d37d78156d 100644 --- a/tvix/eval/src/errors.rs +++ b/tvix/eval/src/errors.rs @@ -38,6 +38,9 @@ pub enum ErrorKind { // Attempt to call something that is not callable. NotCallable, + // Infinite recursion encountered while forcing thunks. + InfiniteRecursion, + ParseErrors(Vec<rnix::parser::ParseError>), AssertionFailed, diff --git a/tvix/eval/src/opcode.rs b/tvix/eval/src/opcode.rs index a8802e8c0a2b..18f0fce3cb0b 100644 --- a/tvix/eval/src/opcode.rs +++ b/tvix/eval/src/opcode.rs @@ -109,6 +109,7 @@ pub enum OpCode { // Thunks OpThunk(ConstantIdx), + OpForce, /// Finalise initialisation of the upvalues of the value in the /// given stack index after the scope is fully bound. diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs index 9d52ded3c43c..892868d0f660 100644 --- a/tvix/eval/src/value/thunk.rs +++ b/tvix/eval/src/value/thunk.rs @@ -23,7 +23,7 @@ use std::{ rc::Rc, }; -use crate::{upvalues::UpvalueCarrier, Value}; +use crate::{errors::ErrorKind, upvalues::UpvalueCarrier, vm::VM, EvalResult, Value}; use super::Lambda; @@ -55,6 +55,36 @@ impl Thunk { lambda, }))) } + + pub fn force(&self, vm: &mut VM) -> EvalResult<Ref<'_, Value>> { + // Due to mutable borrowing rules, the following code can't + // easily use a match statement or something like that; it + // requires a bit of manual fiddling. + let mut thunk_mut = self.0.borrow_mut(); + + if let ThunkRepr::Blackhole = *thunk_mut { + return Err(ErrorKind::InfiniteRecursion.into()); + } + + if matches!(*thunk_mut, ThunkRepr::Suspended { .. }) { + if let ThunkRepr::Suspended { lambda, upvalues } = + std::mem::replace(&mut *thunk_mut, ThunkRepr::Blackhole) + { + vm.call(lambda, upvalues, 0); + *thunk_mut = ThunkRepr::Evaluated(vm.run()?); + } + } + + drop(thunk_mut); + + // Otherwise it's already ThunkRepr::Evaluated and we do not + // need another branch. + + Ok(Ref::map(self.0.borrow(), |t| match t { + ThunkRepr::Evaluated(value) => value, + _ => unreachable!("already evaluated"), + })) + } } impl UpvalueCarrier for Thunk { diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs index e32c0d7f6b62..c55bc81777f1 100644 --- a/tvix/eval/src/vm.rs +++ b/tvix/eval/src/vm.rs @@ -136,7 +136,7 @@ impl VM { self.frames.push(frame); } - fn run(&mut self) -> EvalResult<Value> { + pub fn run(&mut self) -> EvalResult<Value> { #[cfg(feature = "disassembler")] let mut tracer = Tracer::new(); @@ -441,6 +441,16 @@ impl VM { self.populate_upvalues(upvalue_count, upvalues)?; } + OpCode::OpForce => { + let mut value = self.pop(); + + while let Value::Thunk(thunk) = value { + value = thunk.force(self)?.clone(); + } + + self.push(value); + } + OpCode::OpFinalise(StackIdx(idx)) => { match &self.stack[self.frame().stack_offset + idx] { Value::Closure(closure) => closure |