about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-08-14T19·28+0300
committertazjin <tazjin@tvl.su>2022-08-31T22·10+0000
commitf173161f4c0fc754df2b6daeca302d3e65bbf77d (patch)
treee3504fda83f4950def7ea39f06f3d2014d4020e2
parent8c1c9aee3cab52befe55d6f41e1c9acaef1b1843 (diff)
feat(tvix/eval): implement inherit in let expressions r/4549
Note that at this point recursive bindings do not yet work in either
attrsets or let, so inheriting from the same scope is generally not
possible yet.

Change-Id: I6ca820d04b8ded5c22fb7ea18e2ec203bcaa8e9c
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6215
Reviewed-by: sterni <sternenseemann@systemli.org>
Reviewed-by: grfn <grfn@gws.fyi>
Tested-by: BuildkiteCI
-rw-r--r--tvix/eval/src/compiler.rs54
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.nix8
3 files changed, 49 insertions, 14 deletions
diff --git a/tvix/eval/src/compiler.rs b/tvix/eval/src/compiler.rs
index 11bd649f71..2f48a1cea8 100644
--- a/tvix/eval/src/compiler.rs
+++ b/tvix/eval/src/compiler.rs
@@ -688,9 +688,36 @@ 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, node: rnix::types::LetIn) -> Result<(), Error> {
         self.begin_scope();
         let mut entries = vec![];
+        let mut from_inherits = vec![];
+
+        for inherit in node.inherits() {
+            match inherit.from() {
+                // Within a `let` binding, inheriting from the outer
+                // scope is practically a no-op.
+                None => {
+                    self.warnings.push(EvalWarning {
+                        node: inherit.node().clone(),
+                        kind: WarningKind::UselessInherit,
+                    });
+
+                    continue;
+                }
+
+                Some(_) => {
+                    for ident in inherit.idents() {
+                        self.locals.locals.push(Local {
+                            name: ident.as_str().to_string(),
+                            depth: self.locals.scope_depth,
+                        });
+                    }
+                    from_inherits.push(inherit);
+                }
+            }
+        }
 
         // Before compiling the values of a let expression, all keys
         // need to already be added to the known locals. This is
@@ -712,24 +739,23 @@ impl Compiler {
             });
         }
 
-        for inherit in node.inherits() {
-            match inherit.from() {
-                // Within a `let` binding, inheriting from the outer
-                // scope is practically a no-op.
-                None => {
-                    self.warnings.push(EvalWarning {
-                        node: inherit.node().clone(),
-                        kind: WarningKind::UselessInherit,
-                    });
+        // Now we can add instructions to look up each inherited value
+        // ...
+        for inherit in from_inherits {
+            let from = inherit
+                .from()
+                .expect("only inherits with `from` are pushed here");
 
-                    continue;
-                }
-                Some(_) => todo!("let inherit from attrs"),
+            for ident in inherit.idents() {
+                // TODO: Optimised multi-select instruction?
+                self.compile(from.inner().unwrap())?;
+                self.emit_literal_ident(&ident);
+                self.chunk.push_op(OpCode::OpAttrsSelect);
             }
         }
 
-        // Now we can compile each expression, leaving the values on
-        // the stack in the right order.
+        // ... and finally each expression, leaving the values on the
+        // stack in the right order.
         for value in entries {
             self.compile(value)?;
         }
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.exp
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.exp
@@ -0,0 +1 @@
+1
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.nix
new file mode 100644
index 0000000000..12eed10e13
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-let-inherit.nix
@@ -0,0 +1,8 @@
+let
+  set = {
+    a = 1;
+  };
+in
+  let
+    inherit (set) a;
+  in a