fix(tvix/repl): Share globals and sourcemap across evaluations

Now that we can bind (potentially lazy, potentially lambda-containing)
values in the REPL and then reference them in subsequent evaluations,
it's important that the values to which we construct shared references
are shared across those subsequent evaluations - otherwise, we get
panics due to unknown source map locations, or dropped weak references
to globals.

This change assigns both the globals and the source map as fields on the
Repl after the first evaluation, and then passes those in (to the
EvaluationBuilder) on subsequent evaluations.

On the EvaluationBuilder side, there's some panicking introduced - this
is intentional, as my intent is for the builder to be configured
statically enough that panicking is the best way to report errors
here (it's always a bug to misconfigure an Evaluation, and we'd never
want to handle it dynamically).

Change-Id: I37225697235c22b683ca48a17d30fa8fedd12d1b
Reviewed-on: https://cl.tvl.fyi/c/depot/+/11960
Reviewed-by: flokli <flokli@flokli.de>
Autosubmit: aspen <root@gws.fyi>
Tested-by: BuildkiteCI
This commit is contained in:
Aspen Smith 2024-07-07 09:21:52 -04:00 committed by clbot
parent 01765c3717
commit 8821746d6c
10 changed files with 221 additions and 60 deletions

View file

@ -8,7 +8,7 @@ use tvix_build::buildservice;
use tvix_eval::{ use tvix_eval::{
builtins::impure_builtins, builtins::impure_builtins,
observer::{DisassemblingObserver, TracingObserver}, observer::{DisassemblingObserver, TracingObserver},
ErrorKind, EvalIO, Value, ErrorKind, EvalIO, GlobalsMap, SourceCode, Value,
}; };
use tvix_glue::{ use tvix_glue::{
builtins::{add_derivation_builtins, add_fetcher_builtins, add_import_builtins}, builtins::{add_derivation_builtins, add_fetcher_builtins, add_import_builtins},
@ -83,7 +83,13 @@ impl AllowIncomplete {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IncompleteInput; pub struct IncompleteInput;
pub struct EvalResult {
value: Option<Value>,
globals: Rc<GlobalsMap>,
}
/// Interprets the given code snippet, printing out warnings and errors and returning the result /// Interprets the given code snippet, printing out warnings and errors and returning the result
#[allow(clippy::too_many_arguments)]
pub fn evaluate( pub fn evaluate(
tvix_store_io: Rc<TvixStoreIO>, tvix_store_io: Rc<TvixStoreIO>,
code: &str, code: &str,
@ -91,7 +97,9 @@ pub fn evaluate(
args: &Args, args: &Args,
allow_incomplete: AllowIncomplete, allow_incomplete: AllowIncomplete,
env: Option<&HashMap<SmolStr, Value>>, env: Option<&HashMap<SmolStr, Value>>,
) -> Result<Option<Value>, IncompleteInput> { globals: Option<Rc<GlobalsMap>>,
source_map: Option<SourceCode>,
) -> Result<EvalResult, IncompleteInput> {
let span = Span::current(); let span = Span::current();
span.pb_start(); span.pb_start();
span.pb_set_style(&tvix_tracing::PB_SPINNER_STYLE); span.pb_set_style(&tvix_tracing::PB_SPINNER_STYLE);
@ -102,16 +110,27 @@ pub fn evaluate(
)) as Box<dyn EvalIO>) )) as Box<dyn EvalIO>)
.enable_import() .enable_import()
.with_strict(args.strict) .with_strict(args.strict)
.add_builtins(impure_builtins())
.env(env); .env(env);
match globals {
Some(globals) => {
eval_builder = eval_builder.with_globals(globals);
}
None => {
eval_builder = eval_builder.add_builtins(impure_builtins());
eval_builder = add_derivation_builtins(eval_builder, Rc::clone(&tvix_store_io)); eval_builder = add_derivation_builtins(eval_builder, Rc::clone(&tvix_store_io));
eval_builder = add_fetcher_builtins(eval_builder, Rc::clone(&tvix_store_io)); eval_builder = add_fetcher_builtins(eval_builder, Rc::clone(&tvix_store_io));
eval_builder = add_import_builtins(eval_builder, tvix_store_io); eval_builder = add_import_builtins(eval_builder, tvix_store_io);
eval_builder = configure_nix_path(eval_builder, &args.nix_search_path); eval_builder = configure_nix_path(eval_builder, &args.nix_search_path);
}
};
if let Some(source_map) = source_map {
eval_builder = eval_builder.with_source_map(source_map);
}
let source_map = eval_builder.source_map().clone(); let source_map = eval_builder.source_map().clone();
let result = { let (result, globals) = {
let mut compiler_observer = let mut compiler_observer =
DisassemblingObserver::new(source_map.clone(), std::io::stderr()); DisassemblingObserver::new(source_map.clone(), std::io::stderr());
if args.dump_bytecode { if args.dump_bytecode {
@ -129,7 +148,9 @@ pub fn evaluate(
span.pb_set_message("Evaluating…"); span.pb_set_message("Evaluating…");
let eval = eval_builder.build(); let eval = eval_builder.build();
eval.evaluate(code, path) let globals = eval.globals();
let result = eval.evaluate(code, path);
(result, globals)
}; };
if allow_incomplete.allow() if allow_incomplete.allow()
@ -160,19 +181,24 @@ pub fn evaluate(
} }
} }
Ok(result.value) Ok(EvalResult {
globals,
value: result.value,
})
} }
pub struct InterpretResult { pub struct InterpretResult {
output: String, output: String,
success: bool, success: bool,
pub(crate) globals: Option<Rc<GlobalsMap>>,
} }
impl InterpretResult { impl InterpretResult {
pub fn empty_success() -> Self { pub fn empty_success(globals: Option<Rc<GlobalsMap>>) -> Self {
Self { Self {
output: String::new(), output: String::new(),
success: true, success: true,
globals,
} }
} }
@ -194,6 +220,7 @@ impl InterpretResult {
/// and the result itself. The return value indicates whether /// and the result itself. The return value indicates whether
/// evaluation succeeded. /// evaluation succeeded.
#[instrument(skip_all, fields(indicatif.pb_show=1))] #[instrument(skip_all, fields(indicatif.pb_show=1))]
#[allow(clippy::too_many_arguments)]
pub fn interpret( pub fn interpret(
tvix_store_io: Rc<TvixStoreIO>, tvix_store_io: Rc<TvixStoreIO>,
code: &str, code: &str,
@ -202,11 +229,22 @@ pub fn interpret(
explain: bool, explain: bool,
allow_incomplete: AllowIncomplete, allow_incomplete: AllowIncomplete,
env: Option<&HashMap<SmolStr, Value>>, env: Option<&HashMap<SmolStr, Value>>,
globals: Option<Rc<GlobalsMap>>,
source_map: Option<SourceCode>,
) -> Result<InterpretResult, IncompleteInput> { ) -> Result<InterpretResult, IncompleteInput> {
let mut output = String::new(); let mut output = String::new();
let result = evaluate(tvix_store_io, code, path, args, allow_incomplete, env)?; let result = evaluate(
tvix_store_io,
code,
path,
args,
allow_incomplete,
env,
globals,
source_map,
)?;
if let Some(value) = result.as_ref() { if let Some(value) = result.value.as_ref() {
if explain { if explain {
writeln!(&mut output, "=> {}", value.explain()).unwrap(); writeln!(&mut output, "=> {}", value.explain()).unwrap();
} else if args.raw { } else if args.raw {
@ -219,6 +257,7 @@ pub fn interpret(
// inform the caller about any errors // inform the caller about any errors
Ok(InterpretResult { Ok(InterpretResult {
output, output,
success: result.is_some(), success: result.value.is_some(),
globals: Some(result.globals),
}) })
} }

View file

@ -75,6 +75,8 @@ fn main() {
false, false,
AllowIncomplete::RequireComplete, AllowIncomplete::RequireComplete,
None, // TODO(aspen): Pass in --arg/--argstr here None, // TODO(aspen): Pass in --arg/--argstr here
None,
None,
) )
.unwrap() .unwrap()
.finalize() .finalize()
@ -104,6 +106,8 @@ fn run_file(io_handle: Rc<TvixStoreIO>, mut path: PathBuf, args: &Args) {
false, false,
AllowIncomplete::RequireComplete, AllowIncomplete::RequireComplete,
None, None,
None,
None,
) )
.unwrap() .unwrap()
.finalize() .finalize()

View file

@ -3,7 +3,7 @@ use std::{collections::HashMap, path::PathBuf};
use rustyline::{error::ReadlineError, Editor}; use rustyline::{error::ReadlineError, Editor};
use smol_str::SmolStr; use smol_str::SmolStr;
use tvix_eval::Value; use tvix_eval::{GlobalsMap, SourceCode, Value};
use tvix_glue::tvix_store_io::TvixStoreIO; use tvix_glue::tvix_store_io::TvixStoreIO;
use crate::{ use crate::{
@ -92,6 +92,8 @@ pub struct Repl<'a> {
io_handle: Rc<TvixStoreIO>, io_handle: Rc<TvixStoreIO>,
args: &'a Args, args: &'a Args,
source_map: SourceCode,
globals: Option<Rc<GlobalsMap>>,
} }
impl<'a> Repl<'a> { impl<'a> Repl<'a> {
@ -103,6 +105,8 @@ impl<'a> Repl<'a> {
env: HashMap::new(), env: HashMap::new(),
io_handle, io_handle,
args, args,
source_map: Default::default(),
globals: None,
} }
} }
@ -179,7 +183,7 @@ impl<'a> Repl<'a> {
} }
ReplCommand::Help => { ReplCommand::Help => {
println!("{}", ReplCommand::HELP); println!("{}", ReplCommand::HELP);
Ok(InterpretResult::empty_success()) Ok(InterpretResult::empty_success(None))
} }
ReplCommand::Expr(input) => interpret( ReplCommand::Expr(input) => interpret(
Rc::clone(&self.io_handle), Rc::clone(&self.io_handle),
@ -189,6 +193,8 @@ impl<'a> Repl<'a> {
false, false,
AllowIncomplete::Allow, AllowIncomplete::Allow,
Some(&self.env), Some(&self.env),
self.globals.clone(),
Some(self.source_map.clone()),
), ),
ReplCommand::Assign(Assignment { ident, value }) => { ReplCommand::Assign(Assignment { ident, value }) => {
match evaluate( match evaluate(
@ -198,12 +204,15 @@ impl<'a> Repl<'a> {
self.args, self.args,
AllowIncomplete::Allow, AllowIncomplete::Allow,
Some(&self.env), Some(&self.env),
self.globals.clone(),
Some(self.source_map.clone()),
) { ) {
Ok(Some(value)) => { Ok(result) => {
if let Some(value) = result.value {
self.env.insert(ident.into(), value); self.env.insert(ident.into(), value);
Ok(InterpretResult::empty_success())
} }
Ok(None) => Ok(InterpretResult::empty_success()), Ok(InterpretResult::empty_success(Some(result.globals)))
}
Err(incomplete) => Err(incomplete), Err(incomplete) => Err(incomplete),
} }
} }
@ -215,6 +224,8 @@ impl<'a> Repl<'a> {
true, true,
AllowIncomplete::Allow, AllowIncomplete::Allow,
Some(&self.env), Some(&self.env),
self.globals.clone(),
Some(self.source_map.clone()),
), ),
ReplCommand::Print(input) => interpret( ReplCommand::Print(input) => interpret(
Rc::clone(&self.io_handle), Rc::clone(&self.io_handle),
@ -227,13 +238,22 @@ impl<'a> Repl<'a> {
false, false,
AllowIncomplete::Allow, AllowIncomplete::Allow,
Some(&self.env), Some(&self.env),
self.globals.clone(),
Some(self.source_map.clone()),
), ),
}; };
match res { match res {
Ok(InterpretResult { output, .. }) => { Ok(InterpretResult {
output,
globals,
success: _,
}) => {
self.rl.add_history_entry(input); self.rl.add_history_entry(input);
self.multiline_input = None; self.multiline_input = None;
if globals.is_some() {
self.globals = globals;
}
CommandResult { CommandResult {
output, output,
continue_: true, continue_: true,

View file

View file

@ -0,0 +1 @@
{ }: import ./six.nix { }

View file

@ -53,6 +53,22 @@ test_repl!(bind_lazy() {
"#]]; "#]];
}); });
test_repl!(bind_lazy_errors() {
r#"x = (_: "x" + 1)"# => expect![[""]];
"x null" => expect![[""]];
});
test_repl!(bind_referencing_import() {
"six = import ./tests/six.nix {}" => expect![[""]];
"six.six" => expect![[r#"
=> 6 :: int
"#]];
"imported = import ./tests/import.nix" => expect![[""]];
"(imported {}).six" => expect![[r#"
=> 6 :: int
"#]];
});
test_repl!(deep_print() { test_repl!(deep_print() {
"builtins.map (x: x + 1) [ 1 2 3 ]" => expect![[r#" "builtins.map (x: x + 1) [ 1 2 3 ]" => expect![[r#"
=> [ <CODE> <CODE> <CODE> ] :: list => [ <CODE> <CODE> <CODE> ] :: list

1
tvix/cli/tests/six.nix Normal file
View file

@ -0,0 +1 @@
{ }: { six = builtins.foldl' (x: y: x + y) 0 [ 1 2 3 ]; }

View file

@ -117,7 +117,7 @@ impl TrackedFormal {
/// The map of globally available functions and other values that /// The map of globally available functions and other values that
/// should implicitly be resolvable in the global scope. /// should implicitly be resolvable in the global scope.
pub(crate) type GlobalsMap = HashMap<&'static str, Value>; pub type GlobalsMap = HashMap<&'static str, Value>;
/// Set of builtins that (if they exist) should be made available in /// Set of builtins that (if they exist) should be made available in
/// the global scope, meaning that they can be accessed not just /// the global scope, meaning that they can be accessed not just

View file

@ -42,13 +42,12 @@ use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use crate::compiler::GlobalsMap;
use crate::observer::{CompilerObserver, RuntimeObserver}; use crate::observer::{CompilerObserver, RuntimeObserver};
use crate::value::Lambda; use crate::value::Lambda;
use crate::vm::run_lambda; use crate::vm::run_lambda;
// Re-export the public interface used by other crates. // Re-export the public interface used by other crates.
pub use crate::compiler::{compile, prepare_globals, CompilationOutput}; pub use crate::compiler::{compile, prepare_globals, CompilationOutput, GlobalsMap};
pub use crate::errors::{AddContext, CatchableErrorKind, Error, ErrorKind, EvalResult}; pub use crate::errors::{AddContext, CatchableErrorKind, Error, ErrorKind, EvalResult};
pub use crate::io::{DummyIO, EvalIO, FileType}; pub use crate::io::{DummyIO, EvalIO, FileType};
pub use crate::pretty_ast::pretty_print_expr; pub use crate::pretty_ast::pretty_print_expr;
@ -64,6 +63,16 @@ pub use crate::value::{Builtin, CoercionKind, NixAttrs, NixList, NixString, Valu
#[cfg(feature = "impure")] #[cfg(feature = "impure")]
pub use crate::io::StdIO; pub use crate::io::StdIO;
struct BuilderBuiltins {
builtins: Vec<(&'static str, Value)>,
src_builtins: Vec<(&'static str, &'static str)>,
}
enum BuilderGlobals {
Builtins(BuilderBuiltins),
Globals(Rc<GlobalsMap>),
}
/// Builder for building an [`Evaluation`]. /// Builder for building an [`Evaluation`].
/// ///
/// Construct an [`EvaluationBuilder`] by calling one of: /// Construct an [`EvaluationBuilder`] by calling one of:
@ -75,9 +84,8 @@ pub use crate::io::StdIO;
/// Then configure the fields by calling the various methods on [`EvaluationBuilder`], and finally /// Then configure the fields by calling the various methods on [`EvaluationBuilder`], and finally
/// call [`build`](Self::build) to construct an [`Evaluation`] /// call [`build`](Self::build) to construct an [`Evaluation`]
pub struct EvaluationBuilder<'co, 'ro, 'env, IO> { pub struct EvaluationBuilder<'co, 'ro, 'env, IO> {
source_map: SourceCode, source_map: Option<SourceCode>,
builtins: Vec<(&'static str, Value)>, globals: BuilderGlobals,
src_builtins: Vec<(&'static str, &'static str)>,
env: Option<&'env HashMap<SmolStr, Value>>, env: Option<&'env HashMap<SmolStr, Value>>,
io_handle: IO, io_handle: IO,
enable_import: bool, enable_import: bool,
@ -98,21 +106,31 @@ where
/// - Adds a `"storeDir"` builtin containing the store directory of the configured IO handle /// - Adds a `"storeDir"` builtin containing the store directory of the configured IO handle
/// - Sets up globals based on the configured builtins /// - Sets up globals based on the configured builtins
/// - Copies all other configured fields to the [`Evaluation`] /// - Copies all other configured fields to the [`Evaluation`]
pub fn build(mut self) -> Evaluation<'co, 'ro, 'env, IO> { pub fn build(self) -> Evaluation<'co, 'ro, 'env, IO> {
let source_map = self.source_map.unwrap_or_default();
let globals = match self.globals {
BuilderGlobals::Globals(globals) => globals,
BuilderGlobals::Builtins(BuilderBuiltins {
mut builtins,
src_builtins,
}) => {
// Insert a storeDir builtin *iff* a store directory is present. // Insert a storeDir builtin *iff* a store directory is present.
if let Some(store_dir) = self.io_handle.as_ref().store_dir() { if let Some(store_dir) = self.io_handle.as_ref().store_dir() {
self.builtins.push(("storeDir", store_dir.into())); builtins.push(("storeDir", store_dir.into()));
} }
let globals = crate::compiler::prepare_globals( crate::compiler::prepare_globals(
self.builtins, builtins,
self.src_builtins, src_builtins,
self.source_map.clone(), source_map.clone(),
self.enable_import, self.enable_import,
); )
}
};
Evaluation { Evaluation {
source_map: self.source_map, source_map,
globals, globals,
env: self.env, env: self.env,
io_handle: self.io_handle, io_handle: self.io_handle,
@ -132,11 +150,13 @@ impl<'co, 'ro, 'env, IO> EvaluationBuilder<'co, 'ro, 'env, IO> {
builtins.extend(builtins::placeholders()); // these are temporary builtins.extend(builtins::placeholders()); // these are temporary
Self { Self {
source_map: SourceCode::default(), source_map: None,
enable_import: false, enable_import: false,
io_handle, io_handle,
globals: BuilderGlobals::Builtins(BuilderBuiltins {
builtins, builtins,
src_builtins: vec![], src_builtins: vec![],
}),
env: None, env: None,
strict: false, strict: false,
nix_path: None, nix_path: None,
@ -149,8 +169,7 @@ impl<'co, 'ro, 'env, IO> EvaluationBuilder<'co, 'ro, 'env, IO> {
EvaluationBuilder { EvaluationBuilder {
io_handle, io_handle,
source_map: self.source_map, source_map: self.source_map,
builtins: self.builtins, globals: self.globals,
src_builtins: self.src_builtins,
env: self.env, env: self.env,
enable_import: self.enable_import, enable_import: self.enable_import,
strict: self.strict, strict: self.strict,
@ -175,14 +194,64 @@ impl<'co, 'ro, 'env, IO> EvaluationBuilder<'co, 'ro, 'env, IO> {
self.with_enable_import(true) self.with_enable_import(true)
} }
fn builtins_mut(&mut self) -> &mut BuilderBuiltins {
match &mut self.globals {
BuilderGlobals::Builtins(builtins) => builtins,
BuilderGlobals::Globals(_) => {
panic!("Cannot modify builtins on an EvaluationBuilder with globals configured")
}
}
}
/// Add additional builtins (represented as tuples of name and [`Value`]) to this evaluation
/// builder.
///
/// # Panics
///
/// Panics if this evaluation builder has had globals set via [`with_globals`]
pub fn add_builtins<I>(mut self, builtins: I) -> Self pub fn add_builtins<I>(mut self, builtins: I) -> Self
where where
I: IntoIterator<Item = (&'static str, Value)>, I: IntoIterator<Item = (&'static str, Value)>,
{ {
self.builtins.extend(builtins); self.builtins_mut().builtins.extend(builtins);
self self
} }
/// Add additional builtins that are implemented in Nix source code (represented as tuples of
/// name and nix source) to this evaluation builder.
///
/// # Panics
///
/// Panics if this evaluation builder has had globals set via [`with_globals`]
pub fn add_src_builtin(mut self, name: &'static str, src: &'static str) -> Self {
self.builtins_mut().src_builtins.push((name, src));
self
}
/// Set the globals for this evaluation builder to a previously-constructed globals map.
/// Intended to allow sharing globals across multiple evaluations (eg for the REPL).
///
/// Discards any builtins previously configured via [`add_builtins`] and [`add_src_builtins`].
/// If either of those methods is called on the evaluation builder after this one, they will
/// panic.
pub fn with_globals(self, globals: Rc<GlobalsMap>) -> Self {
Self {
globals: BuilderGlobals::Globals(globals),
..self
}
}
pub fn with_source_map(self, source_map: SourceCode) -> Self {
debug_assert!(
self.source_map.is_none(),
"Cannot set the source_map on an EvaluationBuilder twice"
);
Self {
source_map: Some(source_map),
..self
}
}
pub fn with_strict(self, strict: bool) -> Self { pub fn with_strict(self, strict: bool) -> Self {
Self { strict, ..self } Self { strict, ..self }
} }
@ -191,11 +260,6 @@ impl<'co, 'ro, 'env, IO> EvaluationBuilder<'co, 'ro, 'env, IO> {
self.with_strict(true) self.with_strict(true)
} }
pub fn add_src_builtin(mut self, name: &'static str, src: &'static str) -> Self {
self.src_builtins.push((name, src));
self
}
pub fn nix_path(self, nix_path: Option<String>) -> Self { pub fn nix_path(self, nix_path: Option<String>) -> Self {
Self { nix_path, ..self } Self { nix_path, ..self }
} }
@ -234,8 +298,8 @@ impl<'co, 'ro, 'env, IO> EvaluationBuilder<'co, 'ro, 'env, IO> {
} }
impl<'co, 'ro, 'env, IO> EvaluationBuilder<'co, 'ro, 'env, IO> { impl<'co, 'ro, 'env, IO> EvaluationBuilder<'co, 'ro, 'env, IO> {
pub fn source_map(&self) -> &SourceCode { pub fn source_map(&mut self) -> &SourceCode {
&self.source_map self.source_map.get_or_insert_with(SourceCode::default)
} }
} }
@ -255,7 +319,9 @@ impl<'co, 'ro, 'env> EvaluationBuilder<'co, 'ro, 'env, Box<dyn EvalIO>> {
pub fn enable_impure(mut self, io: Option<Box<dyn EvalIO>>) -> Self { pub fn enable_impure(mut self, io: Option<Box<dyn EvalIO>>) -> Self {
self.io_handle = io.unwrap_or_else(|| Box::new(StdIO) as Box<dyn EvalIO>); self.io_handle = io.unwrap_or_else(|| Box::new(StdIO) as Box<dyn EvalIO>);
self.enable_import = true; self.enable_import = true;
self.builtins.extend(builtins::impure_builtins()); self.builtins_mut()
.builtins
.extend(builtins::impure_builtins());
// Make `NIX_PATH` resolutions work by default, unless the // Make `NIX_PATH` resolutions work by default, unless the
// user already overrode this with something else. // user already overrode this with something else.
@ -332,9 +398,26 @@ pub struct EvaluationResult {
} }
impl<'co, 'ro, 'env, IO> Evaluation<'co, 'ro, 'env, IO> { impl<'co, 'ro, 'env, IO> Evaluation<'co, 'ro, 'env, IO> {
/// Make a new [builder][] for configuring an evaluation
///
/// [builder]: EvaluationBuilder
pub fn builder(io_handle: IO) -> EvaluationBuilder<'co, 'ro, 'env, IO> { pub fn builder(io_handle: IO) -> EvaluationBuilder<'co, 'ro, 'env, IO> {
EvaluationBuilder::new(io_handle) EvaluationBuilder::new(io_handle)
} }
/// Clone the reference to the map of Nix globals for this evaluation. If [`Value`]s are shared
/// across subsequent [`Evaluation`]s, it is important that those evaluations all have the same
/// underlying globals map.
pub fn globals(&self) -> Rc<GlobalsMap> {
self.globals.clone()
}
/// Clone the reference to the contained source code map. This is used after an evaluation for
/// pretty error printing. Also, if [`Value`]s are shared across subsequent [`Evaluation`]s, it
/// is important that those evaluations all have the same underlying source code map.
pub fn source_map(&self) -> SourceCode {
self.source_map.clone()
}
} }
impl<'co, 'ro, 'env> Evaluation<'co, 'ro, 'env, Box<dyn EvalIO>> { impl<'co, 'ro, 'env> Evaluation<'co, 'ro, 'env, Box<dyn EvalIO>> {
@ -352,12 +435,6 @@ impl<'co, 'ro, 'env, IO> Evaluation<'co, 'ro, 'env, IO>
where where
IO: AsRef<dyn EvalIO> + 'static, IO: AsRef<dyn EvalIO> + 'static,
{ {
/// Clone the reference to the contained source code map. This is used after
/// an evaluation for pretty error printing.
pub fn source_map(&self) -> SourceCode {
self.source_map.clone()
}
/// Only compile the provided source code, at an optional location of the /// Only compile the provided source code, at an optional location of the
/// source code (i.e. path to the file it was read from; used for error /// source code (i.e. path to the file it was read from; used for error
/// reporting, and for resolving relative paths in impure functions) /// reporting, and for resolving relative paths in impure functions)

View file

@ -78,7 +78,10 @@
}; };
tvix-cli = prev: { tvix-cli = prev: {
src = depot.tvix.utils.filterRustCrateSrc { root = prev.src.origSrc; }; src = depot.tvix.utils.filterRustCrateSrc rec {
root = prev.src.origSrc;
extraFileset = root + "/tests";
};
buildInputs = lib.optional pkgs.stdenv.isDarwin commonDarwinDeps; buildInputs = lib.optional pkgs.stdenv.isDarwin commonDarwinDeps;
}; };