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
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
use std::{ops::DerefMut, path::PathBuf, rc::Rc};
|
||||
|
||||
use codemap::Span;
|
||||
|
||||
use crate::{
|
||||
chunk::Chunk,
|
||||
errors::{Error, ErrorKind, EvalResult},
|
||||
|
|
@ -858,57 +856,6 @@ impl<'o> VM<'o> {
|
|||
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, root_span: Span) -> EvalResult<()> {
|
||||
match value {
|
||||
Value::Attrs(attrs) => {
|
||||
for (_, value) in attrs.iter() {
|
||||
self.force_for_output(value, root_span)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Value::List(list) => list
|
||||
.iter()
|
||||
.try_for_each(|elem| self.force_for_output(elem, root_span)),
|
||||
|
||||
Value::Thunk(thunk) => {
|
||||
// This force is "synthetic", in that there is no
|
||||
// outer expression from which a top-level span for
|
||||
// the call can be derived, so we need to set this
|
||||
// span manually.
|
||||
thunk.force(self).map_err(|kind| Error {
|
||||
kind,
|
||||
span: root_span,
|
||||
})?;
|
||||
|
||||
let value = thunk.value().clone();
|
||||
self.force_for_output(&value, root_span)
|
||||
}
|
||||
|
||||
// If any of these internal values are encountered here a
|
||||
// critical error has happened (likely a compiler bug).
|
||||
Value::AttrNotFound
|
||||
| Value::Blueprint(_)
|
||||
| Value::DeferredUpvalue(_)
|
||||
| Value::UnresolvedPath(_) => {
|
||||
panic!("tvix bug: internal value left on stack: {:?}", value)
|
||||
}
|
||||
|
||||
Value::Null
|
||||
| Value::Bool(_)
|
||||
| Value::Integer(_)
|
||||
| Value::Float(_)
|
||||
| Value::String(_)
|
||||
| Value::Path(_)
|
||||
| Value::Closure(_)
|
||||
| Value::Builtin(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_builtin(&mut self, builtin: Builtin) -> EvalResult<()> {
|
||||
let builtin_name = builtin.name();
|
||||
self.observer.observe_enter_builtin(builtin_name);
|
||||
|
|
@ -939,8 +886,8 @@ pub fn run_lambda(
|
|||
let mut vm = VM::new(nix_search_path, observer);
|
||||
|
||||
// Retain the top-level span of the expression in this lambda, as
|
||||
// synthetic "calls" in force_for_output will otherwise not have a
|
||||
// span to fall back to.
|
||||
// synthetic "calls" in deep_force will otherwise not have a span
|
||||
// to fall back to.
|
||||
//
|
||||
// We exploit the fact that the compiler emits a final instruction
|
||||
// with the span of the entire file for top-level expressions.
|
||||
|
|
@ -948,7 +895,13 @@ pub fn run_lambda(
|
|||
|
||||
vm.enter_frame(lambda, Upvalues::with_capacity(0), 0)?;
|
||||
let value = vm.pop();
|
||||
vm.force_for_output(&value, root_span)?;
|
||||
|
||||
value
|
||||
.deep_force(&mut vm, &mut Default::default())
|
||||
.map_err(|kind| Error {
|
||||
kind,
|
||||
span: root_span,
|
||||
})?;
|
||||
|
||||
Ok(RuntimeResult {
|
||||
value,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue