From b9646ab40c3b7da488b3f3874fb0ce4ddafe6de4 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 4 Jan 2023 17:12:33 +0300 Subject: feat(tvix/eval): add builtins to builtins 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 --- tvix/eval/src/compiler/mod.rs | 34 ++++++++++++++++++++-- .../tvix_tests/eval-okay-builtins-builtins.exp | 1 + .../tvix_tests/eval-okay-builtins-builtins.nix | 1 + 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.exp create mode 100644 tvix/eval/src/tests/tvix_tests/eval-okay-builtins-builtins.nix (limited to 'tvix/eval') 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 ] -- cgit 1.4.1