diff options
Diffstat (limited to 'tvix/eval/src/value')
-rw-r--r-- | tvix/eval/src/value/list.rs | 10 | ||||
-rw-r--r-- | tvix/eval/src/value/mod.rs | 45 | ||||
-rw-r--r-- | tvix/eval/src/value/thunk.rs | 29 |
3 files changed, 78 insertions, 6 deletions
diff --git a/tvix/eval/src/value/list.rs b/tvix/eval/src/value/list.rs index 3be5d414572c..42d91b6b26b2 100644 --- a/tvix/eval/src/value/list.rs +++ b/tvix/eval/src/value/list.rs @@ -102,3 +102,13 @@ impl IntoIterator for NixList { self.0.into_iter() } } + +impl<'a> IntoIterator for &'a NixList { + type Item = &'a Value; + + type IntoIter = std::slice::Iter<'a, Value>; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs index 78f4f5de67a4..1dc39d6832c5 100644 --- a/tvix/eval/src/value/mod.rs +++ b/tvix/eval/src/value/mod.rs @@ -27,6 +27,8 @@ pub use path::canon_path; pub use string::NixString; pub use thunk::Thunk; +use self::thunk::ThunkSet; + #[warn(variant_size_differences)] #[derive(Clone, Debug, PartialEq)] pub enum Value { @@ -341,6 +343,49 @@ impl Value { _ => Ok(ForceResult::Immediate(self)), } } + + /// Ensure `self` is *deeply* forced, including all recursive sub-values + pub(crate) fn deep_force( + &self, + vm: &mut VM, + thunk_set: &mut ThunkSet, + ) -> Result<(), ErrorKind> { + match self { + Value::Null + | Value::Bool(_) + | Value::Integer(_) + | Value::Float(_) + | Value::String(_) + | Value::Path(_) + | Value::Closure(_) + | Value::Builtin(_) + | Value::AttrNotFound + | Value::Blueprint(_) + | Value::DeferredUpvalue(_) + | Value::UnresolvedPath(_) => Ok(()), + Value::Attrs(a) => { + for (_, v) in a.iter() { + v.deep_force(vm, thunk_set)?; + } + Ok(()) + } + Value::List(l) => { + for val in l { + val.deep_force(vm, thunk_set)?; + } + Ok(()) + } + Value::Thunk(thunk) => { + if !thunk_set.insert(thunk) { + return Ok(()); + } + + thunk.force(vm)?; + let value = thunk.value().clone(); + value.deep_force(vm, thunk_set) + } + } + } } impl Display for Value { diff --git a/tvix/eval/src/value/thunk.rs b/tvix/eval/src/value/thunk.rs index 818ec0f58aec..7bad7e8777ba 100644 --- a/tvix/eval/src/value/thunk.rs +++ b/tvix/eval/src/value/thunk.rs @@ -20,6 +20,7 @@ use std::{ cell::{Ref, RefCell, RefMut}, + collections::HashSet, fmt::Display, rc::Rc, }; @@ -86,13 +87,12 @@ impl Thunk { }))) } - /// Evaluate the content of a thunk, potentially repeatedly, until - /// a non-thunk value is returned. + /// Evaluate the content of a thunk, potentially repeatedly, until a + /// non-thunk value is returned. /// - /// This will change the existing thunk (and thus all references - /// to it, providing memoization) through interior mutability. In - /// case of nested thunks, the intermediate thunk representations - /// are replaced. + /// This will change the existing thunk (and thus all references to it, + /// providing memoization) through interior mutability. In case of nested + /// thunks, the intermediate thunk representations are replaced. pub fn force(&self, vm: &mut VM) -> Result<(), ErrorKind> { loop { let mut thunk_mut = self.0.borrow_mut(); @@ -200,3 +200,20 @@ impl Display for Thunk { } } } + +/// A wrapper type for tracking which thunks have already been seen in a +/// context. This is necessary for cycle detection. +/// +/// The inner `HashSet` is not available on the outside, as it would be +/// potentially unsafe to interact with the pointers in the set. +#[derive(Default)] +pub struct ThunkSet(HashSet<*mut ThunkRepr>); + +impl ThunkSet { + /// Check whether the given thunk has already been seen. Will mark the thunk + /// as seen otherwise. + pub fn insert(&mut self, thunk: &Thunk) -> bool { + let ptr: *mut ThunkRepr = thunk.0.as_ptr(); + self.0.insert(ptr) + } +} |