about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--tvix/eval/src/compiler/bindings.rs111
1 files changed, 82 insertions, 29 deletions
diff --git a/tvix/eval/src/compiler/bindings.rs b/tvix/eval/src/compiler/bindings.rs
index 5f4d1c4d0b3a..96ee434a2c3b 100644
--- a/tvix/eval/src/compiler/bindings.rs
+++ b/tvix/eval/src/compiler/bindings.rs
@@ -4,7 +4,8 @@
 //! In the case of recursive scopes these cases share almost all of their
 //! (fairly complex) logic.
 
-use rnix::ast::{AttrpathValue, HasEntry};
+use rnix::ast::HasEntry;
+use rowan::ast::AstChildren;
 
 use super::*;
 
@@ -41,7 +42,10 @@ struct AttributeSet {
     inherits: Vec<ast::Inherit>,
 
     /// All internal entries
-    entries: Vec<ast::AttrpathValue>,
+    entries: Vec<(Span, AstChildren<ast::Attr>, ast::Expr)>,
+
+    /// How deeply nested is this attribute set in the literal definition?
+    nesting_level: usize,
 }
 
 impl ToSpan for AttributeSet {
@@ -67,7 +71,7 @@ enum Binding {
 }
 
 impl Binding {
-    fn merge(&mut self, c: &mut Compiler, expr: ast::Expr) {
+    fn merge(&mut self, c: &mut Compiler, value: ast::Expr) {
         match self {
             Binding::InheritFrom { name, ref span, .. } => {
                 c.emit_error(span, ErrorKind::UnmergeableInherit { name: name.clone() })
@@ -75,8 +79,10 @@ impl Binding {
 
             Binding::Plain { expr: existing } => match existing {
                 ast::Expr::AttrSet(existing) => {
-                    if let ast::Expr::AttrSet(new) = expr {
+                    if let ast::Expr::AttrSet(new) = value {
                         let merged = AttributeSet {
+                            nesting_level: 0, // TODO
+
                             span: c.span_for(existing),
 
                             // Kind of the attrs depends on the first time it is
@@ -94,6 +100,14 @@ impl Binding {
 
                             entries: ast::HasEntry::attrpath_values(existing)
                                 .chain(ast::HasEntry::attrpath_values(&new))
+                                .map(|entry| {
+                                    let span = c.span_for(&entry);
+                                    (
+                                        span,
+                                        entry.attrpath().unwrap().attrs(),
+                                        entry.value().unwrap(),
+                                    )
+                                })
                                 .collect(),
                         };
 
@@ -103,15 +117,22 @@ impl Binding {
                     }
                 }
 
-                _ => c.emit_error(&expr, ErrorKind::UnmergeableValue),
+                _ => c.emit_error(&value, ErrorKind::UnmergeableValue),
             },
 
             Binding::Set(existing) => {
-                if let ast::Expr::AttrSet(new) = expr {
+                if let ast::Expr::AttrSet(new) = value {
                     existing.inherits.extend(ast::HasEntry::inherits(&new));
                     existing
                         .entries
-                        .extend(ast::HasEntry::attrpath_values(&new));
+                        .extend(ast::HasEntry::attrpath_values(&new).map(|entry| {
+                            let span = c.span_for(&entry);
+                            (
+                                span,
+                                entry.attrpath().unwrap().attrs(),
+                                entry.value().unwrap(),
+                            )
+                        }));
                 } else {
                     todo!()
                 }
@@ -170,9 +191,15 @@ impl TrackedBindings {
     ///
     /// Returns true if the binding was merged, false if it needs to be compiled
     /// separately as a new binding.
-    fn try_merge(&mut self, c: &mut Compiler, entry: &AttrpathValue) -> bool {
-        let path = entry.attrpath().unwrap().attrs().collect::<Vec<_>>();
-        let value = entry.value().unwrap();
+    fn try_merge<I: Iterator<Item = ast::Attr>>(
+        &mut self,
+        c: &mut Compiler,
+        _nesting_level: usize,
+        span: Span,
+        path: I,
+        value: ast::Expr,
+    ) -> bool {
+        let path = path.collect::<Vec<_>>();
 
         // If the path only has one element, and if the entry is not an attribute
         // set literal, the entry can not be merged.
@@ -216,7 +243,13 @@ impl TrackedBindings {
 /// implemented for custom types.
 trait HasEntryProxy {
     fn inherits(&self) -> Box<dyn Iterator<Item = ast::Inherit>>;
-    fn attrpath_values(&self) -> Box<dyn Iterator<Item = ast::AttrpathValue>>;
+
+    fn attributes(
+        &self,
+        file: Arc<codemap::File>,
+    ) -> Box<dyn Iterator<Item = (Span, AstChildren<ast::Attr>, ast::Expr)>>;
+
+    fn nesting_level(&self) -> usize;
 }
 
 impl<N: HasEntry> HasEntryProxy for N {
@@ -224,8 +257,21 @@ impl<N: HasEntry> HasEntryProxy for N {
         Box::new(ast::HasEntry::inherits(self))
     }
 
-    fn attrpath_values(&self) -> Box<dyn Iterator<Item = ast::AttrpathValue>> {
-        Box::new(ast::HasEntry::attrpath_values(self))
+    fn attributes(
+        &self,
+        file: Arc<codemap::File>,
+    ) -> Box<dyn Iterator<Item = (Span, AstChildren<ast::Attr>, ast::Expr)>> {
+        Box::new(ast::HasEntry::attrpath_values(self).map(move |entry| {
+            (
+                entry.span_for(&file),
+                entry.attrpath().unwrap().attrs(),
+                entry.value().unwrap(),
+            )
+        }))
+    }
+
+    fn nesting_level(&self) -> usize {
+        0
     }
 }
 
@@ -234,9 +280,16 @@ impl HasEntryProxy for AttributeSet {
         Box::new(self.inherits.clone().into_iter())
     }
 
-    fn attrpath_values(&self) -> Box<dyn Iterator<Item = ast::AttrpathValue>> {
+    fn attributes(
+        &self,
+        _: Arc<codemap::File>,
+    ) -> Box<dyn Iterator<Item = (Span, AstChildren<ast::Attr>, ast::Expr)>> {
         Box::new(self.entries.clone().into_iter())
     }
+
+    fn nesting_level(&self) -> usize {
+        self.nesting_level
+    }
 }
 
 /// AST-traversing functions related to bindings.
@@ -400,21 +453,27 @@ impl Compiler<'_> {
     ) where
         N: ToSpan + HasEntryProxy,
     {
-        for entry in node.attrpath_values() {
-            if bindings.try_merge(self, &entry) {
+        for (span, path, value) in node.attributes(self.file.clone()) {
+            let mut path = path.skip(node.nesting_level());
+
+            if bindings.try_merge(
+                self,
+                node.nesting_level(),
+                span,
+                path.clone(),
+                value.clone(),
+            ) {
                 // Binding is nested, or already exists and was merged, move on.
                 continue;
             }
 
             *count += 1;
 
-            let (key, mut path) = {
-                let mut path = entry.attrpath().unwrap().attrs();
-                (path.next().unwrap(), path.peekable())
-            };
+            let key = path.next().unwrap();
+            let mut path = path.peekable();
 
             if path.peek().is_some() {
-                self.emit_error(&entry, ErrorKind::NotImplemented("nested bindings :("));
+                self.emit_error(&span, ErrorKind::NotImplemented("nested bindings :("));
                 continue;
             }
 
@@ -458,13 +517,7 @@ impl Compiler<'_> {
                 BindingsKind::Attrs => self.scope_mut().declare_phantom(key_span, false),
             };
 
-            bindings.track_new(
-                key_slot,
-                value_slot,
-                Binding::Plain {
-                    expr: entry.value().unwrap(),
-                },
-            );
+            bindings.track_new(key_slot, value_slot, Binding::Plain { expr: value });
         }
     }
 
@@ -542,7 +595,7 @@ impl Compiler<'_> {
 
                 // Binding is a merged or nested attribute set, and needs to be
                 // recursively compiled as another binding.
-                Binding::Set(set) => self.thunk(binding.value_slot, &set, |c, n, s| {
+                Binding::Set(set) => self.thunk(binding.value_slot, &set, |c, _, _| {
                     c.scope_mut().begin_scope();
                     c.compile_bindings(binding.value_slot, set.kind, &set);
                     c.scope_mut().end_scope();