about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2023-01-04T14·12+0300
committertazjin <tazjin@tvl.su>2023-01-17T10·38+0000
commitb9646ab40c3b7da488b3f3874fb0ce4ddafe6de4 (patch)
tree870dd31021047f454ee84937c4b09f830e3a2b4f
parent79e54f46ce1b532a115d3b53482216a4b22f9881 (diff)
feat(tvix/eval): add builtins to builtins r/5674
This is a somewhat terrifying hack that enables us to support
`builtins.builtins`, by running a "fake compilation" inside of a
suspended native thunk that can resolve the weak pointer to the
globals.

With this implementation, the thunk at `builtins.builtins` actually
resolves to the "real" `builtins` (verified with a new test).

This is kind of ugly, and it's something users shouldn't use, but
bubbling a warning out of this is difficult at the moment due to a
little bit of trickery with how the spans in suspended native thunks
work (they don't) (see b/237, b/238)

Change-Id: I67d0e93246dd5b279c960aeda00402031aa12af3
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7748
Tested-by: BuildkiteCI
Reviewed-by: flokli <flokli@flokli.de>
-rw-r--r--tvix/eval/src/compiler/mod.rs34
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.exp1
-rw-r--r--tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.nix1
3 files changed, 34 insertions, 2 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
index 9fb24aa98e..605fce4749 100644
--- a/tvix/eval/src/compiler/mod.rs
+++ b/tvix/eval/src/compiler/mod.rs
@@ -28,7 +28,7 @@ use std::sync::Arc;
 
 use crate::chunk::Chunk;
 use crate::errors::{Error, ErrorKind, EvalResult};
-use crate::observer::CompilerObserver;
+use crate::observer::{CompilerObserver, NoOpObserver};
 use crate::opcode::{CodeIdx, Count, JumpOffset, OpCode, UpvalueIdx};
 use crate::spans::LightSpan;
 use crate::spans::ToSpan;
@@ -1261,7 +1261,7 @@ pub fn prepare_globals(
         // to instantiate its compiler, the `Weak` reference is passed
         // here.
         if enable_import {
-            let import = Value::Builtin(import::builtins_import(weak, source));
+            let import = Value::Builtin(import::builtins_import(weak, source.clone()));
             builtins_under_construction.insert("import", import);
         }
 
@@ -1278,6 +1278,36 @@ pub fn prepare_globals(
             }
         }
 
+        // builtins contain themselves (`builtins.builtins`), which we
+        // can resolve by manually constructing a suspended thunk that
+        // dereferences the same weak pointer as above.
+        //
+        // This is an inefficient hack, but *if* anyone was to use
+        // `builtins.builtins`, it would only happen once as the thunk
+        // is then resolved.
+        let weak_globals = weak.clone();
+        builtins_under_construction.insert(
+            "builtins",
+            Value::Thunk(Thunk::new_suspended_native(Rc::new(Box::new(move |_| {
+                let file = source.add_file("builtins-dot-builtins.nix".into(), "builtins".into());
+                let span = file.span;
+                let mut observer = NoOpObserver::default();
+
+                let mut compiler = Compiler::new(
+                    None,
+                    file,
+                    weak_globals
+                        .upgrade()
+                        .expect("globals dropped while still in use"),
+                    &mut observer,
+                )?;
+
+                weak_globals.upgrade().unwrap().get("builtins").unwrap()(&mut compiler, span);
+
+                Ok(compiler.chunk().constants[0].clone())
+            })))),
+        );
+
         // This is followed by the actual `builtins` attribute set
         // being constructed and inserted in the global scope.
         let builtins_set =
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.exp
@@ -0,0 +1 @@
+true
diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.nix
new file mode 100644
index 0000000000..cfbcbbb768
--- /dev/null
+++ b/tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.nix
@@ -0,0 +1 @@
+[ builtins ]  == [ builtins.builtins ]