mirror of
https://github.com/NixOS/nix
synced 2025-06-25 10:41:16 +02:00
Never update values after setting the type
Thunks are now overwritten by a helper function `Value::finishValue(newType, payload)` (where `payload` is the original anonymous union inside `Value`). This helps to ensure we never update a value elsewhere, since that would be incompatible with parallel evaluation (i.e. after a value has transitioned from being a thunk to being a non-thunk, it should be immutable). There were two places where this happened: `Value::mkString()` and `ExprAttrs::eval()`. This PR also adds a bunch of accessor functions for value contents, like `Value::integer()` to access the integer field in the union.
This commit is contained in:
parent
6d90287f5a
commit
8c0590fa32
35 changed files with 530 additions and 556 deletions
|
@ -131,7 +131,7 @@ void Value::print(EvalState & state, std::ostream & str, PrintOptions options)
|
|||
const Value * getPrimOp(const Value &v) {
|
||||
const Value * primOp = &v;
|
||||
while (primOp->isPrimOpApp()) {
|
||||
primOp = primOp->primOpApp.left;
|
||||
primOp = primOp->payload.primOpApp.left;
|
||||
}
|
||||
assert(primOp->isPrimOp());
|
||||
return primOp;
|
||||
|
@ -163,12 +163,12 @@ std::string showType(const Value & v)
|
|||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (v.internalType) {
|
||||
case tString: return v.string.context ? "a string with context" : "a string";
|
||||
case tString: return v.payload.string.context ? "a string with context" : "a string";
|
||||
case tPrimOp:
|
||||
return fmt("the built-in function '%s'", std::string(v.primOp->name));
|
||||
return fmt("the built-in function '%s'", std::string(v.payload.primOp->name));
|
||||
case tPrimOpApp:
|
||||
return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->primOp->name));
|
||||
case tExternal: return v.external->showType();
|
||||
return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->payload.primOp->name));
|
||||
case tExternal: return v.external()->showType();
|
||||
case tThunk: return v.isBlackhole() ? "a black hole" : "a thunk";
|
||||
case tApp: return "a function application";
|
||||
default:
|
||||
|
@ -183,9 +183,9 @@ PosIdx Value::determinePos(const PosIdx pos) const
|
|||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (internalType) {
|
||||
case tAttrs: return attrs->pos;
|
||||
case tLambda: return lambda.fun->pos;
|
||||
case tApp: return app.left->determinePos(pos);
|
||||
case tAttrs: return attrs()->pos;
|
||||
case tLambda: return payload.lambda.fun->pos;
|
||||
case tApp: return payload.app.left->determinePos(pos);
|
||||
default: return pos;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
@ -197,10 +197,10 @@ bool Value::isTrivial() const
|
|||
internalType != tApp
|
||||
&& internalType != tPrimOpApp
|
||||
&& (internalType != tThunk
|
||||
|| (dynamic_cast<ExprAttrs *>(thunk.expr)
|
||||
&& ((ExprAttrs *) thunk.expr)->dynamicAttrs.empty())
|
||||
|| dynamic_cast<ExprLambda *>(thunk.expr)
|
||||
|| dynamic_cast<ExprList *>(thunk.expr));
|
||||
|| (dynamic_cast<ExprAttrs *>(payload.thunk.expr)
|
||||
&& ((ExprAttrs *) payload.thunk.expr)->dynamicAttrs.empty())
|
||||
|| dynamic_cast<ExprLambda *>(payload.thunk.expr)
|
||||
|| dynamic_cast<ExprList *>(payload.thunk.expr));
|
||||
}
|
||||
|
||||
|
||||
|
@ -584,7 +584,7 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info)
|
|||
/* Install value the base environment. */
|
||||
staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl);
|
||||
baseEnv.values[baseEnvDispl++] = v;
|
||||
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v));
|
||||
baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(name2), v));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -597,32 +597,30 @@ void PrimOp::check()
|
|||
}
|
||||
|
||||
|
||||
std::ostream & operator<<(std::ostream & output, PrimOp & primOp)
|
||||
std::ostream & operator<<(std::ostream & output, const PrimOp & primOp)
|
||||
{
|
||||
output << "primop " << primOp.name;
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
PrimOp * Value::primOpAppPrimOp() const
|
||||
const PrimOp * Value::primOpAppPrimOp() const
|
||||
{
|
||||
Value * left = primOpApp.left;
|
||||
Value * left = payload.primOpApp.left;
|
||||
while (left && !left->isPrimOp()) {
|
||||
left = left->primOpApp.left;
|
||||
left = left->payload.primOpApp.left;
|
||||
}
|
||||
|
||||
if (!left)
|
||||
return nullptr;
|
||||
return left->primOp;
|
||||
return left->primOp();
|
||||
}
|
||||
|
||||
|
||||
void Value::mkPrimOp(PrimOp * p)
|
||||
{
|
||||
p->check();
|
||||
clearValue();
|
||||
internalType = tPrimOp;
|
||||
primOp = p;
|
||||
finishValue(tPrimOp, { .primOp = p });
|
||||
}
|
||||
|
||||
|
||||
|
@ -650,14 +648,14 @@ Value * EvalState::addPrimOp(PrimOp && primOp)
|
|||
v->mkPrimOp(new PrimOp(primOp));
|
||||
staticBaseEnv->vars.emplace_back(envName, baseEnvDispl);
|
||||
baseEnv.values[baseEnvDispl++] = v;
|
||||
baseEnv.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v));
|
||||
baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(primOp.name), v));
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
Value & EvalState::getBuiltin(const std::string & name)
|
||||
{
|
||||
return *baseEnv.values[0]->attrs->find(symbols.create(name))->value;
|
||||
return *baseEnv.values[0]->attrs()->find(symbols.create(name))->value;
|
||||
}
|
||||
|
||||
|
||||
|
@ -665,12 +663,12 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
|||
{
|
||||
if (v.isPrimOp()) {
|
||||
auto v2 = &v;
|
||||
if (auto * doc = v2->primOp->doc)
|
||||
if (auto * doc = v2->primOp()->doc)
|
||||
return Doc {
|
||||
.pos = {},
|
||||
.name = v2->primOp->name,
|
||||
.arity = v2->primOp->arity,
|
||||
.args = v2->primOp->args,
|
||||
.name = v2->primOp()->name,
|
||||
.arity = v2->primOp()->arity,
|
||||
.args = v2->primOp()->args,
|
||||
.doc = doc,
|
||||
};
|
||||
}
|
||||
|
@ -694,8 +692,8 @@ void printWithBindings(const SymbolTable & st, const Env & env)
|
|||
if (!env.values[0]->isThunk()) {
|
||||
std::cout << "with: ";
|
||||
std::cout << ANSI_MAGENTA;
|
||||
Bindings::iterator j = env.values[0]->attrs->begin();
|
||||
while (j != env.values[0]->attrs->end()) {
|
||||
auto j = env.values[0]->attrs()->begin();
|
||||
while (j != env.values[0]->attrs()->end()) {
|
||||
std::cout << st[j->name] << " ";
|
||||
++j;
|
||||
}
|
||||
|
@ -749,11 +747,8 @@ void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const En
|
|||
|
||||
if (se.isWith && !env.values[0]->isThunk()) {
|
||||
// add 'with' bindings.
|
||||
Bindings::iterator j = env.values[0]->attrs->begin();
|
||||
while (j != env.values[0]->attrs->end()) {
|
||||
vm[st[j->name]] = j->value;
|
||||
++j;
|
||||
}
|
||||
for (auto & j : *env.values[0]->attrs())
|
||||
vm[st[j.name]] = j.value;
|
||||
} else {
|
||||
// iterate through staticenv bindings and add them.
|
||||
for (auto & i : se.vars)
|
||||
|
@ -873,28 +868,28 @@ void Value::mkString(std::string_view s)
|
|||
}
|
||||
|
||||
|
||||
static void copyContextToValue(Value & v, const NixStringContext & context)
|
||||
static const char * * encodeContext(const NixStringContext & context)
|
||||
{
|
||||
if (!context.empty()) {
|
||||
size_t n = 0;
|
||||
v.string.context = (const char * *)
|
||||
auto ctx = (const char * *)
|
||||
allocBytes((context.size() + 1) * sizeof(char *));
|
||||
for (auto & i : context)
|
||||
v.string.context[n++] = dupString(i.to_string().c_str());
|
||||
v.string.context[n] = 0;
|
||||
}
|
||||
ctx[n++] = dupString(i.to_string().c_str());
|
||||
ctx[n] = 0;
|
||||
return ctx;
|
||||
} else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Value::mkString(std::string_view s, const NixStringContext & context)
|
||||
{
|
||||
mkString(s);
|
||||
copyContextToValue(*this, context);
|
||||
mkString(makeImmutableString(s), encodeContext(context));
|
||||
}
|
||||
|
||||
void Value::mkStringMove(const char * s, const NixStringContext & context)
|
||||
{
|
||||
mkString(s);
|
||||
copyContextToValue(*this, context);
|
||||
mkString(s, encodeContext(context));
|
||||
}
|
||||
|
||||
|
||||
|
@ -918,8 +913,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval)
|
|||
auto * fromWith = var.fromWith;
|
||||
while (1) {
|
||||
forceAttrs(*env->values[0], fromWith->pos, "while evaluating the first subexpression of a with expression");
|
||||
Bindings::iterator j = env->values[0]->attrs->find(var.name);
|
||||
if (j != env->values[0]->attrs->end()) {
|
||||
if (auto j = env->values[0]->attrs()->get(var.name)) {
|
||||
if (countCalls) attrSelects[j->pos]++;
|
||||
return j->value;
|
||||
}
|
||||
|
@ -1167,7 +1161,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri
|
|||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).atPos(pos).withFrame(env, *e).debugThrow();
|
||||
return v.boolean;
|
||||
return v.boolean();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
|
@ -1235,8 +1229,9 @@ Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up)
|
|||
|
||||
void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
||||
{
|
||||
v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish());
|
||||
auto bindings = state.buildBindings(attrs.size() + dynamicAttrs.size());
|
||||
auto dynamicEnv = &env;
|
||||
bool sort = false;
|
||||
|
||||
if (recursive) {
|
||||
/* Create a new environment that contains the attributes in
|
||||
|
@ -1261,7 +1256,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||
} else
|
||||
vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv));
|
||||
env2.values[displ++] = vAttr;
|
||||
v.attrs->push_back(Attr(i.first, vAttr, i.second.pos));
|
||||
bindings.insert(i.first, vAttr, i.second.pos);
|
||||
}
|
||||
|
||||
/* If the rec contains an attribute called `__overrides', then
|
||||
|
@ -1273,32 +1268,28 @@ void ExprAttrs::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 = (*bindings.bindings)[overrides->second.displ].value;
|
||||
state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "while evaluating the `__overrides` attribute");
|
||||
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size());
|
||||
for (auto & i : *v.attrs)
|
||||
newBnds->push_back(i);
|
||||
for (auto & i : *vOverrides->attrs) {
|
||||
bindings.grow(state.allocBindings(bindings.capacity() + vOverrides->attrs()->size()));
|
||||
for (auto & i : *vOverrides->attrs()) {
|
||||
AttrDefs::iterator j = attrs.find(i.name);
|
||||
if (j != attrs.end()) {
|
||||
(*newBnds)[j->second.displ] = i;
|
||||
(*bindings.bindings)[j->second.displ] = i;
|
||||
env2.values[j->second.displ] = i.value;
|
||||
} else
|
||||
newBnds->push_back(i);
|
||||
bindings.push_back(i);
|
||||
}
|
||||
newBnds->sort();
|
||||
v.attrs = newBnds;
|
||||
sort = true;
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr;
|
||||
for (auto & i : attrs) {
|
||||
v.attrs->push_back(Attr(
|
||||
i.first,
|
||||
i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)),
|
||||
i.second.pos));
|
||||
}
|
||||
for (auto & i : attrs)
|
||||
bindings.insert(
|
||||
i.first,
|
||||
i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)),
|
||||
i.second.pos);
|
||||
}
|
||||
|
||||
/* Dynamic attrs apply *after* rec and __overrides. */
|
||||
|
@ -1310,17 +1301,21 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
|
|||
continue;
|
||||
state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute");
|
||||
auto nameSym = state.symbols.create(nameVal.string_view());
|
||||
Bindings::iterator j = v.attrs->find(nameSym);
|
||||
if (j != v.attrs->end())
|
||||
if (sort)
|
||||
// FIXME: inefficient
|
||||
bindings.bindings->sort();
|
||||
if (auto j = bindings.bindings->get(nameSym))
|
||||
state.error<EvalError>("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow();
|
||||
|
||||
i.valueExpr->setName(nameSym);
|
||||
/* Keep sorted order so find can catch duplicates */
|
||||
v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos));
|
||||
v.attrs->sort(); // FIXME: inefficient
|
||||
bindings.insert(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos);
|
||||
sort = true;
|
||||
}
|
||||
|
||||
v.attrs->pos = pos;
|
||||
bindings.bindings->pos = pos;
|
||||
|
||||
v.mkAttrs(sort ? bindings.finish() : bindings.alreadySorted());
|
||||
}
|
||||
|
||||
|
||||
|
@ -1426,21 +1421,21 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
for (auto & i : attrPath) {
|
||||
state.nrLookups++;
|
||||
Bindings::iterator j;
|
||||
const Attr * j;
|
||||
auto name = getName(i, state, env);
|
||||
if (def) {
|
||||
state.forceValue(*vAttrs, pos);
|
||||
if (vAttrs->type() != nAttrs ||
|
||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
!(j = vAttrs->attrs()->get(name)))
|
||||
{
|
||||
def->eval(state, env, v);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
state.forceAttrs(*vAttrs, pos, "while selecting an attribute");
|
||||
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) {
|
||||
if (!(j = vAttrs->attrs()->get(name))) {
|
||||
std::set<std::string> allAttrNames;
|
||||
for (auto & attr : *vAttrs->attrs)
|
||||
for (auto & attr : *vAttrs->attrs())
|
||||
allAttrNames.insert(state.symbols[attr.name]);
|
||||
auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]);
|
||||
state.error<EvalError>("attribute '%1%' missing", state.symbols[name])
|
||||
|
@ -1478,15 +1473,15 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
for (auto & i : attrPath) {
|
||||
state.forceValue(*vAttrs, getPos());
|
||||
Bindings::iterator j;
|
||||
const Attr * j;
|
||||
auto name = getName(i, state, env);
|
||||
if (vAttrs->type() != nAttrs ||
|
||||
(j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
|
||||
if (vAttrs->type() == nAttrs &&
|
||||
(j = vAttrs->attrs()->get(name)))
|
||||
{
|
||||
vAttrs = j->value;
|
||||
} else {
|
||||
v.mkBool(false);
|
||||
return;
|
||||
} else {
|
||||
vAttrs = j->value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1538,19 +1533,19 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
}
|
||||
};
|
||||
|
||||
Attr * functor;
|
||||
const Attr * functor;
|
||||
|
||||
while (nrArgs > 0) {
|
||||
|
||||
if (vCur.isLambda()) {
|
||||
|
||||
ExprLambda & lambda(*vCur.lambda.fun);
|
||||
ExprLambda & lambda(*vCur.payload.lambda.fun);
|
||||
|
||||
auto size =
|
||||
(!lambda.arg ? 0 : 1) +
|
||||
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
|
||||
Env & env2(allocEnv(size));
|
||||
env2.up = vCur.lambda.env;
|
||||
env2.up = vCur.payload.lambda.env;
|
||||
|
||||
Displacement displ = 0;
|
||||
|
||||
|
@ -1572,7 +1567,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
argument has a default, use the default. */
|
||||
size_t attrsUsed = 0;
|
||||
for (auto & i : lambda.formals->formals) {
|
||||
auto j = args[0]->attrs->get(i.name);
|
||||
auto j = args[0]->attrs()->get(i.name);
|
||||
if (!j) {
|
||||
if (!i.def) {
|
||||
error<TypeError>("function '%1%' called without required argument '%2%'",
|
||||
|
@ -1580,7 +1575,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
symbols[i.name])
|
||||
.atPos(lambda.pos)
|
||||
.withTrace(pos, "from call site")
|
||||
.withFrame(*fun.lambda.env, lambda)
|
||||
.withFrame(*fun.payload.lambda.env, lambda)
|
||||
.debugThrow();
|
||||
}
|
||||
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
||||
|
@ -1592,10 +1587,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
|
||||
/* Check that each actual argument is listed as a formal
|
||||
argument (unless the attribute match specifies a `...'). */
|
||||
if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs->size()) {
|
||||
if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs()->size()) {
|
||||
/* Nope, so show the first unexpected argument to the
|
||||
user. */
|
||||
for (auto & i : *args[0]->attrs)
|
||||
for (auto & i : *args[0]->attrs())
|
||||
if (!lambda.formals->has(i.name)) {
|
||||
std::set<std::string> formalNames;
|
||||
for (auto & formal : lambda.formals->formals)
|
||||
|
@ -1607,7 +1602,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
.atPos(lambda.pos)
|
||||
.withTrace(pos, "from call site")
|
||||
.withSuggestions(suggestions)
|
||||
.withFrame(*fun.lambda.env, lambda)
|
||||
.withFrame(*fun.payload.lambda.env, lambda)
|
||||
.debugThrow();
|
||||
}
|
||||
abort(); // can't happen
|
||||
|
@ -1649,7 +1644,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
|
||||
else if (vCur.isPrimOp()) {
|
||||
|
||||
size_t argsLeft = vCur.primOp->arity;
|
||||
size_t argsLeft = vCur.primOp()->arity;
|
||||
|
||||
if (nrArgs < argsLeft) {
|
||||
/* We don't have enough arguments, so create a tPrimOpApp chain. */
|
||||
|
@ -1657,7 +1652,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
return;
|
||||
} else {
|
||||
/* We have all the arguments, so call the primop. */
|
||||
auto * fn = vCur.primOp;
|
||||
auto * fn = vCur.primOp();
|
||||
|
||||
nrPrimOpCalls++;
|
||||
if (countCalls) primOpCalls[fn->name]++;
|
||||
|
@ -1680,10 +1675,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
Value * primOp = &vCur;
|
||||
while (primOp->isPrimOpApp()) {
|
||||
argsDone++;
|
||||
primOp = primOp->primOpApp.left;
|
||||
primOp = primOp->payload.primOpApp.left;
|
||||
}
|
||||
assert(primOp->isPrimOp());
|
||||
auto arity = primOp->primOp->arity;
|
||||
auto arity = primOp->primOp()->arity;
|
||||
auto argsLeft = arity - argsDone;
|
||||
|
||||
if (nrArgs < argsLeft) {
|
||||
|
@ -1696,13 +1691,13 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
|
||||
Value * vArgs[maxPrimOpArity];
|
||||
auto n = argsDone;
|
||||
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp.left)
|
||||
vArgs[--n] = arg->primOpApp.right;
|
||||
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->payload.primOpApp.left)
|
||||
vArgs[--n] = arg->payload.primOpApp.right;
|
||||
|
||||
for (size_t i = 0; i < argsLeft; ++i)
|
||||
vArgs[argsDone + i] = args[i];
|
||||
|
||||
auto fn = primOp->primOp;
|
||||
auto fn = primOp->primOp();
|
||||
nrPrimOpCalls++;
|
||||
if (countCalls) primOpCalls[fn->name]++;
|
||||
|
||||
|
@ -1722,7 +1717,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
|
|||
}
|
||||
}
|
||||
|
||||
else if (vCur.type() == nAttrs && (functor = vCur.attrs->get(sFunctor))) {
|
||||
else if (vCur.type() == nAttrs && (functor = vCur.attrs()->get(sFunctor))) {
|
||||
/* '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. */
|
||||
|
@ -1797,8 +1792,8 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||
forceValue(fun, pos);
|
||||
|
||||
if (fun.type() == nAttrs) {
|
||||
auto found = fun.attrs->find(sFunctor);
|
||||
if (found != fun.attrs->end()) {
|
||||
auto found = fun.attrs()->find(sFunctor);
|
||||
if (found != fun.attrs()->end()) {
|
||||
Value * v = allocValue();
|
||||
callFunction(*found->value, fun, *v, pos);
|
||||
forceValue(*v, pos);
|
||||
|
@ -1806,14 +1801,14 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||
}
|
||||
}
|
||||
|
||||
if (!fun.isLambda() || !fun.lambda.fun->hasFormals()) {
|
||||
if (!fun.isLambda() || !fun.payload.lambda.fun->hasFormals()) {
|
||||
res = fun;
|
||||
return;
|
||||
}
|
||||
|
||||
auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.lambda.fun->formals->formals.size()), args.size()));
|
||||
auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.payload.lambda.fun->formals->formals.size()), args.size()));
|
||||
|
||||
if (fun.lambda.fun->formals->ellipsis) {
|
||||
if (fun.payload.lambda.fun->formals->ellipsis) {
|
||||
// If the formals have an ellipsis (eg the function accepts extra args) pass
|
||||
// all available automatic arguments (which includes arguments specified on
|
||||
// the command line via --arg/--argstr)
|
||||
|
@ -1821,9 +1816,9 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res)
|
|||
attrs.insert(v);
|
||||
} else {
|
||||
// Otherwise, only pass the arguments that the function accepts
|
||||
for (auto & i : fun.lambda.fun->formals->formals) {
|
||||
Bindings::iterator j = args.find(i.name);
|
||||
if (j != args.end()) {
|
||||
for (auto & i : fun.payload.lambda.fun->formals->formals) {
|
||||
auto j = args.get(i.name);
|
||||
if (j) {
|
||||
attrs.insert(*j);
|
||||
} else if (!i.def) {
|
||||
error<MissingArgumentError>(R"(cannot evaluate a function that has an argument without a value ('%1%')
|
||||
|
@ -1831,7 +1826,7 @@ Nix attempted to evaluate a function as a top level expression; in
|
|||
this case it must have its arguments supplied either by default
|
||||
values, or passed explicitly with '--arg' or '--argstr'. See
|
||||
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name])
|
||||
.atPos(i.pos).withFrame(*fun.lambda.env, *fun.lambda.fun).debugThrow();
|
||||
.atPos(i.pos).withFrame(*fun.payload.lambda.env, *fun.payload.lambda.fun).debugThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1916,17 +1911,17 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
state.nrOpUpdates++;
|
||||
|
||||
if (v1.attrs->size() == 0) { v = v2; return; }
|
||||
if (v2.attrs->size() == 0) { v = v1; return; }
|
||||
if (v1.attrs()->size() == 0) { v = v2; return; }
|
||||
if (v2.attrs()->size() == 0) { v = v1; return; }
|
||||
|
||||
auto attrs = state.buildBindings(v1.attrs->size() + v2.attrs->size());
|
||||
auto attrs = state.buildBindings(v1.attrs()->size() + v2.attrs()->size());
|
||||
|
||||
/* Merge the sets, preferring values from the second set. Make
|
||||
sure to keep the resulting vector in sorted order. */
|
||||
Bindings::iterator i = v1.attrs->begin();
|
||||
Bindings::iterator j = v2.attrs->begin();
|
||||
auto i = v1.attrs()->begin();
|
||||
auto j = v2.attrs()->begin();
|
||||
|
||||
while (i != v1.attrs->end() && j != v2.attrs->end()) {
|
||||
while (i != v1.attrs()->end() && j != v2.attrs()->end()) {
|
||||
if (i->name == j->name) {
|
||||
attrs.insert(*j);
|
||||
++i; ++j;
|
||||
|
@ -1937,12 +1932,12 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v)
|
|||
attrs.insert(*j++);
|
||||
}
|
||||
|
||||
while (i != v1.attrs->end()) attrs.insert(*i++);
|
||||
while (j != v2.attrs->end()) attrs.insert(*j++);
|
||||
while (i != v1.attrs()->end()) attrs.insert(*i++);
|
||||
while (j != v2.attrs()->end()) attrs.insert(*j++);
|
||||
|
||||
v.mkAttrs(attrs.alreadySorted());
|
||||
|
||||
state.nrOpUpdateValuesCopied += v.attrs->size();
|
||||
state.nrOpUpdateValuesCopied += v.attrs()->size();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2034,19 +2029,19 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
|||
|
||||
if (firstType == nInt) {
|
||||
if (vTmp.type() == nInt) {
|
||||
n += vTmp.integer;
|
||||
n += vTmp.integer();
|
||||
} else if (vTmp.type() == nFloat) {
|
||||
// Upgrade the type from int to float;
|
||||
firstType = nFloat;
|
||||
nf = n;
|
||||
nf += vTmp.fpoint;
|
||||
nf += vTmp.fpoint();
|
||||
} else
|
||||
state.error<EvalError>("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow();
|
||||
} else if (firstType == nFloat) {
|
||||
if (vTmp.type() == nInt) {
|
||||
nf += vTmp.integer;
|
||||
nf += vTmp.integer();
|
||||
} else if (vTmp.type() == nFloat) {
|
||||
nf += vTmp.fpoint;
|
||||
nf += vTmp.fpoint();
|
||||
} else
|
||||
state.error<EvalError>("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow();
|
||||
} else {
|
||||
|
@ -2119,11 +2114,11 @@ void EvalState::forceValueDeep(Value & v)
|
|||
forceValue(v, v.determinePos(noPos));
|
||||
|
||||
if (v.type() == nAttrs) {
|
||||
for (auto & i : *v.attrs)
|
||||
for (auto & i : *v.attrs())
|
||||
try {
|
||||
// If the value is a thunk, we're evaling. Otherwise no trace necessary.
|
||||
auto dts = debugRepl && i.value->isThunk()
|
||||
? makeDebugTraceStacker(*this, *i.value->thunk.expr, *i.value->thunk.env, positions[i.pos],
|
||||
? makeDebugTraceStacker(*this, *i.value->payload.thunk.expr, *i.value->payload.thunk.env, positions[i.pos],
|
||||
"while evaluating the attribute '%1%'", symbols[i.name])
|
||||
: nullptr;
|
||||
|
||||
|
@ -2154,13 +2149,13 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCt
|
|||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).atPos(pos).debugThrow();
|
||||
return v.integer;
|
||||
return v.integer();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
|
||||
return v.integer;
|
||||
return v.integer();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2169,14 +2164,14 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view err
|
|||
try {
|
||||
forceValue(v, pos);
|
||||
if (v.type() == nInt)
|
||||
return v.integer;
|
||||
return v.integer();
|
||||
else if (v.type() != nFloat)
|
||||
error<TypeError>(
|
||||
"expected a float but found %1%: %2%",
|
||||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).atPos(pos).debugThrow();
|
||||
return v.fpoint;
|
||||
return v.fpoint();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
|
@ -2194,19 +2189,19 @@ bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx
|
|||
showType(v),
|
||||
ValuePrinter(*this, v, errorPrintOptions)
|
||||
).atPos(pos).debugThrow();
|
||||
return v.boolean;
|
||||
return v.boolean();
|
||||
} catch (Error & e) {
|
||||
e.addTrace(positions[pos], errorCtx);
|
||||
throw;
|
||||
}
|
||||
|
||||
return v.boolean;
|
||||
return v.boolean();
|
||||
}
|
||||
|
||||
|
||||
bool EvalState::isFunctor(Value & fun)
|
||||
{
|
||||
return fun.type() == nAttrs && fun.attrs->find(sFunctor) != fun.attrs->end();
|
||||
return fun.type() == nAttrs && fun.attrs()->find(sFunctor) != fun.attrs()->end();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2247,8 +2242,8 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string
|
|||
|
||||
void copyContext(const Value & v, NixStringContext & context)
|
||||
{
|
||||
if (v.string.context)
|
||||
for (const char * * p = v.string.context; *p; ++p)
|
||||
if (v.payload.string.context)
|
||||
for (const char * * p = v.payload.string.context; *p; ++p)
|
||||
context.insert(NixStringContextElem::parse(*p));
|
||||
}
|
||||
|
||||
|
@ -2274,8 +2269,8 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s
|
|||
bool EvalState::isDerivation(Value & v)
|
||||
{
|
||||
if (v.type() != nAttrs) return false;
|
||||
Bindings::iterator i = v.attrs->find(sType);
|
||||
if (i == v.attrs->end()) return false;
|
||||
auto i = v.attrs()->get(sType);
|
||||
if (!i) return false;
|
||||
forceValue(*i->value, i->pos);
|
||||
if (i->value->type() != nString) return false;
|
||||
return i->value->string_view().compare("derivation") == 0;
|
||||
|
@ -2285,8 +2280,8 @@ bool EvalState::isDerivation(Value & v)
|
|||
std::optional<std::string> EvalState::tryAttrsToString(const PosIdx pos, Value & v,
|
||||
NixStringContext & context, bool coerceMore, bool copyToStore)
|
||||
{
|
||||
auto i = v.attrs->find(sToString);
|
||||
if (i != v.attrs->end()) {
|
||||
auto i = v.attrs()->find(sToString);
|
||||
if (i != v.attrs()->end()) {
|
||||
Value v1;
|
||||
callFunction(*i->value, v, v1, pos);
|
||||
return coerceToString(pos, v1, context,
|
||||
|
@ -2318,7 +2313,7 @@ BackedStringView EvalState::coerceToString(
|
|||
!canonicalizePath && !copyToStore
|
||||
? // FIXME: hack to preserve path literals that end in a
|
||||
// slash, as in /foo/${x}.
|
||||
v._path.path
|
||||
v.payload.path.path
|
||||
: copyToStore
|
||||
? store->printStorePath(copyPathToStore(context, v.path()))
|
||||
: std::string(v.path().path.abs());
|
||||
|
@ -2328,8 +2323,8 @@ BackedStringView EvalState::coerceToString(
|
|||
auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore);
|
||||
if (maybeString)
|
||||
return std::move(*maybeString);
|
||||
auto i = v.attrs->find(sOutPath);
|
||||
if (i == v.attrs->end()) {
|
||||
auto i = v.attrs()->find(sOutPath);
|
||||
if (i == v.attrs()->end()) {
|
||||
error<TypeError>(
|
||||
"cannot coerce %1% to a string: %2%",
|
||||
showType(v),
|
||||
|
@ -2344,7 +2339,7 @@ BackedStringView EvalState::coerceToString(
|
|||
|
||||
if (v.type() == nExternal) {
|
||||
try {
|
||||
return v.external->coerceToString(*this, pos, context, coerceMore, copyToStore);
|
||||
return v.external()->coerceToString(*this, pos, context, coerceMore, copyToStore);
|
||||
} catch (Error & e) {
|
||||
e.addTrace(nullptr, errorCtx);
|
||||
throw;
|
||||
|
@ -2354,10 +2349,10 @@ BackedStringView EvalState::coerceToString(
|
|||
if (coerceMore) {
|
||||
/* Note that `false' is represented as an empty string for
|
||||
shell scripting convenience, just like `null'. */
|
||||
if (v.type() == nBool && v.boolean) return "1";
|
||||
if (v.type() == nBool && !v.boolean) return "";
|
||||
if (v.type() == nInt) return std::to_string(v.integer);
|
||||
if (v.type() == nFloat) return std::to_string(v.fpoint);
|
||||
if (v.type() == nBool && v.boolean()) return "1";
|
||||
if (v.type() == nBool && !v.boolean()) return "";
|
||||
if (v.type() == nInt) return std::to_string(v.integer());
|
||||
if (v.type() == nFloat) return std::to_string(v.fpoint());
|
||||
if (v.type() == nNull) return "";
|
||||
|
||||
if (v.isList()) {
|
||||
|
@ -2436,8 +2431,8 @@ SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext
|
|||
/* Similarly, handle __toString where the result may be a path
|
||||
value. */
|
||||
if (v.type() == nAttrs) {
|
||||
auto i = v.attrs->find(sToString);
|
||||
if (i != v.attrs->end()) {
|
||||
auto i = v.attrs()->find(sToString);
|
||||
if (i != v.attrs()->end()) {
|
||||
Value v1;
|
||||
callFunction(*i->value, v, v1, pos);
|
||||
return coerceToPath(pos, v1, context, errorCtx);
|
||||
|
@ -2531,19 +2526,19 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
|||
|
||||
// Special case type-compatibility between float and int
|
||||
if (v1.type() == nInt && v2.type() == nFloat)
|
||||
return v1.integer == v2.fpoint;
|
||||
return v1.integer() == v2.fpoint();
|
||||
if (v1.type() == nFloat && v2.type() == nInt)
|
||||
return v1.fpoint == v2.integer;
|
||||
return v1.fpoint() == v2.integer();
|
||||
|
||||
// All other types are not compatible with each other.
|
||||
if (v1.type() != v2.type()) return false;
|
||||
|
||||
switch (v1.type()) {
|
||||
case nInt:
|
||||
return v1.integer == v2.integer;
|
||||
return v1.integer() == v2.integer();
|
||||
|
||||
case nBool:
|
||||
return v1.boolean == v2.boolean;
|
||||
return v1.boolean() == v2.boolean();
|
||||
|
||||
case nString:
|
||||
return strcmp(v1.c_str(), v2.c_str()) == 0;
|
||||
|
@ -2551,8 +2546,8 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
|||
case nPath:
|
||||
return
|
||||
// FIXME: compare accessors by their fingerprint.
|
||||
v1._path.accessor == v2._path.accessor
|
||||
&& strcmp(v1._path.path, v2._path.path) == 0;
|
||||
v1.payload.path.accessor == v2.payload.path.accessor
|
||||
&& strcmp(v1.payload.path.path, v2.payload.path.path) == 0;
|
||||
|
||||
case nNull:
|
||||
return true;
|
||||
|
@ -2567,17 +2562,17 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
|||
/* If both sets denote a derivation (type = "derivation"),
|
||||
then compare their outPaths. */
|
||||
if (isDerivation(v1) && isDerivation(v2)) {
|
||||
Bindings::iterator i = v1.attrs->find(sOutPath);
|
||||
Bindings::iterator j = v2.attrs->find(sOutPath);
|
||||
if (i != v1.attrs->end() && j != v2.attrs->end())
|
||||
auto i = v1.attrs()->get(sOutPath);
|
||||
auto j = v2.attrs()->get(sOutPath);
|
||||
if (i && j)
|
||||
return eqValues(*i->value, *j->value, pos, errorCtx);
|
||||
}
|
||||
|
||||
if (v1.attrs->size() != v2.attrs->size()) return false;
|
||||
if (v1.attrs()->size() != v2.attrs()->size()) return false;
|
||||
|
||||
/* Otherwise, compare the attributes one by one. */
|
||||
Bindings::iterator i, j;
|
||||
for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j)
|
||||
Bindings::const_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))
|
||||
return false;
|
||||
|
||||
|
@ -2589,10 +2584,10 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
|||
return false;
|
||||
|
||||
case nExternal:
|
||||
return *v1.external == *v2.external;
|
||||
return *v1.external() == *v2.external();
|
||||
|
||||
case nFloat:
|
||||
return v1.fpoint == v2.fpoint;
|
||||
return v1.fpoint() == v2.fpoint();
|
||||
|
||||
case nThunk: // Must not be left by forceValue
|
||||
default:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue