about summary refs log tree commit diff
path: root/tvix/eval/src/compiler/bindings.rs
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/eval/src/compiler/bindings.rs')
-rw-r--r--tvix/eval/src/compiler/bindings.rs189
1 files changed, 88 insertions, 101 deletions
diff --git a/tvix/eval/src/compiler/bindings.rs b/tvix/eval/src/compiler/bindings.rs
index 3003c234671a..047b9d5b6cc3 100644
--- a/tvix/eval/src/compiler/bindings.rs
+++ b/tvix/eval/src/compiler/bindings.rs
@@ -1,14 +1,13 @@
-//! This module implements compiler logic related to name/value
-//! binding definitions (that is, attribute sets and let-expressions).
+//! This module implements compiler logic related to name/value binding
+//! definitions (that is, attribute sets and let-expressions).
 //!
-//! In the case of recursive scopes these cases share almost all of
-//! their (fairly complex) logic.
+//! In the case of recursive scopes these cases share almost all of their
+//! (fairly complex) logic.
 
 use super::*;
 
-// Data structures to track the bindings observed in the
-// second pass, and forward the information needed to compile
-// their value.
+// Data structures to track the bindings observed in the second pass, and
+// forward the information needed to compile their value.
 enum Binding {
     InheritFrom {
         namespace: ast::Expr,
@@ -91,9 +90,9 @@ impl Compiler<'_> {
                             }
                         };
 
-                        // If the identifier resolves statically in a
-                        // `let`, it has precedence over dynamic
-                        // bindings, and the inherit is useless.
+                        // If the identifier resolves statically in a `let`, it
+                        // has precedence over dynamic bindings, and the inherit
+                        // is useless.
                         if kind == BindingsKind::LetIn
                             && matches!(
                                 self.scope_mut().resolve_local(&name),
@@ -166,9 +165,9 @@ impl Compiler<'_> {
     ) {
         for (from, name, span) in inherit_froms {
             let key_slot = if kind.is_attrs() {
-                // In an attribute set, the keys themselves are placed
-                // on the stack but their stack slot is inaccessible
-                // (it is only consumed by `OpAttrs`).
+                // In an attribute set, the keys themselves are placed on the
+                // stack but their stack slot is inaccessible (it is only
+                // consumed by `OpAttrs`).
                 Some(KeySlot {
                     slot: self.scope_mut().declare_phantom(span, false),
                     name: SmolStr::new(&name),
@@ -178,12 +177,12 @@ impl Compiler<'_> {
             };
 
             let value_slot = match kind {
-                // In recursive scopes, the value needs to be
-                // accessible on the stack.
+                // In recursive scopes, the value needs to be accessible on the
+                // stack.
                 BindingsKind::LetIn | BindingsKind::RecAttrs => self.declare_local(&span, &name),
 
-                // In non-recursive attribute sets, the value is
-                // inaccessible (only consumed by `OpAttrs`).
+                // In non-recursive attribute sets, the value is inaccessible
+                // (only consumed by `OpAttrs`).
                 BindingsKind::Attrs => self.scope_mut().declare_phantom(span, false),
             };
 
@@ -240,14 +239,14 @@ impl Compiler<'_> {
             };
 
             let value_slot = match kind {
-                // In recursive scopes, the value needs to be
-                // accessible on the stack.
+                // In recursive scopes, the value needs to be accessible on the
+                // stack.
                 BindingsKind::LetIn | BindingsKind::RecAttrs => {
                     self.declare_local(&key_span, path.pop().unwrap())
                 }
 
-                // In non-recursive attribute sets, the value is
-                // inaccessible (only consumed by `OpAttrs`).
+                // In non-recursive attribute sets, the value is inaccessible
+                // (only consumed by `OpAttrs`).
                 BindingsKind::Attrs => self.scope_mut().declare_phantom(key_span, false),
             };
 
@@ -261,9 +260,9 @@ impl Compiler<'_> {
         }
     }
 
-    /// Compile the statically known entries of an attribute set. Which
-    /// keys are which is not known from the iterator, so discovered
-    /// dynamic keys are returned from here.
+    /// Compile the statically known entries of an attribute set. Which keys are
+    /// which is not known from the iterator, so discovered dynamic keys are
+    /// returned from here.
     fn compile_static_attr_entries(
         &mut self,
         count: &mut usize,
@@ -272,9 +271,8 @@ impl Compiler<'_> {
         let mut dynamic_attrs: Vec<ast::AttrpathValue> = vec![];
 
         'entries: for kv in entries {
-            // Attempt to turn the attrpath into a list of static
-            // strings, but abort this process if any dynamic
-            // fragments are encountered.
+            // Attempt to turn the attrpath into a list of static strings, but
+            // abort this process if any dynamic fragments are encountered.
             let static_attrpath: Option<Vec<String>> = kv
                 .attrpath()
                 .unwrap()
@@ -290,9 +288,9 @@ impl Compiler<'_> {
                 }
             };
 
-            // At this point we can increase the counter because we
-            // know that this particular attribute is static and can
-            // thus be processed here.
+            // At this point we can increase the counter because we know that
+            // this particular attribute is static and can thus be processed
+            // here.
             *count += 1;
 
             let key_count = fragments.len();
@@ -300,9 +298,8 @@ impl Compiler<'_> {
                 self.emit_constant(Value::String(fragment.into()), &kv.attrpath().unwrap());
             }
 
-            // We're done with the key if there was only one fragment,
-            // otherwise we need to emit an instruction to construct
-            // the attribute path.
+            // We're done with the key if there was only one fragment, otherwise
+            // we need to emit an instruction to construct the attribute path.
             if key_count > 1 {
                 self.push_op(
                     OpCode::OpAttrPath(Count(key_count)),
@@ -310,9 +307,8 @@ impl Compiler<'_> {
                 );
             }
 
-            // The value is just compiled as normal so that its
-            // resulting value is on the stack when the attribute set
-            // is constructed at runtime.
+            // The value is just compiled as normal so that its resulting value
+            // is on the stack when the attribute set is constructed at runtime.
             let value_span = self.span_for(&kv.value().unwrap());
             let value_slot = self.scope_mut().declare_phantom(value_span, false);
             self.compile(value_slot, kv.value().unwrap());
@@ -322,8 +318,8 @@ impl Compiler<'_> {
         dynamic_attrs
     }
 
-    /// Compile the dynamic entries of an attribute set, where keys
-    /// are only known at runtime.
+    /// Compile the dynamic entries of an attribute set, where keys are only
+    /// known at runtime.
     fn compile_dynamic_attr_entries(
         &mut self,
         count: &mut usize,
@@ -337,16 +333,14 @@ impl Compiler<'_> {
             let key_idx = self.scope_mut().declare_phantom(key_span, false);
 
             for fragment in entry.attrpath().unwrap().attrs() {
-                // Key fragments can contain dynamic expressions,
-                // which makes accounting for their stack slots very
-                // tricky.
+                // Key fragments can contain dynamic expressions, which makes
+                // accounting for their stack slots very tricky.
                 //
-                // In order to ensure the locals are correctly cleaned
-                // up, we essentially treat any key fragment after the
-                // first one (which has a locals index that will
-                // become that of the final key itself) as being part
-                // of a separate scope, which results in the somewhat
-                // annoying setup logic below.
+                // In order to ensure the locals are correctly cleaned up, we
+                // essentially treat any key fragment after the first one (which
+                // has a locals index that will become that of the final key
+                // itself) as being part of a separate scope, which results in
+                // the somewhat annoying setup logic below.
                 let fragment_slot = match key_count {
                     0 => key_idx,
                     1 => {
@@ -361,23 +355,21 @@ impl Compiler<'_> {
                 self.scope_mut().mark_initialised(fragment_slot);
             }
 
-            // We're done with the key if there was only one fragment,
-            // otherwise we need to emit an instruction to construct
-            // the attribute path.
+            // We're done with the key if there was only one fragment, otherwise
+            // we need to emit an instruction to construct the attribute path.
             if key_count > 1 {
                 self.push_op(
                     OpCode::OpAttrPath(Count(key_count)),
                     &entry.attrpath().unwrap(),
                 );
 
-                // Close the temporary scope that was set up for the
-                // key fragments.
+                // Close the temporary scope that was set up for the key
+                // fragments.
                 self.scope_mut().end_scope();
             }
 
-            // The value is just compiled as normal so that its
-            // resulting value is on the stack when the attribute set
-            // is constructed at runtime.
+            // The value is just compiled as normal so that its resulting value
+            // is on the stack when the attribute set is constructed at runtime.
             let value_span = self.span_for(&entry.value().unwrap());
             let value_slot = self.scope_mut().declare_phantom(value_span, false);
             self.compile(value_slot, entry.value().unwrap());
@@ -387,15 +379,15 @@ impl Compiler<'_> {
 
     /// Compile attribute set literals into equivalent bytecode.
     ///
-    /// This is complicated by a number of features specific to Nix
-    /// attribute sets, most importantly:
+    /// This is complicated by a number of features specific to Nix attribute
+    /// sets, most importantly:
     ///
     /// 1. Keys can be dynamically constructed through interpolation.
     /// 2. Keys can refer to nested attribute sets.
     /// 3. Attribute sets can (optionally) be recursive.
     pub(super) fn compile_attr_set(&mut self, slot: LocalIdx, node: ast::AttrSet) {
-        // Open a scope to track the positions of the temporaries used
-        // by the `OpAttrs` instruction.
+        // Open a scope to track the positions of the temporaries used by the
+        // `OpAttrs` instruction.
         self.scope_mut().begin_scope();
 
         if node.rec_token().is_some() {
@@ -419,8 +411,8 @@ impl Compiler<'_> {
             self.push_op(OpCode::OpAttrs(Count(count)), &node);
         }
 
-        // Remove the temporary scope, but do not emit any additional
-        // cleanup (OpAttrs consumes all of these locals).
+        // Remove the temporary scope, but do not emit any additional cleanup
+        // (OpAttrs consumes all of these locals).
         self.scope_mut().end_scope();
     }
 
@@ -439,15 +431,15 @@ impl Compiler<'_> {
             }
 
             match binding.binding {
-                // This entry is an inherit (from) expr. The value is
-                // placed on the stack by selecting an attribute.
+                // 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.
+                    // 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);
@@ -457,13 +449,13 @@ impl Compiler<'_> {
                     })
                 }
 
-                // Binding is "just" a plain expression that needs to
-                // be compiled.
+                // 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.
+            // 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);
         }
 
@@ -499,9 +491,8 @@ impl Compiler<'_> {
 
     /// Compile a standard `let ...; in ...` expression.
     ///
-    /// Unless in a non-standard scope, the encountered values are
-    /// simply pushed on the stack and their indices noted in the
-    /// entries vector.
+    /// Unless in a non-standard scope, the encountered values are simply pushed
+    /// on the stack and their indices noted in the entries vector.
     pub(super) fn compile_let_in(&mut self, slot: LocalIdx, node: ast::LetIn) {
         self.compile_recursive_scope(slot, BindingsKind::LetIn, &node);
 
@@ -526,8 +517,8 @@ impl Compiler<'_> {
         ident: &str,
         node: &N,
     ) {
-        // If the identifier is a global, and it is not poisoned, emit
-        // the global directly.
+        // If the identifier is a global, and it is not poisoned, emit the
+        // global directly.
         if let Some(global) = self.globals.get(ident) {
             if !self.scope().is_poisoned(ident) {
                 global.clone()(self, self.span_for(node));
@@ -543,9 +534,8 @@ impl Compiler<'_> {
                     return;
                 }
 
-                // If there is a non-empty `with`-stack (or a parent
-                // context with one), emit a runtime dynamic
-                // resolution instruction.
+                // If there is a non-empty `with`-stack (or a parent context
+                // with one), emit a runtime dynamic resolution instruction.
                 if self.has_dynamic_ancestor() {
                     self.emit_constant(Value::String(SmolStr::new(ident).into()), node);
                     self.push_op(OpCode::OpResolveWith, node);
@@ -561,9 +551,8 @@ impl Compiler<'_> {
                 self.push_op(OpCode::OpGetLocal(stack_idx), node);
             }
 
-            // This identifier is referring to a value from the same
-            // scope which is not yet defined. This identifier access
-            // must be thunked.
+            // 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, node, move |compiler, node, _| {
                 let upvalue_idx = compiler.add_upvalue(
                     compiler.contexts.len() - 1,
@@ -596,10 +585,10 @@ impl Compiler<'_> {
 
         // Determine whether the upvalue is a local in the enclosing context.
         match self.contexts[ctx_idx - 1].scope.resolve_local(name) {
-            // recursive upvalues are dealt with the same way as
-            // standard known ones, as thunks and closures are
-            // guaranteed to be placed on the stack (i.e. in the right
-            // position) *during* their runtime construction
+            // recursive upvalues are dealt with the same way as standard known
+            // ones, as thunks and closures are guaranteed to be placed on the
+            // stack (i.e. in the right position) *during* their runtime
+            // construction
             LocalPosition::Known(idx) | LocalPosition::Recursive(idx) => {
                 return Some(self.add_upvalue(ctx_idx, node, UpvalueKind::Local(idx)))
             }
@@ -607,9 +596,8 @@ impl Compiler<'_> {
             LocalPosition::Unknown => { /* continue below */ }
         };
 
-        // If the upvalue comes from even further up, we need to
-        // recurse to make sure that the upvalues are created at each
-        // level.
+        // If the upvalue comes from even further up, we need to recurse to make
+        // sure that the upvalues are created at each level.
         if let Some(idx) = self.resolve_upvalue(ctx_idx - 1, name, node) {
             return Some(self.add_upvalue(ctx_idx, node, UpvalueKind::Upvalue(idx)));
         }
@@ -623,8 +611,8 @@ impl Compiler<'_> {
         node: &N,
         kind: UpvalueKind,
     ) -> UpvalueIdx {
-        // If there is already an upvalue closing over the specified
-        // index, retrieve that instead.
+        // If there is already an upvalue closing over the specified index,
+        // retrieve that instead.
         for (idx, existing) in self.contexts[ctx_idx].scope.upvalues.iter().enumerate() {
             if existing.kind == kind {
                 return UpvalueIdx(idx);
@@ -642,9 +630,8 @@ impl Compiler<'_> {
         idx
     }
 
-    /// Convert a single identifier path fragment of a let binding to
-    /// a string if possible, or raise an error about the node being
-    /// dynamic.
+    /// Convert a single identifier path fragment of a let binding to a string
+    /// if possible, or raise an error about the node being dynamic.
     fn binding_name(&self, node: ast::Attr) -> EvalResult<String> {
         match self.expr_static_attr_str(&node) {
             Some(s) => Ok(s),
@@ -656,9 +643,9 @@ impl Compiler<'_> {
         }
     }
 
-    /// Normalises identifier fragments into a single string vector
-    /// for `let`-expressions; fails if fragments requiring dynamic
-    /// computation are encountered.
+    /// Normalises identifier fragments into a single string vector for
+    /// `let`-expressions; fails if fragments requiring dynamic computation are
+    /// encountered.
     fn normalise_ident_path<I: Iterator<Item = ast::Attr>>(
         &self,
         path: I,
@@ -681,18 +668,18 @@ impl Compiler<'_> {
         None
     }
 
-    /// Convert the provided `ast::Attr` into a statically known
-    /// string if possible.
+    /// Convert the provided `ast::Attr` into a statically known string if
+    /// possible.
     // TODO(tazjin): these should probably be SmolStr
     fn expr_static_attr_str(&self, node: &ast::Attr) -> Option<String> {
         match node {
             ast::Attr::Ident(ident) => Some(ident.ident_token().unwrap().text().into()),
             ast::Attr::Str(s) => self.expr_static_str(s),
 
-            // The dynamic node type is just a wrapper. C++ Nix does not
-            // care about the dynamic wrapper when determining whether the
-            // node itself is dynamic, it depends solely on the expression
-            // inside (i.e. `let ${"a"} = 1; in a` is valid).
+            // The dynamic node type is just a wrapper. C++ Nix does not care
+            // about the dynamic wrapper when determining whether the node
+            // itself is dynamic, it depends solely on the expression inside
+            // (i.e. `let ${"a"} = 1; in a` is valid).
             ast::Attr::Dynamic(ref dynamic) => match dynamic.expr().unwrap() {
                 ast::Expr::Str(s) => self.expr_static_str(&s),
                 _ => None,