feat(tvix/eval): add Evaluation::strict to toggle top-level deepseq
This makes it possible for callers to control whether they can receive partially evaluated values from an evaluation or not. We're actually flipping the default behaviour to non-strict top-level evaluation, which means that callers have to set `strict = true` on the Evaluation to get the previous behaviour. Change-Id: Ic048e9ba09c88866d4c3177d5fa07db11c4eb20e Reviewed-on: https://cl.tvl.fyi/c/depot/+/8325 Autosubmit: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
This commit is contained in:
parent
a5f28eea94
commit
ba138712e4
5 changed files with 26 additions and 5 deletions
|
@ -104,6 +104,11 @@ pub struct Evaluation<'code, 'co, 'ro> {
|
||||||
/// able to read the files specified as arguments to `import`.
|
/// able to read the files specified as arguments to `import`.
|
||||||
pub enable_import: bool,
|
pub enable_import: bool,
|
||||||
|
|
||||||
|
/// Determines whether the returned value should be strictly
|
||||||
|
/// evaluated, that is whether its list and attribute set elements
|
||||||
|
/// should be forced recursively.
|
||||||
|
pub strict: bool,
|
||||||
|
|
||||||
/// (optional) Nix search path, e.g. the value of `NIX_PATH` used
|
/// (optional) Nix search path, e.g. the value of `NIX_PATH` used
|
||||||
/// for resolving items on the search path (such as `<nixpkgs>`).
|
/// for resolving items on the search path (such as `<nixpkgs>`).
|
||||||
pub nix_path: Option<String>,
|
pub nix_path: Option<String>,
|
||||||
|
@ -161,6 +166,7 @@ impl<'code, 'co, 'ro> Evaluation<'code, 'co, 'ro> {
|
||||||
src_builtins: vec![],
|
src_builtins: vec![],
|
||||||
io_handle: Box::new(DummyIO {}),
|
io_handle: Box::new(DummyIO {}),
|
||||||
enable_import: false,
|
enable_import: false,
|
||||||
|
strict: false,
|
||||||
nix_path: None,
|
nix_path: None,
|
||||||
compiler_observer: None,
|
compiler_observer: None,
|
||||||
runtime_observer: None,
|
runtime_observer: None,
|
||||||
|
@ -256,7 +262,15 @@ impl<'code, 'co, 'ro> Evaluation<'code, 'co, 'ro> {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let runtime_observer = self.runtime_observer.take().unwrap_or(&mut noop_observer);
|
let runtime_observer = self.runtime_observer.take().unwrap_or(&mut noop_observer);
|
||||||
let vm_result = run_lambda(nix_path, self.io_handle, runtime_observer, globals, lambda);
|
|
||||||
|
let vm_result = run_lambda(
|
||||||
|
nix_path,
|
||||||
|
self.io_handle,
|
||||||
|
runtime_observer,
|
||||||
|
globals,
|
||||||
|
lambda,
|
||||||
|
self.strict,
|
||||||
|
);
|
||||||
|
|
||||||
match vm_result {
|
match vm_result {
|
||||||
Ok(mut runtime_result) => {
|
Ok(mut runtime_result) => {
|
||||||
|
|
|
@ -52,6 +52,7 @@ fn eval_test(code_path: &str, expect_success: bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut eval = crate::Evaluation::new_impure(&code, Some(code_path.into()));
|
let mut eval = crate::Evaluation::new_impure(&code, Some(code_path.into()));
|
||||||
|
eval.strict = true;
|
||||||
eval.builtins.extend(mock_builtins::builtins());
|
eval.builtins.extend(mock_builtins::builtins());
|
||||||
|
|
||||||
let result = eval.evaluate();
|
let result = eval.evaluate();
|
||||||
|
@ -100,6 +101,7 @@ fn identity(code_path: &str) {
|
||||||
let code = std::fs::read_to_string(code_path).expect("should be able to read test code");
|
let code = std::fs::read_to_string(code_path).expect("should be able to read test code");
|
||||||
|
|
||||||
let mut eval = crate::Evaluation::new(&code, None);
|
let mut eval = crate::Evaluation::new(&code, None);
|
||||||
|
eval.strict = true;
|
||||||
eval.io_handle = Box::new(crate::StdIO);
|
eval.io_handle = Box::new(crate::StdIO);
|
||||||
|
|
||||||
let result = eval.evaluate();
|
let result = eval.evaluate();
|
||||||
|
|
|
@ -1190,6 +1190,7 @@ pub fn run_lambda(
|
||||||
observer: &mut dyn RuntimeObserver,
|
observer: &mut dyn RuntimeObserver,
|
||||||
globals: Rc<GlobalsMap>,
|
globals: Rc<GlobalsMap>,
|
||||||
lambda: Rc<Lambda>,
|
lambda: Rc<Lambda>,
|
||||||
|
strict: bool,
|
||||||
) -> EvalResult<RuntimeResult> {
|
) -> EvalResult<RuntimeResult> {
|
||||||
// Retain the top-level span of the expression in this lambda, as
|
// Retain the top-level span of the expression in this lambda, as
|
||||||
// synthetic "calls" in deep_force will otherwise not have a span
|
// synthetic "calls" in deep_force will otherwise not have a span
|
||||||
|
@ -1207,9 +1208,11 @@ pub fn run_lambda(
|
||||||
root_span.into(),
|
root_span.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Synthesise a frame that will instruct the VM to deep-force the final
|
// When evaluating strictly, synthesise a frame that will instruct
|
||||||
// value before returning it.
|
// the VM to deep-force the final value before returning it.
|
||||||
|
if strict {
|
||||||
vm.enqueue_generator("final_deep_force", root_span.into(), final_deep_force);
|
vm.enqueue_generator("final_deep_force", root_span.into(), final_deep_force);
|
||||||
|
}
|
||||||
|
|
||||||
vm.frames.push(Frame::CallFrame {
|
vm.frames.push(Frame::CallFrame {
|
||||||
span: root_span.into(),
|
span: root_span.into(),
|
||||||
|
|
|
@ -41,6 +41,7 @@ fn nix_eval(expr: &str) -> String {
|
||||||
fn compare_eval(expr: &str) {
|
fn compare_eval(expr: &str) {
|
||||||
let nix_result = nix_eval(expr);
|
let nix_result = nix_eval(expr);
|
||||||
let mut eval = tvix_eval::Evaluation::new(expr, None);
|
let mut eval = tvix_eval::Evaluation::new(expr, None);
|
||||||
|
eval.strict = true;
|
||||||
eval.io_handle = Box::new(tvix_eval::StdIO);
|
eval.io_handle = Box::new(tvix_eval::StdIO);
|
||||||
|
|
||||||
let tvix_result = eval
|
let tvix_result = eval
|
||||||
|
|
|
@ -33,7 +33,8 @@ where
|
||||||
T: serde::Deserialize<'code>,
|
T: serde::Deserialize<'code>,
|
||||||
{
|
{
|
||||||
// First step is to evaluate the Nix code ...
|
// First step is to evaluate the Nix code ...
|
||||||
let eval = tvix_eval::Evaluation::new(src, None);
|
let mut eval = tvix_eval::Evaluation::new(src, None);
|
||||||
|
eval.strict = true;
|
||||||
let source = eval.source_map();
|
let source = eval.source_map();
|
||||||
let result = eval.evaluate();
|
let result = eval.evaluate();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue