Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/builtins/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ fn list_to_attrs(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word
pos,
"while evaluating the argument passed to builtins.listToAttrs",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
let mut entries: Vec<(Sym, Pos, Word)> = Vec::with_capacity(l.len as usize);
let mut seen: FxHashSet<Sym> = FxHashSet::default();
for c in l.cells() {
Expand Down Expand Up @@ -223,7 +223,7 @@ fn zip_attrs_with(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Wor
pos,
"while evaluating the second argument passed to builtins.zipAttrsWith",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
let mut groups: FxHashMap<Sym, Vec<Word>> = FxHashMap::default();
let mut order: Vec<Sym> = Vec::new();
for c in l.cells() {
Expand Down Expand Up @@ -265,7 +265,7 @@ fn cat_attrs(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word> {
pos,
"while evaluating the second argument passed to builtins.catAttrs",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
let mut out = Vec::new();
for c in l.cells() {
let w = c.get();
Expand All @@ -292,7 +292,7 @@ fn group_by(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word> {
pos,
"while evaluating the second argument passed to builtins.groupBy",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
let mut groups: FxHashMap<Sym, Vec<Word>> = FxHashMap::default();
for c in l.cells() {
let elem = c.get();
Expand Down
10 changes: 5 additions & 5 deletions src/builtins/derivation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ pub struct DrvInfo {
/// The ATerm this evaluation produced; `None` when the drv was read from `/nix/store`.
pub aterm: Option<Arc<Vec<u8>>>,
/// Where the derivation was defined. `derivation` is a wrapper around
/// `derivationStrict`, so the primop's call position is inside
/// `«derivation-internal»` for every normal call; the position of the
/// `name` attribute's value stands in, which is the same position Nix
/// names in "whose name attribute is located at". A direct
/// `derivationStrict`, so for every normal call the position of the
/// primop call is inside `«derivation-internal»`. The position of the
/// value of the `name` attribute is used instead. Nix names the same
/// position in "whose name attribute is located at". A direct
/// `builtins.derivationStrict` call keeps its own call position.
pub pos: Option<Pos>,
/// The attribute path a cli walk reached this derivation by.
Expand Down Expand Up @@ -160,7 +160,7 @@ fn derivation_strict(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<
pos,
"while evaluating the argument passed to builtins.derivationStrict",
)?;
parallel::fanout_attrs_coerce(ev, attrs); // parallel
parallel::fanout_attrs_coerce(ev, attrs);
let Some(name_attr) = attrs.get(ev.s.name) else {
let mut e = Error::new(
ErrorKind::MissingAttr,
Expand Down
10 changes: 5 additions & 5 deletions src/builtins/fetch.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! `builtins.fetchTree` for locked inputs (fetchTree.cc, fetchers.cc): the
//! attrs the evaluator passes become a `source::SourceRef`, which resolves
//! to a tree, and the result attrset mirrors `emitTreeAttrs`. A `path` input
//! is a virtual copy of the local directory, like the root tree; everything
//! else is locked, and `crate::source` fetches it.
//! `builtins.fetchTree` for locked inputs (fetchTree.cc, fetchers.cc). The
//! attrs from the evaluator become a `source::SourceRef`, which resolves to
//! a tree. The result attrset mirrors `emitTreeAttrs`. A `path` input is a
//! virtual copy of the local directory, like the root tree. Every other
//! input is locked, and `crate::source` fetches it.

use std::path::Path;

Expand Down
18 changes: 9 additions & 9 deletions src/builtins/lists.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ fn elem(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word> {
pos,
"while evaluating the second argument passed to builtins.elem",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
for c in l.cells() {
let x = ev.force(args[0])?;
let v = c.get();
Expand Down Expand Up @@ -135,7 +135,7 @@ fn filter(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word> {
pos,
"while evaluating the second argument passed to builtins.filter",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
if l.len == 0 {
return Ok(Word::from_ref(l));
}
Expand Down Expand Up @@ -176,7 +176,7 @@ fn foldl_strict(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word>
pos,
"while evaluating the third argument passed to builtins.foldlStrict",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
let mut acc = args[1];
for c in l.cells() {
acc = ev.call2(f, acc, c.get(), pos)?;
Expand All @@ -190,7 +190,7 @@ fn concat_lists(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word>
pos,
"while evaluating the first argument passed to builtins.concatLists",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
let mut lists = Vec::with_capacity(l.len as usize);
for c in l.cells() {
let v = c.get();
Expand All @@ -214,7 +214,7 @@ fn concat_map(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word> {
pos,
"while evaluating the second argument passed to builtins.concatMap",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
let mut lists = Vec::with_capacity(l.len as usize);
for c in l.cells() {
let r = ev.call1(f, c.get(), pos)?;
Expand Down Expand Up @@ -263,7 +263,7 @@ fn any(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word> {
pos,
"while evaluating the second argument passed to builtins.any",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
for c in l.cells() {
let r = ev.call1(f, c.get(), pos)?;
if ev.force_bool(
Expand All @@ -288,7 +288,7 @@ fn all(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word> {
pos,
"while evaluating the second argument passed to builtins.all",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
for c in l.cells() {
let r = ev.call1(f, c.get(), pos)?;
if !ev.force_bool(
Expand All @@ -312,7 +312,7 @@ fn sort(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word> {
pos,
"while evaluating the second argument passed to builtins.sort",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
if l.len == 0 {
return Ok(Word::from_ref(l));
}
Expand Down Expand Up @@ -525,7 +525,7 @@ fn partition(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult<Word> {
pos,
"while evaluating the second argument passed to builtins.partition",
)?;
parallel::fanout_list(ev, l); // parallel
parallel::fanout_list(ev, l);
let mut right = Vec::new();
let mut wrong = Vec::new();
for c in l.cells() {
Expand Down
6 changes: 3 additions & 3 deletions src/builtins/paths.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! File system primops (primops.cc): readFile, readDir, readFileType,
//! pathExists, toPath, storePath, filterSource, path, toFile. The network
//! fetchers and flake primops are registered so `builtins ? fetchMercurial`
//! holds, but they fail when called; only `fetchurl`, `fetchTree` on a
//! fetchers and flake primops are registered, so `builtins ? fetchMercurial`
//! holds, but a call fails. Only these work: `fetchurl`, `fetchTree` on a
//! locked input, `fetchTarball` with a `sha256`, `fetchGit`, `getFlake`,
//! `parseFlakeRef` and `flakeRefToString` work (`flake`).
//! `parseFlakeRef` and `flakeRefToString` (`flake`).

use crate::sys::errno;
use crate::sys::{self, OsStrExt};
Expand Down
18 changes: 12 additions & 6 deletions src/builtins/store_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@
//! evaluator reads for a store path goes through [`locate`]: `import`,
//! `readFile`, `readDir`, `pathExists`, `readFileType`, `hashFile`,
//! `findFile`, `storePath`, `fetchTree`, `builtins.path`, `filterSource` and
//! string coercion, and a read of a file through [`open`], which applies
//! pure evaluation's allow list first. The answer is a [`SrcPath`]: the real
//! file system, a local tree a virtual copy stands for, a member of a fetched
//! flake input served out of its spool by offset (`crate::treestore`), or a
//! `toFile` text or `.drv` this evaluation holds in memory. Its readers below
//! record what they saw in the tracing cache's read set.
//! string coercion. A read of a file goes through [`open`], which applies
//! the allow list of pure evaluation first. The answer is a [`SrcPath`],
//! one of:
//!
//! - the real file system;
//! - a local tree that a virtual copy stands for;
//! - a member of a fetched flake input, served out of its spool by offset
//! (`crate::treestore`);
//! - a `toFile` text or a `.drv` that this evaluation holds in memory.
//!
//! The readers below record what they saw in the read set of the tracing
//! cache.

use std::ffi::OsStr;
use std::fs;
Expand Down
2 changes: 1 addition & 1 deletion src/builtins/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ fn concat_strings_sep(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult
pos,
"while evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep",
)?;
parallel::fanout_list_coerce(ev, l); // parallel
parallel::fanout_list_coerce(ev, l);
let mut ctx: Vec<Ctx> = sep.ctx.to_vec();
let mut out: Vec<u8> = Vec::new();
for (i, c) in l.cells().iter().enumerate() {
Expand Down
24 changes: 14 additions & 10 deletions src/compile/cache.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
//! Compiled-IR cache on disk. A file's IR is written after its first
//! compile and loaded instead of parsing and compiling on later runs
//! Compiled-IR cache on disk. The first compile of a file writes its IR.
//! Later runs load it and do not parse or compile the file again
//! (docs/explanation/caching.md, "The IR cache: the same file, compiled once").
//!
//! The IR is process-specific in three ways, and the entry undoes each:
//! `Sym` ids (a per-entry name table is re-interned on load), `Pos` values
//! (stored relative to the file, rebased on load), and constant `Word`s
//! (literals are stored by content, globals and `builtins.x` by name and
//! looked up in the live tables, so `__nixPath` follows the run). Binding
//! frames (`let`, `rec`, formals) are slot-ordered by `Sym` id, so the
//! loader re-sorts them and rewrites every `Local` index through a
//! permutation stack that mirrors the compiler's scope pushes.
//! The IR depends on the process in three ways, and the entry undoes each:
//!
//! - `Sym` ids: the entry has its own name table, which the load interns
//! again.
//! - `Pos` values: stored relative to the file, rebased on load.
//! - Constant `Word`s: literals are stored by content. Globals and
//! `builtins.x` are stored by name and looked up in the live tables, so
//! `__nixPath` follows the run.
//!
//! Binding frames (`let`, `rec`, formals) are in `Sym` id order. So the
//! loader sorts them again and rewrites every `Local` index through a
//! permutation stack that mirrors the scope pushes of the compiler.

use std::path::{Path, PathBuf};

Expand Down
8 changes: 4 additions & 4 deletions src/compile/ir.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Resolved intermediate representation. Variables are `(depth, index)`
//! pairs into the runtime env chain, attrpaths are merged, `inherit` is
//! expanded, and `builtins.x` on the unshadowed global is folded to a
//! constant. IR nodes live forever (`&'static`) so thunks can point at them.
//! Resolved intermediate representation. A variable is a `(depth, index)`
//! pair into the runtime env chain. The compiler merges attrpaths, expands
//! `inherit` and folds `builtins.x` on the unshadowed global to a constant.
//! IR nodes live forever (`&'static`) so thunks can point at them.

use crate::eval::Pos;
use crate::runtime::Word;
Expand Down
4 changes: 2 additions & 2 deletions src/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -458,8 +458,8 @@ impl EvalConfig {
self
}

/// What [`EvalConfig::pure`] settled on: the tracing-cache key needs it,
/// and a frontend's flag is not the whole answer any more.
/// The value [`EvalConfig::pure`] settled on. The tracing-cache key needs
/// it, because the flag of a frontend is not the whole answer.
pub fn is_pure(&self) -> bool {
self.pure
}
Expand Down
38 changes: 13 additions & 25 deletions src/eval/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@ use rstest::rstest;

use crate::print;

fn eval_str(src: &str) -> String {
let mut ev = Evaluator::new();
let v = ev.eval_string(src, b"/").expect("eval");
let v = ev.force_deep(v).expect("force");
fn eval_with(mut ev: Evaluator, src: &str) -> Result<String, String> {
let v = ev
.eval_string(src, b"/")
.and_then(|v| ev.force_deep(v))
.map_err(|e| e.msg)?;
let mut out = Vec::new();
print::print_ambiguous(&mut ev, v, 1, &mut out, &mut HashSet::default(), 0).unwrap();
String::from_utf8(out).unwrap()
Ok(String::from_utf8(out).unwrap())
}

fn eval_str(src: &str) -> String {
eval_with(Evaluator::new(), src).unwrap_or_else(|e| panic!("{src:?}: {e}"))
}

fn eval_err(src: &str) -> String {
let mut ev = Evaluator::new();
match ev.eval_string(src, b"/").and_then(|v| ev.force_deep(v)) {
Ok(_) => panic!("expected an error"),
Err(e) => e.msg,
}
eval_with(Evaluator::new(), src).expect_err("expected an error")
}

#[test]
Expand Down Expand Up @@ -195,14 +196,7 @@ fn eval_config_defaults_to_the_system_this_build_is_for() {
}

fn eval_pure(src: &str) -> Result<String, String> {
let mut ev = EvalConfig::new().pure(true).build();
let v = ev
.eval_string(src, b"/")
.and_then(|v| ev.force_deep(v))
.map_err(|e| e.msg)?;
let mut out = Vec::new();
print::print_ambiguous(&mut ev, v, 1, &mut out, &mut HashSet::default(), 0).unwrap();
Ok(String::from_utf8(out).unwrap())
eval_with(EvalConfig::new().pure(true).build(), src)
}

/// The answers of `nix-instantiate --pure-eval --read-write-mode` (Nix 2.34.8).
Expand Down Expand Up @@ -473,13 +467,7 @@ fn nix_eq_forces_what_the_shortcut_skips() {
let eq = |src: &str| {
let mut ev = Evaluator::new();
ev.nix_eq = true;
let v = ev
.eval_string(src, b"/")
.and_then(|v| ev.force_deep(v))
.map_err(|e| e.msg)?;
let mut out = Vec::new();
print::print_ambiguous(&mut ev, v, 1, &mut out, &mut HashSet::default(), 0).unwrap();
Ok::<_, String>(String::from_utf8(out).unwrap())
eval_with(ev, src)
};
let x = "let x = { a = throw \"a\"; }; in";
assert_eq!(eval_str(&format!("{x} x == x")), "true");
Expand Down
1 change: 0 additions & 1 deletion src/parallel/force.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,6 @@ pub fn fanout_range(ev: &Evaluator, kind: u8, obj: Word, lo: usize, hi: usize, b
let mut start = lo as u32;
while (start as usize) < n {
let len = batch.min(n as u32 - start);
// Only ranges with an unclaimed thunk are worth a task.
let worth = (start..start + len)
.any(|i| matches!(cell_state(cell(i as usize)), CellState::Unclaimed));
if worth {
Expand Down
1 change: 0 additions & 1 deletion src/parallel/sched.rs
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,6 @@ impl Runtime {
}
}

/// Note that forcing the thunk at `key` failed.
#[inline]
pub fn note_failed(&self, key: u64) {
ANY_FAILED.store(true, Ordering::Relaxed);
Expand Down
10 changes: 4 additions & 6 deletions src/runtime/arena.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Bump allocator. Memory is never freed while its session lives: every
//! allocation hands out a `'static` reference, and the chunks go only when
//! the session's `Heap` drops. That is what makes graph sharing between
//! cells trivially sound.
//! Bump allocator. Memory is never freed while its session lives. Every
//! allocation gives a `'static` reference, and the chunks go only when the
//! `Heap` of the session drops. Thus it is sound for cells to share any
//! part of the graph with no further check.

use std::sync::Arc;
use std::time::{Duration, Instant};
Expand Down Expand Up @@ -243,7 +243,6 @@ impl Arena {
}
}

/// Move the elements of `v` into one arena slice.
pub(crate) fn alloc_vec<T>(&mut self, v: Vec<T>) -> &'static [T] {
let n = v.len();
if n == 0 {
Expand All @@ -264,7 +263,6 @@ impl Arena {
self.alloc_slice(b)
}

/// Concatenate byte slices into one arena allocation.
pub(crate) fn alloc_concat(&mut self, parts: &[&[u8]]) -> &'static [u8] {
let len: usize = parts.iter().map(|p| p.len()).sum();
if len == 0 {
Expand Down
5 changes: 2 additions & 3 deletions src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ pub mod word;

pub use arena::Arena;
pub use value::{
Attr, AttrIter, AttrsObj, Ctx, EnvObj, FloatObj, IntObj, Kind, LambdaObj, ListObj, MapDesc,
PathObj, PrimFn, PrimOpAppObj, PrimOpObj, StringObj, Taint, ThunkApp2Obj, ThunkAppObj,
ThunkObj, UnionTail, Value, ValueType, split_built,
Attr, AttrsObj, Ctx, EnvObj, Kind, ListObj, PrimFn, StringObj, Taint, ThunkObj, Value,
ValueType, split_built,
};
pub(crate) use value::{MAX_LEAVES, UNION_MIN, ctx_to_arena, leaf_count};
pub use word::{Cell, Word};
Loading
Loading