feat(tvix/eval): semi-strictly evaluate output values of the VM

This essentially makes the VM behave like `nix-instantiate --eval
--strict`, i.e. data structures are traversed strictly and thunks are
forced. Thunks embedded in closures are not forced.

This allows us to re-enable tests that were disabled because they
needed to output nested thunk contents, but is overall a behaviour
that must be configurable later on, as it is not cmopatible with e.g.
an evaluation of nixpkgs.

Change-Id: I5303a5c8e4322feab1384fdb7712fecb950afca5
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6372
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2022-08-31 04:47:54 +03:00 committed by tazjin
parent 6c6f9ae661
commit afcfa78364
3 changed files with 36 additions and 4 deletions

View file

@ -1,3 +0,0 @@
# TODO: temporarily disabled because need "strict output" (b is
# thunked)
{ a = { b = null; }; }

View file

@ -0,0 +1 @@
{ a = { b = null; }; }

View file

@ -602,6 +602,38 @@ impl VM {
Ok(())
}
/// Strictly evaluate the supplied value for outputting it. This
/// will ensure that lists and attribute sets do not contain
/// chunks which, for users, are displayed in a strange and often
/// unexpected way.
fn force_for_output(&mut self, value: &Value) -> EvalResult<()> {
match value {
Value::Attrs(attrs) => {
for (_, value) in attrs.iter() {
self.force_for_output(value)?;
}
Ok(())
}
Value::List(list) => list.iter().try_for_each(|elem| self.force_for_output(elem)),
Value::Thunk(thunk) => {
thunk.force(self)?;
self.force_for_output(&thunk.value())
}
// If any of these internal values are encountered here a
// critical error has happened (likely a compiler bug).
Value::AttrPath(_)
| Value::AttrNotFound
| Value::DynamicUpvalueMissing(_)
| Value::Blueprint(_)
| Value::DeferredUpvalue(_) => panic!("tvix bug: internal value left on stack"),
_ => Ok(()),
}
}
}
// TODO: use Rc::unwrap_or_clone once it is stabilised.
@ -618,5 +650,7 @@ pub fn run_lambda(lambda: Lambda) -> EvalResult<Value> {
};
vm.call(Rc::new(lambda), vec![], 0);
vm.run()
let value = vm.run()?;
vm.force_for_output(&value)?;
Ok(value)
}