about summary refs log tree commit diff
path: root/tvix/eval/src/vm.rs
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-08-27T19·58+0300
committertazjin <tazjin@tvl.su>2022-09-06T07·29+0000
commitae531a2245e83d44ce58ba6c588713265984dbbf (patch)
tree64b32c14b9bd8ed6db4f24407e58f785a15c2f2b /tvix/eval/src/vm.rs
parent06b07f5c47cacca7c3e2d265ba23701f99ceabdf (diff)
feat(tvix/eval): implement capture of self-recursive upvalues r/4653
With this change, it becomes possible for functions to call themselves
as they are being defined in local bindings.

Change-Id: Ib46a39ba17b1452b5673d96fa729d633d237241a
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6314
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
Diffstat (limited to 'tvix/eval/src/vm.rs')
-rw-r--r--tvix/eval/src/vm.rs12
1 files changed, 9 insertions, 3 deletions
diff --git a/tvix/eval/src/vm.rs b/tvix/eval/src/vm.rs
index 527f58ddc8d6..5b6536e98664 100644
--- a/tvix/eval/src/vm.rs
+++ b/tvix/eval/src/vm.rs
@@ -359,7 +359,15 @@ impl VM {
                 }
 
                 OpCode::OpClosure(idx) => {
-                    let closure = self.chunk().constant(idx).clone().to_closure()?;
+                    let value = self.chunk().constant(idx).clone();
+                    self.push(value.clone());
+
+                    // This refers to the same Rc, and from this point
+                    // on internally mutates the closure objects
+                    // upvalues. The closure is already in its stack
+                    // slot, which means that it can capture itself as
+                    // an upvalue for self-recursion.
+                    let closure = value.to_closure()?;
 
                     debug_assert!(
                         closure.upvalue_count() > 0,
@@ -387,8 +395,6 @@ impl VM {
                             _ => panic!("compiler error: missing closure operand"),
                         }
                     }
-
-                    self.push(Value::Closure(closure));
                 }
 
                 // Data-carrying operands should never be executed,