about summary refs log tree commit diff
path: root/tvix/eval/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'tvix/eval/src/compiler')
-rw-r--r--tvix/eval/src/compiler/import.rs164
-rw-r--r--tvix/eval/src/compiler/mod.rs7
2 files changed, 95 insertions, 76 deletions
diff --git a/tvix/eval/src/compiler/import.rs b/tvix/eval/src/compiler/import.rs
index 3a8847f2cbb4..467fc256afc6 100644
--- a/tvix/eval/src/compiler/import.rs
+++ b/tvix/eval/src/compiler/import.rs
@@ -5,21 +5,98 @@
 //! compiler and VM state (such as the [`crate::SourceCode`]
 //! instance, or observers).
 
+use super::GlobalsMap;
+use genawaiter::rc::Gen;
 use std::rc::Weak;
 
 use crate::{
+    builtins::coerce_value_to_path,
+    generators::pin_generator,
     observer::NoOpObserver,
-    value::{Builtin, BuiltinArgument, Thunk},
-    vm::VM,
+    value::{Builtin, Thunk},
+    vm::generators::{self, GenCo},
     ErrorKind, SourceCode, Value,
 };
 
-use super::GlobalsMap;
-use crate::builtins::coerce_value_to_path;
+async fn import_impl(
+    co: GenCo,
+    globals: Weak<GlobalsMap>,
+    source: SourceCode,
+    mut args: Vec<Value>,
+) -> Result<Value, ErrorKind> {
+    let mut path = coerce_value_to_path(&co, args.pop().unwrap()).await?;
+
+    if path.is_dir() {
+        path.push("default.nix");
+    }
+
+    if let Some(cached) = generators::request_import_cache_lookup(&co, path.clone()).await {
+        return Ok(cached);
+    }
+
+    // TODO(tazjin): make this return a string directly instead
+    let contents = generators::request_read_to_string(&co, path.clone())
+        .await
+        .to_str()?
+        .as_str()
+        .to_string();
+
+    let parsed = rnix::ast::Root::parse(&contents);
+    let errors = parsed.errors();
+    let file = source.add_file(path.to_string_lossy().to_string(), contents);
+
+    if !errors.is_empty() {
+        return Err(ErrorKind::ImportParseError {
+            path,
+            file,
+            errors: errors.to_vec(),
+        });
+    }
+
+    let result = crate::compiler::compile(
+        &parsed.tree().expr().unwrap(),
+        Some(path.clone()),
+        file,
+        // The VM must ensure that a strong reference to the globals outlives
+        // any self-references (which are weak) embedded within the globals. If
+        // the expect() below panics, it means that did not happen.
+        globals
+            .upgrade()
+            .expect("globals dropped while still in use"),
+        &mut NoOpObserver::default(),
+    )
+    .map_err(|err| ErrorKind::ImportCompilerError {
+        path: path.clone(),
+        errors: vec![err],
+    })?;
+
+    if !result.errors.is_empty() {
+        return Err(ErrorKind::ImportCompilerError {
+            path,
+            errors: result.errors,
+        });
+    }
+
+    // TODO: emit not just the warning kind, hmm
+    // for warning in result.warnings {
+    //     vm.push_warning(warning);
+    // }
+
+    // Compilation succeeded, we can construct a thunk from whatever it spat
+    // out and return that.
+    let res = Value::Thunk(Thunk::new_suspended(
+        result.lambda,
+        generators::request_span(&co).await,
+    ));
+
+    generators::request_import_cache_put(&co, path, res.clone()).await;
+
+    Ok(res)
+}
 
-/// Constructs and inserts the `import` builtin. This builtin is special in that
-/// it needs to capture the [crate::SourceCode] structure to correctly track
-/// source code locations while invoking a compiler.
+/// Constructs the `import` builtin. This builtin is special in that
+/// it needs to capture the [crate::SourceCode] structure to correctly
+/// track source code locations while invoking a compiler.
 // TODO: need to be able to pass through a CompilationObserver, too.
 // TODO: can the `SourceCode` come from the compiler?
 pub(super) fn builtins_import(globals: &Weak<GlobalsMap>, source: SourceCode) -> Builtin {
@@ -31,75 +108,10 @@ pub(super) fn builtins_import(globals: &Weak<GlobalsMap>, source: SourceCode) ->
 
     Builtin::new(
         "import",
-        &[BuiltinArgument {
-            strict: true,
-            name: "path",
-        }],
-        None,
-        move |mut args: Vec<Value>, vm: &mut VM| {
-            let mut path = coerce_value_to_path(&args.pop().unwrap(), vm)?;
-            if path.is_dir() {
-                path.push("default.nix");
-            }
-
-            let current_span = vm.current_light_span();
-
-            if let Some(cached) = vm.import_cache.get(&path) {
-                return Ok(cached.clone());
-            }
-
-            let contents = vm.io().read_to_string(path.clone())?;
-
-            let parsed = rnix::ast::Root::parse(&contents);
-            let errors = parsed.errors();
-
-            let file = source.add_file(path.to_string_lossy().to_string(), contents);
-
-            if !errors.is_empty() {
-                return Err(ErrorKind::ImportParseError {
-                    path,
-                    file,
-                    errors: errors.to_vec(),
-                });
-            }
-
-            let result = crate::compiler::compile(
-                &parsed.tree().expr().unwrap(),
-                Some(path.clone()),
-                file,
-                // The VM must ensure that a strong reference to the
-                // globals outlives any self-references (which are
-                // weak) embedded within the globals.  If the
-                // expect() below panics, it means that did not
-                // happen.
-                globals
-                    .upgrade()
-                    .expect("globals dropped while still in use"),
-                &mut NoOpObserver::default(),
-            )
-            .map_err(|err| ErrorKind::ImportCompilerError {
-                path: path.clone(),
-                errors: vec![err],
-            })?;
-
-            if !result.errors.is_empty() {
-                return Err(ErrorKind::ImportCompilerError {
-                    path,
-                    errors: result.errors,
-                });
-            }
-
-            // Compilation succeeded, we can construct a thunk from whatever it spat
-            // out and return that.
-            let res = Value::Thunk(Thunk::new_suspended(result.lambda, current_span));
-
-            vm.import_cache.insert(path, res.clone());
-
-            for warning in result.warnings {
-                vm.push_warning(warning);
-            }
-
-            Ok(res)
+        Some("Import the given file and return the Nix value it evaluates to"),
+        1,
+        move |args| {
+            Gen::new(|co| pin_generator(import_impl(co, globals.clone(), source.clone(), args)))
         },
     )
 }
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs
index 69c232926b41..80e1cd27c953 100644
--- a/tvix/eval/src/compiler/mod.rs
+++ b/tvix/eval/src/compiler/mod.rs
@@ -1021,6 +1021,12 @@ impl Compiler<'_> {
         // lambda as a constant.
         let mut compiled = self.contexts.pop().unwrap();
 
+        // Emit an instruction to inform the VM that the chunk has ended.
+        compiled
+            .lambda
+            .chunk
+            .push_op(OpCode::OpReturn, self.span_for(node));
+
         // Capturing the with stack counts as an upvalue, as it is
         // emitted as an upvalue data instruction.
         if compiled.captures_with_stack {
@@ -1433,6 +1439,7 @@ pub fn compile(
     // unevaluated state (though in practice, a value *containing* a
     // thunk might be returned).
     c.emit_force(expr);
+    c.push_op(OpCode::OpReturn, &root_span);
 
     let lambda = Rc::new(c.contexts.pop().unwrap().lambda);
     c.observer.observe_compiled_toplevel(&lambda);