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 <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2022-09-03 04:05:07 +03:00 committed by tazjin
parent 360c805efc
commit fe047885d7
3 changed files with 19 additions and 5 deletions

View file

@ -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<Upvalue>) {
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 {

View file

@ -0,0 +1 @@
{ a = 1; b = 2; c = 3; }

View file

@ -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;
}