From fe047885d75a97bd303176847db7fdb2a781344d Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Sat, 3 Sep 2022 04:05:07 +0300 Subject: fix(tvix/eval): consider local depth when deciding to defer Deferred local upvalues can *only* occur at the same depth as the thing that is closing over them, but there are various situations with scope nesting where the actual stack indexes of the local and the closer look like a deferred value is being accessed. To fix this, simply compare the depth as well. Change-Id: Ice77424cc87ab0a2c4f01379e68d4399a917b12b Reviewed-on: https://cl.tvl.fyi/c/depot/+/6429 Reviewed-by: sterni Tested-by: BuildkiteCI --- tvix/eval/src/compiler/mod.rs | 13 ++++++++----- .../src/tests/tvix_tests/eval-okay-toplevel-finaliser.exp | 1 + .../src/tests/tvix_tests/eval-okay-toplevel-finaliser.nix | 10 ++++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.exp create mode 100644 tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.nix (limited to 'tvix/eval/src') diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs index 731ba9179c4b..f9a2dd32c88d 100644 --- a/tvix/eval/src/compiler/mod.rs +++ b/tvix/eval/src/compiler/mod.rs @@ -973,17 +973,20 @@ impl Compiler<'_> { /// Emit the data instructions that the runtime needs to correctly /// assemble the provided upvalues array. fn emit_upvalue_data(&mut self, slot: LocalIdx, upvalues: Vec) { + let this_depth = self.scope()[slot].depth; let this_stack_slot = self.scope().stack_index(slot); + for upvalue in upvalues { match upvalue.kind { UpvalueKind::Local(idx) => { + let target_depth = self.scope()[idx].depth; let stack_idx = self.scope().stack_index(idx); - // If the upvalue slot is located *after* the - // closure, the upvalue resolution must be - // deferred until the scope is fully initialised - // and can be finalised. - if this_stack_slot < stack_idx { + // If the upvalue slot is located at the same + // depth, but *after* the closure, the upvalue + // resolution must be deferred until the scope is + // fully initialised and can be finalised. + if this_depth == target_depth && this_stack_slot < stack_idx { self.push_op(OpCode::DataDeferredLocal(stack_idx), &upvalue.node); self.scope_mut().mark_needs_finaliser(slot); } else { diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.exp new file mode 100644 index 000000000000..edca9baca9c0 --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.exp @@ -0,0 +1 @@ +{ a = 1; b = 2; c = 3; } diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.nix new file mode 100644 index 000000000000..4c6884bec3df --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-toplevel-finaliser.nix @@ -0,0 +1,10 @@ +# A simple expression with upvalue resolution beyond the target stack +# index of the root expression. + +let + a = 1; + b = 2; + c = 3; +in { + inherit a b c; +} -- cgit 1.4.1