about summary refs log tree commit diff
path: root/tvix
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-08-28T21·11+0300
committertazjin <tazjin@tvl.su>2022-09-06T14·58+0000
commit28a9847c65e316fdb97c1bcb79f48cb0114724af (patch)
treeeb1f917c087a1247f3d951bb96e8d5509fa0bd0d /tvix
parentd1798444bec692a608aa93605cbe449d985c3e16 (diff)
feat(tvix/eval): emit thunks for recursive local scope resolution r/4675
When resolving a value on the same level that is known but not yet
defined, emit a thunk.

Consider for example:

    let
      #       v--- requires a thunk
      a = 1 * b;
      b = 10;
    in a

Change-Id: I922cb50973ebe05e335a7bc7cb851960cf34733b
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6345
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
Diffstat (limited to 'tvix')
-rw-r--r--tvix/eval/src/compiler/mod.rs19
1 files changed, 13 insertions, 6 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
index 7c20307b843b..a1f11501a449 100644
--- a/tvix/eval/src/compiler/mod.rs
+++ b/tvix/eval/src/compiler/mod.rs
@@ -120,7 +120,7 @@ impl Compiler {
             ast::Expr::Assert(assert) => self.compile_assert(assert),
             ast::Expr::IfElse(if_else) => self.compile_if_else(if_else),
             ast::Expr::LetIn(let_in) => self.compile_let_in(let_in),
-            ast::Expr::Ident(ident) => self.compile_ident(ident),
+            ast::Expr::Ident(ident) => self.compile_ident(slot, ident),
             ast::Expr::With(with) => self.compile_with(with),
             ast::Expr::Lambda(lambda) => self.compile_lambda(slot, lambda),
             ast::Expr::Apply(apply) => self.compile_apply(apply),
@@ -620,7 +620,7 @@ impl Compiler {
                             continue;
                         }
 
-                        self.compile_ident(ident.clone());
+                        self.compile_ident(None, ident.clone());
                         let idx = self.declare_local(
                             ident.syntax().clone(),
                             ident.ident_token().unwrap().text(),
@@ -702,7 +702,7 @@ impl Compiler {
         self.end_scope();
     }
 
-    fn compile_ident(&mut self, node: ast::Ident) {
+    fn compile_ident(&mut self, slot: Option<LocalIdx>, node: ast::Ident) {
         let ident = node.ident_token().unwrap();
 
         // If the identifier is a global, and it is not poisoned, emit
@@ -747,15 +747,22 @@ impl Compiler {
                 // Variable needs to be dynamically resolved at
                 // runtime.
                 self.emit_constant(Value::String(ident.text().into()));
-                self.chunk().push_op(OpCode::OpResolveWith)
+                self.chunk().push_op(OpCode::OpResolveWith);
             }
 
             LocalPosition::Known(idx) => {
                 let stack_idx = self.scope().stack_index(idx);
-                self.chunk().push_op(OpCode::OpGetLocal(stack_idx))
+                self.chunk().push_op(OpCode::OpGetLocal(stack_idx));
             }
 
-            LocalPosition::Recursive(_) => panic!("TODO: unclear if this can happen"),
+            // This identifier is referring to a value from the same
+            // scope which is not yet defined. This identifier access
+            // must be thunked.
+            LocalPosition::Recursive(idx) => self.thunk(slot, move |compiler, _| {
+                let upvalue_idx =
+                    compiler.add_upvalue(compiler.contexts.len() - 1, Upvalue::Local(idx));
+                compiler.chunk().push_op(OpCode::OpGetUpvalue(upvalue_idx));
+            }),
         };
     }