about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/eval/src/compiler/bindings.rs102
1 files changed, 55 insertions, 47 deletions
diff --git a/tvix/eval/src/compiler/bindings.rs b/tvix/eval/src/compiler/bindings.rs
index 4f384484a973..1bf0b79c7340 100644
--- a/tvix/eval/src/compiler/bindings.rs
+++ b/tvix/eval/src/compiler/bindings.rs
@@ -434,6 +434,59 @@ impl Compiler<'_> {
         self.scope_mut().end_scope();
     }
 
+    /// Actually binds all tracked bindings by emitting the bytecode that places
+    /// them in their stack slots.
+    fn bind_values(&mut self, bindings: Vec<TrackedBinding>) {
+        let mut value_indices: Vec<LocalIdx> = vec![];
+
+        for binding in bindings.into_iter() {
+            value_indices.push(binding.value_slot);
+
+            if let Some(key_slot) = binding.key_slot {
+                let span = self.scope()[key_slot.slot].span;
+                self.emit_constant(Value::String(key_slot.name.into()), &span);
+                self.scope_mut().mark_initialised(key_slot.slot);
+            }
+
+            match binding.binding {
+                // This entry is an inherit (from) expr. The value is
+                // placed on the stack by selecting an attribute.
+                Binding::InheritFrom {
+                    namespace,
+                    name,
+                    span,
+                } => {
+                    // Create a thunk wrapping value (which may be one as well) to
+                    // avoid forcing the from expr too early.
+                    self.thunk(binding.value_slot, &namespace, move |c, n, s| {
+                        c.compile(s, n.clone());
+                        c.emit_force(n);
+
+                        c.emit_constant(Value::String(name.into()), &span);
+                        c.push_op(OpCode::OpAttrsSelect, &span);
+                    })
+                }
+
+                // Binding is "just" a plain expression that needs to
+                // be compiled.
+                Binding::Plain { expr } => self.compile(binding.value_slot, expr),
+            }
+
+            // Any code after this point will observe the value in the
+            // right stack slot, so mark it as initialised.
+            self.scope_mut().mark_initialised(binding.value_slot);
+        }
+
+        // Final pass to emit finaliser instructions if necessary.
+        for idx in value_indices {
+            if self.scope()[idx].needs_finaliser {
+                let stack_idx = self.scope().stack_index(idx);
+                let span = self.scope()[idx].span;
+                self.push_op(OpCode::OpFinalise(stack_idx), &span);
+            }
+        }
+    }
+
     fn compile_recursive_scope<N>(&mut self, slot: LocalIdx, kind: BindingsKind, node: &N) -> usize
     where
         N: ToSpan + ast::HasEntry,
@@ -488,53 +541,8 @@ impl Compiler<'_> {
             });
         }
 
-        // Third pass to place the values in the correct stack slots.
-        let mut value_indices: Vec<LocalIdx> = vec![];
-        for binding in bindings.into_iter() {
-            value_indices.push(binding.value_slot);
-
-            if let Some(key_slot) = binding.key_slot {
-                let span = self.scope()[key_slot.slot].span;
-                self.emit_constant(Value::String(key_slot.name.into()), &span);
-                self.scope_mut().mark_initialised(key_slot.slot);
-            }
-
-            match binding.binding {
-                // This entry is an inherit (from) expr. The value is
-                // placed on the stack by selecting an attribute.
-                Binding::InheritFrom {
-                    namespace,
-                    name,
-                    span,
-                } => {
-                    // Create a thunk wrapping value (which may be one as well) to
-                    // avoid forcing the from expr too early.
-                    self.thunk(binding.value_slot, &namespace, move |c, n, s| {
-                        c.compile(s, n.clone());
-                        c.emit_force(n);
-
-                        c.emit_constant(Value::String(name.into()), &span);
-                        c.push_op(OpCode::OpAttrsSelect, &span);
-                    })
-                }
-
-                // Binding is "just" a plain expression that needs to
-                // be compiled.
-                Binding::Plain { expr } => self.compile(binding.value_slot, expr),
-            }
-
-            // Any code after this point will observe the value in the
-            // right stack slot, so mark it as initialised.
-            self.scope_mut().mark_initialised(binding.value_slot);
-        }
-
-        // Fourth pass to emit finaliser instructions if necessary.
-        for idx in value_indices {
-            if self.scope()[idx].needs_finaliser {
-                let stack_idx = self.scope().stack_index(idx);
-                self.push_op(OpCode::OpFinalise(stack_idx), node);
-            }
-        }
+        // Actually bind values and ensure they are on the stack.
+        self.bind_values(bindings);
 
         count
     }