libexpr: de-ptr-ize Value references

thunk values are shareable, and we can represent invalid/uninitialized
values with a special bit pattern that makes no sense otherwise. there
is no need to keep allocating values on the heap, instead we can treat
values like reference-counted smart pointers to heap objects, which in
turn lets us save a lot of allocations and, ultimately, gc heap space.

compared to our baseline (main of 2025-09-27) we save 15%+ memory on a
system rebuild and 17% on nix search. eval time regresses by ~3% for a
system rebuild, while nix search is 7% faster. further optimization is
probably possible (but for now this will just have to be good enough).

Change-Id: Ib6c47acdbe2fac4f76a83c2269f16f30ef66b2e1
This commit is contained in:
eldritch horrors
2025-10-05 16:23:05 +02:00
parent 28c4431c69
commit 80654b84b6
39 changed files with 849 additions and 891 deletions
+11 -11
View File
@@ -151,12 +151,12 @@ static void getAllExprs(Evaluator & state,
continue;
}
/* Load the expression on demand. */
auto vArg = state.mem.allocValue();
vArg->mkString(path2.canonical().abs());
Value vArg;
vArg.mkString(path2.canonical().abs());
if (seen.size() == maxAttrs)
throw Error("too many Nix expressions in directory '%1%'", path);
attrs.alloc(attrName
) = {NewValueAs::app, state.mem, state.builtins.get("import"), *vArg};
) = {NewValueAs::app, state.mem, state.builtins.get("import"), vArg};
}
else if (st.type == InputAccessor::tDirectory)
/* `path2' is a directory (with no default.nix in it);
@@ -517,9 +517,9 @@ static bool keep(EvalState & state, DrvInfo & drv)
static void setMetaFlag(EvalState & state, DrvInfo & drv,
const std::string & name, const std::string & value)
{
auto v = state.ctx.mem.allocValue();
v->mkString(value);
drv.setMeta(state, name, *v);
Value v;
v.mkString(value);
drv.setMeta(state, name, v);
}
static void installDerivations(Globals & globals,
@@ -1290,12 +1290,12 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
} else if (v->type() == nList) {
attrs2["type"] = "strings";
XMLOpenElement m(xml, "meta", attrs2);
for (auto elem : v->listItems()) {
if (elem->type() != nString) {
for (auto & elem : v->listItems()) {
if (elem.type() != nString) {
continue;
}
XMLAttrs attrs3;
attrs3["value"] = elem->str();
attrs3["value"] = elem.str();
xml.writeEmptyElement("string", attrs3);
}
} else if (v->type() == nAttrs) {
@@ -1304,12 +1304,12 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs)
Bindings & attrs = *v->attrs();
for (auto &i : attrs) {
const Attr & a(*attrs.get(i.name));
if (a.value->type() != nString) {
if (a.value.type() != nString) {
continue;
}
XMLAttrs attrs3;
attrs3["type"] = globals.state->symbols[i.name];
attrs3["value"] = a.value->str();
attrs3["value"] = a.value.str();
xml.writeEmptyElement("string", attrs3);
}
}
+6 -6
View File
@@ -60,7 +60,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
auto outputsList = state.ctx.mem.newList(outputs.size());
vOutputs = {NewValueAs::list, outputsList};
for (const auto & [m, j] : enumerate(outputs)) {
(outputsList->elems[m] = state.ctx.mem.allocValue())->mkString(j.first);
outputsList->elems[m].mkString(j.first);
auto outputAttrs = state.ctx.buildBindings(2);
outputAttrs.alloc(state.ctx.s.outPath).mkString(state.ctx.store->printStorePath(*j.second));
attrs.alloc(j.first).mkAttrs(outputAttrs);
@@ -78,12 +78,12 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
for (auto & j : metaNames) {
Value * v = i.queryMeta(state, j);
if (!v) continue;
meta.insert(state.ctx.symbols.create(j), v);
meta.insert(state.ctx.symbols.create(j), *v);
}
attrs.alloc(state.ctx.s.meta).mkAttrs(meta);
(manifest->elems[n++] = state.ctx.mem.allocValue())->mkAttrs(attrs);
manifest->elems[n++].mkAttrs(attrs);
if (drvPath) references.insert(*drvPath);
}
@@ -106,7 +106,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
builder with the manifest as argument. */
auto attrs = state.ctx.buildBindings(3);
state.ctx.paths.mkStorePathString(manifestFile, attrs.alloc("manifest"));
attrs.insert(state.ctx.symbols.create("derivations"), &vManifest);
attrs.insert(state.ctx.symbols.create("derivations"), vManifest);
Value args;
args.mkAttrs(attrs);
@@ -117,9 +117,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
state.forceValue(topLevel, noPos);
NixStringContext context;
const Attr & aDrvPath(*topLevel.attrs()->get(state.ctx.s.drvPath));
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, "");
auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, aDrvPath.value, context, "");
const Attr & aOutPath(*topLevel.attrs()->get(state.ctx.s.outPath));
auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context, "");
auto topLevelOut = state.coerceToStorePath(aOutPath.pos, aOutPath.value, context, "");
/* Realise the resulting store expression. */
debug("building user environment");
+3 -3
View File
@@ -183,13 +183,13 @@ Bindings * MixEvalArgs::getAutoArgs(Evaluator & state)
{
auto res = state.buildBindings(autoArgs.size());
for (auto & i : autoArgs) {
auto v = state.mem.allocValue();
Value v;
if (i.second[0] == 'E')
state.evalLazily(
state.parseExprFromString(i.second.substr(1), CanonPath::fromCwd()), *v
state.parseExprFromString(i.second.substr(1), CanonPath::fromCwd()), v
);
else
v->mkString(((std::string_view) i.second).substr(1));
v.mkString(((std::string_view) i.second).substr(1));
res.insert(state.symbols.create(i.first), v);
}
return res.finish();
+6 -6
View File
@@ -419,7 +419,7 @@ ref<eval_cache::EvalCache> openEvalCache(
auto aOutputs = vFlake.attrs()->get(state.ctx.symbols.create("outputs"));
assert(aOutputs);
return *aOutputs->value;
return aOutputs->value;
};
if (fingerprint) {
@@ -449,23 +449,23 @@ Installables SourceExprCommand::parseInstallables(
throw UsageError("'--file' and '--expr' are exclusive");
auto evaluator = getEvaluator();
auto vFile = evaluator->mem.allocValue();
Value vFile;
if (file == "-") {
auto & e = evaluator->parseStdin();
state.eval(e, *vFile);
state.eval(e, vFile);
}
else if (file)
state.evalFile(state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap(), *vFile);
state.evalFile(state.aio.blockOn(lookupFileArg(*evaluator, *file)).unwrap(), vFile);
else {
auto & e = evaluator->parseExprFromString(*expr, CanonPath::fromCwd());
state.eval(e, *vFile);
state.eval(e, vFile);
}
for (auto & s : ss) {
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(s);
result.push_back(make_ref<InstallableAttrPath>(InstallableAttrPath::parse(
evaluator, *this, *vFile, std::move(prefix), std::move(extendedOutputsSpec)
evaluator, *this, vFile, std::move(prefix), std::move(extendedOutputsSpec)
)));
}
+40 -38
View File
@@ -174,35 +174,37 @@ struct NixRepl
/**
* Get a list of each of the `repl-overlays` (parsed and evaluated).
*/
Value * replOverlays();
Value replOverlays();
/**
* Get the Nix function that composes the `repl-overlays` together.
*/
Value * getReplOverlaysEvalFunction();
Value getReplOverlaysEvalFunction();
/**
* Cached return value of `getReplOverlaysEvalFunction`.
*
* Note: This is `shared_ptr` to avoid garbage collection.
*/
std::shared_ptr<Value *> replOverlaysEvalFunction =
std::allocate_shared<Value *>(TraceableAllocator<Value *>(), nullptr);
std::shared_ptr<std::optional<Value>> replOverlaysEvalFunction =
std::allocate_shared<std::optional<Value>>(
TraceableAllocator<std::optional<Value>>(), std::nullopt
);
/**
* Get the `info` AttrSet that's passed as the first argument to each
* of the `repl-overlays`.
*/
Value * replInitInfo();
Value replInitInfo();
/**
* Get the current top-level bindings as an AttrSet.
*/
Value * bindingsToAttrs();
Value bindingsToAttrs();
/**
* Parse a file, evaluate its result, and force the resulting value.
*/
Value * evalFile(SourcePath & path);
Value evalFile(SourcePath & path);
void printValue(std::ostream & str,
Value & v,
@@ -863,10 +865,10 @@ ProcessLineResult NixRepl::processLine(std::string line)
std::visit(overloaded {
[&](ExprReplBindings & b) {
for (auto & [name, e] : b.symbols) {
Value * v = state.ctx.mem.allocValue();
e->eval(state, *env, *v);
Value v;
e->eval(state, *env, v);
(void) e.release(); // NOLINT(bugprone-unused-return-value): leak because of thunk references
addVarToScope(name, *v);
addVarToScope(name, v);
}
},
[&](std::unique_ptr<Expr> & e) {
@@ -964,9 +966,9 @@ void NixRepl::loadReplOverlays()
notice("Loading '%1%'...", "repl-overlays");
auto replInitFilesFunction = getReplOverlaysEvalFunction();
Value &newAttrs(*evaluator.mem.allocValue());
SmallValueVector<3> args = {replInitInfo(), bindingsToAttrs(), replOverlays()};
state.callFunction(*replInitFilesFunction, args, newAttrs, noPos);
Value newAttrs;
Value args[] = {replInitInfo(), bindingsToAttrs(), replOverlays()};
state.callFunction(replInitFilesFunction, args, newAttrs, noPos);
// n.b. this does in fact load the stuff into the environment twice (once
// from the superset of the environment returned by repl-overlays and once
@@ -976,14 +978,14 @@ void NixRepl::loadReplOverlays()
addAttrsToScope(newAttrs);
}
Value * NixRepl::getReplOverlaysEvalFunction()
Value NixRepl::getReplOverlaysEvalFunction()
{
if (replOverlaysEvalFunction && *replOverlaysEvalFunction) {
return *replOverlaysEvalFunction;
return **replOverlaysEvalFunction;
}
auto evalReplInitFilesPath = CanonPath::root + "repl-overlays.nix";
*replOverlaysEvalFunction = evaluator.mem.allocValue();
*replOverlaysEvalFunction = Value{};
auto code =
#include "repl-overlays.nix.gen.hh"
;
@@ -995,14 +997,14 @@ Value * NixRepl::getReplOverlaysEvalFunction()
state.eval(expr, **replOverlaysEvalFunction);
return *replOverlaysEvalFunction;
return **replOverlaysEvalFunction;
}
Value * NixRepl::replOverlays()
Value NixRepl::replOverlays()
{
Value * replInits(evaluator.mem.allocValue());
Value replInits;
auto replInitStorage = evaluator.mem.newList(evalSettings.replOverlays.get().size());
*replInits = {NewValueAs::list, replInitStorage};
replInits = {NewValueAs::list, replInitStorage};
size_t i = 0;
for (auto path : evalSettings.replOverlays.get()) {
@@ -1018,18 +1020,18 @@ Value * NixRepl::replOverlays()
auto replInit = evalFile(sourcePath);
evalSettings.pureEval.setDefault(prevPureEval);
if (!replInit->isLambda()) {
if (!replInit.isLambda()) {
evaluator.errors
.make<TypeError>(
"Expected `repl-overlays` entry %s to be a lambda but found %s: %s",
path,
showType(*replInit),
ValuePrinter(state, *replInit, errorPrintOptions)
showType(replInit),
ValuePrinter(state, replInit, errorPrintOptions)
)
.debugThrow();
}
if (auto attrs = dynamic_cast<AttrsPattern *>(replInit->lambda().fun->pattern.get());
if (auto attrs = dynamic_cast<AttrsPattern *>(replInit.lambda().fun->pattern.get());
attrs && !attrs->ellipsis)
{
evaluator.errors
@@ -1039,7 +1041,7 @@ Value * NixRepl::replOverlays()
"repl-overlays",
"..."
)
.atPos(replInit->lambda().fun->pos)
.atPos(replInit.lambda().fun->pos)
.debugThrow();
}
@@ -1051,16 +1053,16 @@ Value * NixRepl::replOverlays()
return replInits;
}
Value * NixRepl::replInitInfo()
Value NixRepl::replInitInfo()
{
auto builder = evaluator.buildBindings(2);
Value * currentSystem(evaluator.mem.allocValue());
currentSystem->mkString(evalSettings.getCurrentSystem());
Value currentSystem;
currentSystem.mkString(evalSettings.getCurrentSystem());
builder.insert(evaluator.symbols.create("currentSystem"), currentSystem);
Value * info(evaluator.mem.allocValue());
info->mkAttrs(builder.finish());
Value info;
info.mkAttrs(builder.finish());
return info;
}
@@ -1118,19 +1120,19 @@ void NixRepl::addVarToScope(const Symbol name, Value & v)
} else {
notice("Added %s.", evaluator.symbols[name]);
}
env->values[displ++] = &v;
env->values[displ++] = v;
varNames.emplace(evaluator.symbols[name]);
}
Value * NixRepl::bindingsToAttrs()
Value NixRepl::bindingsToAttrs()
{
auto builder = evaluator.buildBindings(staticEnv->vars.size());
for (auto & [symbol, displacement] : staticEnv->vars) {
builder.insert(symbol, env->values[displacement]);
}
Value * attrs(evaluator.mem.allocValue());
attrs->mkAttrs(builder.finish());
Value attrs;
attrs.mkAttrs(builder.finish());
return attrs;
}
@@ -1153,12 +1155,12 @@ void NixRepl::evalString(std::string s, Value & v)
state.forceValue(v, noPos);
}
Value * NixRepl::evalFile(SourcePath & path)
Value NixRepl::evalFile(SourcePath & path)
{
auto & expr = evaluator.parseExprFromFile(evaluator.paths.checkSourcePath(path), staticEnv);
Value * result(evaluator.mem.allocValue());
expr.eval(state, *env, *result);
state.forceValue(*result, noPos);
Value result;
expr.eval(state, *env, result);
state.forceValue(result, noPos);
return result;
}
+2 -2
View File
@@ -130,7 +130,7 @@ findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & au
ValuePrinter(state, v, errorPrintOptions)
);
}
v = *a->value;
v = a->value;
pos = a->pos;
} else {
if (!v.isList()) {
@@ -153,7 +153,7 @@ findAlongAttrPath(EvalState & state, const std::string & attrPath, Bindings & au
);
}
v = *v.listElems()[*attrIndex];
v = v.listElems()[*attrIndex];
pos = noPos;
}
+2 -3
View File
@@ -26,9 +26,8 @@ Bindings * EvalMemory::allocBindings(size_t capacity)
Value & BindingsBuilder::alloc(Symbol name, PosIdx pos)
{
auto value = mem.allocValue();
bindings->push_back(Attr(name, value, pos));
return *value;
bindings->push_back(Attr(name, {}, pos));
return (bindings->end() - 1)->value;
}
+4 -4
View File
@@ -23,8 +23,8 @@ struct Attr
way we keep Attr size at two words with no wasted space. */
Symbol name;
PosIdx pos;
Value * value;
Attr(Symbol name, Value * value, PosIdx pos = noPos) : name(name), pos(pos), value(value) {}
mutable Value value;
Attr(Symbol name, Value value, PosIdx pos = noPos) : name(name), pos(pos), value(value) {}
Attr() { };
bool operator < (const Attr & a) const
{
@@ -72,7 +72,7 @@ public:
const Attr * get(Symbol name)
{
Attr key(name, 0);
Attr key(name, {});
iterator i = std::lower_bound(begin(), end(), key);
if (i != end() && i->name == name) return &*i;
return nullptr;
@@ -135,7 +135,7 @@ public:
{
}
void insert(Symbol name, Value * value, PosIdx pos = noPos)
void insert(Symbol name, Value value, PosIdx pos = noPos)
{
insert(Attr(name, value, pos));
}
+3 -3
View File
@@ -387,7 +387,7 @@ Value & AttrCursor::getValue(EvalState & state)
auto attr = vParent.attrs()->get(state.ctx.symbols.create(parent->second));
if (!attr)
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr(state));
_value = allocRootValue(*attr->value);
_value = allocRootValue(attr->value);
} else
_value = allocRootValue(root->getRootValue(state));
}
@@ -520,7 +520,7 @@ std::shared_ptr<AttrCursor> AttrCursor::maybeGetAttr(EvalState & state, const st
}
return make_ref<AttrCursor>(
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2)
root, std::make_pair(shared_from_this(), name), &attr->value, std::move(cachedValue2)
);
}
@@ -686,7 +686,7 @@ std::vector<std::string> AttrCursor::getListOfStrings(EvalState & state)
for (auto & elem : v.listItems()) {
res.push_back(std::string(
state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching")
state.forceStringNoCtx(elem, noPos, "while evaluating an attribute for caching")
));
}
+14 -21
View File
@@ -13,18 +13,18 @@ namespace nix {
inline Value::Value(app_t, EvalMemory & mem, Value & lhs, Value & rhs)
{
auto app = static_cast<Value::App *>(mem.allocBytes(sizeof(Value::App) + sizeof(Value *)));
app->_left = reinterpret_cast<uintptr_t>(&lhs);
app->_left = lhs;
app->_n = 1;
app->_args[0] = &rhs;
app->_args[0] = rhs;
raw = tag(tApp, app);
}
inline Value::Value(app_t, EvalMemory & mem, Value & lhs, std::span<Value *> args)
inline Value::Value(app_t, EvalMemory & mem, Value & lhs, std::span<Value> args)
{
auto app = static_cast<Value::App *>(mem.allocBytes(sizeof(Value::App) + args.size_bytes()));
app->_left = reinterpret_cast<uintptr_t>(&lhs);
app->_left = lhs;
app->_n = args.size();
memcpy(app->_args, args.data(), args.size_bytes());
std::copy(args.begin(), args.end(), app->_args);
raw = tag(tApp, app);
}
@@ -85,14 +85,6 @@ T * EvalMemory::allocType(size_t n)
return static_cast<T *>(allocBytes(checkedArrayAllocSize(sizeof(T), n)));
}
[[gnu::always_inline]]
Value * EvalMemory::allocValue()
{
static_assert(CACHES * CACHE_INCREMENT >= sizeof(Value));
stats.nrValues++;
return static_cast<Value *>(allocBytes(sizeof(Value)));
}
[[gnu::always_inline]]
Env & EvalMemory::allocEnv(size_t size)
{
@@ -117,15 +109,15 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
if (thunk.resolved()) {
v = thunk.result();
} else {
const auto backup = v;
Env * env = v.thunk().env();
Expr & expr = *v.thunk().expr;
v = Value{NewValueAs::blackhole};
const auto backup = thunk;
Env * env = thunk.env();
Expr & expr = *thunk.expr;
thunk = Value::blackHole;
try {
expr.eval(*this, *env, v);
backup.thunk().resolve(v);
thunk.resolve(v);
} catch (...) {
v = backup;
thunk = backup;
tryFixupBlackHolePos(v, pos);
throw;
}
@@ -136,8 +128,9 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
v = app.result();
} else {
auto target = app.target();
if (!target->isPrimOp() || target->primOp()->arity <= app.totalArgs()) {
callFunction(*v.app().left(), v.app().args(), v, pos);
if (!target.isPrimOp() || target.primOp()->arity <= app.totalArgs()) {
auto tmp = v.app().left();
callFunction(tmp, v.app().args(), v, pos);
app.resolve(v);
}
}
+104 -113
View File
@@ -135,7 +135,7 @@ std::string showType(const Value & v)
case tApp:
if (v.isPrimOpApp()) {
return fmt(
"the partially applied built-in function '%s'", v.app().target()->primOp()->name
"the partially applied built-in function '%s'", v.app().target().primOp()->name
);
} else {
return "a function application";
@@ -563,11 +563,8 @@ Path EvalPaths::toRealPath(const Path & path, const NixStringContext & context)
: path;
}
void EvalBuiltins::addConstant(const std::string & name, const Value & v2, Constant info)
void EvalBuiltins::addConstant(const std::string & name, const Value & v, Constant info)
{
Value * v = mem.allocValue();
*v = v2;
auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name;
constantInfos.push_back({name2, info});
@@ -577,14 +574,14 @@ void EvalBuiltins::addConstant(const std::string & name, const Value & v2, Const
We might know the type of a thunk in advance, so be allowed
to just write it down in that case. */
if (auto gotType = v->type(true); gotType != nThunk) {
if (auto gotType = v.type(true); gotType != nThunk) {
assert(info.type == gotType);
}
/* Install value the base environment. */
staticEnv->vars.insert_or_assign(symbols.create(name), baseEnvDispl);
env.values[baseEnvDispl++] = v;
env.values[0]->attrs()->push_back(Attr(symbols.create(name2), v));
env.values[0].attrs()->push_back(Attr(symbols.create(name2), v));
}
}
@@ -600,15 +597,14 @@ void EvalBuiltins::addPrimOp(PrimOpDetails primOp)
the primop to a dummy value. */
if (primOp.arity == 0) {
primOp.arity = 1;
auto vPrimOp = mem.allocValue();
vPrimOp->mkPrimOp(new PrimOp(primOp));
Value v{NewValueAs::app, mem, *vPrimOp, *vPrimOp};
Value vPrimOp{NewValueAs::primop, *new PrimOp(primOp)};
Value v{NewValueAs::app, mem, vPrimOp, vPrimOp};
addConstant(
primOp.name,
vPrimOp.primOp()->name,
v,
{
.type = nFunction,
.doc = vPrimOp->primOp()->doc,
.doc = vPrimOp.primOp()->doc,
}
);
}
@@ -617,17 +613,16 @@ void EvalBuiltins::addPrimOp(PrimOpDetails primOp)
if (primOp.name.starts_with("__"))
primOp.name = primOp.name.substr(2);
Value * v = mem.allocValue();
v->mkPrimOp(new PrimOp(std::move(primOp)));
Value v{NewValueAs::primop, *new PrimOp(std::move(primOp))};
staticEnv->vars.insert_or_assign(auto(envName), baseEnvDispl);
env.values[baseEnvDispl++] = v;
env.values[0]->attrs()->push_back(Attr(symbols.create(v->primOp()->name), v));
env.values[0].attrs()->push_back(Attr(symbols.create(v.primOp()->name), v));
}
Value & EvalBuiltins::get(const std::string & name)
{
return *env.values[0]->attrs()->get(symbols.create(name))->value;
return env.values[0].attrs()->get(symbols.create(name))->value;
}
@@ -670,9 +665,9 @@ void printStaticEnvBindings(const SymbolTable & st, const StaticEnv & se)
// just for the current level of Env, not the whole chain.
void printWithBindings(const SymbolTable & st, const Env & env)
{
if (env.values[0]->type() == nAttrs) {
if (env.values[0].type() == nAttrs) {
std::set<std::string_view> bindings;
for (const auto & attr : *env.values[0]->attrs()) {
for (const auto & attr : *env.values[0].attrs()) {
bindings.emplace(st[attr.name]);
}
@@ -728,10 +723,10 @@ void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const En
if (env.up && se.up) {
mapStaticEnvBindings(st, *se.up, *env.up, vm);
if (se.isWith && env.values[0]->type() == nAttrs) {
if (se.isWith && env.values[0].type() == nAttrs) {
// add 'with' bindings.
Bindings::iterator j = env.values[0]->attrs()->begin();
while (j != env.values[0]->attrs()->end()) {
Bindings::iterator j = env.values[0].attrs()->begin();
while (j != env.values[0].attrs()->end()) {
vm[std::string(st[j->name])] = j->value;
++j;
}
@@ -860,7 +855,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
for (auto l = var.level; l; --l, env = env->up) ;
if (!var.fromWith) {
return env->values[var.displ];
return &env->values[var.displ];
}
// This early exit defeats the `maybeThunk` optimization for variables from `with`,
@@ -871,14 +866,14 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
auto * fromWith = var.fromWith;
while (1) {
forceAttrs(
*env->values[0],
env->values[0],
fromWith->pos,
"while evaluating the first subexpression of a with expression"
);
auto j = env->values[0]->attrs()->get(var.name);
auto j = env->values[0].attrs()->get(var.name);
if (j) {
if (ctx.stats.countCalls) ctx.stats.attrSelects[j->pos]++;
return j->value;
return &j->value;
}
if (!fromWith->parentWith)
ctx.errors.make<UndefinedVarError>("undefined variable '%1%'", ctx.symbols[var.name]).atPos(var.pos).withFrame(*env, var).debugThrow();
@@ -978,31 +973,28 @@ void EvalState::mkSingleDerivedPathString(
in the given environment. But if the expression is a variable,
then look it up right away. This significantly reduces the number
of thunks allocated. */
Value * Expr::maybeThunk(EvalState & state, Env & env)
Value Expr::maybeThunk(EvalState & state, Env & env)
{
Value * v = state.ctx.mem.allocValue();
*v = {NewValueAs::thunk, state.ctx.mem, env, *this};
state.ctx.stats.nrThunks++;
return v;
return {NewValueAs::thunk, state.ctx.mem, env, *this};
}
Value * ExprVar::maybeThunk(EvalState & state, Env & env)
Value ExprVar::maybeThunk(EvalState & state, Env & env)
{
Value * v = state.lookupVar(&env, *this, true);
/* The value might not be initialised in the environment yet.
In that case, ignore it. */
if (v) {
if (v && !v->isInvalid()) {
state.ctx.stats.nrAvoided++;
return v;
return *v;
}
return Expr::maybeThunk(state, env);
}
Value * ExprLiteral::maybeThunk(EvalState & state, Env & env)
Value ExprLiteral::maybeThunk(EvalState & state, Env & env)
{
state.ctx.stats.nrAvoided++;
return &v;
return v;
}
@@ -1140,10 +1132,9 @@ void ExprSet::eval(EvalState & state, Env & env, Value & v)
in the original environment. */
Displacement displ = 0;
for (auto & i : attrs) {
Value * vAttr;
Value vAttr;
if (hasOverrides && i.second.kind != ExprAttrs::AttrDef::Kind::Inherited) {
vAttr = state.ctx.mem.allocValue();
*vAttr = {
vAttr = {
NewValueAs::thunk,
state.ctx.mem,
*i.second.chooseByKind(&env2, &env, inheritEnv),
@@ -1167,7 +1158,7 @@ void ExprSet::eval(EvalState & state, Env & env, Value & v)
been substituted into the bodies of the other attributes.
Hence we need __overrides.) */
if (hasOverrides) {
Value & vOverrides = *(*v.attrs())[overrides->second.displ].value;
Value & vOverrides = (*v.attrs())[overrides->second.displ].value;
state.forceAttrs(vOverrides, noPos, "while evaluating the `__overrides` attribute");
Bindings * newBnds = state.ctx.mem.allocBindings(capacity + vOverrides.attrs()->size());
for (auto & i : *v.attrs())
@@ -1254,16 +1245,15 @@ void ExprList::eval(EvalState & state, Env & env, Value & v)
{
auto result = state.ctx.mem.newList(elems.size());
v = {NewValueAs::list, result};
for (auto [n, v2] : enumerate(result->span())) {
const_cast<Value *&>(v2) = elems[n]->maybeThunk(state, env);
for (auto && [n, v2] : enumerate(result->span())) {
v2 = elems[n]->maybeThunk(state, env);
}
}
Value * ExprList::maybeThunk(EvalState & state, Env & env)
Value ExprList::maybeThunk(EvalState & state, Env & env)
{
if (elems.empty()) {
return &Value::EMPTY_LIST;
return Value::EMPTY_LIST;
}
return Expr::maybeThunk(state, env);
}
@@ -1291,9 +1281,9 @@ void ExprVar::eval(EvalState & state, Env & env, Value & v)
void ExprInheritFrom::eval(EvalState & state, Env & env, Value & v)
{
Value * v2 = env.values[displ];
state.forceValue(*v2, pos);
v = *v2;
Value & v2 = env.values[displ];
state.forceValue(v2, pos);
v = v2;
}
@@ -1400,7 +1390,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
// If we're here, then we successfully found the attribute.
// Set our currently operated-on attrset to this one, and keep going.
vCurrent = attrIt->value;
vCurrent = &attrIt->value;
posCurrent = attrIt->pos;
posCurrentSyntax = currentAttrName.pos;
if (state.ctx.stats.countCalls) state.ctx.stats.attrSelects[posCurrent]++;
@@ -1435,7 +1425,7 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
v.mkBool(false);
return;
} else {
vAttrs = j->value;
vAttrs = &j->value;
}
}
@@ -1523,7 +1513,7 @@ Env & SimplePattern::match(
{
Env & env2(state.ctx.mem.allocEnv(1));
env2.up = &up;
env2.values[0] = &arg;
env2.values[0] = arg;
return env2;
}
@@ -1547,7 +1537,7 @@ Env & AttrsPattern::match(
}
if (name) {
env2.values[displ++] = &arg;
env2.values[displ++] = arg;
}
///* For each formal argument, get the actual argument. If
@@ -1598,7 +1588,7 @@ Env & AttrsPattern::match(
return env2;
}
void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes, const PosIdx pos)
void EvalState::callFunction(Value & fun, std::span<Value> args, Value & vRes, const PosIdx pos)
{
if (callDepth > evalSettings.maxCallDepth)
ctx.errors.make<EvalError>("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow();
@@ -1612,11 +1602,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
Value vCur(fun);
auto makeAppChain = [&]() {
auto fun2 = ctx.mem.allocValue();
*fun2 = vCur;
vRes = {NewValueAs::app, ctx.mem, *fun2, args};
};
auto makeAppChain = [&]() { vRes = {NewValueAs::app, ctx.mem, vCur, args}; };
const Attr * functor;
@@ -1626,7 +1612,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
ExprLambda & lambda(*vCur.lambda().fun);
Env & env2 = lambda.pattern->match(lambda, *this, *vCur.lambda().env(), *args[0], pos);
Env & env2 = lambda.pattern->match(lambda, *this, *vCur.lambda().env(), args[0], pos);
ctx.stats.nrFunctionCalls++;
if (ctx.stats.countCalls) ctx.stats.addCall(lambda);
@@ -1664,7 +1650,11 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
if (ctx.stats.countCalls) ctx.stats.primOpCalls[fn->name]++;
try {
fn->fun(*this, args.data(), vCur);
SmallVector<Value *, 4> pargs(argsLeft);
for (unsigned i = 0; i < argsLeft; i++) {
pargs[i] = &args[i];
}
fn->fun(*this, pargs.data(), vCur);
} catch (ThrownError & e) {
// Distinguish between an error that simply happened while "throw"
// was being evaluated and an explicit thrown error.
@@ -1684,13 +1674,16 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
}
else if (vCur.isPrimOpApp()) {
/* Figure out the number of arguments still needed. */
size_t argsDone = vCur.app().totalArgs();
Value * primOp = vCur.app().target();
auto arity = primOp->primOp()->arity;
auto argsLeft = arity - argsDone;
auto & app = vCur.app();
auto prevArgs = app.args();
if (args.size() < argsLeft) {
assert(!vCur.app().left().isApp());
/* Figure out the number of arguments still needed. */
Value primOp = app.target();
auto arity = primOp.primOp()->arity;
if (args.size() < arity - prevArgs.size()) {
/* We still don't have enough arguments, so extend the tPrimOpApp chain. */
makeAppChain();
return;
@@ -1699,18 +1692,16 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
the previous and new arguments. */
// max arity as of writing is 3. even 4 seems excessive though.
SmallVector<Value *, 4> vArgs(arity);
auto n = argsDone;
for (Value * arg = &vCur; arg->isApp(); arg = arg->app().left()) {
auto curArgs = arg->app().args();
memcpy(&vArgs[n] - curArgs.size(), curArgs.data(), curArgs.size_bytes());
n -= curArgs.size();
SmallVector<Value *, 4> vArgs;
for (auto & arg : prevArgs) {
vArgs.push_back(&arg);
}
while (vArgs.size() < arity) {
vArgs.push_back(&args[0]);
args = args.subspan(1);
}
for (size_t i = 0; i < argsLeft; ++i)
vArgs[argsDone + i] = args[i];
auto fn = primOp->primOp();
auto fn = primOp.primOp();
ctx.stats.nrPrimOpCalls++;
if (ctx.stats.countCalls) ctx.stats.primOpCalls[fn->name]++;
@@ -1724,8 +1715,6 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
e.addTrace(ctx.positions[pos], "while calling the '%1%' builtin", fn->name);
throw;
}
args = args.subspan(argsLeft);
}
}
@@ -1733,10 +1722,9 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
/* 'vCur' may be allocated on the stack of the calling
function, but for functors we may keep a reference, so
heap-allocate a copy and use that instead. */
Value * args2[] = {ctx.mem.allocValue(), args[0]};
*args2[0] = vCur;
Value args2[] = {vCur, args[0]};
try {
callFunction(*functor->value, args2, vCur, functor->pos);
callFunction(functor->value, args2, vCur, functor->pos);
} catch (Error & e) {
e.addTrace(ctx.positions[pos], "while calling a functor (an attribute set with a '__functor' attribute)");
throw;
@@ -1792,7 +1780,7 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res, PosI
auto found = fun.attrs()->get(ctx.s.functor);
if (found) {
Value v;
callFunction(*found->value, fun, v, pos);
callFunction(found->value, fun, v, pos);
forceValue(v, pos);
return autoCallFunction(args, v, res, pos);
}
@@ -1975,19 +1963,19 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
}
void EvalState::concatLists(
Value & v, size_t nrLists, Value * const * lists, const PosIdx pos, std::string_view errorCtx
Value & v, std::span<Value> lists, const PosIdx pos, std::string_view errorCtx
)
{
ctx.stats.nrListConcats++;
Value * nonEmpty = 0;
size_t len = 0;
for (size_t n = 0; n < nrLists; ++n) {
forceList(*lists[n], pos, errorCtx);
auto l = lists[n]->listSize();
for (size_t n = 0; n < lists.size(); ++n) {
forceList(lists[n], pos, errorCtx);
auto l = lists[n].listSize();
len += l;
if (l) {
nonEmpty = lists[n];
nonEmpty = &lists[n];
}
}
@@ -1999,10 +1987,10 @@ void EvalState::concatLists(
auto list = ctx.mem.newList(len);
v = {NewValueAs::list, list};
auto out = list->elems;
for (size_t n = 0, pos = 0; n < nrLists; ++n) {
auto l = lists[n]->listSize();
for (size_t n = 0, pos = 0; n < lists.size(); ++n) {
auto l = lists[n].listSize();
if (l) {
memcpy(out + pos, lists[n]->listElems(), l * sizeof(Value *));
std::copy(lists[n].listItems().begin(), lists[n].listItems().end(), out + pos);
}
pos += l;
}
@@ -2165,18 +2153,18 @@ void EvalState::forceValueDeep(Value & v)
for (auto & i : *v.attrs())
try {
// If the value is a thunk, we're evaling. Otherwise no trace necessary.
auto dts = ctx.debug && i.value->isThunk()
auto dts = ctx.debug && i.value.isThunk()
? makeDebugTraceStacker(
*this,
*i.value->thunk().expr,
*i.value->thunk().env(),
*i.value.thunk().expr,
*i.value.thunk().env(),
ctx.positions[i.pos],
"while evaluating the attribute '%1%'",
ctx.symbols[i.name]
)
: nullptr;
recurse(*i.value);
recurse(i.value);
} catch (Error & e) {
e.addTrace(ctx.positions[i.pos], "while evaluating the attribute '%1%'", ctx.symbols[i.name]);
throw;
@@ -2184,8 +2172,8 @@ void EvalState::forceValueDeep(Value & v)
}
else if (v.isList()) {
for (auto v2 : v.listItems()) {
recurse(*v2);
for (auto & v2 : v.listItems()) {
recurse(v2);
}
}
};
@@ -2335,11 +2323,11 @@ bool EvalState::isDerivation(Value & v)
if (!i) {
return false;
}
forceValue(*i->value, i->pos);
if (i->value->type() != nString) {
forceValue(i->value, i->pos);
if (i->value.type() != nString) {
return false;
}
return i->value->str() == "derivation";
return i->value.str() == "derivation";
}
@@ -2350,7 +2338,7 @@ std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value &
if (i) {
Value v1;
try {
callFunction(*i->value, v, v1, i->pos);
callFunction(i->value, v, v1, i->pos);
return coerceToString(pos, v1, context,
"while evaluating the result of the `__toString` attribute",
mode, copyToStore).toOwned();
@@ -2406,7 +2394,7 @@ BackedStringView EvalState::coerceToString(
.debugThrow();
}
return coerceToString(
pos, *i->value, context, errorCtx, mode, copyToStore, canonicalizePath
pos, i->value, context, errorCtx, mode, copyToStore, canonicalizePath
);
}
@@ -2445,7 +2433,7 @@ BackedStringView EvalState::coerceToString(
try {
result += *coerceToString(
pos,
*v2,
v2,
context,
"while evaluating one element of the list",
mode,
@@ -2458,7 +2446,7 @@ BackedStringView EvalState::coerceToString(
}
if (n < v.listSize() - 1
/* !!! not quite correct */
&& (!v2->isList() || v2->listSize() != 0))
&& (!v2.isList() || v2.listSize() != 0))
{
result += " ";
}
@@ -2588,11 +2576,6 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
forceValue(v1, pos);
forceValue(v2, pos);
/* !!! Hack to support some old broken code that relies on pointer
equality tests between sets. (Specifically, builderDefs calls
uniqList on a list of sets.) Will remove this eventually. */
if (&v1 == &v2) return true;
// Special case type-compatibility between float and int
if (v1.type() == nInt && v2.type() == nFloat) {
return v1.integer().value == v2.fpoint();
@@ -2604,6 +2587,11 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
// All other types are not compatible with each other.
if (v1.type() != v2.type()) return false;
/* !!! Hack to support some old broken code that relies on pointer
equality tests between sets. (Specifically, builderDefs calls
uniqList on a list of sets.) Will remove this eventually. */
auto pointerEq = [&] { return v1.pointerEqProxy() == v2.pointerEqProxy(); };
switch (v1.type()) {
case nInt:
return v1.integer() == v2.integer();
@@ -2621,22 +2609,24 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
return true;
case nList:
if (pointerEq()) return true;
if (v1.listSize() != v2.listSize()) return false;
for (size_t n = 0; n < v1.listSize(); ++n) {
if (!eqValues(*v1.listElems()[n], *v2.listElems()[n], pos, errorCtx)) {
if (!eqValues(v1.listElems()[n], v2.listElems()[n], pos, errorCtx)) {
return false;
}
}
return true;
case nAttrs: {
if (pointerEq()) return true;
/* If both sets denote a derivation (type = "derivation"),
then compare their outPaths. */
if (isDerivation(v1) && isDerivation(v2)) {
auto i = v1.attrs()->get(ctx.s.outPath);
auto j = v2.attrs()->get(ctx.s.outPath);
if (i && j) {
return eqValues(*i->value, *j->value, pos, errorCtx);
return eqValues(i->value, j->value, pos, errorCtx);
}
}
@@ -2646,7 +2636,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
Bindings::iterator i, j;
for (i = v1.attrs()->begin(), j = v2.attrs()->begin(); i != v1.attrs()->end(); ++i, ++j)
{
if (i->name != j->name || !eqValues(*i->value, *j->value, pos, errorCtx)) {
if (i->name != j->name || !eqValues(i->value, j->value, pos, errorCtx)) {
return false;
}
}
@@ -2659,6 +2649,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
return false;
case nExternal:
if (pointerEq()) return true;
return *v1.external() == *v2.external();
case nFloat:
@@ -2708,7 +2699,6 @@ void Evaluator::printStatistics()
uint64_t bEnvs = mem.nrEnvs * sizeof(Env) + mem.nrValuesInEnvs * sizeof(Value *);
uint64_t bLists = mem.nrListElems * sizeof(Value *);
uint64_t bValues = mem.nrValues * sizeof(Value);
uint64_t bAttrsets = mem.nrAttrsets * sizeof(Bindings) + mem.nrAttrsInAttrsets * sizeof(Attr);
#if HAVE_BOEHMGC
@@ -2732,9 +2722,10 @@ void Evaluator::printStatistics()
{"bytes", bLists},
{"concats", stats.nrListConcats},
};
// reported for compatibility, even though we no longer allocate these on the heap
topObj["values"] = {
{"number", mem.nrValues},
{"bytes", bValues},
{"number", 0},
{"bytes", 0},
};
topObj["symbols"] = {
{"number", symbols.size()},
+6 -14
View File
@@ -61,12 +61,12 @@ struct Constant
bool impureOnly = false;
};
using ValMap = GcMap<std::string, Value *>;
using ValMap = GcMap<std::string, Value>;
struct alignas(Value::Acb::TAG_ALIGN) Env
{
Env * up;
Value * values[0];
Value values[0];
};
void printEnvBindings(const EvalState &es, const Expr & expr, const Env & env);
@@ -192,7 +192,6 @@ public:
{
unsigned long nrEnvs = 0;
unsigned long nrValuesInEnvs = 0;
unsigned long nrValues = 0;
unsigned long nrAttrsets = 0;
unsigned long nrAttrsInAttrsets = 0;
unsigned long nrListElems = 0;
@@ -210,7 +209,6 @@ public:
template<typename T>
inline T * allocType(size_t n = 1);
inline Value * allocValue();
inline Env & allocEnv(size_t size);
Bindings * allocBindings(size_t capacity);
@@ -795,12 +793,11 @@ public:
bool isFunctor(Value & fun);
void callFunction(Value & fun, std::span<Value *> args, Value & vRes, const PosIdx pos);
void callFunction(Value & fun, std::span<Value> args, Value & vRes, const PosIdx pos);
void callFunction(Value & fun, Value & arg, Value & vRes, const PosIdx pos)
{
Value * args[] = {&arg};
callFunction(fun, args, vRes, pos);
callFunction(fun, {&arg, 1}, vRes, pos);
}
/**
@@ -840,13 +837,8 @@ public:
const SingleDerivedPath & p,
Value & v);
void concatLists(
Value & v,
size_t nrLists,
Value * const * lists,
const PosIdx pos,
std::string_view errorCtx
);
void
concatLists(Value & v, std::span<Value> lists, const PosIdx pos, std::string_view errorCtx);
private:
+54 -54
View File
@@ -109,15 +109,15 @@ static void parseFlakeInputAttr(EvalState & state, const Attr & attr, fetchers::
// Allow selecting a subset of enum values
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
switch (attr.value->type()) {
switch (attr.value.type()) {
case nString:
attrs.emplace(state.ctx.symbols[attr.name], std::string(attr.value->str()));
attrs.emplace(state.ctx.symbols[attr.name], std::string(attr.value.str()));
break;
case nBool:
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value->boolean()});
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value.boolean()});
break;
case nInt: {
auto intValue = attr.value->integer().value;
auto intValue = attr.value.integer().value;
if (intValue < 0) {
state.ctx.errors
@@ -137,7 +137,7 @@ static void parseFlakeInputAttr(EvalState & state, const Attr & attr, fetchers::
.make<TypeError>(
"flake input attribute '%s' is %s while a string, Boolean, or integer is expected",
state.ctx.symbols[attr.name],
showType(*attr.value)
showType(attr.value)
)
.debugThrow();
}
@@ -169,21 +169,21 @@ static FlakeInput parseFlakeInput(
for (nix::Attr attr : *(value.attrs())) {
try {
if (attr.name == sUrl) {
expectType(state, nString, *attr.value, attr.pos);
url = attr.value->str();
expectType(state, nString, attr.value, attr.pos);
url = attr.value.str();
attrs.emplace("url", *url);
} else if (attr.name == sFlake) {
expectType(state, nBool, *attr.value, attr.pos);
input.isFlake = attr.value->boolean();
expectType(state, nBool, attr.value, attr.pos);
input.isFlake = attr.value.boolean();
} else if (attr.name == sInputs) {
input.overrides =
parseFlakeInputs(
state, *attr.value, attr.pos, baseDir, lockRootPath, depth + 1, false
state, attr.value, attr.pos, baseDir, lockRootPath, depth + 1, false
)
.first;
} else if (attr.name == sFollows) {
expectType(state, nString, *attr.value, attr.pos);
auto follows(parseInputPath(attr.value->str()));
expectType(state, nString, attr.value, attr.pos);
auto follows(parseInputPath(attr.value.str()));
follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end());
input.follows = follows;
} else {
@@ -247,17 +247,17 @@ static std::pair<std::map<FlakeId, FlakeInput>, std::optional<fetchers::Attrs>>
"'self' input attributes not allowed at %s", state.ctx.positions[inputAttr.pos]
);
}
expectType(state, nAttrs, *inputAttr.value, inputAttr.pos);
expectType(state, nAttrs, inputAttr.value, inputAttr.pos);
selfAttrs = selfAttrs.value_or(fetchers::Attrs{});
for (auto & attr : *inputAttr.value->attrs()) {
for (auto & attr : *inputAttr.value.attrs()) {
parseFlakeInputAttr(state, attr, *selfAttrs);
}
} else {
inputs.emplace(
inputName,
parseFlakeInput(
state, inputName, *inputAttr.value, inputAttr.pos, baseDir, lockRootPath, depth
state, inputName, inputAttr.value, inputAttr.pos, baseDir, lockRootPath, depth
)
);
}
@@ -335,15 +335,15 @@ static Flake getFlake(
state.eval(flakeExpr, vInfo);
if (auto description = vInfo.attrs()->get(state.ctx.s.description)) {
expectType(state, nString, *description->value, description->pos);
flake.description = description->value->str();
expectType(state, nString, description->value, description->pos);
flake.description = description->value.str();
}
auto sInputs = state.ctx.symbols.create("inputs");
if (auto inputs = vInfo.attrs()->get(sInputs)) {
auto [flakeInputs, selfAttrs] =
parseFlakeInputs(state, *inputs->value, inputs->pos, flakeDir, lockRootPath, 0, true);
parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath, 0, true);
flake.inputs = std::move(flakeInputs);
flake.selfAttrs = std::move(selfAttrs);
}
@@ -368,11 +368,11 @@ static Flake getFlake(
}
if (auto outputs = vInfo.attrs()->get(state.ctx.s.outputs)) {
expectType(state, nFunction, *outputs->value, outputs->pos);
expectType(state, nFunction, outputs->value, outputs->pos);
if (outputs->value->isLambda()) {
if (outputs->value.isLambda()) {
if (auto pattern =
dynamic_cast<AttrsPattern *>(outputs->value->lambda().fun->pattern.get());
dynamic_cast<AttrsPattern *>(outputs->value.lambda().fun->pattern.get());
pattern)
{
for (auto & formal : pattern->formals) {
@@ -393,23 +393,23 @@ static Flake getFlake(
auto sNixConfig = state.ctx.symbols.create("nixConfig");
if (auto nixConfig = vInfo.attrs()->get(sNixConfig)) {
expectType(state, nAttrs, *nixConfig->value, nixConfig->pos);
expectType(state, nAttrs, nixConfig->value, nixConfig->pos);
for (auto & setting : *nixConfig->value->attrs()) {
forceTrivialValue(state, *setting.value, setting.pos);
if (setting.value->type() == nString) {
for (auto & setting : *nixConfig->value.attrs()) {
forceTrivialValue(state, setting.value, setting.pos);
if (setting.value.type() == nString) {
flake.config.settings.emplace(
state.ctx.symbols[setting.name],
std::string(state.forceStringNoCtx(*setting.value, setting.pos, ""))
std::string(state.forceStringNoCtx(setting.value, setting.pos, ""))
);
} else if (setting.value->type() == nPath) {
} else if (setting.value.type() == nPath) {
NixStringContext emptyContext = {};
flake.config.settings.emplace(
state.ctx.symbols[setting.name],
state
.coerceToString(
setting.pos,
*setting.value,
setting.value,
emptyContext,
"",
StringCoercionMode::Strict,
@@ -418,30 +418,30 @@ static Flake getFlake(
)
.toOwned()
);
} else if (setting.value->type() == nInt) {
} else if (setting.value.type() == nInt) {
flake.config.settings.emplace(
state.ctx.symbols[setting.name],
state.forceInt(*setting.value, setting.pos, "").value
state.forceInt(setting.value, setting.pos, "").value
);
} else if (setting.value->type() == nBool) {
} else if (setting.value.type() == nBool) {
flake.config.settings.emplace(
state.ctx.symbols[setting.name],
Explicit<bool>{state.forceBool(*setting.value, setting.pos, "")}
Explicit<bool>{state.forceBool(setting.value, setting.pos, "")}
);
} else if (setting.value->type() == nList) {
} else if (setting.value.type() == nList) {
std::vector<std::string> ss;
for (auto elem : setting.value->listItems()) {
if (elem->type() != nString) {
for (auto & elem : setting.value.listItems()) {
if (elem.type() != nString) {
state.ctx.errors
.make<TypeError>(
"list element in flake configuration setting '%s' is %s while a "
"string is expected",
state.ctx.symbols[setting.name],
showType(*setting.value)
showType(setting.value)
)
.debugThrow();
}
ss.emplace_back(state.forceStringNoCtx(*elem, setting.pos, ""));
ss.emplace_back(state.forceStringNoCtx(elem, setting.pos, ""));
}
flake.config.settings.emplace(state.ctx.symbols[setting.name], ss);
} else {
@@ -449,7 +449,7 @@ static Flake getFlake(
.make<TypeError>(
"flake configuration setting '%s' is %s",
state.ctx.symbols[setting.name],
showType(*setting.value)
showType(setting.value)
)
.debugThrow();
}
@@ -941,24 +941,24 @@ void callFlake(EvalState & state,
const LockedFlake & lockedFlake,
Value & vRes)
{
auto vLocks = state.ctx.mem.allocValue();
auto vRootSrc = state.ctx.mem.allocValue();
auto vRootSubdir = state.ctx.mem.allocValue();
auto vTmp1 = state.ctx.mem.allocValue();
auto vTmp2 = state.ctx.mem.allocValue();
Value vLocks;
Value vRootSrc;
Value vRootSubdir;
Value vTmp1;
Value vTmp2;
vLocks->mkString(lockedFlake.lockFile.to_string());
vLocks.mkString(lockedFlake.lockFile.to_string());
emitTreeAttrs(
state.ctx,
*lockedFlake.flake.sourceInfo,
lockedFlake.flake.lockedRef.input,
*vRootSrc,
vRootSrc,
false,
lockedFlake.flake.forceDirty
);
vRootSubdir->mkString(lockedFlake.flake.lockedRef.subdir);
vRootSubdir.mkString(lockedFlake.flake.lockedRef.subdir);
if (!state.ctx.caches.vCallFlake) {
state.ctx.caches.vCallFlake = allocRootValue({});
@@ -971,9 +971,9 @@ void callFlake(EvalState & state,
);
}
state.callFunction(*state.ctx.caches.vCallFlake, *vLocks, *vTmp1, noPos);
state.callFunction(*vTmp1, *vRootSrc, *vTmp2, noPos);
state.callFunction(*vTmp2, *vRootSubdir, vRes, noPos);
state.callFunction(*state.ctx.caches.vCallFlake, vLocks, vTmp1, noPos);
state.callFunction(vTmp1, vRootSrc, vTmp2, noPos);
state.callFunction(vTmp2, vRootSubdir, vRes, noPos);
}
void prim_getFlake(EvalState & state, Value * * args, Value & v)
@@ -1024,9 +1024,9 @@ void prim_flakeRefToString(
"while evaluating the argument passed to builtins.flakeRefToString");
fetchers::Attrs attrs;
for (const auto & attr : *args[0]->attrs()) {
auto t = attr.value->type();
auto t = attr.value.type();
if (t == nInt) {
auto intValue = attr.value->integer().value;
auto intValue = attr.value.integer().value;
if (intValue < 0) {
state.ctx.errors.make<EvalError>("negative value given for flake ref attr %1%: %2%", state.ctx.symbols[attr.name], intValue).debugThrow();
@@ -1035,16 +1035,16 @@ void prim_flakeRefToString(
attrs.emplace(state.ctx.symbols[attr.name], asUnsigned);
} else if (t == nBool) {
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value->boolean()});
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value.boolean()});
} else if (t == nString) {
attrs.emplace(state.ctx.symbols[attr.name], std::string(attr.value->str()));
attrs.emplace(state.ctx.symbols[attr.name], std::string(attr.value.str()));
} else {
state.ctx.errors
.make<EvalError>(
"flake reference attribute sets may only contain integers, Booleans, "
"and strings, but attribute '%s' is %s",
state.ctx.symbols[attr.name],
showType(*attr.value)
showType(attr.value)
)
.debugThrow();
}
+2 -2
View File
@@ -17,8 +17,8 @@ using SmallVector = boost::container::small_vector<T, nItems, TraceableAllocator
/**
* A vector of value pointers. See `SmallVector`.
*/
template <size_t nItems>
using SmallValueVector = SmallVector<Value *, nItems>;
template<size_t nItems>
using SmallValueVector = SmallVector<Value, nItems>;
/**
* A vector of values that must not be referenced after the vector is destroyed.
+35 -35
View File
@@ -69,7 +69,7 @@ std::string DrvInfo::queryName(EvalState & state)
state.ctx.errors.make<TypeError>("derivation name missing").debugThrow();
}
name = state.forceStringNoCtx(
*i->value, noPos, "while evaluating the 'name' attribute of a derivation"
i->value, noPos, "while evaluating the 'name' attribute of a derivation"
);
}
return name;
@@ -83,7 +83,7 @@ std::string DrvInfo::querySystem(EvalState & state)
system = !i
? "unknown"
: state.forceStringNoCtx(
*i->value, i->pos, "while evaluating the 'system' attribute of a derivation"
i->value, i->pos, "while evaluating the 'system' attribute of a derivation"
);
}
return system;
@@ -100,7 +100,7 @@ std::optional<StorePath> DrvInfo::queryDrvPath(EvalState & state)
} else {
drvPath = {state.coerceToStorePath(
i->pos,
*i->value,
i->value,
context,
"while evaluating the 'drvPath' attribute of a derivation"
)};
@@ -125,7 +125,7 @@ StorePath DrvInfo::queryOutPath(EvalState & state)
NixStringContext context;
if (i) {
outPath = state.coerceToStorePath(
i->pos, *i->value, context, "while evaluating the output path of a derivation"
i->pos, i->value, context, "while evaluating the output path of a derivation"
);
}
}
@@ -158,17 +158,17 @@ void DrvInfo::fillOutputs(EvalState & state, bool withPaths)
// NOTE(Qyriad): I don't think there is any codepath that can cause this to error.
state.forceList(
*outputs->value, outputs->pos, "while evaluating the 'outputs' attribute of a derivation"
outputs->value, outputs->pos, "while evaluating the 'outputs' attribute of a derivation"
);
for (auto [idx, elem] : enumerate(outputs->value->listItems())) {
for (auto && [idx, elem] : enumerate(outputs->value.listItems())) {
// NOTE(Qyriad): This error should be *extremely* rare in practice.
// It is impossible to construct with `stdenv.mkDerivation`,
// `builtins.derivation`, or even `derivationStrict`. As far as we can tell,
// it is only possible by overriding a derivation attrset already created by
// one of those with `//` to introduce the failing `outputs` entry.
auto errMsg = fmt("while evaluating output %d of a derivation", idx);
std::string_view outputName = state.forceStringNoCtx(*elem, outputs->pos, errMsg);
std::string_view outputName = state.forceStringNoCtx(elem, outputs->pos, errMsg);
if (withPaths) {
// Find the attr with this output's name...
@@ -180,10 +180,10 @@ void DrvInfo::fillOutputs(EvalState & state, bool withPaths)
// Meanwhile we couldn't figure out any circumstances
// that cause this to error.
state.forceAttrs(*out->value, outputs->pos, errMsg);
state.forceAttrs(out->value, outputs->pos, errMsg);
// ...and evaluate its `outPath` attribute.
const Attr * outPath = out->value->attrs()->get(state.ctx.s.outPath);
const Attr * outPath = out->value.attrs()->get(state.ctx.s.outPath);
if (outPath == nullptr) {
continue;
// FIXME: throw error?
@@ -192,8 +192,7 @@ void DrvInfo::fillOutputs(EvalState & state, bool withPaths)
NixStringContext context;
// And idk what could possibly cause this one to error
// that wouldn't error before here.
auto storePath =
state.coerceToStorePath(outPath->pos, *outPath->value, context, errMsg);
auto storePath = state.coerceToStorePath(outPath->pos, outPath->value, context, errMsg);
this->outputs.emplace(outputName, storePath);
} else {
this->outputs.emplace(outputName, std::nullopt);
@@ -225,7 +224,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state, bool withPaths, bool o
// explicitly selected-into output.
if (const Attr * outSpecAttr = attrs->get(state.ctx.s.outputSpecified)) {
bool outputSpecified = state.forceBool(
*outSpecAttr->value,
outSpecAttr->value,
outSpecAttr->pos,
"while evaluating the 'outputSpecified' attribute of a derivation"
);
@@ -245,16 +244,16 @@ DrvInfo::Outputs DrvInfo::queryOutputs(EvalState & state, bool withPaths, bool o
/* ^ this shows during `nix-env -i` right under the bad derivation */
if (!outTI->isList()) throw Error(errMsg + "expected a list but got %s", Uncolored(showType(outTI->type())));
Outputs result;
for (auto elem : outTI->listItems()) {
if (elem->type() != nString) {
for (auto & elem : outTI->listItems()) {
if (elem.type() != nString) {
throw Error(
errMsg + "element is %s where a string was expected",
Uncolored(showType(elem->type()))
Uncolored(showType(elem.type()))
);
}
auto out = outputs.find(std::string(elem->str()));
auto out = outputs.find(std::string(elem.str()));
if (out == outputs.end()) {
throw Error(errMsg + "output '%s' does not exist", elem->str());
throw Error(errMsg + "output '%s' does not exist", elem.str());
}
result.insert(*out);
}
@@ -267,7 +266,7 @@ std::string DrvInfo::queryOutputName(EvalState & state)
if (outputName == "" && attrs) {
auto i = attrs->get(state.ctx.s.outputName);
outputName = i ? state.forceStringNoCtx(
*i->value, noPos, "while evaluating the output name of a derivation"
i->value, noPos, "while evaluating the output name of a derivation"
)
: "";
}
@@ -283,8 +282,8 @@ Bindings * DrvInfo::getMeta(EvalState & state)
if (!a) {
return 0;
}
state.forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
meta = a->value->attrs();
state.forceAttrs(a->value, a->pos, "while evaluating the 'meta' attribute of a derivation");
meta = a->value.attrs();
return meta;
}
@@ -303,8 +302,8 @@ bool DrvInfo::checkMeta(EvalState & state, Value & v)
{
state.forceValue(v, noPos);
if (v.type() == nList) {
for (auto elem : v.listItems()) {
if (!checkMeta(state, *elem)) {
for (auto & elem : v.listItems()) {
if (!checkMeta(state, elem)) {
return false;
}
}
@@ -316,7 +315,7 @@ bool DrvInfo::checkMeta(EvalState & state, Value & v)
return false;
}
for (auto & i : *v.attrs()) {
if (!checkMeta(state, *i.value)) {
if (!checkMeta(state, i.value)) {
return false;
}
}
@@ -331,10 +330,10 @@ Value * DrvInfo::queryMeta(EvalState & state, const std::string & name)
{
if (!getMeta(state)) return 0;
auto a = meta->get(state.ctx.symbols.create(name));
if (!a || !checkMeta(state, *a->value)) {
if (!a || !checkMeta(state, a->value)) {
return 0;
}
return a->value;
return &a->value;
}
@@ -392,7 +391,7 @@ void DrvInfo::setMeta(EvalState & state, const std::string & name, Value & v)
for (auto i : *meta)
if (i.name != sym)
attrs.insert(i);
attrs.insert(sym, &v);
attrs.insert(sym, v);
meta = attrs.finish();
}
@@ -464,13 +463,13 @@ static void getDerivations(EvalState & state, Value & vIn, PosIdx pos,
if (v.type() == nList) {
// NOTE we can't really deduplicate here because small lists don't have stable addresses
// and can cause spurious duplicate detections due to v being on the stack.
for (auto [n, elem] : enumerate(v.listItems())) {
for (auto && [n, elem] : enumerate(v.listItems())) {
std::string joinedAttrPath = addToPath(pathPrefix, fmt("%d", n));
bool shouldRecurse =
getDerivation(state, *elem, joinedAttrPath, drvs, ignoreAssertionFailures);
getDerivation(state, elem, joinedAttrPath, drvs, ignoreAssertionFailures);
if (shouldRecurse) {
getDerivations(
state, *elem, pos, joinedAttrPath, autoArgs, drvs, done, ignoreAssertionFailures
state, elem, pos, joinedAttrPath, autoArgs, drvs, done, ignoreAssertionFailures
);
}
}
@@ -510,7 +509,7 @@ static void getDerivations(EvalState & state, Value & vIn, PosIdx pos,
if (combineChannels) {
getDerivations(
state,
*attr->value,
attr->value,
attr->pos,
joinedAttrPath,
autoArgs,
@@ -518,18 +517,19 @@ static void getDerivations(EvalState & state, Value & vIn, PosIdx pos,
done,
ignoreAssertionFailures
);
} else if (getDerivation(state, *attr->value, joinedAttrPath, drvs, ignoreAssertionFailures)) {
} else if (getDerivation(state, attr->value, joinedAttrPath, drvs, ignoreAssertionFailures))
{
/* If the value of this attribute is itself a set,
should we recurse into it? => Only if it has a
`recurseForDerivations = true' attribute. */
if (attr->value->type() == nAttrs) {
if (attr->value.type() == nAttrs) {
const Attr * recurseForDrvs =
attr->value->attrs()->get(state.ctx.s.recurseForDerivations);
attr->value.attrs()->get(state.ctx.s.recurseForDerivations);
if (recurseForDrvs == nullptr) {
continue;
}
bool shouldRecurse = state.forceBool(
*recurseForDrvs->value,
recurseForDrvs->value,
attr->pos,
fmt("while evaluating the '%s' attribute", Magenta("recurseForDerivations"))
);
@@ -539,7 +539,7 @@ static void getDerivations(EvalState & state, Value & vIn, PosIdx pos,
getDerivations(
state,
*attr->value,
attr->value,
attr->pos,
joinedAttrPath,
autoArgs,
+24 -27
View File
@@ -23,7 +23,7 @@ class JSONSax : nlohmann::json_sax<JSON> {
explicit JSONState(std::unique_ptr<JSONState> && p) : parent(std::move(p)) {}
JSONState() = default;
JSONState(JSONState & p) = delete;
Value & value(EvalState & state)
Value & value()
{
if (!v) {
v = allocRootValue({});
@@ -31,7 +31,7 @@ class JSONSax : nlohmann::json_sax<JSON> {
return *v;
}
virtual ~JSONState() {}
virtual void add(EvalState & state) {}
virtual void add() {}
};
class JSONObjectState : public JSONState {
@@ -41,17 +41,14 @@ class JSONSax : nlohmann::json_sax<JSON> {
std::unique_ptr<JSONState> resolve(EvalState & state) override
{
auto attrs2 = state.ctx.buildBindings(attrs.size());
for (auto & i : attrs) {
auto v = state.ctx.mem.allocValue();
*v = i.second;
attrs2.insert(i.first, v);
}
parent->value(state).mkAttrs(attrs2.alreadySorted());
for (auto & i : attrs)
attrs2.insert(i.first, i.second);
parent->value().mkAttrs(attrs2.alreadySorted());
return std::move(parent);
}
void add(EvalState & state) override
void add() override
{
attrs.insert_or_assign(_key, value(state));
attrs.insert_or_assign(_key, value());
v = nullptr;
}
public:
@@ -66,13 +63,13 @@ class JSONSax : nlohmann::json_sax<JSON> {
std::unique_ptr<JSONState> resolve(EvalState & state) override
{
auto list = state.ctx.mem.newList(values.size());
parent->value(state) = {NewValueAs::list, list};
parent->value() = {NewValueAs::list, list};
for (size_t n = 0; n < values.size(); ++n) {
*(list->elems[n] = state.ctx.mem.allocValue()) = values[n];
list->elems[n] = values[n];
}
return std::move(parent);
}
void add(EvalState & state) override
void add() override
{
values.push_back(*v);
v = nullptr;
@@ -92,27 +89,27 @@ public:
Value result()
{
return rs->value(state);
return rs->value();
}
bool null() override
{
rs->value(state).mkNull();
rs->add(state);
rs->value().mkNull();
rs->add();
return true;
}
bool boolean(bool val) override
{
rs->value(state).mkBool(val);
rs->add(state);
rs->value().mkBool(val);
rs->add();
return true;
}
bool number_integer(number_integer_t val) override
{
rs->value(state).mkInt(val);
rs->add(state);
rs->value().mkInt(val);
rs->add();
return true;
}
@@ -124,22 +121,22 @@ public:
return number_float(static_cast<number_float_t>(val_), "");
}
NixInt::Inner val = val_;
rs->value(state).mkInt(val);
rs->add(state);
rs->value().mkInt(val);
rs->add();
return true;
}
bool number_float(number_float_t val, const string_t & s) override
{
rs->value(state).mkFloat(val);
rs->add(state);
rs->value().mkFloat(val);
rs->add();
return true;
}
bool string(string_t & val) override
{
rs->value(state).mkString(val);
rs->add(state);
rs->value().mkString(val);
rs->add();
return true;
}
@@ -166,7 +163,7 @@ public:
bool end_object() override {
rs = rs->resolve(state);
rs->add(state);
rs->add();
return true;
}
+4 -4
View File
@@ -127,7 +127,7 @@ public:
virtual JSON toJSON(const SymbolTable & symbols) const;
virtual void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) = 0;
virtual void eval(EvalState & state, Env & env, Value & v);
virtual Value * maybeThunk(EvalState & state, Env & env);
virtual Value maybeThunk(EvalState & state, Env & env);
virtual void setName(Symbol name);
PosIdx getPos() const { return pos; }
@@ -175,7 +175,7 @@ protected:
Value v;
ExprLiteral(const PosIdx pos) : Expr(pos) {};
public:
Value * maybeThunk(EvalState & state, Env & env) override;
Value maybeThunk(EvalState & state, Env & env) override;
JSON toJSON(const SymbolTable & symbols) const override;
void eval(EvalState & state, Env & env, Value & v) override;
void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) override { ev.visit(*this, ptr); }
@@ -253,7 +253,7 @@ struct ExprVar : Expr
ExprVar(Symbol name) : name(name), needsRoot(false) { };
ExprVar(const PosIdx & pos, Symbol name, bool needsRoot = false) : Expr(pos), name(name), needsRoot(needsRoot) { };
Value * maybeThunk(EvalState & state, Env & env) override;
Value maybeThunk(EvalState & state, Env & env) override;
JSON toJSON(const SymbolTable & symbols) const override;
void eval(EvalState & state, Env & env, Value & v) override;
void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) override { ev.visit(*this, ptr); }
@@ -395,7 +395,7 @@ struct ExprList : Expr
JSON toJSON(const SymbolTable & symbols) const override;
void eval(EvalState & state, Env & env, Value & v) override;
void accept(ExprVisitor & ev, std::unique_ptr<Expr> & ptr) override { ev.visit(*this, ptr); }
Value * maybeThunk(EvalState & state, Env & env) override;
Value maybeThunk(EvalState & state, Env & env) override;
};
struct Pattern {
+133 -141
View File
@@ -189,11 +189,10 @@ static void import(EvalState & state, Value & vPath, Value * vScope, Value & v)
for (const auto & [i, o] : enumerate(drv.outputs)) {
mkOutputString(state, attrs, *storePath, o);
(outputsList->elems[i] = state.ctx.mem.allocValue())->mkString(o.first);
outputsList->elems[i].mkString(o.first);
}
auto w = state.ctx.mem.allocValue();
w->mkAttrs(attrs);
Value w{NewValueAs::attrs, attrs.finish()};
if (!state.ctx.caches.vImportedDrvToDerivation) {
state.ctx.caches.vImportedDrvToDerivation = allocRootValue({});
@@ -211,7 +210,7 @@ static void import(EvalState & state, Value & vPath, Value * vScope, Value & v)
noPos,
"while evaluating imported-drv-to-derivation.nix.gen.hh"
);
v = {NewValueAs::app, state.ctx.mem, *state.ctx.caches.vImportedDrvToDerivation, *w};
v = {NewValueAs::app, state.ctx.mem, *state.ctx.caches.vImportedDrvToDerivation, w};
state.forceAttrs(v, noPos, "while calling imported-drv-to-derivation.nix.gen.hh");
}
@@ -307,7 +306,7 @@ void prim_exec(EvalState & state, Value * * args, Value & v)
state
.coerceToString(
noPos,
*elems[0],
elems[0],
context,
"while evaluating the first element of the argument passed to builtins.exec",
StringCoercionMode::Strict,
@@ -320,7 +319,7 @@ void prim_exec(EvalState & state, Value * * args, Value & v)
state
.coerceToString(
noPos,
*elems[i],
elems[i],
context,
"while evaluating an element of the argument passed to builtins.exec",
StringCoercionMode::Strict,
@@ -489,12 +488,12 @@ struct CompareValues : NeverAsync
} else if (i == v1.listSize()) {
return true;
} else if (!state.eqValues(
*v1.listElems()[i], *v2.listElems()[i], noPos, errorCtx
v1.listElems()[i], v2.listElems()[i], noPos, errorCtx
))
{
return (*this)(
*v1.listElems()[i],
*v2.listElems()[i],
v1.listElems()[i],
v2.listElems()[i],
"while comparing two list elements"
);
}
@@ -547,18 +546,18 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
);
state.forceList(
*startSet->value,
startSet->value,
noPos,
"while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure"
);
UnsafeValueList workSet;
for (auto elem : startSet->value->listItems()) {
workSet.push_back(elem);
for (auto & elem : startSet->value.listItems()) {
workSet.push_back(&elem);
}
if (startSet->value->listSize() == 0) {
v = *startSet->value;
if (startSet->value.listSize() == 0) {
v = startSet->value;
return;
}
@@ -570,7 +569,7 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
"in the attrset passed as argument to builtins.genericClosure"
);
state.forceFunction(
*op->value,
op->value,
noPos,
"while evaluating the 'operator' attribute passed as argument to builtins.genericClosure"
);
@@ -595,22 +594,23 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
e->attrs(),
"in one of the attrsets generated by (or initially passed to) builtins.genericClosure"
);
state.forceValue(*key->value, noPos);
state.forceValue(key->value, noPos);
if (!doneKeys.insert(key->value).second) {
if (!doneKeys.insert(&key->value).second) {
continue;
}
res.push_back(e);
/* Call the `operator' function with `e' as argument. */
Value newElements;
state.callFunction(*op->value, {&e, 1}, newElements, noPos);
state.callFunction(op->value, {e, 1}, newElements, noPos);
state.forceList(newElements, noPos, "while evaluating the return value of the `operator` passed to builtins.genericClosure");
/* Add the values returned by the operator to the work set. */
for (auto elem : newElements.listItems()) {
state.forceValue(*elem, noPos); // "while evaluating one one of the elements returned by the `operator` passed to builtins.genericClosure");
workSet.push_back(elem);
for (auto & elem : newElements.listItems()) {
state.forceValue(elem, noPos); // "while evaluating one one of the elements returned by
// the `operator` passed to builtins.genericClosure");
workSet.push_back(&elem);
}
}
@@ -619,7 +619,7 @@ static void prim_genericClosure(EvalState & state, Value * * args, Value & v)
v = {NewValueAs::list, result};
unsigned int n = 0;
for (auto & i : res)
result->elems[n++] = i;
result->elems[n++] = *i;
}
@@ -719,7 +719,7 @@ static void prim_tryEval(EvalState & state, Value * * args, Value & v)
return true;
}();
if (success)
attrs.insert(state.ctx.s.value, args[0]);
attrs.insert(state.ctx.s.value, *args[0]);
else
attrs.alloc(state.ctx.s.value).mkBool(false);
attrs.alloc("success").mkBool(success);
@@ -810,7 +810,7 @@ static void prim_derivationStrict(EvalState & state, Value * * args, Value & v)
std::string drvName;
try {
drvName = state.forceStringNoCtx(
*nameAttr->value,
nameAttr->value,
noPos,
"while evaluating the `name` attribute passed to builtins.derivationStrict"
);
@@ -858,7 +858,7 @@ drvName, Bindings * attrs, Value & v)
auto attr = attrs->get(state.ctx.s.structuredAttrs);
if (attr
&& state.forceBool(
*attr->value,
attr->value,
attr->pos,
"while evaluating the `__structuredAttrs` "
"attribute passed to builtins.derivationStrict"
@@ -872,7 +872,7 @@ drvName, Bindings * attrs, Value & v)
attr = attrs->get(state.ctx.s.ignoreNulls);
if (attr) {
ignoreNulls = state.forceBool(
*attr->value,
attr->value,
attr->pos,
"while evaluating the `__ignoreNulls` attribute "
"passed to builtins.derivationStrict"
@@ -933,21 +933,21 @@ drvName, Bindings * attrs, Value & v)
const std::string_view context_below("");
if (ignoreNulls) {
state.forceValue(*i->value, noPos);
if (i->value->type() == nNull) {
state.forceValue(i->value, noPos);
if (i->value.type() == nNull) {
continue;
}
}
if (i->name == state.ctx.s.contentAddressed
&& state.forceBool(*i->value, noPos, context_below))
&& state.forceBool(i->value, noPos, context_below))
{
state.ctx.errors.make<EvalError>("ca derivations are not supported in Lix")
.debugThrow();
}
else if (i->name == state.ctx.s.impure
&& state.forceBool(*i->value, noPos, context_below))
&& state.forceBool(i->value, noPos, context_below))
{
state.ctx.errors.make<EvalError>("impure derivations are not supported in Lix")
.debugThrow();
@@ -957,12 +957,12 @@ drvName, Bindings * attrs, Value & v)
command-line arguments to the builder. */
else if (i->name == state.ctx.s.args)
{
state.forceList(*i->value, noPos, context_below);
for (auto elem : i->value->listItems()) {
state.forceList(i->value, noPos, context_below);
for (auto & elem : i->value.listItems()) {
auto s = state
.coerceToString(
noPos,
*elem,
elem,
context,
"while evaluating an element of the argument list",
StringCoercionMode::ToString
@@ -982,24 +982,24 @@ drvName, Bindings * attrs, Value & v)
if (i->name == state.ctx.s.structuredAttrs) continue;
(*jsonObject)[std::string(key)] =
printValueAsJSON(state, true, *i->value, noPos, context);
printValueAsJSON(state, true, i->value, noPos, context);
if (i->name == state.ctx.s.builder)
drv.builder = state.forceString(*i->value, context, noPos, context_below);
drv.builder = state.forceString(i->value, context, noPos, context_below);
else if (i->name == state.ctx.s.system)
drv.platform = state.forceStringNoCtx(*i->value, noPos, context_below);
drv.platform = state.forceStringNoCtx(i->value, noPos, context_below);
else if (i->name == state.ctx.s.outputHash)
outputHash = state.forceStringNoCtx(*i->value, noPos, context_below);
outputHash = state.forceStringNoCtx(i->value, noPos, context_below);
else if (i->name == state.ctx.s.outputHashAlgo)
outputHashAlgo = state.forceStringNoCtx(*i->value, noPos, context_below);
outputHashAlgo = state.forceStringNoCtx(i->value, noPos, context_below);
else if (i->name == state.ctx.s.outputHashMode)
handleHashMode(state.forceStringNoCtx(*i->value, noPos, context_below));
handleHashMode(state.forceStringNoCtx(i->value, noPos, context_below));
else if (i->name == state.ctx.s.outputs) {
/* Require outputs to be a list of strings. */
state.forceList(*i->value, noPos, context_below);
state.forceList(i->value, noPos, context_below);
Strings ss;
for (auto elem : i->value->listItems()) {
ss.emplace_back(state.forceStringNoCtx(*elem, noPos, context_below));
for (auto & elem : i->value.listItems()) {
ss.emplace_back(state.forceStringNoCtx(elem, noPos, context_below));
}
handleOutputs(ss);
}
@@ -1051,7 +1051,7 @@ drvName, Bindings * attrs, Value & v)
auto s = state
.coerceToString(
noPos,
*i->value,
i->value,
context,
context_below,
StringCoercionMode::ToString
@@ -1381,29 +1381,29 @@ static void prim_findFile(EvalState & state, Value * * args, Value & v)
SearchPath searchPath;
for (auto v2 : args[0]->listItems()) {
for (auto & v2 : args[0]->listItems()) {
state.forceAttrs(
*v2, noPos, "while evaluating an element of the list passed to builtins.findFile"
v2, noPos, "while evaluating an element of the list passed to builtins.findFile"
);
std::string prefix;
auto i = v2->attrs()->get(state.ctx.s.prefix);
auto i = v2.attrs()->get(state.ctx.s.prefix);
if (i) {
prefix = state.forceStringNoCtx(
*i->value,
i->value,
noPos,
"while evaluating the `prefix` attribute of an element of the list passed to "
"builtins.findFile"
);
}
i = getAttr(state, state.ctx.s.path, v2->attrs(), "in an element of the __nixPath");
i = getAttr(state, state.ctx.s.path, v2.attrs(), "in an element of the __nixPath");
NixStringContext context;
auto path = state
.coerceToString(
noPos,
*i->value,
i->value,
context,
"while evaluating the `path` attribute of an element of the list "
"passed to builtins.findFile",
@@ -1488,11 +1488,11 @@ static void prim_readDir(EvalState & state, Value * * args, Value & v)
// Some filesystems or operating systems may not be able to return
// detailed node info quickly in this case we produce a thunk to
// query the file type lazily.
auto epath = state.ctx.mem.allocValue();
epath->mkPath(path + name);
Value epath;
epath.mkPath(path + name);
if (!readFileType)
readFileType = &state.ctx.builtins.get("readFileType");
attr = {NewValueAs::app, state.ctx.mem, *readFileType, *epath};
attr = {NewValueAs::app, state.ctx.mem, *readFileType, epath};
} else {
// This branch of the conditional is much more likely.
// Here we just stringize the directory entry type.
@@ -1630,7 +1630,7 @@ static void addPath(
S_ISLNK(st.st_mode) ? "symlink" :
"unknown" /* not supported, will fail! */);
Value * args []{&arg1, &arg2};
Value args[]{arg1, arg2};
Value res;
state.callFunction(*filterFun, args, res, noPos);
@@ -1697,32 +1697,32 @@ static void prim_path(EvalState & state, Value * * args, Value & v)
if (n == "path") {
path.emplace(state.coerceToPath(
attr.pos,
*attr.value,
attr.value,
context,
"while evaluating the 'path' attribute passed to 'builtins.path'"
));
} else if (attr.name == state.ctx.s.name) {
name = state.forceStringNoCtx(
*attr.value,
attr.value,
attr.pos,
"while evaluating the `name` attribute passed to builtins.path"
);
} else if (n == "filter") {
state.forceFunction(
*(filterFun = attr.value),
*(filterFun = &attr.value),
attr.pos,
"while evaluating the `filter` parameter passed to builtins.path"
);
} else if (n == "recursive") {
method = FileIngestionMethod{state.forceBool(
*attr.value,
attr.value,
attr.pos,
"while evaluating the `recursive` attribute passed to builtins.path"
)};
} else if (n == "sha256") {
expectedHash = newHashAllowEmpty(
state.forceStringNoCtx(
*attr.value,
attr.value,
attr.pos,
"while evaluating the `sha256` attribute passed to builtins.path"
),
@@ -1764,10 +1764,10 @@ static void prim_attrNames(EvalState & state, Value * * args, Value & v)
size_t n = 0;
for (auto & i : *args[0]->attrs())
result->elems[n++] = const_cast<Value *>(state.ctx.symbols[i.name].toValuePtr());
result->elems[n++] = state.ctx.symbols[i.name].toValue();
std::sort(result->elems, result->elems + n, [](Value * v1, Value * v2) {
return v1->str() < v2->str();
std::sort(result->elems, result->elems + n, [](Value & v1, Value & v2) {
return v1.str() < v2.str();
});
}
@@ -1809,8 +1809,8 @@ void prim_getAttr(EvalState & state, Value * * args, Value & v)
);
// !!! add to stack trace?
if (state.ctx.stats.countCalls && i->pos) state.ctx.stats.attrSelects[i->pos]++;
state.forceValue(*i->value, noPos);
v = *i->value;
state.forceValue(i->value, noPos);
v = i->value;
}
/* Return position information of the specified attribute. */
@@ -1859,10 +1859,9 @@ static struct LazyPosAcessors {
void operator()(EvalState & state, const PosIdx pos, Value & line, Value & column)
{
Value * posV = state.ctx.mem.allocValue();
posV->mkInt(pos.id);
line = {NewValueAs::app, state.ctx.mem, lineOfPos, *posV};
column = {NewValueAs::app, state.ctx.mem, columnOfPos, *posV};
Value posV{NewValueAs::integer, NixInt{pos.id}};
line = {NewValueAs::app, state.ctx.mem, lineOfPos, posV};
column = {NewValueAs::app, state.ctx.mem, columnOfPos, posV};
}
} makeLazyPosAccessors;
@@ -1897,13 +1896,13 @@ static void prim_removeAttrs(EvalState & state, Value * * args, Value & v)
// 64: large enough to fit the attributes of a derivation
boost::container::small_vector<Attr, 64> names;
names.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) {
for (auto & elem : args[1]->listItems()) {
state.forceStringNoCtx(
*elem,
elem,
noPos,
"while evaluating the values of the second argument passed to builtins.removeAttrs"
);
names.emplace_back(state.ctx.symbols.create(elem->str()), nullptr);
names.emplace_back(state.ctx.symbols.create(elem.str()), Value());
}
std::sort(names.begin(), names.end());
@@ -1931,15 +1930,15 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
std::set<Symbol> seen;
for (auto v2 : args[0]->listItems()) {
for (auto & v2 : args[0]->listItems()) {
state.forceAttrs(
*v2, noPos, "while evaluating an element of the list passed to builtins.listToAttrs"
v2, noPos, "while evaluating an element of the list passed to builtins.listToAttrs"
);
auto j = getAttr(state, state.ctx.s.name, v2->attrs(), "in a {name=...; value=...;} pair");
auto j = getAttr(state, state.ctx.s.name, v2.attrs(), "in a {name=...; value=...;} pair");
auto name = state.forceStringNoCtx(
*j->value,
j->value,
j->pos,
"while evaluating the `name` attribute of an element of the list passed to "
"builtins.listToAttrs"
@@ -1948,7 +1947,7 @@ static void prim_listToAttrs(EvalState & state, Value * * args, Value & v)
auto sym = state.ctx.symbols.create(name);
if (seen.insert(sym).second) {
auto j2 =
getAttr(state, state.ctx.s.value, v2->attrs(), "in a {name=...; value=...;} pair");
getAttr(state, state.ctx.s.value, v2.attrs(), "in a {name=...; value=...;} pair");
attrs.insert(sym, j2->value, j2->pos);
}
}
@@ -2032,13 +2031,13 @@ static void prim_catAttrs(EvalState & state, Value * * args, Value & v)
SmallValueVector<nonRecursiveStackReservation> res(args[1]->listSize());
size_t found = 0;
for (auto v2 : args[1]->listItems()) {
for (auto & v2 : args[1]->listItems()) {
state.forceAttrs(
*v2,
v2,
noPos,
"while evaluating an element in the list passed as second argument to builtins.catAttrs"
);
auto i = v2->attrs()->get(attrName);
auto i = v2.attrs()->get(attrName);
if (i) {
res[found++] = i->value;
}
@@ -2082,8 +2081,8 @@ static void prim_mapAttrs(EvalState & state, Value * * args, Value & v)
auto attrs = state.ctx.buildBindings(args[1]->attrs()->size());
for (auto & i : *args[1]->attrs()) {
auto vName = const_cast<Value *>(state.ctx.symbols[i.name].toValuePtr());
Value * appArgs[] = {vName, i.value};
auto vName = state.ctx.symbols[i.name].toValue();
Value appArgs[] = {vName, i.value};
attrs.alloc(i.name) = {NewValueAs::app, state.ctx.mem, *args[0], appArgs};
}
@@ -2099,7 +2098,7 @@ static void prim_zipAttrsWith(EvalState & state, Value * * args, Value & v)
// attribute with the merge function application. this way we need not
// use (slightly slower) temporary storage the GC does not know about.
std::map<Symbol, std::pair<size_t, Value * *>> attrsSeen;
std::map<Symbol, std::pair<size_t, Value *>> attrsSeen;
state.forceFunction(*args[0], noPos, "while evaluating the first argument passed to builtins.zipAttrsWith");
state.forceList(*args[1], noPos, "while evaluating the second argument passed to builtins.zipAttrsWith");
@@ -2107,14 +2106,14 @@ static void prim_zipAttrsWith(EvalState & state, Value * * args, Value & v)
const auto listElems = args[1]->listElems();
for (unsigned int n = 0; n < listSize; ++n) {
Value * vElem = listElems[n];
Value & vElem = listElems[n];
state.forceAttrs(
*vElem,
vElem,
noPos,
"while evaluating a value of the list passed as second argument to "
"builtins.zipAttrsWith"
);
for (auto & attr : *vElem->attrs()) {
for (auto & attr : *vElem.attrs()) {
attrsSeen[attr.name].first++;
}
}
@@ -2122,16 +2121,14 @@ static void prim_zipAttrsWith(EvalState & state, Value * * args, Value & v)
auto attrs = state.ctx.buildBindings(attrsSeen.size());
for (auto & [sym, elem] : attrsSeen) {
/* Take care of the returned lists. */
auto list = state.ctx.mem.allocValue();
auto content = state.ctx.mem.newList(elem.first);
*list = {NewValueAs::list, content};
Value list{NewValueAs::list, content};
elem.second = content->elems;
/* Construct a `fn name list` function call value. */
auto name = const_cast<Value *>(state.ctx.symbols[sym].toValuePtr());
Value * callArgs[] = {name, list};
auto call = state.ctx.mem.allocValue();
*call = {NewValueAs::app, state.ctx.mem, *args[0], callArgs};
auto name = state.ctx.symbols[sym].toValue();
Value callArgs[] = {name, list};
Value call{NewValueAs::app, state.ctx.mem, *args[0], callArgs};
/* Insert it inside the returned attribute set. */
attrs.insert(sym, call);
@@ -2139,8 +2136,8 @@ static void prim_zipAttrsWith(EvalState & state, Value * * args, Value & v)
/* Populate the lists inside the attribute set */
for (unsigned int n = 0; n < listSize; ++n) {
Value * vElem = listElems[n];
for (auto & attr : *vElem->attrs()) {
Value & vElem = listElems[n];
for (auto & attr : *vElem.attrs()) {
*attrsSeen[attr.name].second++ = attr.value;
}
}
@@ -2167,8 +2164,8 @@ static void elemAt(EvalState & state, Value & list, NixInt::Inner n, Value & v)
if (n < 0 || std::make_unsigned_t<NixInt::Inner>(n) >= list.listSize()) {
state.ctx.errors.make<EvalError>("list index %1% is out of bounds", n).debugThrow();
}
state.forceValue(*list.listElems()[n], noPos);
v = *list.listElems()[n];
state.forceValue(list.listElems()[n], noPos);
v = list.listElems()[n];
}
/* Return the n-1'th element of a list. */
@@ -2214,8 +2211,7 @@ static void prim_map(EvalState & state, Value * * args, Value & v)
auto result = state.ctx.mem.newList(args[1]->listSize());
v = {NewValueAs::list, result};
for (unsigned int n = 0; n < v.listSize(); ++n) {
result->elems[n] = state.ctx.mem.allocValue();
*result->elems[n] = {NewValueAs::app, state.ctx.mem, *args[0], *args[1]->listElems()[n]};
result->elems[n] = {NewValueAs::app, state.ctx.mem, *args[0], args[1]->listElems()[n]};
}
}
@@ -2240,7 +2236,7 @@ static void prim_filter(EvalState & state, Value * * args, Value & v)
bool same = true;
for (size_t n = 0; n < len; ++n) {
Value res;
state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos);
state.callFunction(*args[0], args[1]->listElems()[n], res, noPos);
if (state.forceBool(res, noPos, "while evaluating the return value of the filtering function passed to builtins.filter"))
vs[k++] = args[1]->listElems()[n];
else
@@ -2263,10 +2259,10 @@ static void prim_elem(EvalState & state, Value * * args, Value & v)
{
bool res = false;
state.forceList(*args[1], noPos, "while evaluating the second argument passed to builtins.elem");
for (auto elem : args[1]->listItems()) {
for (auto & elem : args[1]->listItems()) {
if (state.eqValues(
*args[0],
*elem,
elem,
noPos,
"while searching for the presence of the given element in the list"
))
@@ -2284,8 +2280,7 @@ static void prim_concatLists(EvalState & state, Value * * args, Value & v)
state.forceList(*args[0], noPos, "while evaluating the first argument passed to builtins.concatLists");
state.concatLists(
v,
args[0]->listSize(),
args[0]->listElems(),
std::span{args[0]->listElems(), args[0]->listSize()},
noPos,
"while evaluating a value of the list passed to builtins.concatLists"
);
@@ -2306,13 +2301,13 @@ static void prim_foldlStrict(EvalState & state, Value * * args, Value & v)
state.forceList(*args[2], noPos, "while evaluating the third argument passed to builtins.foldlStrict");
if (args[2]->listSize()) {
Value * vCur = args[1];
Value vCur = *args[1];
for (auto [n, elem] : enumerate(args[2]->listItems())) {
Value * vs []{vCur, elem};
vCur = n == args[2]->listSize() - 1 ? &v : state.ctx.mem.allocValue();
state.callFunction(*args[0], vs, *vCur, noPos);
for (auto && [n, elem] : enumerate(args[2]->listItems())) {
Value vs[]{vCur, elem};
state.callFunction(*args[0], vs, vCur, noPos);
}
v = vCur;
state.forceValue(v, noPos);
} else {
state.forceValue(*args[1], noPos);
@@ -2330,8 +2325,8 @@ static void anyOrAll(bool any, EvalState & state, Value * * args, Value & v)
: "while evaluating the return value of the function passed to builtins.all";
Value vTmp;
for (auto elem : args[1]->listItems()) {
state.callFunction(*args[0], *elem, vTmp, noPos);
for (auto & elem : args[1]->listItems()) {
state.callFunction(*args[0], elem, vTmp, noPos);
bool res = state.forceBool(vTmp, noPos, errorCtx);
if (res == any) {
v.mkBool(any);
@@ -2371,10 +2366,8 @@ static void prim_genList(EvalState & state, Value * * args, Value & v)
auto result = state.ctx.mem.newList(len);
v = {NewValueAs::list, result};
for (size_t n = 0; n < len; ++n) {
auto arg = state.ctx.mem.allocValue();
arg->mkInt(n);
result->elems[n] = state.ctx.mem.allocValue();
*result->elems[n] = {NewValueAs::app, state.ctx.mem, *args[0], *arg};
Value arg{NewValueAs::integer, NixInt{ssize_t(n)}};
result->elems[n] = {NewValueAs::app, state.ctx.mem, *args[0], arg};
}
}
@@ -2396,11 +2389,11 @@ static void prim_sort(EvalState & state, Value * * args, Value & v)
auto list = state.ctx.mem.newList(len);
v = {NewValueAs::list, list};
for (unsigned int n = 0; n < len; ++n) {
state.forceValue(*args[1]->listElems()[n], noPos);
state.forceValue(args[1]->listElems()[n], noPos);
list->elems[n] = args[1]->listElems()[n];
}
auto comparator = [&](Value * a, Value * b) {
auto comparator = [&](Value a, Value b) {
/* Optimization: if the comparator is lessThan, bypass
callFunction. */
/* TODO: (layus) this is absurd. An optimisation like this
@@ -2408,10 +2401,10 @@ static void prim_sort(EvalState & state, Value * * args, Value & v)
if (args[0]->isPrimOp()) {
auto ptr = args[0]->primOp()->fun.target<decltype(&prim_lessThan)>();
if (ptr && *ptr == prim_lessThan)
return CompareValues(state, "while evaluating the ordering function passed to builtins.sort")(*a, *b);
return CompareValues(state, "while evaluating the ordering function passed to builtins.sort")(a, b);
}
Value * vs[] = {a, b};
Value vs[] = {a, b};
Value vBool;
state.callFunction(*args[0], vs, vBool, noPos);
return state.forceBool(vBool, noPos, "while evaluating the return value of the sorting function passed to builtins.sort");
@@ -2434,10 +2427,10 @@ static void prim_partition(EvalState & state, Value * * args, Value & v)
std::vector<size_t> right, wrong;
for (size_t n = 0; n < len; ++n) {
auto vElem = elems[n];
state.forceValue(*vElem, noPos);
auto & vElem = args[1]->listElems()[n];
state.forceValue(vElem, noPos);
Value res;
state.callFunction(*args[0], *vElem, res, noPos);
state.callFunction(*args[0], vElem, res, noPos);
if (state.forceBool(res, noPos, "while evaluating the return value of the partition function passed to builtins.partition"))
right.push_back(n);
else
@@ -2476,7 +2469,7 @@ static void prim_groupBy(EvalState & state, Value * * args, Value & v)
for (auto [i, vElem] : enumerate(args[1]->listItems())) {
Value res;
state.callFunction(*args[0], *vElem, res, noPos);
state.callFunction(*args[0], vElem, res, noPos);
auto name = state.forceStringNoCtx(res, noPos, "while evaluating the return value of the grouping function passed to builtins.groupBy");
auto sym = state.ctx.symbols.create(name);
auto vector = attrs.try_emplace(sym, std::vector<size_t>()).first;
@@ -2509,8 +2502,8 @@ static void prim_concatMap(EvalState & state, Value * * args, Value & v)
size_t len = 0;
for (size_t n = 0; n < nrLists; ++n) {
Value * vElem = args[1]->listElems()[n];
state.callFunction(*args[0], *vElem, lists[n], noPos);
Value & vElem = args[1]->listElems()[n];
state.callFunction(*args[0], vElem, lists[n], noPos);
state.forceList(lists[n], noPos, "while evaluating the return value of the function passed to builtins.concatMap");
len += lists[n].listSize();
}
@@ -2521,7 +2514,7 @@ static void prim_concatMap(EvalState & state, Value * * args, Value & v)
for (unsigned int n = 0, pos = 0; n < nrLists; ++n) {
auto l = lists[n].listSize();
if (l) {
memcpy(out + pos, lists[n].listElems(), l * sizeof(Value *));
std::copy(lists[n].listItems().begin(), lists[n].listItems().end(), out + pos);
}
pos += l;
}
@@ -2785,9 +2778,9 @@ void prim_match(EvalState & state, Value * * args, Value & v)
v = {NewValueAs::list, result};
for (size_t i = 0; i < len; ++i) {
if (!match[i+1].matched)
(result->elems[i] = state.ctx.mem.allocValue())->mkNull();
result->elems[i].mkNull();
else
(result->elems[i] = state.ctx.mem.allocValue())->mkString(match[i + 1].str());
result->elems[i].mkString(match[i + 1].str());
}
} catch (regex::Error & e) {
@@ -2818,7 +2811,7 @@ void prim_split(EvalState & state, Value * * args, Value & v)
size_t idx = 0;
if (len == 0) {
result->elems[idx++] = args[1];
result->elems[idx++] = *args[1];
return;
}
@@ -2827,26 +2820,25 @@ void prim_split(EvalState & state, Value * * args, Value & v)
auto match = *i;
// Add a string for non-matched characters.
(result->elems[idx++] = state.ctx.mem.allocValue())->mkString(match.prefix().str());
result->elems[idx++].mkString(match.prefix().str());
// Add a list for matched substrings.
const size_t slen = match.size() - 1;
auto elem = result->elems[idx++] = state.ctx.mem.allocValue();
auto & elem = result->elems[idx++];
// Start at 1, beacause the first match is the whole string.
auto content = state.ctx.mem.newList(slen);
*elem = {NewValueAs::list, content};
elem = {NewValueAs::list, content};
for (size_t si = 0; si < slen; ++si) {
if (!match[si + 1].matched)
(content->elems[si] = state.ctx.mem.allocValue())->mkNull();
content->elems[si].mkNull();
else
(content->elems[si] = state.ctx.mem.allocValue())
->mkString(match[si + 1].str());
content->elems[si].mkString(match[si + 1].str());
}
// Add a string for non-matched suffix characters.
if (idx == 2 * len) {
(result->elems[idx++] = state.ctx.mem.allocValue())->mkString(match.suffix().str());
result->elems[idx++].mkString(match.suffix().str());
}
}
@@ -2868,11 +2860,11 @@ static void prim_concatStringsSep(EvalState & state, Value * * args, Value & v)
res.reserve((args[1]->listSize() + 32) * sep.size());
bool first = true;
for (auto elem : args[1]->listItems()) {
for (auto & elem : args[1]->listItems()) {
if (first) first = false; else res += sep;
res += *state.coerceToString(
noPos,
*elem,
elem,
context,
"while evaluating one element of the list of strings to concat passed to "
"builtins.concatStringsSep"
@@ -2893,9 +2885,9 @@ static void prim_replaceStrings(EvalState & state, Value * * args, Value & v)
std::vector<std::string> from;
from.reserve(args[0]->listSize());
for (auto elem : args[0]->listItems()) {
for (auto & elem : args[0]->listItems()) {
from.emplace_back(state.forceString(
*elem,
elem,
noPos,
"while evaluating one of the strings to replace passed to builtins.replaceStrings"
));
@@ -2921,7 +2913,7 @@ static void prim_replaceStrings(EvalState & state, Value * * args, Value & v)
if (v == cache.end()) {
NixStringContext ctx;
auto ts = state.forceString(
**j,
*j,
ctx,
noPos,
"while evaluating one of the replacement strings passed to "
@@ -2990,7 +2982,7 @@ static void prim_splitVersion(EvalState & state, Value * * args, Value & v)
auto result = state.ctx.mem.newList(components.size());
v = {NewValueAs::list, result};
for (const auto & [n, component] : enumerate(components))
(result->elems[n] = state.ctx.mem.allocValue())->mkString(std::move(component));
result->elems[n].mkString(std::move(component));
}
@@ -3016,7 +3008,7 @@ Value EvalBuiltins::prepareNixPath(const SearchPath & searchPath)
auto attrs = mem.buildBindings(symbols, 2);
attrs.alloc("path").mkString(i.path.s);
attrs.alloc("prefix").mkString(i.prefix.s);
(v->elems[n++] = mem.allocValue())->mkAttrs(attrs);
v->elems[n++].mkAttrs(attrs);
}
return {NewValueAs::list, v};
}
@@ -3081,7 +3073,7 @@ void EvalBuiltins::createBaseEnv(const SearchPath & searchPath, const Path & sto
/* Now that we've added all primops, sort the `builtins' set,
because attribute lookups expect it to be sorted. */
env.values[0]->attrs()->sort();
env.values[0].attrs()->sort();
staticEnv->isRoot = true;
}
+11 -11
View File
@@ -151,7 +151,7 @@ void prim_getContext(EvalState & state, Value * * args, Value & v)
auto content = state.ctx.mem.newList(info.second.outputs.size());
outputsVal = {NewValueAs::list, content};
for (const auto & [i, output] : enumerate(info.second.outputs))
(content->elems[i] = state.ctx.mem.allocValue())->mkString(output);
content->elems[i].mkString(output);
}
attrs.alloc(state.ctx.store->printStorePath(info.first)).mkAttrs(infoAttrs);
}
@@ -183,11 +183,11 @@ static void prim_appendContext(EvalState & state, Value * * args, Value & v)
auto namePath = state.ctx.store->parseStorePath(name);
if (!settings.readOnlyMode)
state.aio.blockOn(state.ctx.store->ensurePath(namePath));
state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context");
auto a = i.value->attrs()->get(state.ctx.s.path);
state.forceAttrs(i.value, i.pos, "while evaluating the value of a string context");
auto a = i.value.attrs()->get(state.ctx.s.path);
if (a) {
if (state.forceBool(
*a->value, a->pos, "while evaluating the `path` attribute of a string context"
a->value, a->pos, "while evaluating the `path` attribute of a string context"
))
{
context.emplace(NixStringContextElem::Opaque{
@@ -196,10 +196,10 @@ static void prim_appendContext(EvalState & state, Value * * args, Value & v)
}
}
a = i.value->attrs()->get(sAllOutputs);
a = i.value.attrs()->get(sAllOutputs);
if (a) {
if (state.forceBool(
*a->value,
a->value,
a->pos,
"while evaluating the `allOutputs` attribute of a string context"
))
@@ -216,20 +216,20 @@ static void prim_appendContext(EvalState & state, Value * * args, Value & v)
}
}
a = i.value->attrs()->get(state.ctx.s.outputs);
a = i.value.attrs()->get(state.ctx.s.outputs);
if (a) {
state.forceList(
*a->value, a->pos, "while evaluating the `outputs` attribute of a string context"
a->value, a->pos, "while evaluating the `outputs` attribute of a string context"
);
if (a->value->listSize() && !isDerivation(name)) {
if (a->value.listSize() && !isDerivation(name)) {
state.ctx.errors.make<EvalError>(
"tried to add derivation output context of %s, which is not a derivation, to a string",
name
).atPos(i.pos).debugThrow();
}
for (auto elem : a->value->listItems()) {
for (auto & elem : a->value.listItems()) {
auto outputName = state.forceStringNoCtx(
*elem, a->pos, "while evaluating an output name within a string context"
elem, a->pos, "while evaluating an output name within a string context"
);
context.emplace(NixStringContextElem::Built {
.drvPath = makeConstantStorePath(namePath),
+6 -6
View File
@@ -126,26 +126,26 @@ void prim_fetchClosure(EvalState & state, Value * * args, Value & v)
if (attrName == "fromPath") {
NixStringContext context;
fromPath = state.coerceToStorePath(attr.pos, *attr.value, context, attrHint());
fromPath = state.coerceToStorePath(attr.pos, attr.value, context, attrHint());
}
else if (attrName == "toPath") {
state.forceValue(*attr.value, attr.pos);
bool isEmptyString = attr.value->type() == nString && attr.value->str().empty();
state.forceValue(attr.value, attr.pos);
bool isEmptyString = attr.value.type() == nString && attr.value.str().empty();
if (isEmptyString) {
toPath = StorePathOrGap {};
}
else {
NixStringContext context;
toPath = state.coerceToStorePath(attr.pos, *attr.value, context, attrHint());
toPath = state.coerceToStorePath(attr.pos, attr.value, context, attrHint());
}
}
else if (attrName == "fromStore")
fromStoreUrl = state.forceStringNoCtx(*attr.value, attr.pos, attrHint());
fromStoreUrl = state.forceStringNoCtx(attr.value, attr.pos, attrHint());
else if (attrName == "inputAddressed")
inputAddressedMaybe = state.forceBool(*attr.value, attr.pos, attrHint());
inputAddressedMaybe = state.forceBool(attr.value, attr.pos, attrHint());
else
throw Error({
+3 -3
View File
@@ -23,7 +23,7 @@ static void prim_fetchMercurial(EvalState & state, Value * * args, Value & v)
url = state
.coerceToString(
attr.pos,
*attr.value,
attr.value,
context,
"while evaluating the `url` attribute passed to "
"builtins.fetchMercurial",
@@ -35,7 +35,7 @@ static void prim_fetchMercurial(EvalState & state, Value * * args, Value & v)
// Ugly: unlike fetchGit, here the "rev" attribute can
// be both a revision or a branch/tag name.
auto value = state.forceStringNoCtx(
*attr.value,
attr.value,
attr.pos,
"while evaluating the `rev` attribute passed to builtins.fetchMercurial"
);
@@ -46,7 +46,7 @@ static void prim_fetchMercurial(EvalState & state, Value * * args, Value & v)
}
else if (n == "name")
name = state.forceStringNoCtx(
*attr.value,
attr.value,
attr.pos,
"while evaluating the `name` attribute passed to builtins.fetchMercurial"
);
+12 -12
View File
@@ -128,7 +128,7 @@ static void fetchTree(
"unexpected attribute 'type'"
).atPos(pos).debugThrow();
type = state.forceStringNoCtx(
*aType->value,
aType->value,
aType->pos,
"while evaluating the `type` attribute passed to builtins.fetchTree"
);
@@ -142,12 +142,12 @@ static void fetchTree(
for (auto & attr : *args[0]->attrs()) {
if (attr.name == state.ctx.s.type) continue;
state.forceValue(*attr.value, attr.pos);
if (attr.value->type() == nPath || attr.value->type() == nString) {
state.forceValue(attr.value, attr.pos);
if (attr.value.type() == nPath || attr.value.type() == nString) {
auto s =
state
.coerceToString(
attr.pos, *attr.value, context, "", StringCoercionMode::Strict, false
attr.pos, attr.value, context, "", StringCoercionMode::Strict, false
)
.toOwned();
attrs.emplace(state.ctx.symbols[attr.name],
@@ -156,10 +156,10 @@ static void fetchTree(
? fixURIForGit(s, state)
: fixURI(s, state)
: s);
} else if (attr.value->type() == nBool) {
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value->boolean()});
} else if (attr.value->type() == nInt) {
auto intValue = attr.value->integer().value;
} else if (attr.value.type() == nBool) {
attrs.emplace(state.ctx.symbols[attr.name], Explicit<bool>{attr.value.boolean()});
} else if (attr.value.type() == nInt) {
auto intValue = attr.value.integer().value;
if (intValue < 0) {
state.ctx.errors.make<EvalError>("negative value given for fetchTree attr %1%: %2%", state.ctx.symbols[attr.name], intValue).atPos(pos).debugThrow();
@@ -173,7 +173,7 @@ static void fetchTree(
"fetchTree argument '%s' is %s while a string, Boolean or integer is "
"expected",
state.ctx.symbols[attr.name],
showType(*attr.value)
showType(attr.value)
)
.debugThrow();
}
@@ -241,12 +241,12 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
std::string_view n(state.ctx.symbols[attr.name]);
if (n == "url")
url = state.forceStringNoCtx(
*attr.value, attr.pos, "while evaluating the url we should fetch"
attr.value, attr.pos, "while evaluating the url we should fetch"
);
else if (n == "sha256") {
expectedHash = newHashAllowEmpty(
state.forceStringNoCtx(
*attr.value,
attr.value,
attr.pos,
"while evaluating the sha256 of the content we should fetch"
),
@@ -254,7 +254,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v
);
} else if (n == "name")
name = state.forceStringNoCtx(
*attr.value,
attr.value,
attr.pos,
"while evaluating the name of the content we should fetch"
);
+1 -1
View File
@@ -34,7 +34,7 @@ void prim_fromTOML(EvalState & state, Value ** args, Value & val)
auto list = state.ctx.mem.newList(size);
v = {NewValueAs::list, list};
for (size_t i = 0; i < size; ++i) {
self(*(list->elems[i] = state.ctx.mem.allocValue()), array[i]);
self(list->elems[i], array[i]);
}
} break;
case toml::value_t::boolean:
+13 -11
View File
@@ -9,11 +9,12 @@ namespace nix {
// See: https://github.com/NixOS/nix/issues/9730
void printAmbiguous(
Value &v,
const SymbolTable &symbols,
std::ostream &str,
std::set<const void *> *seen,
int depth)
const Value & v,
const SymbolTable & symbols,
std::ostream & str,
std::set<const void *> * seen,
int depth
)
{
checkInterrupt();
@@ -21,6 +22,10 @@ void printAmbiguous(
str << "«too deep»";
return;
}
if (v.isInvalid()) {
str << "<INVALID>";
return;
}
switch (v.type()) {
case nInt:
str << v.integer();
@@ -44,7 +49,7 @@ void printAmbiguous(
str << "{ ";
for (auto & i : v.attrs()->lexicographicOrder(symbols)) {
str << symbols[i->name] << " = ";
printAmbiguous(*i->value, symbols, str, seen, depth - 1);
printAmbiguous(i->value, symbols, str, seen, depth - 1);
str << "; ";
}
str << "}";
@@ -56,11 +61,8 @@ void printAmbiguous(
str << "«repeated»";
else {
str << "[ ";
for (auto v2 : v.listItems()) {
if (v2)
printAmbiguous(*v2, symbols, str, seen, depth - 1);
else
str << "(nullptr)";
for (auto & v2 : v.listItems()) {
printAmbiguous(v2, symbols, str, seen, depth - 1);
str << " ";
}
str << "]";
+6 -6
View File
@@ -17,10 +17,10 @@ namespace nix {
* See: https://github.com/NixOS/nix/issues/9730
*/
void printAmbiguous(
Value &v,
const SymbolTable &symbols,
std::ostream &str,
std::set<const void *> *seen,
int depth);
const Value & v,
const SymbolTable & symbols,
std::ostream & str,
std::set<const void *> * seen,
int depth
);
}
+25 -18
View File
@@ -237,7 +237,7 @@ private:
std::string storePath;
if (i) {
storePath = state.ctx.store->printStorePath(state.coerceToStorePath(
i->pos, *i->value, context, "while evaluating the drvPath of a derivation"
i->pos, i->value, context, "while evaluating the drvPath of a derivation"
));
}
@@ -264,18 +264,18 @@ private:
}
auto item = v[0].second;
if (!item->value) {
if (item->value.isInvalid()) {
return true;
}
if (options.force) {
// The item is going to be forced during printing anyway, but we need its type now.
state.forceValue(*item->value, noPos);
state.forceValue(item->value, noPos);
}
// Pretty-print single-item attrsets only if they contain nested
// structures.
auto itemType = item->value->type();
auto itemType = item->value.type();
return itemType == nList || itemType == nAttrs;
}
@@ -324,7 +324,7 @@ private:
}
output << " = ";
print(*i.second->value, depth + 1);
print(i.second->value, depth + 1);
output << ";";
attrsPrinted++;
printedHere++;
@@ -338,7 +338,7 @@ private:
}
}
bool shouldPrettyPrintList(std::span<Value * const> list)
bool shouldPrettyPrintList(std::span<Value> list)
{
if (!options.shouldPrettyPrint() || list.empty()) {
return false;
@@ -349,19 +349,19 @@ private:
return true;
}
auto item = list[0];
if (!item) {
auto & item = list[0];
if (item.isInvalid()) {
return true;
}
if (options.force) {
// The item is going to be forced during printing anyway, but we need its type now.
state.forceValue(*item, noPos);
state.forceValue(item, noPos);
}
// Pretty-print single-item lists only if they contain nested
// structures.
auto itemType = item->type();
auto itemType = item.type();
return itemType == nList || itemType == nAttrs;
}
@@ -378,7 +378,7 @@ private:
auto listItems = v.listItems();
auto prettyPrint = shouldPrettyPrintList(listItems);
size_t printedHere = 0;
for (auto elem : listItems) {
for (auto & elem : listItems) {
printSpace(prettyPrint);
if (listItemsPrinted >= options.maxListItems) {
@@ -386,11 +386,7 @@ private:
break;
}
if (elem) {
print(*elem, depth + 1);
} else {
printNullptr();
}
print(elem, depth + 1);
listItemsPrinted++;
printedHere++;
}
@@ -427,7 +423,7 @@ private:
output << "primop";
} else if (v.isPrimOpApp()) {
output << "partially applied ";
auto primOp = v.app().target()->primOp();
auto primOp = v.app().target().primOp();
if (primOp)
output << *primOp;
else
@@ -495,11 +491,22 @@ private:
checkInterrupt();
try {
if (v.isInvalid()) {
if (options.ansiColors) {
output << ANSI_MAGENTA;
}
output << "«invalid»";
if (options.ansiColors) {
output << ANSI_NORMAL;
}
return;
}
if (options.force) {
state.forceValue(v, noPos);
}
switch (v.type()) {
switch (v.type(true)) {
case nInt:
printInt(v);
+2 -2
View File
@@ -82,9 +82,9 @@ public:
return contents;
}
const Value * toValuePtr() const
Value toValue() const
{
return &underlyingValue;
return underlyingValue;
}
friend std::ostream & operator<<(std::ostream & os, const InternedSymbol & symbol);
+3 -3
View File
@@ -61,7 +61,7 @@ JSON printValueAsJSON(EvalState & state, bool strict,
const Attr & a(*v.attrs()->get(state.ctx.symbols.create(j)));
try {
out[j] =
printValueAsJSON(state, strict, *a.value, a.pos, context, copyToStore);
printValueAsJSON(state, strict, a.value, a.pos, context, copyToStore);
} catch (Error & e) {
e.addTrace(
state.ctx.positions[a.pos],
@@ -71,7 +71,7 @@ JSON printValueAsJSON(EvalState & state, bool strict,
}
}
} else {
return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore);
return printValueAsJSON(state, strict, i->value, i->pos, context, copyToStore);
}
break;
}
@@ -81,7 +81,7 @@ JSON printValueAsJSON(EvalState & state, bool strict,
int i = 0;
for (auto elem : v.listItems()) {
try {
out.push_back(printValueAsJSON(state, strict, *elem, pos, context, copyToStore));
out.push_back(printValueAsJSON(state, strict, elem, pos, context, copyToStore));
} catch (Error & e) {
e.addTrace(state.ctx.positions[pos],
HintFmt("while evaluating list element at index %1%", i));
+9 -9
View File
@@ -43,7 +43,7 @@ static void showAttrs(EvalState & state, bool strict, bool location,
if (location && a.pos) posToXML(state, xmlAttrs, state.ctx.positions[a.pos]);
XMLOpenElement _(doc, "attr", xmlAttrs);
printValueAsXML(state, strict, location, *a.value, doc, context, drvsSeen, a.pos);
printValueAsXML(state, strict, location, a.value, doc, context, drvsSeen, a.pos);
}
}
@@ -90,20 +90,20 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
a = v.attrs()->get(state.ctx.s.drvPath);
if (a) {
if (strict) {
state.forceValue(*a->value, a->pos);
state.forceValue(a->value, a->pos);
}
if (a->value->type() == nString) {
xmlAttrs["drvPath"] = drvPath = a->value->str();
if (a->value.type() == nString) {
xmlAttrs["drvPath"] = drvPath = a->value.str();
}
}
a = v.attrs()->get(state.ctx.s.outPath);
if (a) {
if (strict) {
state.forceValue(*a->value, a->pos);
state.forceValue(a->value, a->pos);
}
if (a->value->type() == nString) {
xmlAttrs["outPath"] = a->value->str();
if (a->value.type() == nString) {
xmlAttrs["outPath"] = a->value.str();
}
}
@@ -124,8 +124,8 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
case nList: {
XMLOpenElement _(doc, "list");
for (auto v2 : v.listItems()) {
printValueAsXML(state, strict, location, *v2, doc, context, drvsSeen, pos);
for (auto & v2 : v.listItems()) {
printValueAsXML(state, strict, location, v2, doc, context, drvsSeen, pos);
}
break;
}
+101 -117
View File
@@ -75,13 +75,15 @@ struct PrimOpDetails
// NOTE value.cc contains alignment assertions for pointers tagged thusly.
// *always* ensure that these assertions match the tag types declared here
typedef enum {
// NOTE: tThunk *must* be 0, otherwise invalid value detection breaks
// since invalid values are encoded as thunks with a null thunk state
tThunk = 0,
tApp,
tInt,
tBool,
tString,
tAttrs,
tList,
tThunk,
tApp,
tAuxiliary,
} InternalType;
@@ -483,29 +485,6 @@ public:
/// allocating memory.
Value(list_t, const List * items) : raw(tag(tList, items)) {}
/// Constructs a nix language value of type "list", with an element array
/// initialized by applying @ref transformer to each element in @ref items.
///
/// This allows "in-place" construction of a nix list when some logic is
/// needed to get each Value pointer. This constructor dynamically (GC)
/// allocates memory for the size of @ref items, and the Value pointers
/// returned by @ref transformer are shallow copied into it.
template<
std::ranges::sized_range SizedIterableT,
InvocableR<Value *, typename SizedIterableT::value_type const &> TransformerT
>
Value(list_t, SizedIterableT & items, TransformerT const & transformer)
{
auto list =
reinterpret_cast<List *>(gcAllocBytes(sizeof(List) + items.size() * sizeof(Value *)));
list->size = items.size();
auto it = items.begin();
for (size_t i = 0; i < items.size(); i++, it++) {
list->elems[i] = transformer(*it);
}
raw = tag(tList, list);
}
/// Constructs a nix language value of the singleton type "null".
Value(null_t) : raw(tag(tAuxiliary, &NULL_ACB)) {}
@@ -533,7 +512,7 @@ public:
/// Constructs a nix language value of type "lambda", which represents a
/// lazy and/or partial application of a function.
Value(app_t, EvalMemory & mem, Value & lhs, std::span<Value *> args);
Value(app_t, EvalMemory & mem, Value & lhs, std::span<Value> args);
/// Constructs a nix language value of type "external", which is only used
/// by plugins. Do any existing plugins even use this mechanism?
@@ -597,9 +576,10 @@ public:
{
return internalType() == tApp;
}
inline bool isBlackhole() const
inline bool isBlackhole() const;
inline bool isInvalid() const
{
return internalType() == tThunk && untag<const Thunk *>() == &blackHole;
return raw == 0;
}
// type() == nFunction
@@ -611,21 +591,7 @@ public:
{
return internalType() == tAuxiliary && auxiliary()->type() == Acb::tPrimOp;
}
inline bool isPrimOpApp() const
{
return internalType() == tApp && !app().resolved() && app().target()->isPrimOp();
}
struct alignas(TAG_ALIGN) List
{
size_t size;
Value * elems[0];
std::span<Value *> span()
{
return {elems, elems + size};
}
};
inline bool isPrimOpApp() const;
/**
* Strings in the evaluator carry a so-called `context` which
@@ -663,50 +629,7 @@ public:
}
};
struct alignas(TAG_ALIGN) App
{
uintptr_t _left;
size_t _n;
Value * _args[0];
bool resolved() const
{
return _n == 0;
}
void resolve(Value v)
{
_left = v.raw;
_n = 0;
}
Value * left() const
{
return reinterpret_cast<Value *>(_left);
}
Value result() const
{
Value v;
v.raw = _left;
return v;
}
Value * target() const
{
return left()->isApp() ? left()->app().target() : left();
}
std::span<Value *> args()
{
return std::span{_args, _n};
}
size_t totalArgs() const
{
return _n + (left()->isApp() ? left()->app().totalArgs() : 0);
}
};
struct App;
/// auxiliary control block for values that require more space.
/// these blocks are usually heap-allocated in GC memory space.
@@ -861,15 +784,9 @@ public:
return internalType() == tList;
}
Value * const * listElems() const
{
return untag<const List *>()->elems;
}
Value * listElems() const;
size_t listSize() const
{
return untag<const List *>()->size;
}
size_t listSize() const;
/**
* Check whether forcing this value requires a trivial amount of
@@ -878,11 +795,11 @@ public:
*/
bool isTrivial() const;
auto listItems()
auto listItems() const
{
struct ListIterable
{
typedef Value * const * iterator;
typedef Value * iterator;
iterator _begin, _end;
iterator begin() const { return _begin; }
iterator end() const { return _end; }
@@ -892,20 +809,6 @@ public:
return ListIterable { begin, begin + listSize() };
}
auto listItems() const
{
struct ConstListIterable
{
typedef const Value * const * iterator;
iterator _begin, _end;
iterator begin() const { return _begin; }
iterator end() const { return _end; }
};
assert(isList());
auto begin = listElems();
return ConstListIterable { begin, begin + listSize() };
}
SourcePath path() const
{
assert(internalType() == tString && untag<const String *>()->isPath());
@@ -982,6 +885,11 @@ public:
{
return untag<const Acb *>();
}
uintptr_t pointerEqProxy() const
{
return raw;
}
};
struct alignas(Value::TAG_ALIGN) Value::Thunk
@@ -1014,6 +922,60 @@ struct alignas(Value::TAG_ALIGN) Value::Thunk
}
};
struct alignas(Value::TAG_ALIGN) Value::List
{
size_t size;
Value elems[0];
std::span<Value> span()
{
return {elems, elems + size};
}
};
struct alignas(Value::TAG_ALIGN) Value::App
{
Value _left;
size_t _n;
Value _args[0];
bool resolved() const
{
return _n == ~size_t(0);
}
void resolve(Value v)
{
_left = v;
_n = ~size_t(0);
}
Value left() const
{
return _left;
}
Value result() const
{
return left();
}
Value target() const
{
return left().isApp() ? left().app().target() : left();
}
std::span<Value> args()
{
return std::span{_args, _n};
}
size_t totalArgs() const
{
return _n + (left().isApp() ? left().app().totalArgs() : 0);
}
};
inline ValueType Value::type(bool invalidIsThunk) const
{
again:
@@ -1043,7 +1005,13 @@ again:
return nInt;
}
case tThunk:
if (thunk().resolved()) {
if (isInvalid()) {
if (invalidIsThunk) {
return nThunk;
} else {
abort();
}
} else if (thunk().resolved()) {
raw = thunk().result().raw;
goto again;
}
@@ -1053,12 +1021,28 @@ again:
raw = app().result().raw;
goto again;
}
return app().target()->isPrimOp() ? nFunction : nThunk;
return app().target().isPrimOp() ? nFunction : nThunk;
}
if (invalidIsThunk)
return nThunk;
else
abort();
}
inline bool Value::isBlackhole() const
{
return internalType() == tThunk && untag<const Thunk *>()->expr == blackHole.expr;
}
inline bool Value::isPrimOpApp() const
{
return internalType() == tApp && !app().resolved() && app().target().isPrimOp();
}
inline Value * Value::listElems() const
{
return untag<List *>()->elems;
}
inline size_t Value::listSize() const
{
return untag<const List *>()->size;
}
using PrimOp = Value::PrimOp;
+3 -3
View File
@@ -109,13 +109,13 @@ struct CmdBundle : InstallableCommand
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
NixStringContext context2;
auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2, "");
auto drvPath = evalState->coerceToStorePath(attr1->pos, attr1->value, context2, "");
auto attr2 = vRes.attrs()->get(evaluator->s.outPath);
if (!attr2)
throw Error("the bundler '%s' does not produce a derivation", bundler.what());
auto outPath = evalState->coerceToStorePath(attr2->pos, *attr2->value, context2, "");
auto outPath = evalState->coerceToStorePath(attr2->pos, attr2->value, context2, "");
aio().blockOn(store->buildPaths({
DerivedPath::Built {
@@ -128,7 +128,7 @@ struct CmdBundle : InstallableCommand
auto * attr = vRes.attrs()->get(evaluator->s.name);
if (!attr)
throw Error("attribute 'name' missing");
outLink = evalState->forceStringNoCtx(*attr->value, attr->pos, "");
outLink = evalState->forceStringNoCtx(attr->value, attr->pos, "");
}
// TODO: will crash if not a localFSStore?
+32 -33
View File
@@ -185,20 +185,20 @@ static void enumerateOutputs(
auto aOutputs = vFlake.attrs()->get(state.ctx.symbols.create("outputs"));
assert(aOutputs);
state.forceAttrs(*aOutputs->value, noPos, "while evaluating the outputs of a flake");
state.forceAttrs(aOutputs->value, noPos, "while evaluating the outputs of a flake");
auto sHydraJobs = state.ctx.symbols.create("hydraJobs");
/* Hack: ensure that hydraJobs is evaluated before anything
else. This way we can disable IFD for hydraJobs and then enable
it for other outputs. */
if (auto attr = aOutputs->value->attrs()->get(sHydraJobs)) {
callback(state.ctx.symbols[attr->name], *attr->value, attr->pos);
if (auto attr = aOutputs->value.attrs()->get(sHydraJobs)) {
callback(state.ctx.symbols[attr->name], attr->value, attr->pos);
}
for (auto & attr : *aOutputs->value->attrs()) {
for (auto & attr : *aOutputs->value.attrs()) {
if (attr.name != sHydraJobs) {
callback(state.ctx.symbols[attr.name], *attr.value, attr.pos);
callback(state.ctx.symbols[attr.name], attr.value, attr.pos);
}
}
}
@@ -502,14 +502,14 @@ struct CmdFlakeCheck : FlakeCommand
throw Error("jobset should not be a derivation at top-level");
for (auto & attr : *v.attrs()) {
state->forceAttrs(*attr.value, attr.pos, "");
state->forceAttrs(attr.value, attr.pos, "");
auto attrPath2 = concatStrings(attrPath, ".", evaluator->symbols[attr.name]);
if (state->isDerivation(*attr.value)) {
if (state->isDerivation(attr.value)) {
Activity act(*logger, lvlInfo, actUnknown,
fmt("checking Hydra job '%s'", attrPath2));
checkDerivation(attrPath2, *attr.value, attr.pos);
checkDerivation(attrPath2, attr.value, attr.pos);
} else {
checkHydraJobs(attrPath2, *attr.value, attr.pos);
checkHydraJobs(attrPath2, attr.value, attr.pos);
}
}
@@ -546,7 +546,7 @@ struct CmdFlakeCheck : FlakeCommand
if (attr->name == evaluator->symbols.create("path")) {
NixStringContext context;
auto path = state->ctx.paths.checkSourcePath(
state->coerceToPath(attr->pos, *attr->value, context, "")
state->coerceToPath(attr->pos, attr->value, context, "")
);
if (!path.pathExists())
throw Error("template '%s' refers to a non-existent path '%s'", attrPath, path);
@@ -556,7 +556,7 @@ struct CmdFlakeCheck : FlakeCommand
throw Error("template '%s' lacks attribute 'path'", attrPath);
if (auto attr = v.attrs()->get(evaluator->symbols.create("description")))
state->forceStringNoCtx(*attr->value, attr->pos, "");
state->forceStringNoCtx(attr->value, attr->pos, "");
else
throw Error("template '%s' lacks attribute 'description'", attrPath);
@@ -625,18 +625,17 @@ struct CmdFlakeCheck : FlakeCommand
const auto & attr_name = evaluator->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) {
state->forceAttrs(*attr.value, attr.pos, "");
for (auto & attr2 : *attr.value->attrs()) {
state->forceAttrs(attr.value, attr.pos, "");
for (auto & attr2 : *attr.value.attrs()) {
auto drvPath = checkDerivation(
fmt("%s.%s.%s",
name,
attr_name,
evaluator->symbols[attr2.name]),
*attr2.value,
attr2.value,
attr2.pos
);
if (drvPath && attr_name == evalSettings.getCurrentSystem())
{
if (drvPath && attr_name == evalSettings.getCurrentSystem()) {
drvPaths.push_back(DerivedPath::Built {
.drvPath = makeConstantStorePath(*drvPath),
.outputs = OutputsSpec::All { },
@@ -654,7 +653,7 @@ struct CmdFlakeCheck : FlakeCommand
const auto & attr_name = evaluator->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) {
checkApp(fmt("%s.%s", name, attr_name), *attr.value, attr.pos);
checkApp(fmt("%s.%s", name, attr_name), attr.value, attr.pos);
};
}
}
@@ -666,14 +665,14 @@ struct CmdFlakeCheck : FlakeCommand
const auto & attr_name = evaluator->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) {
state->forceAttrs(*attr.value, attr.pos, "");
for (auto & attr2 : *attr.value->attrs()) {
state->forceAttrs(attr.value, attr.pos, "");
for (auto & attr2 : *attr.value.attrs()) {
checkDerivation(
fmt("%s.%s.%s",
name,
attr_name,
evaluator->symbols[attr2.name]),
*attr2.value,
attr2.value,
attr2.pos
);
}
@@ -688,14 +687,14 @@ struct CmdFlakeCheck : FlakeCommand
const auto & attr_name = evaluator->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) {
state->forceAttrs(*attr.value, attr.pos, "");
for (auto & attr2 : *attr.value->attrs()) {
state->forceAttrs(attr.value, attr.pos, "");
for (auto & attr2 : *attr.value.attrs()) {
checkApp(
fmt("%s.%s.%s",
name,
attr_name,
evaluator->symbols[attr2.name]),
*attr2.value,
attr2.value,
attr2.pos
);
}
@@ -711,7 +710,7 @@ struct CmdFlakeCheck : FlakeCommand
checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) {
checkDerivation(
fmt("%s.%s", name, attr_name), *attr.value, attr.pos
fmt("%s.%s", name, attr_name), attr.value, attr.pos
);
};
}
@@ -724,7 +723,7 @@ struct CmdFlakeCheck : FlakeCommand
const auto & attr_name = evaluator->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos) ) {
checkApp(fmt("%s.%s", name, attr_name), *attr.value, attr.pos);
checkApp(fmt("%s.%s", name, attr_name), attr.value, attr.pos);
};
}
}
@@ -750,7 +749,7 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs())
checkOverlay(
fmt("%s.%s", name, evaluator->symbols[attr.name]),
*attr.value,
attr.value,
attr.pos
);
}
@@ -766,7 +765,7 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs())
checkModule(
fmt("%s.%s", name, evaluator->symbols[attr.name]),
*attr.value,
attr.value,
attr.pos
);
}
@@ -777,7 +776,7 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs())
checkNixOSConfiguration(
fmt("%s.%s", name, evaluator->symbols[attr.name]),
*attr.value,
attr.value,
attr.pos
);
}
@@ -798,7 +797,7 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs())
checkTemplate(
fmt("%s.%s", name, evaluator->symbols[attr.name]),
*attr.value,
attr.value,
attr.pos
);
}
@@ -811,7 +810,7 @@ struct CmdFlakeCheck : FlakeCommand
checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) {
checkBundler(
fmt("%s.%s", name, attr_name), *attr.value, attr.pos
fmt("%s.%s", name, attr_name), attr.value, attr.pos
);
};
}
@@ -824,14 +823,14 @@ struct CmdFlakeCheck : FlakeCommand
const auto & attr_name = evaluator->symbols[attr.name];
checkSystemName(attr_name, attr.pos);
if (checkSystemType(attr_name, attr.pos)) {
state->forceAttrs(*attr.value, attr.pos, "");
for (auto & attr2 : *attr.value->attrs()) {
state->forceAttrs(attr.value, attr.pos, "");
for (auto & attr2 : *attr.value.attrs()) {
checkBundler(
fmt("%s.%s.%s",
name,
attr_name,
evaluator->symbols[attr2.name]),
*attr2.value,
attr2.value,
attr2.pos
);
}
+4 -4
View File
@@ -378,7 +378,7 @@ static void showHelp(AsyncIoRoot & aio, std::vector<std::string> subcommand, Nix
throw UsageError("`nix` has no subcommand '%s'", concatStringsSep("", subcommand));
auto markdown =
state->forceString(*attr->value, noPos, "while evaluating the lowdown help text");
state->forceString(attr->value, noPos, "while evaluating the lowdown help text");
RunPager pager;
std::cout << renderMarkdownToTerminal(markdown) << "\n";
@@ -528,13 +528,13 @@ void mainWrapped(AsyncIoRoot & aio, int argc, char * * argv)
auto res = JSON::object();
res["builtins"] = ({
auto builtinsJson = JSON::object();
auto builtins = state.builtins.env.values[0]->attrs();
auto builtins = state.builtins.env.values[0].attrs();
for (auto & builtin : *builtins) {
auto b = JSON::object();
if (!builtin.value->isPrimOp()) {
if (!builtin.value.isPrimOp()) {
continue;
}
auto primOp = builtin.value->primOp();
auto primOp = builtin.value.primOp();
if (!primOp->doc) {
continue;
}
+8 -8
View File
@@ -39,14 +39,14 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url)
if (!mirrorList) {
throw Error("unknown mirror name '%s'", mirrorName);
}
state.forceList(*mirrorList->value, noPos, "while evaluating one mirror configuration");
state.forceList(mirrorList->value, noPos, "while evaluating one mirror configuration");
if (mirrorList->value->listSize() < 1) {
if (mirrorList->value.listSize() < 1) {
throw Error("mirror URL '%s' did not expand to anything", url);
}
std::string mirror(state.forceString(
*mirrorList->value->listElems()[0], noPos, "while evaluating the first available mirror"
mirrorList->value.listElems()[0], noPos, "while evaluating the first available mirror"
));
return mirror + (mirror.ends_with("/") ? "" : "/") + s.substr(p + 1);
}
@@ -217,12 +217,12 @@ static int main_nix_prefetch_url(AsyncIoRoot & aio, std::string programName, Str
auto * attr = v.attrs()->get(evaluator->symbols.create("urls"));
if (!attr)
throw Error("attribute 'urls' missing");
state->forceList(*attr->value, noPos, "while evaluating the urls to prefetch");
if (attr->value->listSize() < 1) {
state->forceList(attr->value, noPos, "while evaluating the urls to prefetch");
if (attr->value.listSize() < 1) {
throw Error("'urls' list is empty");
}
url = state->forceString(
*attr->value->listElems()[0],
attr->value.listElems()[0],
noPos,
"while evaluating the first url from the urls list"
);
@@ -233,7 +233,7 @@ static int main_nix_prefetch_url(AsyncIoRoot & aio, std::string programName, Str
printInfo("warning: this does not look like a fetchurl call");
else
unpack = state->forceString(
*attr2->value,
attr2->value,
noPos,
"while evaluating the outputHashMode of the source to prefetch"
)
@@ -244,7 +244,7 @@ static int main_nix_prefetch_url(AsyncIoRoot & aio, std::string programName, Str
auto attr3 = v.attrs()->get(evaluator->symbols.create("name"));
if (!attr3)
name = state->forceString(
*attr3->value, noPos, "while evaluating the name of the source to prefetch"
attr3->value, noPos, "while evaluating the name of the source to prefetch"
);
}
}
+4 -4
View File
@@ -68,10 +68,10 @@ struct CmdRepl : RawInstallablesCommand
auto what = installable.what();
state->forceValue(val, pos);
auto autoArgs = getAutoArgs(*evaluator);
auto valPost = evaluator->mem.allocValue();
state->autoCallFunction(*autoArgs, val, *valPost, pos);
state->forceValue(*valPost, pos);
values.push_back({*valPost, what});
Value valPost;
state->autoCallFunction(*autoArgs, val, valPost, pos);
state->forceValue(valPost, pos);
values.push_back( {valPost, what });
} else {
auto [val, pos] = installable.toValue(*state);
values.push_back({val, what});
+22 -22
View File
@@ -41,9 +41,9 @@
#include "buffered-io.hh"
#include "eval-args.hh"
static nix::Value *releaseExprTopLevelValue(nix::EvalState &state,
nix::Bindings &autoArgs,
MyArgs &args) {
static nix::Value releaseExprTopLevelValue(nix::EvalState &state,
nix::Bindings &autoArgs,
MyArgs &args) {
nix::Value vTop;
if (args.fromArgs) {
@@ -57,9 +57,9 @@ static nix::Value *releaseExprTopLevelValue(nix::EvalState &state,
vTop);
}
auto vRoot = state.ctx.mem.allocValue();
nix::Value vRoot;
state.autoCallFunction(autoArgs, vTop, *vRoot, {});
state.autoCallFunction(autoArgs, vTop, vRoot, {});
return vRoot;
}
@@ -79,7 +79,7 @@ static std::optional<Constituents>
readConstituents(const nix::Value *v, nix::box_ptr<nix::EvalState> &state,
nix::ref<nix::eval_cache::CachingEvaluator> &evaluator) {
auto a = v->attrs()->get(state->ctx.symbols.create("_hydraAggregate"));
if (a && state->forceBool(*a->value, a->pos,
if (a && state->forceBool(a->value, a->pos,
"while evaluating the "
"`_hydraAggregate` attribute")) {
std::vector<std::string> constituents;
@@ -92,7 +92,7 @@ readConstituents(const nix::Value *v, nix::box_ptr<nix::EvalState> &state,
.debugThrow(nix::always_progresses); // we can't have a debugger here
nix::NixStringContext context;
state->coerceToString(a->pos, *a->value, context,
state->coerceToString(a->pos, a->value, context,
"while evaluating the `constituents` attribute",
nix::StringCoercionMode::ToString, false);
for (auto &c : context)
@@ -106,14 +106,14 @@ readConstituents(const nix::Value *v, nix::box_ptr<nix::EvalState> &state,
},
c.raw);
state->forceList(*a->value, a->pos,
state->forceList(a->value, a->pos,
"while evaluating the "
"`constituents` attribute");
for (unsigned int n = 0; n < a->value->listSize(); ++n) {
auto v = a->value->listElems()[n];
state->forceValue(*v, nix::noPos);
if (v->type() == nix::nString)
namedConstituents.emplace_back(v->str());
for (unsigned int n = 0; n < a->value.listSize(); ++n) {
auto v = a->value.listElems()[n];
state->forceValue(v, nix::noPos);
if (v.type() == nix::nString)
namedConstituents.emplace_back(v.str());
}
return Constituents(constituents, namedConstituents);
@@ -138,7 +138,7 @@ void worker(nix::ref<nix::eval_cache::CachingEvaluator> evaluator,
return flake.toValue(*state).first;
} else {
return *releaseExprTopLevelValue(*state, autoArgs, args);
return releaseExprTopLevelValue(*state, autoArgs, args);
}
}();
@@ -171,15 +171,15 @@ void worker(nix::ref<nix::eval_cache::CachingEvaluator> evaluator,
nix::findAlongAttrPath(*state, attrPathS, autoArgs, vRoot)
.first;
auto v = evaluator->mem.allocValue();
state->autoCallFunction(autoArgs, vTmp, *v, {});
nix::Value v;
state->autoCallFunction(autoArgs, vTmp, v, {});
if (v->type() == nix::nAttrs) {
if (auto drvInfo = nix::getDerivation(*state, *v, false)) {
if (v.type() == nix::nAttrs) {
if (auto drvInfo = nix::getDerivation(*state, v, false)) {
std::optional<Constituents> maybeConstituents;
if (args.constituents) {
maybeConstituents =
readConstituents(v, state, evaluator);
readConstituents(&v, state, evaluator);
}
auto drv = Drv(attrPathS, *state, *drvInfo, args,
maybeConstituents);
@@ -197,16 +197,16 @@ void worker(nix::ref<nix::eval_cache::CachingEvaluator> evaluator,
// = true;` for top-level attrset
for (auto &i :
v->attrs()->lexicographicOrder(evaluator->symbols)) {
v.attrs()->lexicographicOrder(evaluator->symbols)) {
const std::string_view name = evaluator->symbols[i->name];
attrs.emplace_back(name);
if (name == "recurseForDerivations" &&
!args.forceRecurse) {
auto attrv = v->attrs()->get(
auto attrv = v.attrs()->get(
evaluator->s.recurseForDerivations);
recurse = state->forceBool(
*attrv->value, attrv->pos,
attrv->value, attrv->pos,
"while evaluating recurseForDerivations");
}
}
+74 -74
View File
@@ -73,7 +73,7 @@ namespace nix {
auto s = createSymbol("success");
auto p = v.attrs()->get(s);
ASSERT_NE(p, nullptr);
ASSERT_THAT(*p->value, IsFalse());
ASSERT_THAT(p->value, IsFalse());
}
TEST_F(PrimOpTest, tryEvalSuccess) {
@@ -82,11 +82,11 @@ namespace nix {
auto s = createSymbol("success");
auto p = v.attrs()->get(s);
ASSERT_NE(p, nullptr);
ASSERT_THAT(*p->value, IsTrue());
ASSERT_THAT(p->value, IsTrue());
s = createSymbol("value");
p = v.attrs()->get(s);
ASSERT_NE(p, nullptr);
ASSERT_THAT(*p->value, IsIntEq(123));
ASSERT_THAT(p->value, IsIntEq(123));
}
TEST_F(PrimOpTest, getEnv) {
@@ -134,8 +134,8 @@ namespace nix {
TEST_F(PrimOpTest, attrValues) {
auto v = eval("builtins.attrValues { x = \"foo\"; a = 1; }");
ASSERT_THAT(v, IsListOfSize(2));
ASSERT_THAT(*v.listElems()[0], IsIntEq(1));
ASSERT_THAT(*v.listElems()[1], IsStringEq("foo"));
ASSERT_THAT(v.listElems()[0], IsIntEq(1));
ASSERT_THAT(v.listElems()[1], IsStringEq("foo"));
}
TEST_F(PrimOpTest, getAttr) {
@@ -203,7 +203,7 @@ namespace nix {
ASSERT_THAT(v, IsAttrsOfSize(1));
auto key = v.attrs()->get(createSymbol("key"));
ASSERT_NE(key, nullptr);
ASSERT_THAT(*key->value, IsIntEq(123));
ASSERT_THAT(key->value, IsIntEq(123));
}
TEST_F(PrimOpTest, intersectAttrs) {
@@ -211,14 +211,14 @@ namespace nix {
ASSERT_THAT(v, IsAttrsOfSize(1));
auto b = v.attrs()->get(createSymbol("b"));
ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsIntEq(3));
ASSERT_THAT(b->value, IsIntEq(3));
}
TEST_F(PrimOpTest, catAttrs) {
auto v = eval("builtins.catAttrs \"a\" [{a = 1;} {b = 0;} {a = 2;}]");
ASSERT_THAT(v, IsListOfSize(2));
ASSERT_THAT(*v.listElems()[0], IsIntEq(1));
ASSERT_THAT(*v.listElems()[1], IsIntEq(2));
ASSERT_THAT(v.listElems()[0], IsIntEq(1));
ASSERT_THAT(v.listElems()[1], IsIntEq(2));
}
TEST_F(PrimOpTest, functionArgs) {
@@ -227,11 +227,11 @@ namespace nix {
auto x = v.attrs()->get(createSymbol("x"));
ASSERT_NE(x, nullptr);
ASSERT_THAT(*x->value, IsFalse());
ASSERT_THAT(x->value, IsFalse());
auto y = v.attrs()->get(createSymbol("y"));
ASSERT_NE(y, nullptr);
ASSERT_THAT(*y->value, IsTrue());
ASSERT_THAT(y->value, IsTrue());
}
TEST_F(PrimOpTest, mapAttrs) {
@@ -240,15 +240,15 @@ namespace nix {
auto a = v.attrs()->get(createSymbol("a"));
ASSERT_NE(a, nullptr);
ASSERT_THAT(*a->value, IsThunk());
state.forceValue(*a->value, noPos);
ASSERT_THAT(*a->value, IsIntEq(10));
ASSERT_THAT(a->value, IsThunk());
state.forceValue(a->value, noPos);
ASSERT_THAT(a->value, IsIntEq(10));
auto b = v.attrs()->get(createSymbol("b"));
ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsThunk());
state.forceValue(*b->value, noPos);
ASSERT_THAT(*b->value, IsIntEq(20));
ASSERT_THAT(b->value, IsThunk());
state.forceValue(b->value, noPos);
ASSERT_THAT(b->value, IsIntEq(20));
}
TEST_F(PrimOpTest, isList) {
@@ -288,7 +288,7 @@ namespace nix {
auto v = eval("builtins.tail [ 3 2 1 0 ]");
ASSERT_THAT(v, IsListOfSize(3));
for (const auto [n, elem] : enumerate(v.listItems()))
ASSERT_THAT(*elem, IsIntEq(2 - static_cast<int>(n)));
ASSERT_THAT(elem, IsIntEq(2 - static_cast<int>(n)));
}
TEST_F(PrimOpTest, tailEmpty) {
@@ -299,26 +299,26 @@ namespace nix {
auto v = eval("map (x: \"foo\" + x) [ \"bar\" \"bla\" \"abc\" ]");
ASSERT_THAT(v, IsListOfSize(3));
auto elem = v.listElems()[0];
ASSERT_THAT(*elem, IsThunk());
state.forceValue(*elem, noPos);
ASSERT_THAT(*elem, IsStringEq("foobar"));
ASSERT_THAT(elem, IsThunk());
state.forceValue(elem, noPos);
ASSERT_THAT(elem, IsStringEq("foobar"));
elem = v.listElems()[1];
ASSERT_THAT(*elem, IsThunk());
state.forceValue(*elem, noPos);
ASSERT_THAT(*elem, IsStringEq("foobla"));
ASSERT_THAT(elem, IsThunk());
state.forceValue(elem, noPos);
ASSERT_THAT(elem, IsStringEq("foobla"));
elem = v.listElems()[2];
ASSERT_THAT(*elem, IsThunk());
state.forceValue(*elem, noPos);
ASSERT_THAT(*elem, IsStringEq("fooabc"));
ASSERT_THAT(elem, IsThunk());
state.forceValue(elem, noPos);
ASSERT_THAT(elem, IsStringEq("fooabc"));
}
TEST_F(PrimOpTest, filter) {
auto v = eval("builtins.filter (x: x == 2) [ 3 2 3 2 3 2 ]");
ASSERT_THAT(v, IsListOfSize(3));
for (const auto elem : v.listItems())
ASSERT_THAT(*elem, IsIntEq(2));
ASSERT_THAT(elem, IsIntEq(2));
}
TEST_F(PrimOpTest, elemTrue) {
@@ -335,7 +335,7 @@ namespace nix {
auto v = eval("builtins.concatLists [[1 2] [3 4]]");
ASSERT_THAT(v, IsListOfSize(4));
for (const auto [i, elem] : enumerate(v.listItems()))
ASSERT_THAT(*elem, IsIntEq(static_cast<int>(i)+1));
ASSERT_THAT(elem, IsIntEq(static_cast<int>(i) + 1));
}
TEST_F(PrimOpTest, length) {
@@ -373,9 +373,9 @@ namespace nix {
ASSERT_EQ(v.type(), nList);
ASSERT_EQ(v.listSize(), 3);
for (const auto [i, elem] : enumerate(v.listItems())) {
ASSERT_THAT(*elem, IsThunk());
state.forceValue(*elem, noPos);
ASSERT_THAT(*elem, IsIntEq(static_cast<int>(i)+1));
ASSERT_THAT(elem, IsThunk());
state.forceValue(elem, noPos);
ASSERT_THAT(elem, IsIntEq(static_cast<int>(i) + 1));
}
}
@@ -386,7 +386,7 @@ namespace nix {
const std::vector<int> numbers = { 42, 77, 147, 249, 483, 526 };
for (const auto [n, elem] : enumerate(v.listItems()))
ASSERT_THAT(*elem, IsIntEq(numbers[n]));
ASSERT_THAT(elem, IsIntEq(numbers[n]));
}
TEST_F(PrimOpTest, partition) {
@@ -395,18 +395,18 @@ namespace nix {
auto right = v.attrs()->get(createSymbol("right"));
ASSERT_NE(right, nullptr);
ASSERT_THAT(*right->value, IsListOfSize(2));
ASSERT_THAT(*right->value->listElems()[0], IsIntEq(23));
ASSERT_THAT(*right->value->listElems()[1], IsIntEq(42));
ASSERT_THAT(right->value, IsListOfSize(2));
ASSERT_THAT(right->value.listElems()[0], IsIntEq(23));
ASSERT_THAT(right->value.listElems()[1], IsIntEq(42));
auto wrong = v.attrs()->get(createSymbol("wrong"));
ASSERT_NE(wrong, nullptr);
ASSERT_EQ(wrong->value->type(), nList);
ASSERT_EQ(wrong->value->listSize(), 3);
ASSERT_THAT(*wrong->value, IsListOfSize(3));
ASSERT_THAT(*wrong->value->listElems()[0], IsIntEq(1));
ASSERT_THAT(*wrong->value->listElems()[1], IsIntEq(9));
ASSERT_THAT(*wrong->value->listElems()[2], IsIntEq(3));
ASSERT_EQ(wrong->value.type(), nList);
ASSERT_EQ(wrong->value.listSize(), 3);
ASSERT_THAT(wrong->value, IsListOfSize(3));
ASSERT_THAT(wrong->value.listElems()[0], IsIntEq(1));
ASSERT_THAT(wrong->value.listElems()[1], IsIntEq(9));
ASSERT_THAT(wrong->value.listElems()[2], IsIntEq(3));
}
TEST_F(PrimOpTest, concatMap) {
@@ -416,7 +416,7 @@ namespace nix {
const std::vector<int> numbers = { 1, 2, 0, 3, 4, 0 };
for (const auto [n, elem] : enumerate(v.listItems()))
ASSERT_THAT(*elem, IsIntEq(numbers[n]));
ASSERT_THAT(elem, IsIntEq(numbers[n]));
}
TEST_F(PrimOpTest, addInt) {
@@ -652,7 +652,7 @@ namespace nix {
const std::vector<std::string_view> strings = { "1", "2", "3", "git" };
for (const auto [n, p] : enumerate(v.listItems()))
ASSERT_THAT(*p, IsStringEq(strings[n]));
ASSERT_THAT(p, IsStringEq(strings[n]));
}
class CompareVersionsPrimOpTest :
@@ -706,11 +706,11 @@ namespace nix {
auto name = v.attrs()->get(createSymbol("name"));
ASSERT_TRUE(name);
ASSERT_THAT(*name->value, IsStringEq(expectedName));
ASSERT_THAT(name->value, IsStringEq(expectedName));
auto version = v.attrs()->get(createSymbol("version"));
ASSERT_TRUE(version);
ASSERT_THAT(*version->value, IsStringEq(expectedVersion));
ASSERT_THAT(version->value, IsStringEq(expectedVersion));
}
INSTANTIATE_TEST_SUITE_P(
@@ -741,12 +741,12 @@ namespace nix {
auto v = eval("builtins.split \"(a)b\" \"abc\"");
ASSERT_THAT(v, IsListOfSize(3));
ASSERT_THAT(*v.listElems()[0], IsStringEq(""));
ASSERT_THAT(v.listElems()[0], IsStringEq(""));
ASSERT_THAT(*v.listElems()[1], IsListOfSize(1));
ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a"));
ASSERT_THAT(v.listElems()[1], IsListOfSize(1));
ASSERT_THAT(v.listElems()[1].listElems()[0], IsStringEq("a"));
ASSERT_THAT(*v.listElems()[2], IsStringEq("c"));
ASSERT_THAT(v.listElems()[2], IsStringEq("c"));
}
TEST_F(PrimOpTest, split2) {
@@ -754,17 +754,17 @@ namespace nix {
auto v = eval("builtins.split \"([ac])\" \"abc\"");
ASSERT_THAT(v, IsListOfSize(5));
ASSERT_THAT(*v.listElems()[0], IsStringEq(""));
ASSERT_THAT(v.listElems()[0], IsStringEq(""));
ASSERT_THAT(*v.listElems()[1], IsListOfSize(1));
ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a"));
ASSERT_THAT(v.listElems()[1], IsListOfSize(1));
ASSERT_THAT(v.listElems()[1].listElems()[0], IsStringEq("a"));
ASSERT_THAT(*v.listElems()[2], IsStringEq("b"));
ASSERT_THAT(v.listElems()[2], IsStringEq("b"));
ASSERT_THAT(*v.listElems()[3], IsListOfSize(1));
ASSERT_THAT(*v.listElems()[3]->listElems()[0], IsStringEq("c"));
ASSERT_THAT(v.listElems()[3], IsListOfSize(1));
ASSERT_THAT(v.listElems()[3].listElems()[0], IsStringEq("c"));
ASSERT_THAT(*v.listElems()[4], IsStringEq(""));
ASSERT_THAT(v.listElems()[4], IsStringEq(""));
}
TEST_F(PrimOpTest, split3) {
@@ -772,23 +772,23 @@ namespace nix {
ASSERT_THAT(v, IsListOfSize(5));
// First list element
ASSERT_THAT(*v.listElems()[0], IsStringEq(""));
ASSERT_THAT(v.listElems()[0], IsStringEq(""));
// 2nd list element is a list [ "" null ]
ASSERT_THAT(*v.listElems()[1], IsListOfSize(2));
ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a"));
ASSERT_THAT(*v.listElems()[1]->listElems()[1], IsNull());
ASSERT_THAT(v.listElems()[1], IsListOfSize(2));
ASSERT_THAT(v.listElems()[1].listElems()[0], IsStringEq("a"));
ASSERT_THAT(v.listElems()[1].listElems()[1], IsNull());
// 3rd element
ASSERT_THAT(*v.listElems()[2], IsStringEq("b"));
ASSERT_THAT(v.listElems()[2], IsStringEq("b"));
// 4th element is a list: [ null "c" ]
ASSERT_THAT(*v.listElems()[3], IsListOfSize(2));
ASSERT_THAT(*v.listElems()[3]->listElems()[0], IsNull());
ASSERT_THAT(*v.listElems()[3]->listElems()[1], IsStringEq("c"));
ASSERT_THAT(v.listElems()[3], IsListOfSize(2));
ASSERT_THAT(v.listElems()[3].listElems()[0], IsNull());
ASSERT_THAT(v.listElems()[3].listElems()[1], IsStringEq("c"));
// 5th element is the empty string
ASSERT_THAT(*v.listElems()[4], IsStringEq(""));
ASSERT_THAT(v.listElems()[4], IsStringEq(""));
}
TEST_F(PrimOpTest, split4) {
@@ -798,12 +798,12 @@ namespace nix {
auto second = v.listElems()[1];
auto third = v.listElems()[2];
ASSERT_THAT(*first, IsStringEq(" "));
ASSERT_THAT(first, IsStringEq(" "));
ASSERT_THAT(*second, IsListOfSize(1));
ASSERT_THAT(*second->listElems()[0], IsStringEq("FOO"));
ASSERT_THAT(second, IsListOfSize(1));
ASSERT_THAT(second.listElems()[0], IsStringEq("FOO"));
ASSERT_THAT(*third, IsStringEq(" "));
ASSERT_THAT(third, IsStringEq(" "));
}
TEST_F(PrimOpTest, match1) {
@@ -819,14 +819,14 @@ namespace nix {
TEST_F(PrimOpTest, match3) {
auto v = eval("builtins.match \"a(b)(c)\" \"abc\"");
ASSERT_THAT(v, IsListOfSize(2));
ASSERT_THAT(*v.listElems()[0], IsStringEq("b"));
ASSERT_THAT(*v.listElems()[1], IsStringEq("c"));
ASSERT_THAT(v.listElems()[0], IsStringEq("b"));
ASSERT_THAT(v.listElems()[1], IsStringEq("c"));
}
TEST_F(PrimOpTest, match4) {
auto v = eval("builtins.match \"[[:space:]]+([[:upper:]]+)[[:space:]]+\" \" FOO \"");
ASSERT_THAT(v, IsListOfSize(1));
ASSERT_THAT(*v.listElems()[0], IsStringEq("FOO"));
ASSERT_THAT(v.listElems()[0], IsStringEq("FOO"));
}
TEST_F(PrimOpTest, attrNames) {
@@ -836,7 +836,7 @@ namespace nix {
// ensure that the list is sorted
const std::vector<std::string_view> expected { "a", "x", "y", "z" };
for (const auto [n, elem] : enumerate(v.listItems()))
ASSERT_THAT(*elem, IsStringEq(expected[n]));
ASSERT_THAT(elem, IsStringEq(expected[n]));
}
TEST_F(PrimOpTest, genericClosure_not_strict) {
+10 -10
View File
@@ -69,11 +69,11 @@ namespace nix {
ASSERT_THAT(v, IsAttrsOfSize(2));
auto a = v.attrs()->get(createSymbol("a"));
ASSERT_NE(a, nullptr);
ASSERT_THAT(*a->value, IsIntEq(3));
ASSERT_THAT(a->value, IsIntEq(3));
auto b = v.attrs()->get(createSymbol("b"));
ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsIntEq(2));
ASSERT_THAT(b->value, IsIntEq(2));
}
TEST_F(TrivialExpressionTest, hasAttrOpFalse) {
@@ -171,18 +171,18 @@ namespace nix {
auto a = v.attrs()->get(createSymbol("a"));
ASSERT_NE(a, nullptr);
ASSERT_THAT(*a->value, IsThunk());
state.forceValue(*a->value, noPos);
ASSERT_THAT(a->value, IsThunk());
state.forceValue(a->value, noPos);
ASSERT_THAT(*a->value, IsAttrsOfSize(2));
ASSERT_THAT(a->value, IsAttrsOfSize(2));
auto b = a->value->attrs()->get(createSymbol("b"));
auto b = a->value.attrs()->get(createSymbol("b"));
ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsIntEq(1));
ASSERT_THAT(b->value, IsIntEq(1));
auto c = a->value->attrs()->get(createSymbol("c"));
auto c = a->value.attrs()->get(createSymbol("c"));
ASSERT_NE(c, nullptr);
ASSERT_THAT(*c->value, IsIntEq(2));
ASSERT_THAT(c->value, IsIntEq(2));
}
INSTANTIATE_TEST_SUITE_P(
@@ -204,7 +204,7 @@ namespace nix {
ASSERT_THAT(v, IsAttrsOfSize(1));
auto b = v.attrs()->get(createSymbol("or"));
ASSERT_NE(b, nullptr);
ASSERT_THAT(*b->value, IsIntEq(1));
ASSERT_THAT(b->value, IsIntEq(1));
}
TEST_F(TrivialExpressionTest, orCantBeUsed) {
+47 -47
View File
@@ -66,8 +66,8 @@ TEST_F(ValuePrintingTests, tAttrs)
vTwo.mkInt(2);
BindingsBuilder builder = evaluator.buildBindings(10);
builder.insert(evaluator.symbols.create("one"), &vOne);
builder.insert(evaluator.symbols.create("two"), &vTwo);
builder.insert(evaluator.symbols.create("one"), vOne);
builder.insert(evaluator.symbols.create("two"), vTwo);
Value vAttrs;
vAttrs.mkAttrs(builder.finish());
@@ -84,11 +84,11 @@ TEST_F(ValuePrintingTests, tList)
vTwo.mkInt(2);
auto vList = evaluator.mem.newList(5);
vList->elems[0] = &vOne;
vList->elems[1] = &vTwo;
vList->elems[0] = vOne;
vList->elems[1] = vTwo;
vList->size = 3;
test(Value(NewValueAs::list, vList), "[ 1 2 «nullptr» ]");
test(Value(NewValueAs::list, vList), "[ 1 2 «invalid» ]");
}
TEST_F(ValuePrintingTests, vThunk)
@@ -210,23 +210,23 @@ TEST_F(ValuePrintingTests, depthAttrs)
vAttrsEmpty.mkAttrs(builderEmpty.finish());
BindingsBuilder builderNested = evaluator.buildBindings(1);
builderNested.insert(evaluator.symbols.create("zero"), &vZero);
builderNested.insert(evaluator.symbols.create("zero"), vZero);
Value vAttrsNested;
vAttrsNested.mkAttrs(builderNested.finish());
BindingsBuilder builder = evaluator.buildBindings(10);
builder.insert(evaluator.symbols.create("one"), &vOne);
builder.insert(evaluator.symbols.create("two"), &vTwo);
builder.insert(evaluator.symbols.create("empty"), &vAttrsEmpty);
builder.insert(evaluator.symbols.create("nested"), &vAttrsNested);
builder.insert(evaluator.symbols.create("one"), vOne);
builder.insert(evaluator.symbols.create("two"), vTwo);
builder.insert(evaluator.symbols.create("empty"), vAttrsEmpty);
builder.insert(evaluator.symbols.create("nested"), vAttrsNested);
Value vAttrs;
vAttrs.mkAttrs(builder.finish());
BindingsBuilder builder2 = evaluator.buildBindings(10);
builder2.insert(evaluator.symbols.create("one"), &vOne);
builder2.insert(evaluator.symbols.create("two"), &vTwo);
builder2.insert(evaluator.symbols.create("nested"), &vAttrs);
builder2.insert(evaluator.symbols.create("one"), vOne);
builder2.insert(evaluator.symbols.create("two"), vTwo);
builder2.insert(evaluator.symbols.create("nested"), vAttrs);
Value vNested;
vNested.mkAttrs(builder2.finish());
@@ -246,24 +246,24 @@ TEST_F(ValuePrintingTests, depthList)
vTwo.mkInt(2);
BindingsBuilder builder = evaluator.buildBindings(10);
builder.insert(evaluator.symbols.create("one"), &vOne);
builder.insert(evaluator.symbols.create("two"), &vTwo);
builder.insert(evaluator.symbols.create("one"), vOne);
builder.insert(evaluator.symbols.create("two"), vTwo);
Value vAttrs;
vAttrs.mkAttrs(builder.finish());
BindingsBuilder builder2 = evaluator.buildBindings(10);
builder2.insert(evaluator.symbols.create("one"), &vOne);
builder2.insert(evaluator.symbols.create("two"), &vTwo);
builder2.insert(evaluator.symbols.create("nested"), &vAttrs);
builder2.insert(evaluator.symbols.create("one"), vOne);
builder2.insert(evaluator.symbols.create("two"), vTwo);
builder2.insert(evaluator.symbols.create("nested"), vAttrs);
Value vNested;
vNested.mkAttrs(builder2.finish());
auto list = evaluator.mem.newList(5);
list->elems[0] = &vOne;
list->elems[1] = &vTwo;
list->elems[2] = &vNested;
list->elems[0] = vOne;
list->elems[1] = vTwo;
list->elems[2] = vNested;
list->size = 3;
Value vList{NewValueAs::list, list};
@@ -310,8 +310,8 @@ TEST_F(ValuePrintingTests, attrsTypeFirst)
vApple.mkString("apple");
BindingsBuilder builder = evaluator.buildBindings(10);
builder.insert(evaluator.symbols.create("type"), &vType);
builder.insert(evaluator.symbols.create("apple"), &vApple);
builder.insert(evaluator.symbols.create("type"), vType);
builder.insert(evaluator.symbols.create("apple"), vApple);
Value vAttrs;
vAttrs.mkAttrs(builder.finish());
@@ -421,8 +421,8 @@ TEST_F(ValuePrintingTests, ansiColorsAttrs)
vTwo.mkInt(2);
BindingsBuilder builder = evaluator.buildBindings(10);
builder.insert(evaluator.symbols.create("one"), &vOne);
builder.insert(evaluator.symbols.create("two"), &vTwo);
builder.insert(evaluator.symbols.create("one"), vOne);
builder.insert(evaluator.symbols.create("two"), vTwo);
Value vAttrs;
vAttrs.mkAttrs(builder.finish());
@@ -440,7 +440,7 @@ TEST_F(ValuePrintingTests, ansiColorsDerivation)
vDerivation.mkString("derivation");
BindingsBuilder builder = evaluator.buildBindings(10);
builder.insert(evaluator.s.type, &vDerivation);
builder.insert(evaluator.s.type, vDerivation);
Value vAttrs;
vAttrs.mkAttrs(builder.finish());
@@ -468,7 +468,7 @@ TEST_F(ValuePrintingTests, ansiColorsError)
state.eval(e, vError);
test(
*vError.attrs()->begin()->value,
vError.attrs()->begin()->value,
ANSI_RED "«error: uh oh!»" ANSI_NORMAL,
PrintOptions{
.ansiColors = true,
@@ -519,7 +519,7 @@ TEST_F(ValuePrintingTests, ansiColorsAssert)
ASSERT_EQ(v.type(), nAttrs);
test(
*v.attrs()->begin()->value,
v.attrs()->begin()->value,
ANSI_RED "«error: assertion failed»" ANSI_NORMAL,
PrintOptions{.ansiColors = true, .force = true}
);
@@ -534,14 +534,14 @@ TEST_F(ValuePrintingTests, ansiColorsList)
vTwo.mkInt(2);
auto vList = evaluator.mem.newList(5);
vList->elems[0] = &vOne;
vList->elems[1] = &vTwo;
vList->elems[0] = vOne;
vList->elems[1] = vTwo;
vList->size = 3;
test(
Value(NewValueAs::list, vList),
"[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_CYAN "2" ANSI_NORMAL " " ANSI_MAGENTA
"«nullptr»" ANSI_NORMAL " ]",
"«invalid»" ANSI_NORMAL " ]",
PrintOptions{.ansiColors = true}
);
}
@@ -640,14 +640,14 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsRepeated)
vZero.mkInt(0);
BindingsBuilder innerBuilder = evaluator.buildBindings(1);
innerBuilder.insert(evaluator.symbols.create("x"), &vZero);
innerBuilder.insert(evaluator.symbols.create("x"), vZero);
Value vInner;
vInner.mkAttrs(innerBuilder.finish());
BindingsBuilder builder = evaluator.buildBindings(10);
builder.insert(evaluator.symbols.create("a"), &vInner);
builder.insert(evaluator.symbols.create("b"), &vInner);
builder.insert(evaluator.symbols.create("a"), vInner);
builder.insert(evaluator.symbols.create("b"), vInner);
Value vAttrs;
vAttrs.mkAttrs(builder.finish());
@@ -665,14 +665,14 @@ TEST_F(ValuePrintingTests, ansiColorsListRepeated)
vZero.mkInt(0);
BindingsBuilder innerBuilder = evaluator.buildBindings(1);
innerBuilder.insert(evaluator.symbols.create("x"), &vZero);
innerBuilder.insert(evaluator.symbols.create("x"), vZero);
Value vInner;
vInner.mkAttrs(innerBuilder.finish());
auto vList = evaluator.mem.newList(3);
vList->elems[0] = &vInner;
vList->elems[1] = &vInner;
vList->elems[0] = vInner;
vList->elems[1] = vInner;
vList->size = 2;
test(
@@ -688,14 +688,14 @@ TEST_F(ValuePrintingTests, listRepeated)
vZero.mkInt(0);
BindingsBuilder innerBuilder = evaluator.buildBindings(1);
innerBuilder.insert(evaluator.symbols.create("x"), &vZero);
innerBuilder.insert(evaluator.symbols.create("x"), vZero);
Value vInner;
vInner.mkAttrs(innerBuilder.finish());
auto list = evaluator.mem.newList(3);
list->elems[0] = &vInner;
list->elems[1] = &vInner;
list->elems[0] = vInner;
list->elems[1] = vInner;
list->size = 2;
Value vList(NewValueAs::list, list);
@@ -716,8 +716,8 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
vTwo.mkInt(2);
BindingsBuilder builder = evaluator.buildBindings(10);
builder.insert(evaluator.symbols.create("one"), &vOne);
builder.insert(evaluator.symbols.create("two"), &vTwo);
builder.insert(evaluator.symbols.create("one"), vOne);
builder.insert(evaluator.symbols.create("two"), vTwo);
Value vAttrs;
vAttrs.mkAttrs(builder.finish());
@@ -732,7 +732,7 @@ TEST_F(ValuePrintingTests, ansiColorsAttrsElided)
Value vThree;
vThree.mkInt(3);
builder.insert(evaluator.symbols.create("three"), &vThree);
builder.insert(evaluator.symbols.create("three"), vThree);
vAttrs.mkAttrs(builder.finish());
test(vAttrs,
@@ -753,8 +753,8 @@ TEST_F(ValuePrintingTests, ansiColorsListElided)
auto list = evaluator.mem.newList(4);
Value vList{NewValueAs::list, list};
list->elems[0] = &vOne;
list->elems[1] = &vTwo;
list->elems[0] = vOne;
list->elems[1] = vTwo;
list->size = 2;
test(vList,
@@ -767,7 +767,7 @@ TEST_F(ValuePrintingTests, ansiColorsListElided)
Value vThree;
vThree.mkInt(3);
list->elems[2] = &vThree;
list->elems[2] = vThree;
list->size = 3;
test(vList,
@@ -787,7 +787,7 @@ TEST_F(ValuePrintingTests, osc8InAttrSets)
auto vZero = Value{NewValueAs::integer, NixInt{0}};
builder.insert(evaluator.symbols.create("x"), &vZero, pos);
builder.insert(evaluator.symbols.create("x"), vZero, pos);
auto vAttrs = Value{NewValueAs::attrs, builder.finish()};
auto hyperlink = makeHyperlink("x", makeHyperlinkLocalPath("/dev/null", 1));