feat(tvix/eval): Implement builtins.deepSeq
This is done via a new `deepForce` function on Value. Since values can be cyclical (for example, see the test-case), we need to do some extra work to avoid RefCell borrow errors if we ever hit a graph cycle: While deep-forcing values, we keep a set of thunks that we have already seen and avoid doing any work on the same thunk twice. The set is encapsulated in a separate type to stop potentially invalid pointers from leaking out. Finally, since deep_force is conceptually similar to `VM::force_for_output` (but more suited to usage in eval since it doesn't clone the values) this removes the latter, replacing it with the former. Co-Authored-By: Vincent Ambo <tazjin@tvl.su> Change-Id: Iefddefcf09fae3b6a4d161a5873febcff54b9157 Reviewed-on: https://cl.tvl.fyi/c/depot/+/7000 Tested-by: BuildkiteCI Reviewed-by: grfn <grfn@gws.fyi> Reviewed-by: tazjin <tazjin@tvl.su>
This commit is contained in:
parent
8724d2fff8
commit
d4fa3152e9
10 changed files with 100 additions and 62 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue