about summary refs log tree commit diff
path: root/tvix/eval/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/eval/src/compiler')
-rw-r--r--tvix/eval/src/compiler/mod.rs61
-rw-r--r--tvix/eval/src/compiler/scope.rs4
2 files changed, 32 insertions, 33 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
index 3ea27f843cf2..242001c81fa5 100644
--- a/tvix/eval/src/compiler/mod.rs
+++ b/tvix/eval/src/compiler/mod.rs
@@ -149,7 +149,7 @@ impl Compiler<'_> {
 
 // Actual code-emitting AST traversal methods.
 impl Compiler<'_> {
-    fn compile(&mut self, slot: Option<LocalIdx>, expr: ast::Expr) {
+    fn compile(&mut self, slot: LocalIdx, expr: ast::Expr) {
         match expr {
             ast::Expr::Literal(literal) => self.compile_literal(literal),
             ast::Expr::Path(path) => self.compile_path(path),
@@ -234,7 +234,7 @@ impl Compiler<'_> {
         self.emit_constant(value, &node);
     }
 
-    fn compile_str(&mut self, slot: Option<LocalIdx>, node: ast::Str) {
+    fn compile_str(&mut self, slot: LocalIdx, node: ast::Str) {
         let mut count = 0;
 
         // The string parts are produced in literal order, however
@@ -261,7 +261,7 @@ impl Compiler<'_> {
         }
     }
 
-    fn compile_unary_op(&mut self, slot: Option<LocalIdx>, op: ast::UnaryOp) {
+    fn compile_unary_op(&mut self, slot: LocalIdx, op: ast::UnaryOp) {
         self.compile(slot, op.expr().unwrap());
         self.emit_force(&op);
 
@@ -273,7 +273,7 @@ impl Compiler<'_> {
         self.push_op(opcode, &op);
     }
 
-    fn compile_binop(&mut self, slot: Option<LocalIdx>, op: ast::BinOp) {
+    fn compile_binop(&mut self, slot: LocalIdx, op: ast::BinOp) {
         use ast::BinOpKind;
 
         // Short-circuiting and other strange operators, which are
@@ -322,7 +322,7 @@ impl Compiler<'_> {
         };
     }
 
-    fn compile_and(&mut self, slot: Option<LocalIdx>, node: ast::BinOp) {
+    fn compile_and(&mut self, slot: LocalIdx, node: ast::BinOp) {
         debug_assert!(
             matches!(node.operator(), Some(ast::BinOpKind::And)),
             "compile_and called with wrong operator kind: {:?}",
@@ -348,7 +348,7 @@ impl Compiler<'_> {
         self.push_op(OpCode::OpAssertBool, &node);
     }
 
-    fn compile_or(&mut self, slot: Option<LocalIdx>, node: ast::BinOp) {
+    fn compile_or(&mut self, slot: LocalIdx, node: ast::BinOp) {
         debug_assert!(
             matches!(node.operator(), Some(ast::BinOpKind::Or)),
             "compile_or called with wrong operator kind: {:?}",
@@ -370,7 +370,7 @@ impl Compiler<'_> {
         self.push_op(OpCode::OpAssertBool, &node);
     }
 
-    fn compile_implication(&mut self, slot: Option<LocalIdx>, node: ast::BinOp) {
+    fn compile_implication(&mut self, slot: LocalIdx, node: ast::BinOp) {
         debug_assert!(
             matches!(node.operator(), Some(ast::BinOpKind::Implication)),
             "compile_implication called with wrong operator kind: {:?}",
@@ -392,7 +392,7 @@ impl Compiler<'_> {
         self.push_op(OpCode::OpAssertBool, &node);
     }
 
-    fn compile_has_attr(&mut self, slot: Option<LocalIdx>, node: ast::HasAttr) {
+    fn compile_has_attr(&mut self, slot: LocalIdx, node: ast::HasAttr) {
         // Put the attribute set on the stack.
         self.compile(slot, node.expr().unwrap());
 
@@ -411,7 +411,7 @@ impl Compiler<'_> {
         self.push_op(OpCode::OpAttrsIsSet, &node);
     }
 
-    fn compile_attr(&mut self, slot: Option<LocalIdx>, node: ast::Attr) {
+    fn compile_attr(&mut self, slot: LocalIdx, node: ast::Attr) {
         match node {
             ast::Attr::Dynamic(dynamic) => {
                 self.compile(slot, dynamic.expr().unwrap());
@@ -433,7 +433,7 @@ impl Compiler<'_> {
     //
     // The VM, after evaluating the code for each element, simply
     // constructs the list from the given number of elements.
-    fn compile_list(&mut self, slot: Option<LocalIdx>, node: ast::List) {
+    fn compile_list(&mut self, slot: LocalIdx, node: ast::List) {
         let mut count = 0;
 
         for item in node.items() {
@@ -452,7 +452,7 @@ impl Compiler<'_> {
     // 1. Keys can be dynamically constructed through interpolation.
     // 2. Keys can refer to nested attribute sets.
     // 3. Attribute sets can (optionally) be recursive.
-    fn compile_attr_set(&mut self, slot: Option<LocalIdx>, node: ast::AttrSet) {
+    fn compile_attr_set(&mut self, slot: LocalIdx, node: ast::AttrSet) {
         if node.rec_token().is_some() {
             todo!("recursive attribute sets are not yet implemented")
         }
@@ -534,7 +534,7 @@ impl Compiler<'_> {
         self.push_op(OpCode::OpAttrs(Count(count)), &node);
     }
 
-    fn compile_select(&mut self, slot: Option<LocalIdx>, node: ast::Select) {
+    fn compile_select(&mut self, slot: LocalIdx, node: ast::Select) {
         let set = node.expr().unwrap();
         let path = node.attrpath().unwrap();
 
@@ -588,7 +588,7 @@ impl Compiler<'_> {
     /// ```
     fn compile_select_or(
         &mut self,
-        slot: Option<LocalIdx>,
+        slot: LocalIdx,
         set: ast::Expr,
         path: ast::Attrpath,
         default: ast::Expr,
@@ -615,7 +615,7 @@ impl Compiler<'_> {
         self.patch_jump(final_jump);
     }
 
-    fn compile_assert(&mut self, slot: Option<LocalIdx>, node: ast::Assert) {
+    fn compile_assert(&mut self, slot: LocalIdx, node: ast::Assert) {
         // Compile the assertion condition to leave its value on the stack.
         self.compile(slot, node.condition().unwrap());
         self.push_op(OpCode::OpAssert, &node);
@@ -636,7 +636,7 @@ impl Compiler<'_> {
     //  Jump over else body  ││ 4  [  else body  ]←┼─┘
     //  if condition is true.└┼─5─→     ...        │
     //                        └────────────────────┘
-    fn compile_if_else(&mut self, slot: Option<LocalIdx>, node: ast::IfElse) {
+    fn compile_if_else(&mut self, slot: LocalIdx, node: ast::IfElse) {
         self.compile(slot, node.condition().unwrap());
 
         let then_idx = self.push_op(
@@ -659,7 +659,7 @@ impl Compiler<'_> {
     // Compile an `inherit` node of a `let`-expression.
     fn compile_let_inherit<I: Iterator<Item = ast::Inherit>>(
         &mut self,
-        slot: Option<LocalIdx>,
+        slot: LocalIdx,
         inherits: I,
     ) {
         for inherit in inherits {
@@ -712,7 +712,7 @@ impl Compiler<'_> {
     // Unless in a non-standard scope, the encountered values are
     // simply pushed on the stack and their indices noted in the
     // entries vector.
-    fn compile_let_in(&mut self, slot: Option<LocalIdx>, node: ast::LetIn) {
+    fn compile_let_in(&mut self, slot: LocalIdx, node: ast::LetIn) {
         self.begin_scope();
 
         self.compile_let_inherit(slot, node.inherits());
@@ -741,7 +741,7 @@ impl Compiler<'_> {
         // Second pass to place the values in the correct stack slots.
         let indices: Vec<LocalIdx> = entries.iter().map(|(idx, _)| *idx).collect();
         for (idx, value) in entries.into_iter() {
-            self.compile(Some(idx), value);
+            self.compile(idx, value);
 
             // Any code after this point will observe the value in the
             // right stack slot, so mark it as initialised.
@@ -761,7 +761,7 @@ impl Compiler<'_> {
         self.end_scope(&node);
     }
 
-    fn compile_ident(&mut self, slot: Option<LocalIdx>, node: ast::Ident) {
+    fn compile_ident(&mut self, slot: LocalIdx, node: ast::Ident) {
         let ident = node.ident_token().unwrap();
 
         // If the identifier is a global, and it is not poisoned, emit
@@ -833,7 +833,7 @@ impl Compiler<'_> {
     // Compile `with` expressions by emitting instructions that
     // pop/remove the indices of attribute sets that are implicitly in
     // scope through `with` on the "with-stack".
-    fn compile_with(&mut self, slot: Option<LocalIdx>, node: ast::With) {
+    fn compile_with(&mut self, slot: LocalIdx, node: ast::With) {
         self.begin_scope();
         // TODO: Detect if the namespace is just an identifier, and
         // resolve that directly (thus avoiding duplication on the
@@ -856,7 +856,7 @@ impl Compiler<'_> {
         self.end_scope(&node);
     }
 
-    fn compile_lambda(&mut self, slot: Option<LocalIdx>, node: ast::Lambda) {
+    fn compile_lambda(&mut self, slot: LocalIdx, node: ast::Lambda) {
         self.new_context();
         self.begin_scope();
 
@@ -913,7 +913,7 @@ impl Compiler<'_> {
         self.emit_upvalue_data(slot, compiled.scope.upvalues);
     }
 
-    fn compile_apply(&mut self, slot: Option<LocalIdx>, node: ast::Apply) {
+    fn compile_apply(&mut self, slot: LocalIdx, node: ast::Apply) {
         // To call a function, we leave its arguments on the stack,
         // followed by the function expression itself, and then emit a
         // call instruction. This way, the stack is perfectly laid out
@@ -926,10 +926,10 @@ impl Compiler<'_> {
     /// Compile an expression into a runtime thunk which should be
     /// lazily evaluated when accessed.
     // TODO: almost the same as Compiler::compile_lambda; unify?
-    fn thunk<N, F>(&mut self, slot: Option<LocalIdx>, node: &N, content: F)
+    fn thunk<N, F>(&mut self, slot: LocalIdx, node: &N, content: F)
     where
         N: AstNode + Clone,
-        F: FnOnce(&mut Compiler, &N, Option<LocalIdx>),
+        F: FnOnce(&mut Compiler, &N, LocalIdx),
     {
         self.new_context();
         self.begin_scope();
@@ -961,14 +961,9 @@ impl Compiler<'_> {
 
     /// Emit the data instructions that the runtime needs to correctly
     /// assemble the provided upvalues array.
-    fn emit_upvalue_data(&mut self, slot: Option<LocalIdx>, upvalues: Vec<Upvalue>) {
+    fn emit_upvalue_data(&mut self, slot: LocalIdx, upvalues: Vec<Upvalue>) {
         for upvalue in upvalues {
             match upvalue.kind {
-                UpvalueKind::Local(idx) if slot.is_none() => {
-                    let stack_idx = self.scope().stack_index(idx);
-                    self.push_op(OpCode::DataLocalIdx(stack_idx), &upvalue.node);
-                }
-
                 UpvalueKind::Local(idx) => {
                     let stack_idx = self.scope().stack_index(idx);
 
@@ -976,9 +971,9 @@ impl Compiler<'_> {
                     // closure, the upvalue resolution must be
                     // deferred until the scope is fully initialised
                     // and can be finalised.
-                    if slot.unwrap() < idx {
+                    if slot < idx {
                         self.push_op(OpCode::DataDeferredLocal(stack_idx), &upvalue.node);
-                        self.scope_mut().mark_needs_finaliser(slot.unwrap());
+                        self.scope_mut().mark_needs_finaliser(slot);
                     } else {
                         self.push_op(OpCode::DataLocalIdx(stack_idx), &upvalue.node);
                     }
@@ -1382,7 +1377,7 @@ pub fn compile<'code>(
         c.context_mut().lambda.chunk.codemap = c.codemap.clone();
     }
 
-    c.compile(None, expr.clone());
+    c.compile(LocalIdx::ZERO, expr.clone());
 
     // The final operation of any top-level Nix program must always be
     // `OpForce`. A thunk should not be returned to the user in an
diff --git a/tvix/eval/src/compiler/scope.rs b/tvix/eval/src/compiler/scope.rs
index a50b3b79343e..7d557a7d2fd3 100644
--- a/tvix/eval/src/compiler/scope.rs
+++ b/tvix/eval/src/compiler/scope.rs
@@ -104,6 +104,10 @@ pub struct Upvalue {
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
 pub struct LocalIdx(usize);
 
+impl LocalIdx {
+    pub const ZERO: LocalIdx = LocalIdx(0);
+}
+
 /// Represents a scope known during compilation, which can be resolved
 /// directly to stack indices.
 ///