Add error context for most basic coercions

This commit is contained in:
Guillaume Maudoux 2022-03-04 05:04:47 +01:00
parent 00e242feed
commit be1f069746
21 changed files with 309 additions and 275 deletions

View file

@ -524,7 +524,7 @@ ref<eval_cache::EvalCache> openEvalCache(
auto vFlake = state.allocValue(); auto vFlake = state.allocValue();
flake::callFlake(state, *lockedFlake, *vFlake); flake::callFlake(state, *lockedFlake, *vFlake);
state.forceAttrs(*vFlake, noPos); state.forceAttrs(*vFlake, noPos, "While evaluating a cached flake");
auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs"));
assert(aOutputs); assert(aOutputs);

View file

@ -112,7 +112,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what)
// FIXME: is it possible to extract the Pos object instead of doing this // FIXME: is it possible to extract the Pos object instead of doing this
// toString + parsing? // toString + parsing?
auto pos = state.forceString(*v2); auto pos = state.forceString(*v2, noPos, "While evaluating the meta.position attribute of a derivation");
auto colon = pos.rfind(':'); auto colon = pos.rfind(':');
if (colon == std::string::npos) if (colon == std::string::npos)

View file

@ -336,7 +336,7 @@ Value & AttrCursor::getValue()
if (!_value) { if (!_value) {
if (parent) { if (parent) {
auto & vParent = parent->first->getValue(); auto & vParent = parent->first->getValue();
root->state.forceAttrs(vParent, noPos); root->state.forceAttrs(vParent, noPos, "While evaluating the parent attr set");
auto attr = vParent.attrs->get(parent->second); auto attr = vParent.attrs->get(parent->second);
if (!attr) if (!attr)
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());

View file

@ -15,10 +15,10 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s))
}); });
} }
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v)) LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v, const std::string & s2))
{ {
throw TypeError({ throw TypeError({
.msg = hintfmt(s, showType(v)), .msg = hintfmt(s, showType(v), s2),
.errPos = pos .errPos = pos
}); });
} }
@ -52,26 +52,26 @@ void EvalState::forceValue(Value & v, Callable getPos)
} }
inline void EvalState::forceAttrs(Value & v, const Pos & pos) inline void EvalState::forceAttrs(Value & v, const Pos & pos, const std::string & errorCtx)
{ {
forceAttrs(v, [&]() { return pos; }); forceAttrs(v, [&]() { return pos; }, errorCtx);
} }
template <typename Callable> template <typename Callable>
inline void EvalState::forceAttrs(Value & v, Callable getPos) inline void EvalState::forceAttrs(Value & v, Callable getPos, const std::string & errorCtx)
{ {
forceValue(v, getPos); forceValue(v, getPos);
if (v.type() != nAttrs) if (v.type() != nAttrs)
throwTypeError(getPos(), "value is %1% while a set was expected", v); throwTypeError(getPos(), "%2%: value is %1% while a set was expected", v, errorCtx);
} }
inline void EvalState::forceList(Value & v, const Pos & pos) inline void EvalState::forceList(Value & v, const Pos & pos, const std::string & errorCtx)
{ {
forceValue(v, pos); forceValue(v, pos);
if (!v.isList()) if (!v.isList())
throwTypeError(pos, "value is %1% while a list was expected", v); throwTypeError(pos, "%2%: value is %1% while a list was expected", v, errorCtx);
} }
/* Note: Various places expect the allocated memory to be zeroed. */ /* Note: Various places expect the allocated memory to be zeroed. */

View file

@ -297,7 +297,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env)
} else { } else {
Value nameValue; Value nameValue;
name.expr->eval(state, env, nameValue); name.expr->eval(state, env, nameValue);
state.forceStringNoCtx(nameValue); state.forceStringNoCtx(nameValue, noPos, "While evaluating an attribute name");
return state.symbols.create(nameValue.string.s); return state.symbols.create(nameValue.string.s);
} }
} }
@ -763,9 +763,17 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const
}); });
} }
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v)) //LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v))
//{
// throw TypeError(s, showType(v));
//}
LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v))
{ {
throw TypeError(s, showType(v)); throw AssertionError({
.msg = hintfmt(s, showType(v)),
.errPos = pos
});
} }
LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1)) LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1))
@ -1060,7 +1068,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, const std:
Value v; Value v;
e->eval(*this, env, v); e->eval(*this, env, v);
if (v.type() != nBool) if (v.type() != nBool)
throwTypeError(pos, (location + ": value is %1% while a Boolean was expected").c_str(), v); throwTypeError(pos, "%2%: value is %1% while a Boolean was expected", v, location);
return v.boolean; return v.boolean;
} }
@ -1069,7 +1077,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos
{ {
e->eval(*this, env, v); e->eval(*this, env, v);
if (v.type() != nAttrs) if (v.type() != nAttrs)
throwTypeError(pos, (location + ": value is %1% while a set was expected").c_str(), v); throwTypeError(pos, "%2%: value is %1% while a set was expected", v, location);
} }
@ -1142,7 +1150,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
Hence we need __overrides.) */ Hence we need __overrides.) */
if (hasOverrides) { if (hasOverrides) {
Value * vOverrides = (*v.attrs)[overrides->second.displ].value; Value * vOverrides = (*v.attrs)[overrides->second.displ].value;
state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }); state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "While evaluating the `__overrides` attribute");
Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size()); Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size());
for (auto & i : *v.attrs) for (auto & i : *v.attrs)
newBnds->push_back(i); newBnds->push_back(i);
@ -1170,7 +1178,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v)
state.forceValue(nameVal, i.pos); state.forceValue(nameVal, i.pos);
if (nameVal.type() == nNull) if (nameVal.type() == nNull)
continue; continue;
state.forceStringNoCtx(nameVal); state.forceStringNoCtx(nameVal, i.pos, "While evaluating the name of a dynamic attribute");
Symbol nameSym = state.symbols.create(nameVal.string.s); Symbol nameSym = state.symbols.create(nameVal.string.s);
Bindings::iterator j = v.attrs->find(nameSym); Bindings::iterator j = v.attrs->find(nameSym);
if (j != v.attrs->end()) if (j != v.attrs->end())
@ -1260,7 +1268,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
return; return;
} }
} else { } else {
state.forceAttrs(*vAttrs, pos); state.forceAttrs(*vAttrs, pos, "While selecting an attribute");
if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end())
throwEvalError(pos, "attribute '%1%' missing", name); throwEvalError(pos, "attribute '%1%' missing", name);
} }
@ -1351,7 +1359,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
env2.values[displ++] = args[0]; env2.values[displ++] = args[0];
else { else {
forceAttrs(*args[0], pos); forceAttrs(*args[0], pos, "While evaluating the value passed as argument to a function expecting an attribute set");
if (!lambda.arg.empty()) if (!lambda.arg.empty())
env2.values[displ++] = args[0]; env2.values[displ++] = args[0];
@ -1567,7 +1575,8 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v)
void ExprIf::eval(EvalState & state, Env & env, Value & v) void ExprIf::eval(EvalState & state, Env & env, Value & v)
{ {
(state.evalBool(env, cond, pos, "In the condition of the if operator") ? then : else_)->eval(state, env, v); // We cheat in the parser, an pass the position of the condition as the position of the if itself.
(state.evalBool(env, cond, pos, "") ? then : else_)->eval(state, env, v);
} }
@ -1665,18 +1674,18 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v)
Value v1; e1->eval(state, env, v1); Value v1; e1->eval(state, env, v1);
Value v2; e2->eval(state, env, v2); Value v2; e2->eval(state, env, v2);
Value * lists[2] = { &v1, &v2 }; Value * lists[2] = { &v1, &v2 };
state.concatLists(v, 2, lists, pos); state.concatLists(v, 2, lists, pos, "While evaluating one of the elements to concatenate");
} }
void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos) void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos, const std::string & errorCtx)
{ {
nrListConcats++; nrListConcats++;
Value * nonEmpty = 0; Value * nonEmpty = 0;
size_t len = 0; size_t len = 0;
for (size_t n = 0; n < nrLists; ++n) { for (size_t n = 0; n < nrLists; ++n) {
forceList(*lists[n], pos); forceList(*lists[n], pos, errorCtx);
auto l = lists[n]->listSize(); auto l = lists[n]->listSize();
len += l; len += l;
if (l) nonEmpty = lists[n]; if (l) nonEmpty = lists[n];
@ -1824,31 +1833,31 @@ void EvalState::forceValueDeep(Value & v)
} }
NixInt EvalState::forceInt(Value & v, const Pos & pos) NixInt EvalState::forceInt(Value & v, const Pos & pos, const std::string & errorCtx)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() != nInt) if (v.type() != nInt)
throwTypeError(pos, "value is %1% while an integer was expected", v); throwTypeError(pos, "%2%: value is %1% while an integer was expected", v, errorCtx);
return v.integer; return v.integer;
} }
NixFloat EvalState::forceFloat(Value & v, const Pos & pos) NixFloat EvalState::forceFloat(Value & v, const Pos & pos, const std::string & errorCtx)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() == nInt) if (v.type() == nInt)
return v.integer; return v.integer;
else if (v.type() != nFloat) else if (v.type() != nFloat)
throwTypeError(pos, "value is %1% while a float was expected", v); throwTypeError(pos, "%2%: value is %1% while a float was expected", v, errorCtx);
return v.fpoint; return v.fpoint;
} }
bool EvalState::forceBool(Value & v, const Pos & pos) bool EvalState::forceBool(Value & v, const Pos & pos, const std::string & errorCtx)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() != nBool) if (v.type() != nBool)
throwTypeError(pos, "value is %1% while a Boolean was expected", v); throwTypeError(pos, "%2%: value is %1% while a Boolean was expected", v, errorCtx);
return v.boolean; return v.boolean;
} }
@ -1859,22 +1868,19 @@ bool EvalState::isFunctor(Value & fun)
} }
void EvalState::forceFunction(Value & v, const Pos & pos) void EvalState::forceFunction(Value & v, const Pos & pos, const std::string & errorCtx)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() != nFunction && !isFunctor(v)) if (v.type() != nFunction && !isFunctor(v))
throwTypeError(pos, "value is %1% while a function was expected", v); throwTypeError(pos, "%2%: value is %1% while a function was expected", v, errorCtx);
} }
std::string_view EvalState::forceString(Value & v, const Pos & pos) std::string_view EvalState::forceString(Value & v, const Pos & pos, const std::string & errorCtx)
{ {
forceValue(v, pos); forceValue(v, pos);
if (v.type() != nString) { if (v.type() != nString) {
if (pos) throwTypeError(pos, "%2%: value is %1% while a string was expected", v, errorCtx);
throwTypeError(pos, "value is %1% while a string was expected", v);
else
throwTypeError("value is %1% while a string was expected", v);
} }
return v.string.s; return v.string.s;
} }
@ -1911,23 +1917,23 @@ std::vector<std::pair<Path, std::string>> Value::getContext()
} }
std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos) std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos, const std::string & errorCtx)
{ {
auto s = forceString(v, pos); auto s = forceString(v, pos, errorCtx);
copyContext(v, context); copyContext(v, context);
return s; return s;
} }
std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos) std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos, const std::string & errorCtx)
{ {
auto s = forceString(v, pos); auto s = forceString(v, pos, errorCtx);
if (v.string.context) { if (v.string.context) {
if (pos) if (pos)
throwEvalError(pos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", throwEvalError(pos, (errorCtx + ": the string '%1%' is not allowed to refer to a store path (such as '%2%')").c_str(),
v.string.s, v.string.context[0]); v.string.s, v.string.context[0]);
else else
throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", throwEvalError((errorCtx + ": the string '%1%' is not allowed to refer to a store path (such as '%2%')").c_str(),
v.string.s, v.string.context[0]); v.string.s, v.string.context[0]);
} }
return s; return s;

View file

@ -234,20 +234,20 @@ public:
void forceValueDeep(Value & v); void forceValueDeep(Value & v);
/* Force `v', and then verify that it has the expected type. */ /* Force `v', and then verify that it has the expected type. */
NixInt forceInt(Value & v, const Pos & pos); NixInt forceInt(Value & v, const Pos & pos, const std::string & errorCtx);
NixFloat forceFloat(Value & v, const Pos & pos); NixFloat forceFloat(Value & v, const Pos & pos, const std::string & errorCtx);
bool forceBool(Value & v, const Pos & pos); bool forceBool(Value & v, const Pos & pos, const std::string & errorCtx);
void forceAttrs(Value & v, const Pos & pos); void forceAttrs(Value & v, const Pos & pos, const std::string & errorCtx);
template <typename Callable> template <typename Callable>
inline void forceAttrs(Value & v, Callable getPos); inline void forceAttrs(Value & v, Callable getPos, const std::string & errorCtx);
inline void forceList(Value & v, const Pos & pos); inline void forceList(Value & v, const Pos & pos, const std::string & errorCtx);
void forceFunction(Value & v, const Pos & pos); // either lambda or primop void forceFunction(Value & v, const Pos & pos, const std::string & errorCtx); // either lambda or primop
std::string_view forceString(Value & v, const Pos & pos = noPos); std::string_view forceString(Value & v, const Pos & pos, const std::string & errorCtx);
std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos); std::string_view forceString(Value & v, PathSet & context, const Pos & pos, const std::string & errorCtx);
std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos); std::string_view forceStringNoCtx(Value & v, const Pos & pos, const std::string & errorCtx);
/* Return true iff the value `v' denotes a derivation (i.e. a /* Return true iff the value `v' denotes a derivation (i.e. a
set with attribute `type = "derivation"'). */ set with attribute `type = "derivation"'). */
@ -363,7 +363,7 @@ public:
void mkThunk_(Value & v, Expr * expr); void mkThunk_(Value & v, Expr * expr);
void mkPos(Value & v, ptr<Pos> pos); void mkPos(Value & v, ptr<Pos> pos);
void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos); void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos, const std::string & errorCtx);
/* Print statistics. */ /* Print statistics. */
void printStats(); void printStats();

View file

@ -255,7 +255,7 @@ static Flake getFlake(
for (auto & setting : *nixConfig->value->attrs) { for (auto & setting : *nixConfig->value->attrs) {
forceTrivialValue(state, *setting.value, *setting.pos); forceTrivialValue(state, *setting.value, *setting.pos);
if (setting.value->type() == nString) if (setting.value->type() == nString)
flake.config.settings.insert({setting.name, std::string(state.forceStringNoCtx(*setting.value, *setting.pos))}); flake.config.settings.insert({setting.name, std::string(state.forceStringNoCtx(*setting.value, *setting.pos, ""))});
else if (setting.value->type() == nPath) { else if (setting.value->type() == nPath) {
PathSet emptyContext = {}; PathSet emptyContext = {};
flake.config.settings.emplace( flake.config.settings.emplace(
@ -263,16 +263,16 @@ static Flake getFlake(
state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned()); state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned());
} }
else if (setting.value->type() == nInt) else if (setting.value->type() == nInt)
flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos)}); flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos, "")});
else if (setting.value->type() == nBool) else if (setting.value->type() == nBool)
flake.config.settings.insert({setting.name, Explicit<bool> { state.forceBool(*setting.value, *setting.pos) }}); flake.config.settings.insert({setting.name, 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; std::vector<std::string> ss;
for (auto elem : setting.value->listItems()) { for (auto elem : setting.value->listItems()) {
if (elem->type() != nString) if (elem->type() != nString)
throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected", throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected",
setting.name, showType(*setting.value)); setting.name, showType(*setting.value));
ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos)); ss.emplace_back(state.forceStringNoCtx(*elem, *setting.pos, ""));
} }
flake.config.settings.insert({setting.name, ss}); flake.config.settings.insert({setting.name, ss});
} }
@ -708,7 +708,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va
{ {
state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos); state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos);
std::string flakeRefS(state.forceStringNoCtx(*args[0], pos)); std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.getFlake"));
auto flakeRef = parseFlakeRef(flakeRefS, {}, true); auto flakeRef = parseFlakeRef(flakeRefS, {}, true);
if (evalSettings.pureEval && !flakeRef.input.isLocked()) if (evalSettings.pureEval && !flakeRef.input.isLocked())
throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos);

View file

@ -50,7 +50,7 @@ std::string DrvInfo::queryName() const
if (name == "" && attrs) { if (name == "" && attrs) {
auto i = attrs->find(state->sName); auto i = attrs->find(state->sName);
if (i == attrs->end()) throw TypeError("derivation name missing"); if (i == attrs->end()) throw TypeError("derivation name missing");
name = state->forceStringNoCtx(*i->value); name = state->forceStringNoCtx(*i->value, noPos, "While evaluating the name of a DrvInfo");
} }
return name; return name;
} }
@ -60,7 +60,7 @@ std::string DrvInfo::querySystem() const
{ {
if (system == "" && attrs) { if (system == "" && attrs) {
auto i = attrs->find(state->sSystem); auto i = attrs->find(state->sSystem);
system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos); system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos, "While evaluating the system of a DrvInfo");
} }
return system; return system;
} }
@ -108,15 +108,15 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall)
/* Get the outputs list. */ /* Get the outputs list. */
Bindings::iterator i; Bindings::iterator i;
if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) { if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) {
state->forceList(*i->value, *i->pos); state->forceList(*i->value, *i->pos, "While evaluating the outputs of a DrvInfo");
/* For each output... */ /* For each output... */
for (auto elem : i->value->listItems()) { for (auto elem : i->value->listItems()) {
/* Evaluate the corresponding set. */ /* Evaluate the corresponding set. */
std::string name(state->forceStringNoCtx(*elem, *i->pos)); std::string name(state->forceStringNoCtx(*elem, *i->pos, "While evaluating the name of one output of a DrvInfo"));
Bindings::iterator out = attrs->find(state->symbols.create(name)); Bindings::iterator out = attrs->find(state->symbols.create(name));
if (out == attrs->end()) continue; // FIXME: throw error? if (out == attrs->end()) continue; // FIXME: throw error?
state->forceAttrs(*out->value, *i->pos); state->forceAttrs(*out->value, *i->pos, "While evaluating the description of a DrvInfo output");
/* And evaluate its outPath attribute. */ /* And evaluate its outPath attribute. */
Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); Bindings::iterator outPath = out->value->attrs->find(state->sOutPath);
@ -151,7 +151,7 @@ std::string DrvInfo::queryOutputName() const
{ {
if (outputName == "" && attrs) { if (outputName == "" && attrs) {
Bindings::iterator i = attrs->find(state->sOutputName); Bindings::iterator i = attrs->find(state->sOutputName);
outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value) : ""; outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "While evaluating the output name of a DrvInfo") : "";
} }
return outputName; return outputName;
} }
@ -163,7 +163,7 @@ Bindings * DrvInfo::getMeta()
if (!attrs) return 0; if (!attrs) return 0;
Bindings::iterator a = attrs->find(state->sMeta); Bindings::iterator a = attrs->find(state->sMeta);
if (a == attrs->end()) return 0; if (a == attrs->end()) return 0;
state->forceAttrs(*a->value, *a->pos); state->forceAttrs(*a->value, *a->pos, "While evaluating the `meta` attribute of a DrvInfo");
meta = a->value->attrs; meta = a->value->attrs;
return meta; return meta;
} }
@ -364,7 +364,7 @@ static void getDerivations(EvalState & state, Value & vIn,
`recurseForDerivations = true' attribute. */ `recurseForDerivations = true' attribute. */
if (i->value->type() == nAttrs) { if (i->value->type() == nAttrs) {
Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations); Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations);
if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos)) if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos, "While evaluating the attribute `recurseForDerivations`"))
getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures);
} }
} }

View file

@ -4,7 +4,6 @@
#include "symbol-table.hh" #include "symbol-table.hh"
#include "error.hh" #include "error.hh"
namespace nix { namespace nix {

View file

@ -396,7 +396,7 @@ expr_function
; ;
expr_if expr_if
: IF expr THEN expr ELSE expr { $$ = new ExprIf(CUR_POS, $2, $4, $6); } : IF expr THEN expr ELSE expr { $$ = new ExprIf(makeCurPos(@2, data), $2, $4, $6); }
| expr_op | expr_op
; ;
@ -405,21 +405,21 @@ expr_op
| '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {new ExprInt(0), $2}); } | '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {new ExprInt(0), $2}); }
| expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); } | expr_op EQ expr_op { $$ = new ExprOpEq($1, $3); }
| expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); } | expr_op NEQ expr_op { $$ = new ExprOpNEq($1, $3); }
| expr_op '<' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$1, $3}); } | expr_op '<' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$1, $3}); }
| expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$3, $1})); } | expr_op LEQ expr_op { $$ = new ExprOpNot(new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$3, $1})); }
| expr_op '>' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$3, $1}); } | expr_op '>' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$3, $1}); }
| expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); } | expr_op GEQ expr_op { $$ = new ExprOpNot(new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); }
| expr_op AND expr_op { $$ = new ExprOpAnd(CUR_POS, $1, $3); } | expr_op AND expr_op { $$ = new ExprOpAnd(makeCurPos(@2, data), $1, $3); }
| expr_op OR expr_op { $$ = new ExprOpOr(CUR_POS, $1, $3); } | expr_op OR expr_op { $$ = new ExprOpOr(makeCurPos(@2, data), $1, $3); }
| expr_op IMPL expr_op { $$ = new ExprOpImpl(CUR_POS, $1, $3); } | expr_op IMPL expr_op { $$ = new ExprOpImpl(makeCurPos(@2, data), $1, $3); }
| expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $1, $3); } | expr_op UPDATE expr_op { $$ = new ExprOpUpdate(makeCurPos(@2, data), $1, $3); }
| expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); } | expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); }
| expr_op '+' expr_op | expr_op '+' expr_op
{ $$ = new ExprConcatStrings(CUR_POS, false, new std::vector<std::pair<Pos, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); } { $$ = new ExprConcatStrings(makeCurPos(@2, data), false, new std::vector<std::pair<Pos, Expr *> >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); }
| expr_op '-' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__sub")), {$1, $3}); } | expr_op '-' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__sub")), {$1, $3}); }
| expr_op '*' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__mul")), {$1, $3}); } | expr_op '*' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__mul")), {$1, $3}); }
| expr_op '/' expr_op { $$ = new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__div")), {$1, $3}); } | expr_op '/' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__div")), {$1, $3}); }
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(CUR_POS, $1, $3); } | expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(makeCurPos(@2, data), $1, $3); }
| expr_app | expr_app
; ;

View file

@ -195,9 +195,9 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
, "/"), **state.vImportedDrvToDerivation); , "/"), **state.vImportedDrvToDerivation);
} }
state.forceFunction(**state.vImportedDrvToDerivation, pos); state.forceFunction(**state.vImportedDrvToDerivation, pos, "While evaluating imported-drv-to-derivation.nix.gen.hh");
v.mkApp(*state.vImportedDrvToDerivation, w); v.mkApp(*state.vImportedDrvToDerivation, w);
state.forceAttrs(v, pos); state.forceAttrs(v, pos, "While calling imported-drv-to-derivation.nix.gen.hh");
} }
else if (path == corepkgsPrefix + "fetchurl.nix") { else if (path == corepkgsPrefix + "fetchurl.nix") {
@ -210,7 +210,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS
if (!vScope) if (!vScope)
state.evalFile(path, v); state.evalFile(path, v);
else { else {
state.forceAttrs(*vScope, pos); state.forceAttrs(*vScope, pos, "While evaluating the first argument passed to builtins.scopedImport");
Env * env = &state.allocEnv(vScope->attrs->size()); Env * env = &state.allocEnv(vScope->attrs->size());
env->up = &state.baseEnv; env->up = &state.baseEnv;
@ -314,7 +314,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
{ {
auto path = realisePath(state, pos, *args[0]); auto path = realisePath(state, pos, *args[0]);
std::string sym(state.forceStringNoCtx(*args[1], pos)); std::string sym(state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.importNative"));
void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL); void *handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!handle) if (!handle)
@ -340,7 +340,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value
/* Execute a program and parse its output */ /* Execute a program and parse its output */
void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceList(*args[0], pos); state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.exec");
auto elems = args[0]->listElems(); auto elems = args[0]->listElems();
auto count = args[0]->listSize(); auto count = args[0]->listSize();
if (count == 0) { if (count == 0) {
@ -381,7 +381,6 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v)
} }
} }
/* Return a string representing the type of the expression. */ /* Return a string representing the type of the expression. */
static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
@ -535,8 +534,10 @@ static RegisterPrimOp primop_isPath({
struct CompareValues struct CompareValues
{ {
EvalState & state; EvalState & state;
const Pos & pos;
const std::string errorCtx;
CompareValues(EvalState & state) : state(state) { }; CompareValues(EvalState & state, const Pos & pos, const std::string && errorCtx) : state(state), pos(pos), errorCtx(std::move(errorCtx)) { };
bool operator () (Value * v1, Value * v2) const bool operator () (Value * v1, Value * v2) const
{ {
@ -545,7 +546,10 @@ struct CompareValues
if (v1->type() == nInt && v2->type() == nFloat) if (v1->type() == nInt && v2->type() == nFloat)
return v1->integer < v2->fpoint; return v1->integer < v2->fpoint;
if (v1->type() != v2->type()) if (v1->type() != v2->type())
throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2)); throw EvalError({
.msg = hintfmt("%s: cannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)),
.errPos = pos,
});
switch (v1->type()) { switch (v1->type()) {
case nInt: case nInt:
return v1->integer < v2->integer; return v1->integer < v2->integer;
@ -567,7 +571,10 @@ struct CompareValues
} }
} }
default: default:
throw EvalError("cannot compare %1% with %2%", showType(*v1), showType(*v2)); throw EvalError({
.msg = hintfmt("%s: cannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)),
.errPos = pos,
});
} }
} }
}; };
@ -619,7 +626,7 @@ static Bindings::iterator getAttr(
static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.genericClosure");
/* Get the start set. */ /* Get the start set. */
Bindings::iterator startSet = getAttr( Bindings::iterator startSet = getAttr(
@ -630,7 +637,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
pos pos
); );
state.forceList(*startSet->value, pos); state.forceList(*startSet->value, pos, "While evaluating the `startSet` attribute passed to builtins.genericClosure");
ValueList workSet; ValueList workSet;
for (auto elem : startSet->value->listItems()) for (auto elem : startSet->value->listItems())
@ -645,7 +652,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
pos pos
); );
state.forceValue(*op->value, pos); state.forceFunction(*op->value, pos, "While evaluating the `operator` attribute passed to builtins.genericClosure");
/* Construct the closure by applying the operator to element of /* Construct the closure by applying the operator to element of
`workSet', adding the result to `workSet', continuing until `workSet', adding the result to `workSet', continuing until
@ -653,19 +660,19 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
ValueList res; ValueList res;
// `doneKeys' doesn't need to be a GC root, because its values are // `doneKeys' doesn't need to be a GC root, because its values are
// reachable from res. // reachable from res.
auto cmp = CompareValues(state); auto cmp = CompareValues(state, pos, "While comparing the `key` attributes of two genericClosure elements");
std::set<Value *, decltype(cmp)> doneKeys(cmp); std::set<Value *, decltype(cmp)> doneKeys(cmp);
while (!workSet.empty()) { while (!workSet.empty()) {
Value * e = *(workSet.begin()); Value * e = *(workSet.begin());
workSet.pop_front(); workSet.pop_front();
state.forceAttrs(*e, pos); state.forceAttrs(*e, pos, "While evaluating one item to be part of the genericClosure");
Bindings::iterator key = Bindings::iterator key =
e->attrs->find(state.sKey); e->attrs->find(state.sKey);
if (key == e->attrs->end()) if (key == e->attrs->end())
throw EvalError({ throw EvalError({
.msg = hintfmt("attribute 'key' required"), .msg = hintfmt("While evaluating one of the attribute sets to be part of the genericClosure: attribute `key` required"),
.errPos = pos .errPos = pos
}); });
state.forceValue(*key->value, pos); state.forceValue(*key->value, pos);
@ -676,7 +683,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar
/* Call the `operator' function with `e' as argument. */ /* Call the `operator' function with `e' as argument. */
Value call; Value call;
call.mkApp(op->value, e); call.mkApp(op->value, e);
state.forceList(call, pos); state.forceList(call, pos, "While evaluating the return value of the `operator` passed to builtins.genericClosure");
/* Add the values returned by the operator to the work set. */ /* Add the values returned by the operator to the work set. */
for (auto elem : call.listItems()) { for (auto elem : call.listItems()) {
@ -750,7 +757,7 @@ static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info {
static void prim_ceil(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_ceil(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos)); auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "While evaluating the first argument passed to builtins.ceil");
v.mkInt(ceil(value)); v.mkInt(ceil(value));
} }
@ -769,7 +776,7 @@ static RegisterPrimOp primop_ceil({
static void prim_floor(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_floor(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto value = state.forceFloat(*args[0], args[0]->determinePos(pos)); auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "While evaluating the first argument passed to builtins.floor");
v.mkInt(floor(value)); v.mkInt(floor(value));
} }
@ -826,7 +833,7 @@ static RegisterPrimOp primop_tryEval({
/* Return an environment variable. Use with care. */ /* Return an environment variable. Use with care. */
static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
std::string name(state.forceStringNoCtx(*args[0], pos)); std::string name(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.getEnv"));
v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or(""));
} }
@ -924,7 +931,7 @@ static RegisterPrimOp primop_trace({
derivation. */ derivation. */
static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.derivationStrict");
/* Figure out the name first (for stack backtraces). */ /* Figure out the name first (for stack backtraces). */
Bindings::iterator attr = getAttr( Bindings::iterator attr = getAttr(
@ -938,7 +945,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
std::string drvName; std::string drvName;
Pos & posDrvName(*attr->pos); Pos & posDrvName(*attr->pos);
try { try {
drvName = state.forceStringNoCtx(*attr->value, pos); drvName = state.forceStringNoCtx(*attr->value, pos, "While evaluating the `name` attribute passed to builtins.derivationStrict");
} catch (Error & e) { } catch (Error & e) {
e.addTrace(posDrvName, "while evaluating the derivation attribute 'name'"); e.addTrace(posDrvName, "while evaluating the derivation attribute 'name'");
throw; throw;
@ -948,14 +955,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
std::ostringstream jsonBuf; std::ostringstream jsonBuf;
std::unique_ptr<JSONObject> jsonObject; std::unique_ptr<JSONObject> jsonObject;
attr = args[0]->attrs->find(state.sStructuredAttrs); attr = args[0]->attrs->find(state.sStructuredAttrs);
if (attr != args[0]->attrs->end() && state.forceBool(*attr->value, pos)) if (attr != args[0]->attrs->end() && state.forceBool(*attr->value, pos, "While evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict"))
jsonObject = std::make_unique<JSONObject>(jsonBuf); jsonObject = std::make_unique<JSONObject>(jsonBuf);
/* Check whether null attributes should be ignored. */ /* Check whether null attributes should be ignored. */
bool ignoreNulls = false; bool ignoreNulls = false;
attr = args[0]->attrs->find(state.sIgnoreNulls); attr = args[0]->attrs->find(state.sIgnoreNulls);
if (attr != args[0]->attrs->end()) if (attr != args[0]->attrs->end())
ignoreNulls = state.forceBool(*attr->value, pos); ignoreNulls = state.forceBool(*attr->value, pos, "While evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict");
/* Build the derivation expression by processing the attributes. */ /* Build the derivation expression by processing the attributes. */
Derivation drv; Derivation drv;
@ -1021,7 +1028,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
} }
if (i->name == state.sContentAddressed) { if (i->name == state.sContentAddressed) {
contentAddressed = state.forceBool(*i->value, pos); contentAddressed = state.forceBool(*i->value, pos, "While evaluating the `__contentAddressed` attribute passed to builtins.derivationStrict");
if (contentAddressed) if (contentAddressed)
settings.requireExperimentalFeature(Xp::CaDerivations); settings.requireExperimentalFeature(Xp::CaDerivations);
} }
@ -1029,7 +1036,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
/* The `args' attribute is special: it supplies the /* The `args' attribute is special: it supplies the
command-line arguments to the builder. */ command-line arguments to the builder. */
else if (i->name == state.sArgs) { else if (i->name == state.sArgs) {
state.forceList(*i->value, pos); state.forceList(*i->value, pos, "While evaluating the `args` attribute passed to builtins.derivationStrict");
for (auto elem : i->value->listItems()) { for (auto elem : i->value->listItems()) {
auto s = state.coerceToString(posDrvName, *elem, context, true).toOwned(); auto s = state.coerceToString(posDrvName, *elem, context, true).toOwned();
drv.args.push_back(s); drv.args.push_back(s);
@ -1048,21 +1055,21 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * *
printValueAsJSON(state, true, *i->value, pos, placeholder, context); printValueAsJSON(state, true, *i->value, pos, placeholder, context);
if (i->name == state.sBuilder) if (i->name == state.sBuilder)
drv.builder = state.forceString(*i->value, context, posDrvName); drv.builder = state.forceString(*i->value, context, posDrvName, "While evaluating the `builder` attribute passed to builtins.derivationStrict");
else if (i->name == state.sSystem) else if (i->name == state.sSystem)
drv.platform = state.forceStringNoCtx(*i->value, posDrvName); drv.platform = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `system` attribute passed to builtins.derivationStrict");
else if (i->name == state.sOutputHash) else if (i->name == state.sOutputHash)
outputHash = state.forceStringNoCtx(*i->value, posDrvName); outputHash = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHash` attribute passed to builtins.derivationStrict");
else if (i->name == state.sOutputHashAlgo) else if (i->name == state.sOutputHashAlgo)
outputHashAlgo = state.forceStringNoCtx(*i->value, posDrvName); outputHashAlgo = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashAlgo` attribute passed to builtins.derivationStrict");
else if (i->name == state.sOutputHashMode) else if (i->name == state.sOutputHashMode)
handleHashMode(state.forceStringNoCtx(*i->value, posDrvName)); handleHashMode(state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashMode` attribute passed to builtins.derivationStrict"));
else if (i->name == state.sOutputs) { else if (i->name == state.sOutputs) {
/* Require outputs to be a list of strings. */ /* Require outputs to be a list of strings. */
state.forceList(*i->value, posDrvName); state.forceList(*i->value, posDrvName, "While evaluating the `outputs` attribute passed to builtins.derivationStrict");
Strings ss; Strings ss;
for (auto elem : i->value->listItems()) for (auto elem : i->value->listItems())
ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName)); ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName, "While evaluating an element of the `outputs` attribute passed to builtins.derivationStrict"));
handleOutputs(ss); handleOutputs(ss);
} }
@ -1275,7 +1282,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info {
out. */ out. */
static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos))); v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.placeholder")));
} }
static RegisterPrimOp primop_placeholder({ static RegisterPrimOp primop_placeholder({
@ -1467,17 +1474,17 @@ static RegisterPrimOp primop_readFile({
which are desugared to 'findFile __nixPath "x"'. */ which are desugared to 'findFile __nixPath "x"'. */
static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceList(*args[0], pos); state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.findFile");
SearchPath searchPath; SearchPath searchPath;
for (auto v2 : args[0]->listItems()) { for (auto v2 : args[0]->listItems()) {
state.forceAttrs(*v2, pos); state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.findFile");
std::string prefix; std::string prefix;
Bindings::iterator i = v2->attrs->find(state.sPrefix); Bindings::iterator i = v2->attrs->find(state.sPrefix);
if (i != v2->attrs->end()) if (i != v2->attrs->end())
prefix = state.forceStringNoCtx(*i->value, pos); prefix = state.forceStringNoCtx(*i->value, pos, "While evaluating the `prefix` attribute passed to an element of the list passed to builtins.findFile");
i = getAttr( i = getAttr(
state, state,
@ -1504,7 +1511,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va
searchPath.emplace_back(prefix, path); searchPath.emplace_back(prefix, path);
} }
auto path = state.forceStringNoCtx(*args[1], pos); auto path = state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.findFile");
v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos))); v.mkPath(state.checkSourcePath(state.findFile(searchPath, path, pos)));
} }
@ -1518,7 +1525,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info {
/* Return the cryptographic hash of a file in base-16. */ /* Return the cryptographic hash of a file in base-16. */
static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto type = state.forceStringNoCtx(*args[0], pos); auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashFile");
std::optional<HashType> ht = parseHashType(type); std::optional<HashType> ht = parseHashType(type);
if (!ht) if (!ht)
throw Error({ throw Error({
@ -1725,7 +1732,7 @@ static RegisterPrimOp primop_toJSON({
/* Parse a JSON string to a value. */ /* Parse a JSON string to a value. */
static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto s = state.forceStringNoCtx(*args[0], pos); auto s = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.fromJSON");
try { try {
parseJSON(state, s, v); parseJSON(state, s, v);
} catch (JSONParseError &e) { } catch (JSONParseError &e) {
@ -1754,8 +1761,8 @@ static RegisterPrimOp primop_fromJSON({
static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
std::string name(state.forceStringNoCtx(*args[0], pos)); std::string name(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.toFile"));
std::string contents(state.forceString(*args[1], context, pos)); std::string contents(state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.toFile"));
StorePathSet refs; StorePathSet refs;
@ -1912,7 +1919,7 @@ static void addPath(
Value res; Value res;
state.callFunction(*filterFun, 2, args, res, pos); state.callFunction(*filterFun, 2, args, res, pos);
return state.forceBool(res, pos); return state.forceBool(res, pos, "While evaluating the return value of the path filter function");
}) : defaultPathFilter; }) : defaultPathFilter;
std::optional<StorePath> expectedStorePath; std::optional<StorePath> expectedStorePath;
@ -2009,7 +2016,7 @@ static RegisterPrimOp primop_filterSource({
static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.path");
Path path; Path path;
std::string name; std::string name;
Value * filterFun = nullptr; Value * filterFun = nullptr;
@ -2022,14 +2029,14 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value
if (n == "path") if (n == "path")
path = state.coerceToPath(*attr.pos, *attr.value, context); path = state.coerceToPath(*attr.pos, *attr.value, context);
else if (attr.name == state.sName) else if (attr.name == state.sName)
name = state.forceStringNoCtx(*attr.value, *attr.pos); name = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the `name` attribute passed to builtins.path");
else if (n == "filter") { else if (n == "filter") {
state.forceValue(*attr.value, pos); state.forceValue(*attr.value, pos);
filterFun = attr.value; filterFun = attr.value;
} else if (n == "recursive") } else if (n == "recursive")
method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos) }; method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos, "While evaluating the `recursive` attribute passed to builtins.path") };
else if (n == "sha256") else if (n == "sha256")
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256); expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `sha256` attribute passed to builtins.path"), htSHA256);
else else
throw EvalError({ throw EvalError({
.msg = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name), .msg = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name),
@ -2092,7 +2099,7 @@ static RegisterPrimOp primop_path({
strings. */ strings. */
static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrNames");
state.mkList(v, args[0]->attrs->size()); state.mkList(v, args[0]->attrs->size());
@ -2119,7 +2126,7 @@ static RegisterPrimOp primop_attrNames({
order as attrNames. */ order as attrNames. */
static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrValues");
state.mkList(v, args[0]->attrs->size()); state.mkList(v, args[0]->attrs->size());
@ -2150,8 +2157,8 @@ static RegisterPrimOp primop_attrValues({
/* Dynamic version of the `.' operator. */ /* Dynamic version of the `.' operator. */
void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.getAttr");
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.getAttr");
Bindings::iterator i = getAttr( Bindings::iterator i = getAttr(
state, state,
"getAttr", "getAttr",
@ -2180,8 +2187,8 @@ static RegisterPrimOp primop_getAttr({
/* Return position information of the specified attribute. */ /* Return position information of the specified attribute. */
static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.unsafeGetAttrPos");
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.unsafeGetAttrPos");
Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr));
if (i == args[1]->attrs->end()) if (i == args[1]->attrs->end())
v.mkNull(); v.mkNull();
@ -2198,8 +2205,8 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info {
/* Dynamic version of the `?' operator. */ /* Dynamic version of the `?' operator. */
static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto attr = state.forceStringNoCtx(*args[0], pos); auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hasAttr");
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.hasAttr");
v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end());
} }
@ -2232,8 +2239,8 @@ static RegisterPrimOp primop_isAttrs({
static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.removeAttrs");
state.forceList(*args[1], pos); state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.removeAttrs");
/* Get the attribute names to be removed. /* Get the attribute names to be removed.
We keep them as Attrs instead of Symbols so std::set_difference We keep them as Attrs instead of Symbols so std::set_difference
@ -2241,7 +2248,7 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args,
boost::container::small_vector<Attr, 64> names; boost::container::small_vector<Attr, 64> names;
names.reserve(args[1]->listSize()); names.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) { for (auto elem : args[1]->listItems()) {
state.forceStringNoCtx(*elem, pos); state.forceStringNoCtx(*elem, pos, "While evaluating the values of the second argument passed to builtins.removeAttrs");
names.emplace_back(state.symbols.create(elem->string.s), nullptr); names.emplace_back(state.symbols.create(elem->string.s), nullptr);
} }
std::sort(names.begin(), names.end()); std::sort(names.begin(), names.end());
@ -2280,14 +2287,14 @@ static RegisterPrimOp primop_removeAttrs({
name, the first takes precedence. */ name, the first takes precedence. */
static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceList(*args[0], pos); state.forceList(*args[0], pos, "While evaluating the argument passed to builtins.listToAttrs");
auto attrs = state.buildBindings(args[0]->listSize()); auto attrs = state.buildBindings(args[0]->listSize());
std::set<Symbol> seen; std::set<Symbol> seen;
for (auto v2 : args[0]->listItems()) { for (auto v2 : args[0]->listItems()) {
state.forceAttrs(*v2, pos); state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.listToAttrs");
Bindings::iterator j = getAttr( Bindings::iterator j = getAttr(
state, state,
@ -2297,7 +2304,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args,
pos pos
); );
auto name = state.forceStringNoCtx(*j->value, *j->pos); auto name = state.forceStringNoCtx(*j->value, *j->pos, "While evaluating the `name` attribute of an element of the list passed to builtins.listToAttrs");
Symbol sym = state.symbols.create(name); Symbol sym = state.symbols.create(name);
if (seen.insert(sym).second) { if (seen.insert(sym).second) {
@ -2342,8 +2349,8 @@ static RegisterPrimOp primop_listToAttrs({
static void prim_intersectAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_intersectAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.intersectAttrs");
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.intersectAttrs");
auto attrs = state.buildBindings(std::min(args[0]->attrs->size(), args[1]->attrs->size())); auto attrs = state.buildBindings(std::min(args[0]->attrs->size(), args[1]->attrs->size()));
@ -2368,14 +2375,14 @@ static RegisterPrimOp primop_intersectAttrs({
static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos)); Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.catAttrs"));
state.forceList(*args[1], pos); state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.catAttrs");
Value * res[args[1]->listSize()]; Value * res[args[1]->listSize()];
unsigned int found = 0; unsigned int found = 0;
for (auto v2 : args[1]->listItems()) { for (auto v2 : args[1]->listItems()) {
state.forceAttrs(*v2, pos); state.forceAttrs(*v2, pos, "While evaluating an element in the list passed as second argument to builtins.catAttrs");
Bindings::iterator i = v2->attrs->find(attrName); Bindings::iterator i = v2->attrs->find(attrName);
if (i != v2->attrs->end()) if (i != v2->attrs->end())
res[found++] = i->value; res[found++] = i->value;
@ -2448,7 +2455,7 @@ static RegisterPrimOp primop_functionArgs({
/* */ /* */
static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.mapAttrs");
auto attrs = state.buildBindings(args[1]->attrs->size()); auto attrs = state.buildBindings(args[1]->attrs->size());
@ -2489,15 +2496,15 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args
std::map<Symbol, std::pair<size_t, Value * *>> attrsSeen; std::map<Symbol, std::pair<size_t, Value * *>> attrsSeen;
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.zipAttrsWith");
state.forceList(*args[1], pos); state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.zipAttrsWith");
const auto listSize = args[1]->listSize(); const auto listSize = args[1]->listSize();
const auto listElems = args[1]->listElems(); const auto listElems = args[1]->listElems();
for (unsigned int n = 0; n < listSize; ++n) { for (unsigned int n = 0; n < listSize; ++n) {
Value * vElem = listElems[n]; Value * vElem = listElems[n];
try { try {
state.forceAttrs(*vElem, noPos); state.forceAttrs(*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++; attrsSeen[attr.name].first++;
} catch (TypeError & e) { } catch (TypeError & e) {
@ -2587,7 +2594,7 @@ static RegisterPrimOp primop_isList({
static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v) static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v)
{ {
state.forceList(list, pos); state.forceList(list, pos, "While evaluating the first argument passed to builtins.elemAt");
if (n < 0 || (unsigned int) n >= list.listSize()) if (n < 0 || (unsigned int) n >= list.listSize())
throw Error({ throw Error({
.msg = hintfmt("list index %1% is out of bounds", n), .msg = hintfmt("list index %1% is out of bounds", n),
@ -2600,7 +2607,7 @@ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Valu
/* Return the n-1'th element of a list. */ /* Return the n-1'th element of a list. */
static void prim_elemAt(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_elemAt(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
elemAt(state, pos, *args[0], state.forceInt(*args[1], pos), v); elemAt(state, pos, *args[0], state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.elemAt"), v);
} }
static RegisterPrimOp primop_elemAt({ static RegisterPrimOp primop_elemAt({
@ -2635,7 +2642,7 @@ static RegisterPrimOp primop_head({
don't want to use it! */ don't want to use it! */
static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceList(*args[0], pos); state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.tail");
if (args[0]->listSize() == 0) if (args[0]->listSize() == 0)
throw Error({ throw Error({
.msg = hintfmt("'tail' called on an empty list"), .msg = hintfmt("'tail' called on an empty list"),
@ -2666,13 +2673,17 @@ static RegisterPrimOp primop_tail({
/* Apply a function to every element of a list. */ /* Apply a function to every element of a list. */
static void prim_map(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_map(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceList(*args[1], pos); state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.map");
state.mkList(v, args[1]->listSize()); state.mkList(v, args[1]->listSize());
if (args[1]->listSize() > 0) {
state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.map");
for (unsigned int n = 0; n < v.listSize(); ++n) for (unsigned int n = 0; n < v.listSize(); ++n)
(v.listElems()[n] = state.allocValue())->mkApp( (v.listElems()[n] = state.allocValue())->mkApp(
args[0], args[1]->listElems()[n]); args[0], args[1]->listElems()[n]);
};
} }
static RegisterPrimOp primop_map({ static RegisterPrimOp primop_map({
@ -2696,8 +2707,14 @@ static RegisterPrimOp primop_map({
returns true. */ returns true. */
static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceFunction(*args[0], pos); state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.filter");
state.forceList(*args[1], pos);
if (args[1]->listSize() == 0) {
v = *args[1];
return;
}
state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.filter");
// FIXME: putting this on the stack is risky. // FIXME: putting this on the stack is risky.
Value * vs[args[1]->listSize()]; Value * vs[args[1]->listSize()];
@ -2707,7 +2724,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu
for (unsigned int n = 0; n < args[1]->listSize(); ++n) { for (unsigned int n = 0; n < args[1]->listSize(); ++n) {
Value res; 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, pos)) if (state.forceBool(res, pos, "While evaluating the return value of the filtering function passed to builtins.filter"))
vs[k++] = args[1]->listElems()[n]; vs[k++] = args[1]->listElems()[n];
else else
same = false; same = false;
@ -2735,7 +2752,7 @@ static RegisterPrimOp primop_filter({
static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
bool res = false; bool res = false;
state.forceList(*args[1], pos); state.forceList(*args[1], pos, "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)) { if (state.eqValues(*args[0], *elem)) {
res = true; res = true;
@ -2757,8 +2774,8 @@ static RegisterPrimOp primop_elem({
/* Concatenate a list of lists. */ /* Concatenate a list of lists. */
static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceList(*args[0], pos); state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.concatLists");
state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos); state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos, "While evaluating a value of the list passed to builtins.concatLists");
} }
static RegisterPrimOp primop_concatLists({ static RegisterPrimOp primop_concatLists({
@ -2773,7 +2790,7 @@ static RegisterPrimOp primop_concatLists({
/* Return the length of a list. This is an O(1) time operation. */ /* Return the length of a list. This is an O(1) time operation. */
static void prim_length(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_length(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceList(*args[0], pos); state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.length");
v.mkInt(args[0]->listSize()); v.mkInt(args[0]->listSize());
} }
@ -2790,8 +2807,8 @@ static RegisterPrimOp primop_length({
right. The operator is applied strictly. */ right. The operator is applied strictly. */
static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.foldlStrict");
state.forceList(*args[2], pos); state.forceList(*args[2], pos, "While evaluating the third argument passed to builtins.foldlStrict");
if (args[2]->listSize()) { if (args[2]->listSize()) {
Value * vCur = args[1]; Value * vCur = args[1];
@ -2823,13 +2840,13 @@ static RegisterPrimOp primop_foldlStrict({
static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * args, Value & v) static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos, std::string("While evaluating the first argument passed to builtins.") + (any ? "any" : "all"));
state.forceList(*args[1], pos); state.forceList(*args[1], pos, std::string("While evaluating the second argument passed to builtins.") + (any ? "any" : "all"));
Value vTmp; Value vTmp;
for (auto elem : args[1]->listItems()) { for (auto elem : args[1]->listItems()) {
state.callFunction(*args[0], *elem, vTmp, pos); state.callFunction(*args[0], *elem, vTmp, pos);
bool res = state.forceBool(vTmp, pos); bool res = state.forceBool(vTmp, pos, std::string("While evaluating the return value of the function passed to builtins.") + (any ? "any" : "all"));
if (res == any) { if (res == any) {
v.mkBool(any); v.mkBool(any);
return; return;
@ -2872,7 +2889,7 @@ static RegisterPrimOp primop_all({
static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto len = state.forceInt(*args[1], pos); auto len = state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.genList");
if (len < 0) if (len < 0)
throw EvalError({ throw EvalError({
@ -2910,10 +2927,16 @@ static void prim_lessThan(EvalState & state, const Pos & pos, Value * * args, Va
static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceFunction(*args[0], pos); state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.sort");
state.forceList(*args[1], pos);
auto len = args[1]->listSize(); auto len = args[1]->listSize();
if (len == 0) {
v = *args[1];
return;
}
state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.sort");
state.mkList(v, len); state.mkList(v, len);
for (unsigned int n = 0; n < len; ++n) { for (unsigned int n = 0; n < len; ++n) {
state.forceValue(*args[1]->listElems()[n], pos); state.forceValue(*args[1]->listElems()[n], pos);
@ -2924,12 +2947,12 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value
/* Optimization: if the comparator is lessThan, bypass /* Optimization: if the comparator is lessThan, bypass
callFunction. */ callFunction. */
if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan) if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan)
return CompareValues(state)(a, b); return CompareValues(state, pos, "While evaluating the ordering function passed to builtins.sort")(a, b);
Value * vs[] = {a, b}; Value * vs[] = {a, b};
Value vBool; Value vBool;
state.callFunction(*args[0], 2, vs, vBool, pos); state.callFunction(*args[0], 2, vs, vBool, pos);
return state.forceBool(vBool, pos); return state.forceBool(vBool, pos, "While evaluating the return value of the sorting function passed to builtins.sort");
}; };
/* FIXME: std::sort can segfault if the comparator is not a strict /* FIXME: std::sort can segfault if the comparator is not a strict
@ -2961,8 +2984,8 @@ static RegisterPrimOp primop_sort({
static void prim_partition(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_partition(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.partition");
state.forceList(*args[1], pos); state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.partition");
auto len = args[1]->listSize(); auto len = args[1]->listSize();
@ -2973,7 +2996,7 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V
state.forceValue(*vElem, pos); state.forceValue(*vElem, pos);
Value res; Value res;
state.callFunction(*args[0], *vElem, res, pos); state.callFunction(*args[0], *vElem, res, pos);
if (state.forceBool(res, pos)) if (state.forceBool(res, pos, "While evaluating the return value of the partition function passed to builtins.partition"))
right.push_back(vElem); right.push_back(vElem);
else else
wrong.push_back(vElem); wrong.push_back(vElem);
@ -3021,15 +3044,15 @@ static RegisterPrimOp primop_partition({
static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.groupBy");
state.forceList(*args[1], pos); state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.groupBy");
ValueVectorMap attrs; ValueVectorMap attrs;
for (auto vElem : args[1]->listItems()) { for (auto vElem : args[1]->listItems()) {
Value res; Value res;
state.callFunction(*args[0], *vElem, res, pos); state.callFunction(*args[0], *vElem, res, pos);
auto name = state.forceStringNoCtx(res, pos); auto name = state.forceStringNoCtx(res, pos, "While evaluating the return value of the grouping function passed to builtins.groupBy");
Symbol sym = state.symbols.create(name); Symbol sym = state.symbols.create(name);
auto vector = attrs.try_emplace(sym, ValueVector()).first; auto vector = attrs.try_emplace(sym, ValueVector()).first;
vector->second.push_back(vElem); vector->second.push_back(vElem);
@ -3073,8 +3096,8 @@ static RegisterPrimOp primop_groupBy({
static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceFunction(*args[0], pos); state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.concatMap");
state.forceList(*args[1], pos); state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.concatMap");
auto nrLists = args[1]->listSize(); auto nrLists = args[1]->listSize();
Value lists[nrLists]; Value lists[nrLists];
@ -3084,7 +3107,7 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V
Value * vElem = args[1]->listElems()[n]; Value * vElem = args[1]->listElems()[n];
state.callFunction(*args[0], *vElem, lists[n], pos); state.callFunction(*args[0], *vElem, lists[n], pos);
try { try {
state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos))); state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos)), "While evaluating the return value of the function passed to buitlins.concatMap");
} catch (TypeError &e) { } catch (TypeError &e) {
e.addTrace(pos, hintfmt("while invoking '%s'", "concatMap")); e.addTrace(pos, hintfmt("while invoking '%s'", "concatMap"));
throw; throw;
@ -3123,9 +3146,11 @@ static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value &
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
if (args[0]->type() == nFloat || args[1]->type() == nFloat) if (args[0]->type() == nFloat || args[1]->type() == nFloat)
v.mkFloat(state.forceFloat(*args[0], pos) + state.forceFloat(*args[1], pos)); v.mkFloat(state.forceFloat(*args[0], pos, "While evaluating the first argument of the addition")
+ state.forceFloat(*args[1], pos, "While evaluating the second argument of the addition"));
else else
v.mkInt(state.forceInt(*args[0], pos) + state.forceInt(*args[1], pos)); v.mkInt( state.forceInt(*args[0], pos, "While evaluating the first argument of the addition")
+ state.forceInt(*args[1], pos, "While evaluating the second argument of the addition"));
} }
static RegisterPrimOp primop_add({ static RegisterPrimOp primop_add({
@ -3142,9 +3167,11 @@ static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value &
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
if (args[0]->type() == nFloat || args[1]->type() == nFloat) if (args[0]->type() == nFloat || args[1]->type() == nFloat)
v.mkFloat(state.forceFloat(*args[0], pos) - state.forceFloat(*args[1], pos)); v.mkFloat(state.forceFloat(*args[0], pos, "While evaluating the first argument of the subtraction")
- state.forceFloat(*args[1], pos, "While evaluating the second argument of the subtraction"));
else else
v.mkInt(state.forceInt(*args[0], pos) - state.forceInt(*args[1], pos)); v.mkInt( state.forceInt(*args[0], pos, "While evaluating the first argument of the subtraction")
- state.forceInt(*args[1], pos, "While evaluating the second argument of the subtraction"));
} }
static RegisterPrimOp primop_sub({ static RegisterPrimOp primop_sub({
@ -3161,9 +3188,11 @@ static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value &
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
if (args[0]->type() == nFloat || args[1]->type() == nFloat) if (args[0]->type() == nFloat || args[1]->type() == nFloat)
v.mkFloat(state.forceFloat(*args[0], pos) * state.forceFloat(*args[1], pos)); v.mkFloat(state.forceFloat(*args[0], pos, "While evaluating the first of the multiplication")
* state.forceFloat(*args[1], pos, "While evaluating the second argument of the multiplication"));
else else
v.mkInt(state.forceInt(*args[0], pos) * state.forceInt(*args[1], pos)); v.mkInt( state.forceInt(*args[0], pos, "While evaluating the first argument of the multiplication")
* state.forceInt(*args[1], pos, "While evaluating the second argument of the multiplication"));
} }
static RegisterPrimOp primop_mul({ static RegisterPrimOp primop_mul({
@ -3180,7 +3209,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
NixFloat f2 = state.forceFloat(*args[1], pos); NixFloat f2 = state.forceFloat(*args[1], pos, "While evaluating the second operand of the division");
if (f2 == 0) if (f2 == 0)
throw EvalError({ throw EvalError({
.msg = hintfmt("division by zero"), .msg = hintfmt("division by zero"),
@ -3188,10 +3217,10 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value &
}); });
if (args[0]->type() == nFloat || args[1]->type() == nFloat) { if (args[0]->type() == nFloat || args[1]->type() == nFloat) {
v.mkFloat(state.forceFloat(*args[0], pos) / state.forceFloat(*args[1], pos)); v.mkFloat(state.forceFloat(*args[0], pos, "While evaluating the first operand of the division") / f2);
} else { } else {
NixInt i1 = state.forceInt(*args[0], pos); NixInt i1 = state.forceInt(*args[0], pos, "While evaluating the first operand of the division");
NixInt i2 = state.forceInt(*args[1], pos); NixInt i2 = state.forceInt(*args[1], pos, "While evaluating the second operand of the division");
/* Avoid division overflow as it might raise SIGFPE. */ /* Avoid division overflow as it might raise SIGFPE. */
if (i1 == std::numeric_limits<NixInt>::min() && i2 == -1) if (i1 == std::numeric_limits<NixInt>::min() && i2 == -1)
throw EvalError({ throw EvalError({
@ -3214,7 +3243,8 @@ static RegisterPrimOp primop_div({
static void prim_bitAnd(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_bitAnd(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
v.mkInt(state.forceInt(*args[0], pos) & state.forceInt(*args[1], pos)); v.mkInt(state.forceInt(*args[0], pos, "While evaluating the first argument passed to builtins.bitAnd")
& state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.bitAnd"));
} }
static RegisterPrimOp primop_bitAnd({ static RegisterPrimOp primop_bitAnd({
@ -3228,7 +3258,8 @@ static RegisterPrimOp primop_bitAnd({
static void prim_bitOr(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_bitOr(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
v.mkInt(state.forceInt(*args[0], pos) | state.forceInt(*args[1], pos)); v.mkInt(state.forceInt(*args[0], pos, "While evaluating the first argument passed to builtins.bitOr")
| state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.bitOr"));
} }
static RegisterPrimOp primop_bitOr({ static RegisterPrimOp primop_bitOr({
@ -3242,7 +3273,8 @@ static RegisterPrimOp primop_bitOr({
static void prim_bitXor(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_bitXor(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
v.mkInt(state.forceInt(*args[0], pos) ^ state.forceInt(*args[1], pos)); v.mkInt(state.forceInt(*args[0], pos, "While evaluating the first argument passed to builtins.bitXor")
^ state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.bitXor"));
} }
static RegisterPrimOp primop_bitXor({ static RegisterPrimOp primop_bitXor({
@ -3258,7 +3290,8 @@ static void prim_lessThan(EvalState & state, const Pos & pos, Value * * args, Va
{ {
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
state.forceValue(*args[1], pos); state.forceValue(*args[1], pos);
CompareValues comp{state}; // pos is exact here, no need for a message.
CompareValues comp(state, pos, "");
v.mkBool(comp(args[0], args[1])); v.mkBool(comp(args[0], args[1]));
} }
@ -3319,8 +3352,8 @@ static RegisterPrimOp primop_toString({
non-negative. */ non-negative. */
static void prim_substring(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_substring(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
int start = state.forceInt(*args[0], pos); int start = state.forceInt(*args[0], pos, "While evaluating the first argument passed to builtins.substring");
int len = state.forceInt(*args[1], pos); int len = state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.substring");
PathSet context; PathSet context;
auto s = state.coerceToString(pos, *args[2], context); auto s = state.coerceToString(pos, *args[2], context);
@ -3373,7 +3406,7 @@ static RegisterPrimOp primop_stringLength({
/* Return the cryptographic hash of a string in base-16. */ /* Return the cryptographic hash of a string in base-16. */
static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto type = state.forceStringNoCtx(*args[0], pos); auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashString");
std::optional<HashType> ht = parseHashType(type); std::optional<HashType> ht = parseHashType(type);
if (!ht) if (!ht)
throw Error({ throw Error({
@ -3382,7 +3415,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args,
}); });
PathSet context; // discarded PathSet context; // discarded
auto s = state.forceString(*args[1], context, pos); auto s = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.hashString");
v.mkString(hashString(*ht, s).to_string(Base16, false)); v.mkString(hashString(*ht, s).to_string(Base16, false));
} }
@ -3421,14 +3454,14 @@ std::shared_ptr<RegexCache> makeRegexCache()
void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v) void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto re = state.forceStringNoCtx(*args[0], pos); auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.match");
try { try {
auto regex = state.regexCache->get(re); auto regex = state.regexCache->get(re);
PathSet context; PathSet context;
const auto str = state.forceString(*args[1], context, pos); const auto str = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.match");
std::cmatch match; std::cmatch match;
if (!std::regex_match(str.begin(), str.end(), match, regex)) { if (!std::regex_match(str.begin(), str.end(), match, regex)) {
@ -3502,14 +3535,14 @@ static RegisterPrimOp primop_match({
non-matching parts interleaved by the lists of the matching groups. */ non-matching parts interleaved by the lists of the matching groups. */
void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v) void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto re = state.forceStringNoCtx(*args[0], pos); auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.split");
try { try {
auto regex = state.regexCache->get(re); auto regex = state.regexCache->get(re);
PathSet context; PathSet context;
const auto str = state.forceString(*args[1], context, pos); const auto str = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.split");
auto begin = std::cregex_iterator(str.begin(), str.end(), regex); auto begin = std::cregex_iterator(str.begin(), str.end(), regex);
auto end = std::cregex_iterator(); auto end = std::cregex_iterator();
@ -3608,8 +3641,8 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * *
{ {
PathSet context; PathSet context;
auto sep = state.forceString(*args[0], context, pos); auto sep = state.forceString(*args[0], context, pos, "While evaluating the first argument passed to builtins.concatStringsSep");
state.forceList(*args[1], pos); state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.concatStringsSep");
std::string res; std::string res;
res.reserve((args[1]->listSize() + 32) * sep.size()); res.reserve((args[1]->listSize() + 32) * sep.size());
@ -3636,8 +3669,8 @@ static RegisterPrimOp primop_concatStringsSep({
static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
state.forceList(*args[0], pos); state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.replaceStrings");
state.forceList(*args[1], pos); state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.replaceStrings");
if (args[0]->listSize() != args[1]->listSize()) if (args[0]->listSize() != args[1]->listSize())
throw EvalError({ throw EvalError({
.msg = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"), .msg = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"),
@ -3647,18 +3680,18 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar
std::vector<std::string> from; std::vector<std::string> from;
from.reserve(args[0]->listSize()); from.reserve(args[0]->listSize());
for (auto elem : args[0]->listItems()) for (auto elem : args[0]->listItems())
from.emplace_back(state.forceString(*elem, pos)); from.emplace_back(state.forceString(*elem, pos, "While evaluating one of the strings to replace in builtins.replaceStrings"));
std::vector<std::pair<std::string, PathSet>> to; std::vector<std::pair<std::string, PathSet>> to;
to.reserve(args[1]->listSize()); to.reserve(args[1]->listSize());
for (auto elem : args[1]->listItems()) { for (auto elem : args[1]->listItems()) {
PathSet ctx; PathSet ctx;
auto s = state.forceString(*elem, ctx, pos); auto s = state.forceString(*elem, ctx, pos, "While evaluating one of the replacement strings of builtins.replaceStrings");
to.emplace_back(s, std::move(ctx)); to.emplace_back(s, std::move(ctx));
} }
PathSet context; PathSet context;
auto s = state.forceString(*args[2], context, pos); auto s = state.forceString(*args[2], context, pos, "While evaluating the third argument passed to builtins.replaceStrings");
std::string res; std::string res;
// Loops one past last character to handle the case where 'from' contains an empty string. // Loops one past last character to handle the case where 'from' contains an empty string.
@ -3716,7 +3749,7 @@ static RegisterPrimOp primop_replaceStrings({
static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto name = state.forceStringNoCtx(*args[0], pos); auto name = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.parseDrvName");
DrvName parsed(name); DrvName parsed(name);
auto attrs = state.buildBindings(2); auto attrs = state.buildBindings(2);
attrs.alloc(state.sName).mkString(parsed.name); attrs.alloc(state.sName).mkString(parsed.name);
@ -3740,8 +3773,8 @@ static RegisterPrimOp primop_parseDrvName({
static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto version1 = state.forceStringNoCtx(*args[0], pos); auto version1 = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.compareVersions");
auto version2 = state.forceStringNoCtx(*args[1], pos); auto version2 = state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.compareVersions");
v.mkInt(compareVersions(version1, version2)); v.mkInt(compareVersions(version1, version2));
} }
@ -3760,7 +3793,7 @@ static RegisterPrimOp primop_compareVersions({
static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
auto version = state.forceStringNoCtx(*args[0], pos); auto version = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.splitVersion");
auto iter = version.cbegin(); auto iter = version.cbegin();
Strings components; Strings components;
while (iter != version.cend()) { while (iter != version.cend()) {

View file

@ -17,7 +17,7 @@ static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringCo
static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
state.forceString(*args[0], context, pos); state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.hasContext");
v.mkBool(!context.empty()); v.mkBool(!context.empty());
} }
@ -72,7 +72,7 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args,
Strings outputs; Strings outputs;
}; };
PathSet context; PathSet context;
state.forceString(*args[0], context, pos); state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.getContext");
auto contextInfos = std::map<Path, ContextInfo>(); auto contextInfos = std::map<Path, ContextInfo>();
for (const auto & p : context) { for (const auto & p : context) {
Path drv; Path drv;
@ -136,9 +136,9 @@ static RegisterPrimOp primop_getContext("__getContext", 1, prim_getContext);
static void prim_appendContext(EvalState & state, const Pos & pos, Value * * args, Value & v) static void prim_appendContext(EvalState & state, const Pos & pos, Value * * args, Value & v)
{ {
PathSet context; PathSet context;
auto orig = state.forceString(*args[0], context, pos); auto orig = state.forceString(*args[0], context, pos, "while evaluating the first argument passed to builtins.appendContext");
state.forceAttrs(*args[1], pos); state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.appendContext");
auto sPath = state.symbols.create("path"); auto sPath = state.symbols.create("path");
auto sAllOutputs = state.symbols.create("allOutputs"); auto sAllOutputs = state.symbols.create("allOutputs");
@ -150,16 +150,16 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
}); });
if (!settings.readOnlyMode) if (!settings.readOnlyMode)
state.store->ensurePath(state.store->parseStorePath(i.name)); state.store->ensurePath(state.store->parseStorePath(i.name));
state.forceAttrs(*i.value, *i.pos); state.forceAttrs(*i.value, *i.pos, "While evaluating the value of a string context");
auto iter = i.value->attrs->find(sPath); auto iter = i.value->attrs->find(sPath);
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, *iter->pos)) if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `path` attribute of a string context"))
context.insert(i.name); context.insert(i.name);
} }
iter = i.value->attrs->find(sAllOutputs); iter = i.value->attrs->find(sAllOutputs);
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
if (state.forceBool(*iter->value, *iter->pos)) { if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `allOutputs` attribute of a string context")) {
if (!isDerivation(i.name)) { if (!isDerivation(i.name)) {
throw EvalError({ throw EvalError({
.msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name), .msg = hintfmt("Tried to add all-outputs context of %s, which is not a derivation, to a string", i.name),
@ -172,7 +172,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
iter = i.value->attrs->find(state.sOutputs); iter = i.value->attrs->find(state.sOutputs);
if (iter != i.value->attrs->end()) { if (iter != i.value->attrs->end()) {
state.forceList(*iter->value, *iter->pos); state.forceList(*iter->value, *iter->pos, "While evaluating the `outputs` attribute of a string context");
if (iter->value->listSize() && !isDerivation(i.name)) { if (iter->value->listSize() && !isDerivation(i.name)) {
throw EvalError({ throw EvalError({
.msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name), .msg = hintfmt("Tried to add derivation output context of %s, which is not a derivation, to a string", i.name),
@ -180,7 +180,7 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg
}); });
} }
for (auto elem : iter->value->listItems()) { for (auto elem : iter->value->listItems()) {
auto name = state.forceStringNoCtx(*elem, *iter->pos); auto name = state.forceStringNoCtx(*elem, *iter->pos, "While evaluating an output name within a string context");
context.insert(concatStrings("!", name, "!", i.name)); context.insert(concatStrings("!", name, "!", i.name));
} }
} }

View file

@ -19,8 +19,6 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
if (args[0]->type() == nAttrs) { if (args[0]->type() == nAttrs) {
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
std::string_view n(attr.name); std::string_view n(attr.name);
if (n == "url") if (n == "url")
@ -28,14 +26,14 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar
else if (n == "rev") { else if (n == "rev") {
// Ugly: unlike fetchGit, here the "rev" attribute can // Ugly: unlike fetchGit, here the "rev" attribute can
// be both a revision or a branch/tag name. // be both a revision or a branch/tag name.
auto value = state.forceStringNoCtx(*attr.value, *attr.pos); auto value = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `rev` attribute passed to builtins.fetchMercurial");
if (std::regex_match(value.begin(), value.end(), revRegex)) if (std::regex_match(value.begin(), value.end(), revRegex))
rev = Hash::parseAny(value, htSHA1); rev = Hash::parseAny(value, htSHA1);
else else
ref = value; ref = value;
} }
else if (n == "name") else if (n == "name")
name = state.forceStringNoCtx(*attr.value, *attr.pos); name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `name` attribute passed to builtins.fetchMercurial");
else else
throw EvalError({ throw EvalError({
.msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name), .msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name),

View file

@ -102,7 +102,7 @@ static void fetchTree(
state.forceValue(*args[0], pos); state.forceValue(*args[0], pos);
if (args[0]->type() == nAttrs) { if (args[0]->type() == nAttrs) {
state.forceAttrs(*args[0], pos); state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.fetchTree");
fetchers::Attrs attrs; fetchers::Attrs attrs;
@ -112,7 +112,7 @@ static void fetchTree(
.msg = hintfmt("unexpected attribute 'type'"), .msg = hintfmt("unexpected attribute 'type'"),
.errPos = pos .errPos = pos
}); });
type = state.forceStringNoCtx(*aType->value, *aType->pos); type = state.forceStringNoCtx(*aType->value, *aType->pos, "While evaluating the `type` attribute passed to builtins.fetchTree");
} else if (!type) } else if (!type)
throw Error({ throw Error({
.msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"), .msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"),
@ -195,16 +195,14 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
if (args[0]->type() == nAttrs) { if (args[0]->type() == nAttrs) {
state.forceAttrs(*args[0], pos);
for (auto & attr : *args[0]->attrs) { for (auto & attr : *args[0]->attrs) {
std::string n(attr.name); std::string n(attr.name);
if (n == "url") if (n == "url")
url = state.forceStringNoCtx(*attr.value, *attr.pos); url = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the url we should fetch");
else if (n == "sha256") else if (n == "sha256")
expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos), htSHA256); expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the sha256 of the content we should fetch"), htSHA256);
else if (n == "name") else if (n == "name")
name = state.forceStringNoCtx(*attr.value, *attr.pos); name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the name of the content we should fetch");
else else
throw EvalError({ throw EvalError({
.msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who), .msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who),
@ -218,7 +216,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
.errPos = pos .errPos = pos
}); });
} else } else
url = state.forceStringNoCtx(*args[0], pos); url = state.forceStringNoCtx(*args[0], pos, "While evaluating the url we should fetch");
url = resolveUri(*url); url = resolveUri(*url);

View file

@ -7,7 +7,7 @@ namespace nix {
static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val) static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val)
{ {
auto toml = state.forceStringNoCtx(*args[0], pos); auto toml = state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.fromTOML");
std::istringstream tomlStream(std::string{toml}); std::istringstream tomlStream(std::string{toml});

View file

@ -111,7 +111,7 @@ struct CmdBundle : InstallableCommand
if (!outLink) { if (!outLink) {
auto &attr = vRes->attrs->need(evalState->sName); auto &attr = vRes->attrs->need(evalState->sName);
outLink = evalState->forceStringNoCtx(*attr.value,*attr.pos); outLink = evalState->forceStringNoCtx(*attr.value, *attr.pos, "");
} }
// TODO: will crash if not a localFSStore? // TODO: will crash if not a localFSStore?

View file

@ -126,12 +126,12 @@ static void enumerateOutputs(EvalState & state, Value & vFlake,
std::function<void(const std::string & name, Value & vProvide, const Pos & pos)> callback) std::function<void(const std::string & name, Value & vProvide, const Pos & pos)> callback)
{ {
auto pos = vFlake.determinePos(noPos); auto pos = vFlake.determinePos(noPos);
state.forceAttrs(vFlake, pos); state.forceAttrs(vFlake, pos, "While evaluating a flake to get its outputs");
auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs")); auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs"));
assert(aOutputs); assert(aOutputs);
state.forceAttrs(*aOutputs->value, pos); state.forceAttrs(*aOutputs->value, pos, "While evaluating the outputs of a flake");
auto sHydraJobs = state.symbols.create("hydraJobs"); auto sHydraJobs = state.symbols.create("hydraJobs");
@ -401,13 +401,13 @@ struct CmdFlakeCheck : FlakeCommand
checkHydraJobs = [&](const std::string & attrPath, Value & v, const Pos & pos) { checkHydraJobs = [&](const std::string & attrPath, Value & v, const Pos & pos) {
try { try {
state->forceAttrs(v, pos); state->forceAttrs(v, pos, "");
if (state->isDerivation(v)) if (state->isDerivation(v))
throw Error("jobset should not be a derivation at top-level"); throw Error("jobset should not be a derivation at top-level");
for (auto & attr : *v.attrs) { for (auto & attr : *v.attrs) {
state->forceAttrs(*attr.value, *attr.pos); state->forceAttrs(*attr.value, *attr.pos, "");
auto attrPath2 = attrPath + "." + (std::string) attr.name; auto attrPath2 = attrPath + "." + (std::string) attr.name;
if (state->isDerivation(*attr.value)) { if (state->isDerivation(*attr.value)) {
Activity act(*logger, lvlChatty, actUnknown, Activity act(*logger, lvlChatty, actUnknown,
@ -429,7 +429,7 @@ struct CmdFlakeCheck : FlakeCommand
fmt("checking NixOS configuration '%s'", attrPath)); fmt("checking NixOS configuration '%s'", attrPath));
Bindings & bindings(*state->allocBindings(0)); Bindings & bindings(*state->allocBindings(0));
auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first; auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first;
state->forceAttrs(*vToplevel, pos); state->forceValue(*vToplevel, pos);
if (!state->isDerivation(*vToplevel)) if (!state->isDerivation(*vToplevel))
throw Error("attribute 'config.system.build.toplevel' is not a derivation"); throw Error("attribute 'config.system.build.toplevel' is not a derivation");
} catch (Error & e) { } catch (Error & e) {
@ -443,7 +443,7 @@ struct CmdFlakeCheck : FlakeCommand
Activity act(*logger, lvlChatty, actUnknown, Activity act(*logger, lvlChatty, actUnknown,
fmt("checking template '%s'", attrPath)); fmt("checking template '%s'", attrPath));
state->forceAttrs(v, pos); state->forceAttrs(v, pos, "");
if (auto attr = v.attrs->get(state->symbols.create("path"))) { if (auto attr = v.attrs->get(state->symbols.create("path"))) {
if (attr->name == state->symbols.create("path")) { if (attr->name == state->symbols.create("path")) {
@ -457,7 +457,7 @@ struct CmdFlakeCheck : FlakeCommand
throw Error("template '%s' lacks attribute 'path'", attrPath); throw Error("template '%s' lacks attribute 'path'", attrPath);
if (auto attr = v.attrs->get(state->symbols.create("description"))) if (auto attr = v.attrs->get(state->symbols.create("description")))
state->forceStringNoCtx(*attr->value, *attr->pos); state->forceStringNoCtx(*attr->value, *attr->pos, "");
else else
throw Error("template '%s' lacks attribute 'description'", attrPath); throw Error("template '%s' lacks attribute 'description'", attrPath);
@ -513,10 +513,10 @@ struct CmdFlakeCheck : FlakeCommand
warn("flake output attribute '%s' is deprecated; use '%s' instead", name, replacement); warn("flake output attribute '%s' is deprecated; use '%s' instead", name, replacement);
if (name == "checks") { if (name == "checks") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, *attr.pos);
state->forceAttrs(*attr.value, *attr.pos); state->forceAttrs(*attr.value, *attr.pos, "");
for (auto & attr2 : *attr.value->attrs) { for (auto & attr2 : *attr.value->attrs) {
auto drvPath = checkDerivation( auto drvPath = checkDerivation(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr.name, attr2.name),
@ -528,10 +528,10 @@ struct CmdFlakeCheck : FlakeCommand
} }
else if (name == "packages" || name == "devShells") { else if (name == "packages" || name == "devShells") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, *attr.pos);
state->forceAttrs(*attr.value, *attr.pos); state->forceAttrs(*attr.value, *attr.pos, "");
for (auto & attr2 : *attr.value->attrs) for (auto & attr2 : *attr.value->attrs)
checkDerivation( checkDerivation(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr.name, attr2.name),
@ -540,10 +540,10 @@ struct CmdFlakeCheck : FlakeCommand
} }
else if (name == "apps") { else if (name == "apps") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, *attr.pos);
state->forceAttrs(*attr.value, *attr.pos); state->forceAttrs(*attr.value, *attr.pos, "");
for (auto & attr2 : *attr.value->attrs) for (auto & attr2 : *attr.value->attrs)
checkApp( checkApp(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr.name, attr2.name),
@ -552,7 +552,7 @@ struct CmdFlakeCheck : FlakeCommand
} }
else if (name == "defaultPackage" || name == "devShell") { else if (name == "defaultPackage" || name == "devShell") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, *attr.pos);
checkDerivation( checkDerivation(
@ -562,7 +562,7 @@ struct CmdFlakeCheck : FlakeCommand
} }
else if (name == "defaultApp") { else if (name == "defaultApp") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, *attr.pos);
checkApp( checkApp(
@ -572,7 +572,7 @@ struct CmdFlakeCheck : FlakeCommand
} }
else if (name == "legacyPackages") { else if (name == "legacyPackages") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, *attr.pos);
// FIXME: do getDerivations? // FIXME: do getDerivations?
@ -583,7 +583,7 @@ struct CmdFlakeCheck : FlakeCommand
checkOverlay(name, vOutput, pos); checkOverlay(name, vOutput, pos);
else if (name == "overlays") { else if (name == "overlays") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkOverlay(fmt("%s.%s", name, attr.name), checkOverlay(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, *attr.pos);
@ -593,14 +593,14 @@ struct CmdFlakeCheck : FlakeCommand
checkModule(name, vOutput, pos); checkModule(name, vOutput, pos);
else if (name == "nixosModules") { else if (name == "nixosModules") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkModule(fmt("%s.%s", name, attr.name), checkModule(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, *attr.pos);
} }
else if (name == "nixosConfigurations") { else if (name == "nixosConfigurations") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkNixOSConfiguration(fmt("%s.%s", name, attr.name), checkNixOSConfiguration(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, *attr.pos);
@ -613,14 +613,14 @@ struct CmdFlakeCheck : FlakeCommand
checkTemplate(name, vOutput, pos); checkTemplate(name, vOutput, pos);
else if (name == "templates") { else if (name == "templates") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) for (auto & attr : *vOutput.attrs)
checkTemplate(fmt("%s.%s", name, attr.name), checkTemplate(fmt("%s.%s", name, attr.name),
*attr.value, *attr.pos); *attr.value, *attr.pos);
} }
else if (name == "defaultBundler") { else if (name == "defaultBundler") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, *attr.pos);
checkBundler( checkBundler(
@ -630,10 +630,10 @@ struct CmdFlakeCheck : FlakeCommand
} }
else if (name == "bundlers") { else if (name == "bundlers") {
state->forceAttrs(vOutput, pos); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(attr.name, *attr.pos); checkSystemName(attr.name, *attr.pos);
state->forceAttrs(*attr.value, *attr.pos); state->forceAttrs(*attr.value, *attr.pos, "");
for (auto & attr2 : *attr.value->attrs) { for (auto & attr2 : *attr.value->attrs) {
checkBundler( checkBundler(
fmt("%s.%s.%s", name, attr.name, attr2.name), fmt("%s.%s.%s", name, attr.name, attr2.name),

View file

@ -196,7 +196,7 @@ static void showHelp(std::vector<std::string> subcommand, MultiCommand & topleve
if (!attr) if (!attr)
throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand));
auto markdown = state.forceString(*attr->value); auto markdown = state.forceString(*attr->value, noPos, "While evaluating the lowdown help text");
RunPager pager; RunPager pager;
std::cout << renderMarkdownToTerminal(markdown) << "\n"; std::cout << renderMarkdownToTerminal(markdown) << "\n";

View file

@ -28,17 +28,17 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url)
Value vMirrors; Value vMirrors;
// FIXME: use nixpkgs flake // FIXME: use nixpkgs flake
state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors); state.eval(state.parseExprFromString("import <nixpkgs/pkgs/build-support/fetchurl/mirrors.nix>", "."), vMirrors);
state.forceAttrs(vMirrors, noPos); state.forceAttrs(vMirrors, noPos, "While evaluating the set of all mirrors");
auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName));
if (mirrorList == vMirrors.attrs->end()) if (mirrorList == vMirrors.attrs->end())
throw Error("unknown mirror name '%s'", mirrorName); throw Error("unknown mirror name '%s'", mirrorName);
state.forceList(*mirrorList->value, noPos); state.forceList(*mirrorList->value, noPos, "While evaluating this mirror configuration");
if (mirrorList->value->listSize() < 1) if (mirrorList->value->listSize() < 1)
throw Error("mirror URL '%s' did not expand to anything", url); throw Error("mirror URL '%s' did not expand to anything", url);
std::string mirror(state.forceString(*mirrorList->value->listElems()[0])); std::string mirror(state.forceString(*mirrorList->value->listElems()[0], noPos, "While evaluating the first available mirror"));
return mirror + (hasSuffix(mirror, "/") ? "" : "/") + s.substr(p + 1); return mirror + (hasSuffix(mirror, "/") ? "" : "/") + s.substr(p + 1);
} }
@ -196,27 +196,27 @@ static int main_nix_prefetch_url(int argc, char * * argv)
Value vRoot; Value vRoot;
state->evalFile(path, vRoot); state->evalFile(path, vRoot);
Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first); Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first);
state->forceAttrs(v, noPos); state->forceAttrs(v, noPos, "While evaluating the source attribute to prefetch");
/* Extract the URL. */ /* Extract the URL. */
auto & attr = v.attrs->need(state->symbols.create("urls")); auto & attr = v.attrs->need(state->symbols.create("urls"));
state->forceList(*attr.value, noPos); state->forceList(*attr.value, noPos, "While evaluating the urls to prefetch");
if (attr.value->listSize() < 1) if (attr.value->listSize() < 1)
throw Error("'urls' list is empty"); throw Error("'urls' list is empty");
url = state->forceString(*attr.value->listElems()[0]); url = state->forceString(*attr.value->listElems()[0], noPos, "While evaluating the first url from the urls list");
/* Extract the hash mode. */ /* Extract the hash mode. */
auto attr2 = v.attrs->get(state->symbols.create("outputHashMode")); auto attr2 = v.attrs->get(state->symbols.create("outputHashMode"));
if (!attr2) if (!attr2)
printInfo("warning: this does not look like a fetchurl call"); printInfo("warning: this does not look like a fetchurl call");
else else
unpack = state->forceString(*attr2->value) == "recursive"; unpack = state->forceString(*attr2->value, noPos, "While evaluating the outputHashMode of the source to prefetch") == "recursive";
/* Extract the name. */ /* Extract the name. */
if (!name) { if (!name) {
auto attr3 = v.attrs->get(state->symbols.create("name")); auto attr3 = v.attrs->get(state->symbols.create("name"));
if (!attr3) if (!attr3)
name = state->forceString(*attr3->value); name = state->forceString(*attr3->value, noPos, "While evaluating the name of the source to prefetch");
} }
} }

View file

@ -342,7 +342,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix)
Expr * e = parseString(expr); Expr * e = parseString(expr);
Value v; Value v;
e->eval(*state, *env, v); e->eval(*state, *env, v);
state->forceAttrs(v, noPos); state->forceAttrs(v, noPos, "nevermind, it is ignored anyway");
for (auto & i : *v.attrs) { for (auto & i : *v.attrs) {
std::string name = i.name; std::string name = i.name;
@ -675,7 +675,7 @@ void NixRepl::reloadFiles()
void NixRepl::addAttrsToScope(Value & attrs) void NixRepl::addAttrsToScope(Value & attrs)
{ {
state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }); state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "While evaluating an attribute set to be merged in the global scope");
if (displ + attrs.attrs->size() >= envSize) if (displ + attrs.attrs->size() >= envSize)
throw Error("environment full; cannot add more variables"); throw Error("environment full; cannot add more variables");

View file

@ -144,7 +144,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
Bindings & bindings(*state->allocBindings(0)); Bindings & bindings(*state->allocBindings(0));
auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first; auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first;
return store->parseStorePath(state->forceString(*v2)); return store->parseStorePath(state->forceString(*v2, noPos, "While evaluating the path tho latest nix version"));
} }
}; };