From 00e242feed5ac848f5948dd2731bfabe603999ce Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 3 Mar 2022 21:46:20 +0100 Subject: [PATCH 01/39] Add some context to coercion error strings --- src/libexpr/eval.cc | 36 +++++++++++++----------------------- src/libexpr/eval.hh | 5 ++--- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2d7309738..99a06f14e 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -850,7 +850,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) if (env->type == Env::HasWithExpr) { if (noEval) return 0; Value * v = allocValue(); - evalAttrs(*env->up, (Expr *) env->values[0], *v); + evalAttrs(*env->up, (Expr *) env->values[0], *v, noPos, ""); env->values[0] = v; env->type = Env::HasWithAttrs; } @@ -1055,31 +1055,21 @@ void EvalState::eval(Expr * e, Value & v) } -inline bool EvalState::evalBool(Env & env, Expr * e) +inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, const std::string & location) { Value v; e->eval(*this, env, v); if (v.type() != nBool) - throwTypeError("value is %1% while a Boolean was expected", v); + throwTypeError(pos, (location + ": value is %1% while a Boolean was expected").c_str(), v); return v.boolean; } -inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos) -{ - Value v; - e->eval(*this, env, v); - if (v.type() != nBool) - throwTypeError(pos, "value is %1% while a Boolean was expected", v); - return v.boolean; -} - - -inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v) +inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string & location) { e->eval(*this, env, v); if (v.type() != nAttrs) - throwTypeError("value is %1% while a set was expected", v); + throwTypeError(pos, (location + ": value is %1% while a set was expected").c_str(), v); } @@ -1577,13 +1567,13 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) void ExprIf::eval(EvalState & state, Env & env, Value & v) { - (state.evalBool(env, cond, pos) ? then : else_)->eval(state, env, v); + (state.evalBool(env, cond, pos, "In the condition of the if operator") ? then : else_)->eval(state, env, v); } void ExprAssert::eval(EvalState & state, Env & env, Value & v) { - if (!state.evalBool(env, cond, pos)) { + if (!state.evalBool(env, cond, pos, "In the condition of the assert statement")) { std::ostringstream out; cond->show(out); throwAssertionError(pos, "assertion '%1%' failed", out.str()); @@ -1594,7 +1584,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v) void ExprOpNot::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(!state.evalBool(env, e)); + v.mkBool(!state.evalBool(env, e, noPos, "In the argument of the not operator")); // XXX: FIXME: ! } @@ -1616,27 +1606,27 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v) void ExprOpAnd::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(state.evalBool(env, e1, pos) && state.evalBool(env, e2, pos)); + v.mkBool(state.evalBool(env, e1, pos, "In the left operand of the AND (&&) operator") && state.evalBool(env, e2, pos, "In the right operand of the AND (&&) operator")); } void ExprOpOr::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos)); + v.mkBool(state.evalBool(env, e1, pos, "In the left operand of the OR (||) operator") || state.evalBool(env, e2, pos, "In the right operand of the OR (||) operator")); } void ExprOpImpl::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(!state.evalBool(env, e1, pos) || state.evalBool(env, e2, pos)); + v.mkBool(!state.evalBool(env, e1, pos, "In the left operand of the IMPL (->) operator") || state.evalBool(env, e2, pos, "In the right operand of the IMPL (->) operator")); } void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) { Value v1, v2; - state.evalAttrs(env, e1, v1); - state.evalAttrs(env, e2, v2); + state.evalAttrs(env, e1, v1, pos, "In the left operand of the update (//) operator"); + state.evalAttrs(env, e2, v2, pos, "In the right operand of the update (//) operator"); state.nrOpUpdates++; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 800b00eef..f6298f74c 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -217,9 +217,8 @@ public: /* Evaluation the expression, then verify that it has the expected type. */ - inline bool evalBool(Env & env, Expr * e); - inline bool evalBool(Env & env, Expr * e, const Pos & pos); - inline void evalAttrs(Env & env, Expr * e, Value & v); + inline bool evalBool(Env & env, Expr * e, const Pos & pos, const std::string & location); + inline void evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string & location); /* If `v' is a thunk, enter it and overwrite `v' with the result of the evaluation of the thunk. If `v' is a delayed function From be1f0697468bd6c0f2be4f7e058270c161098e9f Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 4 Mar 2022 05:04:47 +0100 Subject: [PATCH 02/39] Add error context for most basic coercions --- src/libcmd/installables.cc | 2 +- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval-cache.cc | 2 +- src/libexpr/eval-inline.hh | 16 +- src/libexpr/eval.cc | 70 +++--- src/libexpr/eval.hh | 22 +- src/libexpr/flake/flake.cc | 10 +- src/libexpr/get-drvs.cc | 16 +- src/libexpr/nixexpr.hh | 1 - src/libexpr/parser.y | 28 +-- src/libexpr/primops.cc | 303 ++++++++++++++------------ src/libexpr/primops/context.cc | 18 +- src/libexpr/primops/fetchMercurial.cc | 6 +- src/libexpr/primops/fetchTree.cc | 14 +- src/libexpr/primops/fromTOML.cc | 2 +- src/nix/bundle.cc | 2 +- src/nix/flake.cc | 46 ++-- src/nix/main.cc | 2 +- src/nix/prefetch.cc | 16 +- src/nix/repl.cc | 4 +- src/nix/upgrade-nix.cc | 2 +- 21 files changed, 309 insertions(+), 275 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 3209456bf..e85482239 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -524,7 +524,7 @@ ref openEvalCache( auto vFlake = state.allocValue(); 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")); assert(aOutputs); diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index eb0e706c7..63ee8b1ae 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -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 // 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(':'); if (colon == std::string::npos) diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 00d0749f9..66078b694 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -336,7 +336,7 @@ Value & AttrCursor::getValue() if (!_value) { if (parent) { 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); if (!attr) throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index aef1f6351..112c70e7b 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -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({ - .msg = hintfmt(s, showType(v)), + .msg = hintfmt(s, showType(v), s2), .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 -inline void EvalState::forceAttrs(Value & v, Callable getPos) +inline void EvalState::forceAttrs(Value & v, Callable getPos, const std::string & errorCtx) { forceValue(v, getPos); 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); 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. */ diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 99a06f14e..6a21701f3 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -297,7 +297,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) } else { Value 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); } } @@ -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)) @@ -1060,7 +1068,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, const std: Value v; e->eval(*this, env, v); 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; } @@ -1069,7 +1077,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos { e->eval(*this, env, v); 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.) */ if (hasOverrides) { 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()); for (auto & i : *v.attrs) newBnds->push_back(i); @@ -1170,7 +1178,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) state.forceValue(nameVal, i.pos); if (nameVal.type() == nNull) 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); Bindings::iterator j = v.attrs->find(nameSym); if (j != v.attrs->end()) @@ -1260,7 +1268,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) return; } } else { - state.forceAttrs(*vAttrs, pos); + state.forceAttrs(*vAttrs, pos, "While selecting an attribute"); if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) 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]; 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()) 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) { - (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 v2; e2->eval(state, env, 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++; Value * nonEmpty = 0; size_t len = 0; for (size_t n = 0; n < nrLists; ++n) { - forceList(*lists[n], pos); + forceList(*lists[n], pos, errorCtx); auto l = lists[n]->listSize(); len += l; 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); 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; } -NixFloat EvalState::forceFloat(Value & v, const Pos & pos) +NixFloat EvalState::forceFloat(Value & v, const Pos & pos, const std::string & errorCtx) { forceValue(v, pos); if (v.type() == nInt) return v.integer; 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; } -bool EvalState::forceBool(Value & v, const Pos & pos) +bool EvalState::forceBool(Value & v, const Pos & pos, const std::string & errorCtx) { forceValue(v, pos); 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; } @@ -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); 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); if (v.type() != nString) { - if (pos) - throwTypeError(pos, "value is %1% while a string was expected", v); - else - throwTypeError("value is %1% while a string was expected", v); + throwTypeError(pos, "%2%: value is %1% while a string was expected", v, errorCtx); } return v.string.s; } @@ -1911,23 +1917,23 @@ std::vector> 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); 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 (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]); 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]); } return s; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index f6298f74c..ae2a5f4f1 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -234,20 +234,20 @@ public: void forceValueDeep(Value & v); /* Force `v', and then verify that it has the expected type. */ - NixInt forceInt(Value & v, const Pos & pos); - NixFloat forceFloat(Value & v, const Pos & pos); - bool forceBool(Value & v, const Pos & pos); + NixInt forceInt(Value & v, const Pos & pos, const std::string & errorCtx); + NixFloat forceFloat(Value & v, const Pos & pos, const std::string & errorCtx); + 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 - 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); - void forceFunction(Value & v, const Pos & pos); // either lambda or primop - std::string_view forceString(Value & v, const Pos & pos = noPos); - std::string_view forceString(Value & v, PathSet & context, const Pos & pos = noPos); - std::string_view forceStringNoCtx(Value & v, const Pos & pos = noPos); + inline void forceList(Value & v, const Pos & pos, const std::string & errorCtx); + void forceFunction(Value & v, const Pos & pos, const std::string & errorCtx); // either lambda or primop + std::string_view forceString(Value & v, const Pos & pos, const std::string & errorCtx); + std::string_view forceString(Value & v, PathSet & context, const Pos & pos, const std::string & errorCtx); + 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 set with attribute `type = "derivation"'). */ @@ -363,7 +363,7 @@ public: void mkThunk_(Value & v, Expr * expr); void mkPos(Value & v, ptr 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. */ void printStats(); diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 6a1aca40d..b0f087b11 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -255,7 +255,7 @@ static Flake getFlake( for (auto & setting : *nixConfig->value->attrs) { forceTrivialValue(state, *setting.value, *setting.pos); 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) { PathSet emptyContext = {}; flake.config.settings.emplace( @@ -263,16 +263,16 @@ static Flake getFlake( state.coerceToString(*setting.pos, *setting.value, emptyContext, false, true, true) .toOwned()); } 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) - flake.config.settings.insert({setting.name, Explicit { state.forceBool(*setting.value, *setting.pos) }}); + flake.config.settings.insert({setting.name, Explicit { state.forceBool(*setting.value, *setting.pos, "") }}); else if (setting.value->type() == nList) { std::vector ss; for (auto elem : setting.value->listItems()) { if (elem->type() != nString) throw TypeError("list element in flake configuration setting '%s' is %s while a string is expected", 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}); } @@ -708,7 +708,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va { 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); if (evalSettings.pureEval && !flakeRef.input.isLocked()) throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 7630c5ff4..975490d36 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -50,7 +50,7 @@ std::string DrvInfo::queryName() const if (name == "" && attrs) { auto i = attrs->find(state->sName); 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; } @@ -60,7 +60,7 @@ std::string DrvInfo::querySystem() const { if (system == "" && attrs) { 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; } @@ -108,15 +108,15 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) /* Get the ‘outputs’ list. */ Bindings::iterator i; 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 (auto elem : i->value->listItems()) { /* 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)); 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. */ Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); @@ -151,7 +151,7 @@ std::string DrvInfo::queryOutputName() const { if (outputName == "" && attrs) { 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; } @@ -163,7 +163,7 @@ Bindings * DrvInfo::getMeta() if (!attrs) return 0; Bindings::iterator a = attrs->find(state->sMeta); 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; return meta; } @@ -364,7 +364,7 @@ static void getDerivations(EvalState & state, Value & vIn, `recurseForDerivations = true' attribute. */ if (i->value->type() == nAttrs) { 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); } } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 12b54b8eb..dc001fef8 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -4,7 +4,6 @@ #include "symbol-table.hh" #include "error.hh" - namespace nix { diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 919b9cfae..76d45180b 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -396,7 +396,7 @@ expr_function ; 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 ; @@ -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 EQ expr_op { $$ = new ExprOpEq($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 LEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, 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 GEQ expr_op { $$ = new ExprOpNot(new ExprCall(CUR_POS, new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); } - | expr_op AND expr_op { $$ = new ExprOpAnd(CUR_POS, $1, $3); } - | expr_op OR expr_op { $$ = new ExprOpOr(CUR_POS, $1, $3); } - | expr_op IMPL expr_op { $$ = new ExprOpImpl(CUR_POS, $1, $3); } - | expr_op UPDATE expr_op { $$ = new ExprOpUpdate(CUR_POS, $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(makeCurPos(@2, data), 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(makeCurPos(@2, data), new ExprVar(data->symbols.create("__lessThan")), {$1, $3})); } + | expr_op AND expr_op { $$ = new ExprOpAnd(makeCurPos(@2, data), $1, $3); } + | expr_op OR expr_op { $$ = new ExprOpOr(makeCurPos(@2, data), $1, $3); } + | expr_op IMPL expr_op { $$ = new ExprOpImpl(makeCurPos(@2, data), $1, $3); } + | expr_op UPDATE expr_op { $$ = new ExprOpUpdate(makeCurPos(@2, data), $1, $3); } | expr_op '?' attrpath { $$ = new ExprOpHasAttr($1, *$3); } | expr_op '+' expr_op - { $$ = new ExprConcatStrings(CUR_POS, false, new std::vector >({{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(CUR_POS, 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 CONCAT expr_op { $$ = new ExprOpConcatLists(CUR_POS, $1, $3); } + { $$ = new ExprConcatStrings(makeCurPos(@2, data), false, new std::vector >({{makeCurPos(@1, data), $1}, {makeCurPos(@3, data), $3}})); } + | expr_op '-' expr_op { $$ = new ExprCall(makeCurPos(@2, data), new ExprVar(data->symbols.create("__sub")), {$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(makeCurPos(@2, data), new ExprVar(data->symbols.create("__div")), {$1, $3}); } + | expr_op CONCAT expr_op { $$ = new ExprOpConcatLists(makeCurPos(@2, data), $1, $3); } | expr_app ; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 3124025aa..e05e6d45a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -195,9 +195,9 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS , "/"), **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); - state.forceAttrs(v, pos); + state.forceAttrs(v, pos, "While calling imported-drv-to-derivation.nix.gen.hh"); } else if (path == corepkgsPrefix + "fetchurl.nix") { @@ -210,7 +210,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS if (!vScope) state.evalFile(path, v); 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->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]); - 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); if (!handle) @@ -340,7 +340,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value /* Execute a program and parse its output */ 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 count = args[0]->listSize(); 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. */ static void prim_typeOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { @@ -535,8 +534,10 @@ static RegisterPrimOp primop_isPath({ struct CompareValues { 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 { @@ -545,7 +546,10 @@ struct CompareValues if (v1->type() == nInt && v2->type() == nFloat) return v1->integer < v2->fpoint; 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()) { case nInt: return v1->integer < v2->integer; @@ -567,7 +571,10 @@ struct CompareValues } } 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) { - state.forceAttrs(*args[0], pos); + state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.genericClosure"); /* Get the start set. */ Bindings::iterator startSet = getAttr( @@ -630,7 +637,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar pos ); - state.forceList(*startSet->value, pos); + state.forceList(*startSet->value, pos, "While evaluating the `startSet` attribute passed to builtins.genericClosure"); ValueList workSet; for (auto elem : startSet->value->listItems()) @@ -645,7 +652,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar 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 `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; // `doneKeys' doesn't need to be a GC root, because its values are // reachable from res. - auto cmp = CompareValues(state); + auto cmp = CompareValues(state, pos, "While comparing the `key` attributes of two genericClosure elements"); std::set doneKeys(cmp); while (!workSet.empty()) { Value * e = *(workSet.begin()); workSet.pop_front(); - state.forceAttrs(*e, pos); + state.forceAttrs(*e, pos, "While evaluating one item to be part of the genericClosure"); Bindings::iterator key = e->attrs->find(state.sKey); if (key == e->attrs->end()) 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 }); 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. */ Value call; 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. */ 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) { - 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)); } @@ -769,7 +776,7 @@ static RegisterPrimOp primop_ceil({ 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)); } @@ -826,7 +833,7 @@ static RegisterPrimOp primop_tryEval({ /* Return an environment variable. Use with care. */ 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("")); } @@ -924,7 +931,7 @@ static RegisterPrimOp primop_trace({ derivation. */ 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). */ Bindings::iterator attr = getAttr( @@ -938,7 +945,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * std::string drvName; Pos & posDrvName(*attr->pos); 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) { e.addTrace(posDrvName, "while evaluating the derivation attribute 'name'"); throw; @@ -948,14 +955,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * std::ostringstream jsonBuf; std::unique_ptr jsonObject; 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(jsonBuf); /* Check whether null attributes should be ignored. */ bool ignoreNulls = false; attr = args[0]->attrs->find(state.sIgnoreNulls); 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. */ Derivation drv; @@ -1021,7 +1028,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * } 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) 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 command-line arguments to the builder. */ 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()) { auto s = state.coerceToString(posDrvName, *elem, context, true).toOwned(); 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); 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) - 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) - 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) - 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) - 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) { /* 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; 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); } @@ -1275,7 +1282,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info { ‘out’. */ 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({ @@ -1467,17 +1474,17 @@ static RegisterPrimOp primop_readFile({ which are desugared to 'findFile __nixPath "x"'. */ 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; 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; Bindings::iterator i = v2->attrs->find(state.sPrefix); 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( state, @@ -1504,7 +1511,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va 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))); } @@ -1518,7 +1525,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info { /* Return the cryptographic hash of a file in base-16. */ 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 ht = parseHashType(type); if (!ht) throw Error({ @@ -1725,7 +1732,7 @@ static RegisterPrimOp primop_toJSON({ /* Parse a JSON string to a value. */ 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 { parseJSON(state, s, v); } catch (JSONParseError &e) { @@ -1754,8 +1761,8 @@ static RegisterPrimOp primop_fromJSON({ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - std::string name(state.forceStringNoCtx(*args[0], pos)); - std::string contents(state.forceString(*args[1], context, 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, "While evaluating the second argument passed to builtins.toFile")); StorePathSet refs; @@ -1912,7 +1919,7 @@ static void addPath( Value res; 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; std::optional expectedStorePath; @@ -2009,7 +2016,7 @@ static RegisterPrimOp primop_filterSource({ 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; std::string name; Value * filterFun = nullptr; @@ -2022,14 +2029,14 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value if (n == "path") path = state.coerceToPath(*attr.pos, *attr.value, context); 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") { state.forceValue(*attr.value, pos); filterFun = attr.value; } 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") - 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 throw EvalError({ .msg = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name), @@ -2092,7 +2099,7 @@ static RegisterPrimOp primop_path({ strings. */ 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()); @@ -2119,7 +2126,7 @@ static RegisterPrimOp primop_attrNames({ order as attrNames. */ 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()); @@ -2150,8 +2157,8 @@ static RegisterPrimOp primop_attrValues({ /* Dynamic version of the `.' operator. */ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos); - state.forceAttrs(*args[1], pos); + auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.getAttr"); + state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.getAttr"); Bindings::iterator i = getAttr( state, "getAttr", @@ -2180,8 +2187,8 @@ static RegisterPrimOp primop_getAttr({ /* Return position information of the specified attribute. */ static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos); - state.forceAttrs(*args[1], pos); + auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.unsafeGetAttrPos"); + 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)); if (i == args[1]->attrs->end()) v.mkNull(); @@ -2198,8 +2205,8 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info { /* Dynamic version of the `?' operator. */ static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos); - state.forceAttrs(*args[1], pos); + auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hasAttr"); + 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()); } @@ -2232,8 +2239,8 @@ static RegisterPrimOp primop_isAttrs({ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos); - state.forceList(*args[1], pos); + state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.removeAttrs"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.removeAttrs"); /* Get the attribute names to be removed. 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 names; names.reserve(args[1]->listSize()); 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); } std::sort(names.begin(), names.end()); @@ -2280,14 +2287,14 @@ static RegisterPrimOp primop_removeAttrs({ name, the first takes precedence. */ 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()); std::set seen; 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( state, @@ -2297,7 +2304,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, 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); 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) { - state.forceAttrs(*args[0], pos); - state.forceAttrs(*args[1], pos); + state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.intersectAttrs"); + 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())); @@ -2368,14 +2375,14 @@ static RegisterPrimOp primop_intersectAttrs({ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos)); - state.forceList(*args[1], 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, "While evaluating the second argument passed to builtins.catAttrs"); Value * res[args[1]->listSize()]; unsigned int found = 0; 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); if (i != v2->attrs->end()) 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) { - 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()); @@ -2489,15 +2496,15 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args std::map> attrsSeen; - state.forceFunction(*args[0], pos); - state.forceList(*args[1], pos); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.zipAttrsWith"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.zipAttrsWith"); const auto listSize = args[1]->listSize(); const auto listElems = args[1]->listElems(); for (unsigned int n = 0; n < listSize; ++n) { Value * vElem = listElems[n]; 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) attrsSeen[attr.name].first++; } 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) { - 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()) throw Error({ .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. */ 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({ @@ -2635,7 +2642,7 @@ static RegisterPrimOp primop_head({ don't want to use it! */ 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) throw Error({ .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. */ 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()); - for (unsigned int n = 0; n < v.listSize(); ++n) - (v.listElems()[n] = state.allocValue())->mkApp( - args[0], args[1]->listElems()[n]); + 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) + (v.listElems()[n] = state.allocValue())->mkApp( + args[0], args[1]->listElems()[n]); + }; } static RegisterPrimOp primop_map({ @@ -2696,8 +2707,14 @@ static RegisterPrimOp primop_map({ returns true. */ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos); - state.forceList(*args[1], pos); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.filter"); + + 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. 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) { Value res; 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]; else same = false; @@ -2735,7 +2752,7 @@ static RegisterPrimOp primop_filter({ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value & v) { 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()) if (state.eqValues(*args[0], *elem)) { res = true; @@ -2757,8 +2774,8 @@ static RegisterPrimOp primop_elem({ /* Concatenate a list of lists. */ static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos); - state.concatLists(v, args[0]->listSize(), args[0]->listElems(), 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, "While evaluating a value of the list passed to builtins.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. */ 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()); } @@ -2790,8 +2807,8 @@ static RegisterPrimOp primop_length({ right. The operator is applied strictly. */ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos); - state.forceList(*args[2], pos); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.foldlStrict"); + state.forceList(*args[2], pos, "While evaluating the third argument passed to builtins.foldlStrict"); if (args[2]->listSize()) { 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) { - state.forceFunction(*args[0], pos); - state.forceList(*args[1], pos); + state.forceFunction(*args[0], pos, std::string("While evaluating the first argument passed to builtins.") + (any ? "any" : "all")); + state.forceList(*args[1], pos, std::string("While evaluating the second argument passed to builtins.") + (any ? "any" : "all")); Value vTmp; for (auto elem : args[1]->listItems()) { 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) { v.mkBool(any); return; @@ -2872,7 +2889,7 @@ static RegisterPrimOp primop_all({ 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) 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) { - state.forceFunction(*args[0], pos); - state.forceList(*args[1], pos); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.sort"); 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); for (unsigned int n = 0; n < len; ++n) { 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 callFunction. */ 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 vBool; 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 @@ -2961,8 +2984,8 @@ static RegisterPrimOp primop_sort({ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos); - state.forceList(*args[1], pos); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.partition"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.partition"); 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); Value res; 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); else 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) { - state.forceFunction(*args[0], pos); - state.forceList(*args[1], pos); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.groupBy"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.groupBy"); ValueVectorMap attrs; for (auto vElem : args[1]->listItems()) { Value res; 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); auto vector = attrs.try_emplace(sym, ValueVector()).first; 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) { - state.forceFunction(*args[0], pos); - state.forceList(*args[1], pos); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.concatMap"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.concatMap"); auto nrLists = args[1]->listSize(); 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]; state.callFunction(*args[0], *vElem, lists[n], pos); 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) { e.addTrace(pos, hintfmt("while invoking '%s'", "concatMap")); 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[1], pos); 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 - 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({ @@ -3142,9 +3167,11 @@ static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); 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 - 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({ @@ -3161,9 +3188,11 @@ static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); 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 - 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({ @@ -3180,7 +3209,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], 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) throw EvalError({ .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) { - 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 { - NixInt i1 = state.forceInt(*args[0], pos); - NixInt i2 = state.forceInt(*args[1], pos); + NixInt i1 = state.forceInt(*args[0], pos, "While evaluating the first operand of the division"); + NixInt i2 = state.forceInt(*args[1], pos, "While evaluating the second operand of the division"); /* Avoid division overflow as it might raise SIGFPE. */ if (i1 == std::numeric_limits::min() && i2 == -1) throw EvalError({ @@ -3214,7 +3243,8 @@ static RegisterPrimOp primop_div({ 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({ @@ -3228,7 +3258,8 @@ static RegisterPrimOp primop_bitAnd({ 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({ @@ -3242,7 +3273,8 @@ static RegisterPrimOp primop_bitOr({ 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({ @@ -3258,7 +3290,8 @@ static void prim_lessThan(EvalState & state, const Pos & pos, Value * * args, Va { state.forceValue(*args[0], 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])); } @@ -3319,8 +3352,8 @@ static RegisterPrimOp primop_toString({ non-negative. */ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, Value & v) { - int start = state.forceInt(*args[0], pos); - int len = state.forceInt(*args[1], pos); + int start = state.forceInt(*args[0], pos, "While evaluating the first argument passed to builtins.substring"); + int len = state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.substring"); PathSet 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. */ 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 ht = parseHashType(type); if (!ht) throw Error({ @@ -3382,7 +3415,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, }); 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)); } @@ -3421,14 +3454,14 @@ std::shared_ptr makeRegexCache() 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 { auto regex = state.regexCache->get(re); 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; 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. */ 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 { auto regex = state.regexCache->get(re); 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 end = std::cregex_iterator(); @@ -3608,8 +3641,8 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * { PathSet context; - auto sep = state.forceString(*args[0], context, pos); - state.forceList(*args[1], pos); + auto sep = state.forceString(*args[0], context, pos, "While evaluating the first argument passed to builtins.concatStringsSep"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.concatStringsSep"); std::string res; 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) { - state.forceList(*args[0], pos); - state.forceList(*args[1], pos); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.replaceStrings"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.replaceStrings"); if (args[0]->listSize() != args[1]->listSize()) throw EvalError({ .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 from; from.reserve(args[0]->listSize()); 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> to; to.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { 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)); } 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; // 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) { - 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); auto attrs = state.buildBindings(2); 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) { - auto version1 = state.forceStringNoCtx(*args[0], pos); - auto version2 = state.forceStringNoCtx(*args[1], pos); + auto version1 = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.compareVersions"); + auto version2 = state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.compareVersions"); 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) { - 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(); Strings components; while (iter != version.cend()) { diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 3701bd442..f1feb4dc6 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -17,7 +17,7 @@ static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringCo static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v) { 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()); } @@ -72,7 +72,7 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, Strings outputs; }; 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(); for (const auto & p : context) { 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) { 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 sAllOutputs = state.symbols.create("allOutputs"); @@ -150,16 +150,16 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg }); if (!settings.readOnlyMode) 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); 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); } iter = i.value->attrs->find(sAllOutputs); 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)) { throw EvalError({ .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); 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)) { throw EvalError({ .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()) { - 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)); } } diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index b7f715859..199f7508e 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -19,8 +19,6 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar if (args[0]->type() == nAttrs) { - state.forceAttrs(*args[0], pos); - for (auto & attr : *args[0]->attrs) { std::string_view n(attr.name); if (n == "url") @@ -28,14 +26,14 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar else if (n == "rev") { // Ugly: unlike fetchGit, here the "rev" attribute can // 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)) rev = Hash::parseAny(value, htSHA1); else ref = value; } 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 throw EvalError({ .msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name), diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 9c2da2178..a40eddfca 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -102,7 +102,7 @@ static void fetchTree( state.forceValue(*args[0], pos); 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; @@ -112,7 +112,7 @@ static void fetchTree( .msg = hintfmt("unexpected attribute 'type'"), .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) throw Error({ .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) { - state.forceAttrs(*args[0], pos); - for (auto & attr : *args[0]->attrs) { std::string n(attr.name); 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") - 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") - 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 throw EvalError({ .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 }); } else - url = state.forceStringNoCtx(*args[0], pos); + url = state.forceStringNoCtx(*args[0], pos, "While evaluating the url we should fetch"); url = resolveUri(*url); diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index dd4280030..6bfd8e285 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -7,7 +7,7 @@ namespace nix { 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}); diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 7ed558dee..aa074edc2 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -111,7 +111,7 @@ struct CmdBundle : InstallableCommand if (!outLink) { 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? diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 144f8f886..703c3beb2 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -126,12 +126,12 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, std::function callback) { 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")); 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"); @@ -401,13 +401,13 @@ struct CmdFlakeCheck : FlakeCommand checkHydraJobs = [&](const std::string & attrPath, Value & v, const Pos & pos) { try { - state->forceAttrs(v, pos); + state->forceAttrs(v, pos, ""); if (state->isDerivation(v)) throw Error("jobset should not be a derivation at top-level"); for (auto & attr : *v.attrs) { - state->forceAttrs(*attr.value, *attr.pos); + state->forceAttrs(*attr.value, *attr.pos, ""); auto attrPath2 = attrPath + "." + (std::string) attr.name; if (state->isDerivation(*attr.value)) { Activity act(*logger, lvlChatty, actUnknown, @@ -429,7 +429,7 @@ struct CmdFlakeCheck : FlakeCommand fmt("checking NixOS configuration '%s'", attrPath)); Bindings & bindings(*state->allocBindings(0)); auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first; - state->forceAttrs(*vToplevel, pos); + state->forceValue(*vToplevel, pos); if (!state->isDerivation(*vToplevel)) throw Error("attribute 'config.system.build.toplevel' is not a derivation"); } catch (Error & e) { @@ -443,7 +443,7 @@ struct CmdFlakeCheck : FlakeCommand Activity act(*logger, lvlChatty, actUnknown, fmt("checking template '%s'", attrPath)); - state->forceAttrs(v, pos); + state->forceAttrs(v, pos, ""); if (auto attr = v.attrs->get(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); if (auto attr = v.attrs->get(state->symbols.create("description"))) - state->forceStringNoCtx(*attr->value, *attr->pos); + state->forceStringNoCtx(*attr->value, *attr->pos, ""); else 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); if (name == "checks") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { checkSystemName(attr.name, *attr.pos); - state->forceAttrs(*attr.value, *attr.pos); + state->forceAttrs(*attr.value, *attr.pos, ""); for (auto & attr2 : *attr.value->attrs) { auto drvPath = checkDerivation( fmt("%s.%s.%s", name, attr.name, attr2.name), @@ -528,10 +528,10 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "packages" || name == "devShells") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { checkSystemName(attr.name, *attr.pos); - state->forceAttrs(*attr.value, *attr.pos); + state->forceAttrs(*attr.value, *attr.pos, ""); for (auto & attr2 : *attr.value->attrs) checkDerivation( fmt("%s.%s.%s", name, attr.name, attr2.name), @@ -540,10 +540,10 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "apps") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { checkSystemName(attr.name, *attr.pos); - state->forceAttrs(*attr.value, *attr.pos); + state->forceAttrs(*attr.value, *attr.pos, ""); for (auto & attr2 : *attr.value->attrs) checkApp( fmt("%s.%s.%s", name, attr.name, attr2.name), @@ -552,7 +552,7 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "defaultPackage" || name == "devShell") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { checkSystemName(attr.name, *attr.pos); checkDerivation( @@ -562,7 +562,7 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "defaultApp") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { checkSystemName(attr.name, *attr.pos); checkApp( @@ -572,7 +572,7 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "legacyPackages") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { checkSystemName(attr.name, *attr.pos); // FIXME: do getDerivations? @@ -583,7 +583,7 @@ struct CmdFlakeCheck : FlakeCommand checkOverlay(name, vOutput, pos); else if (name == "overlays") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) checkOverlay(fmt("%s.%s", name, attr.name), *attr.value, *attr.pos); @@ -593,14 +593,14 @@ struct CmdFlakeCheck : FlakeCommand checkModule(name, vOutput, pos); else if (name == "nixosModules") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) checkModule(fmt("%s.%s", name, attr.name), *attr.value, *attr.pos); } else if (name == "nixosConfigurations") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) checkNixOSConfiguration(fmt("%s.%s", name, attr.name), *attr.value, *attr.pos); @@ -613,14 +613,14 @@ struct CmdFlakeCheck : FlakeCommand checkTemplate(name, vOutput, pos); else if (name == "templates") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) checkTemplate(fmt("%s.%s", name, attr.name), *attr.value, *attr.pos); } else if (name == "defaultBundler") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { checkSystemName(attr.name, *attr.pos); checkBundler( @@ -630,10 +630,10 @@ struct CmdFlakeCheck : FlakeCommand } else if (name == "bundlers") { - state->forceAttrs(vOutput, pos); + state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs) { checkSystemName(attr.name, *attr.pos); - state->forceAttrs(*attr.value, *attr.pos); + state->forceAttrs(*attr.value, *attr.pos, ""); for (auto & attr2 : *attr.value->attrs) { checkBundler( fmt("%s.%s.%s", name, attr.name, attr2.name), diff --git a/src/nix/main.cc b/src/nix/main.cc index b923f2535..0c6286686 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -196,7 +196,7 @@ static void showHelp(std::vector subcommand, MultiCommand & topleve if (!attr) 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; std::cout << renderMarkdownToTerminal(markdown) << "\n"; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index f2dd44ba4..4376da896 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -28,17 +28,17 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url) Value vMirrors; // FIXME: use nixpkgs flake state.eval(state.parseExprFromString("import ", "."), 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)); if (mirrorList == vMirrors.attrs->end()) 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) 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); } @@ -196,27 +196,27 @@ static int main_nix_prefetch_url(int argc, char * * argv) Value vRoot; state->evalFile(path, vRoot); 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. */ 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) 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. */ auto attr2 = v.attrs->get(state->symbols.create("outputHashMode")); if (!attr2) printInfo("warning: this does not look like a fetchurl call"); 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. */ if (!name) { auto attr3 = v.attrs->get(state->symbols.create("name")); if (!attr3) - name = state->forceString(*attr3->value); + name = state->forceString(*attr3->value, noPos, "While evaluating the name of the source to prefetch"); } } diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 3a51a13e6..7011ff939 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -342,7 +342,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) Expr * e = parseString(expr); Value v; e->eval(*state, *env, v); - state->forceAttrs(v, noPos); + state->forceAttrs(v, noPos, "nevermind, it is ignored anyway"); for (auto & i : *v.attrs) { std::string name = i.name; @@ -675,7 +675,7 @@ void NixRepl::reloadFiles() 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) throw Error("environment full; cannot add more variables"); diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 17a5a77ee..1f433a199 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -144,7 +144,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand Bindings & bindings(*state->allocBindings(0)); 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")); } }; From 3a5855353e5dc2986c38bb66eaff5538dea70da1 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 4 Mar 2022 21:47:58 +0100 Subject: [PATCH 03/39] Add detailed error mesage for coerceTo{String,Path} --- src/libexpr/eval.cc | 34 +++++++------- src/libexpr/eval.hh | 7 +-- src/libexpr/flake/flake.cc | 2 +- src/libexpr/get-drvs.cc | 6 +-- src/libexpr/primops.cc | 66 +++++++++++++++------------ src/libexpr/primops/context.cc | 4 +- src/libexpr/primops/fetchMercurial.cc | 4 +- src/libexpr/primops/fetchTree.cc | 4 +- src/libexpr/value.hh | 2 +- src/nix-env/user-env.cc | 4 +- src/nix/bundle.cc | 4 +- src/nix/eval.cc | 2 +- src/nix/flake.cc | 2 +- src/nix/repl.cc | 4 +- 14 files changed, 79 insertions(+), 66 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 6a21701f3..b2a8651d9 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1775,7 +1775,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) /* skip canonization of first path, which would only be not canonized in the first place if it's coming from a ./${foo} type path */ - auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first); + auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first, ""); sSize += part->size(); s.emplace_back(std::move(part)); } @@ -1958,14 +1958,15 @@ std::optional EvalState::tryAttrsToString(const Pos & pos, Value & if (i != v.attrs->end()) { Value v1; callFunction(*i->value, v, v1, pos); - return coerceToString(pos, v1, context, coerceMore, copyToStore).toOwned(); + return coerceToString(pos, v1, context, coerceMore, copyToStore, + "While evaluating the result of the `toString` attribute").toOwned(); } return {}; } BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, - bool coerceMore, bool copyToStore, bool canonicalizePath) + bool coerceMore, bool copyToStore, bool canonicalizePath, const std::string & errorCtx) { forceValue(v, pos); @@ -1988,12 +1989,12 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & if (maybeString) return std::move(*maybeString); auto i = v.attrs->find(sOutPath); - if (i == v.attrs->end()) throwTypeError(pos, "cannot coerce a set to a string"); - return coerceToString(pos, *i->value, context, coerceMore, copyToStore); + if (i == v.attrs->end()) throwTypeError(pos, "%2%: cannot coerce %1% to a string", v, errorCtx); + return coerceToString(pos, *i->value, context, coerceMore, copyToStore, canonicalizePath, errorCtx); } if (v.type() == nExternal) - return v.external->coerceToString(pos, context, coerceMore, copyToStore); + return v.external->coerceToString(pos, context, coerceMore, copyToStore, errorCtx); if (coerceMore) { @@ -2008,7 +2009,8 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & if (v.isList()) { std::string result; for (auto [n, v2] : enumerate(v.listItems())) { - result += *coerceToString(pos, *v2, context, coerceMore, copyToStore); + result += *coerceToString(pos, *v2, context, coerceMore, copyToStore, canonicalizePath, + errorCtx + ": While evaluating one element of the list"); if (n < v.listSize() - 1 /* !!! not quite correct */ && (!v2->isList() || v2->listSize() != 0)) @@ -2018,7 +2020,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & } } - throwTypeError(pos, "cannot coerce %1% to a string", v); + throwTypeError(pos, "%2%: cannot coerce %1% to a string", v, errorCtx); } @@ -2046,22 +2048,22 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path) } -Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context) +Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context, const std::string & errorCtx) { - auto path = coerceToString(pos, v, context, false, false).toOwned(); + auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (path == "" || path[0] != '/') - throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path); + throwEvalError(pos, "%2%: string '%1%' doesn't represent an absolute path", path, errorCtx); return path; } -StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context) +StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context, const std::string & errorCtx) { - auto path = coerceToString(pos, v, context, false, false).toOwned(); + auto path = coerceToString(pos, v, context, false, false, errorCtx).toOwned(); if (auto storePath = store->maybeParseStorePath(path)) return *storePath; throw EvalError({ - .msg = hintfmt("path '%1%' is not in the Nix store", path), + .msg = hintfmt("%2%: path '%1%' is not in the Nix store", path, errorCtx), .errPos = pos }); } @@ -2263,10 +2265,10 @@ void EvalState::printStats() } -std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const +std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, const std::string & errorCtx) const { throw TypeError({ - .msg = hintfmt("cannot coerce %1% to a string", showType()), + .msg = hintfmt("%2%: cannot coerce %1% to a string", showType(), errorCtx), .errPos = pos }); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index ae2a5f4f1..9e8ac4926 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -262,17 +262,18 @@ public: referenced paths are copied to the Nix store as a side effect. */ BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context, bool coerceMore = false, bool copyToStore = true, - bool canonicalizePath = true); + bool canonicalizePath = true, + const std::string & errorCtx = ""); std::string copyPathToStore(PathSet & context, const Path & path); /* Path coercion. Converts strings, paths and derivations to a path. The result is guaranteed to be a canonicalised, absolute path. Nothing is copied to the store. */ - Path coerceToPath(const Pos & pos, Value & v, PathSet & context); + Path coerceToPath(const Pos & pos, Value & v, PathSet & context, const std::string & errorCtx); /* Like coerceToPath, but the result must be a store path. */ - StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context); + StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context, const std::string & errorCtx); public: diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index b0f087b11..fe2daaf21 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -260,7 +260,7 @@ static Flake getFlake( PathSet emptyContext = {}; flake.config.settings.emplace( setting.name, - 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) flake.config.settings.insert({setting.name, state.forceInt(*setting.value, *setting.pos, "")}); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 975490d36..9bb545430 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -74,7 +74,7 @@ std::optional DrvInfo::queryDrvPath() const if (i == attrs->end()) drvPath = {std::nullopt}; else - drvPath = {state->coerceToStorePath(*i->pos, *i->value, context)}; + drvPath = {state->coerceToStorePath(*i->pos, *i->value, context, "Whyle evaluating the drv path of a DrvInfo")}; } return drvPath.value_or(std::nullopt); } @@ -94,7 +94,7 @@ StorePath DrvInfo::queryOutPath() const Bindings::iterator i = attrs->find(state->sOutPath); PathSet context; if (i != attrs->end()) - outPath = state->coerceToStorePath(*i->pos, *i->value, context); + outPath = state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the output path of a DrvInfo"); } if (!outPath) throw UnimplementedError("CA derivations are not yet supported"); @@ -122,7 +122,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? PathSet context; - outputs.emplace(name, state->coerceToStorePath(*outPath->pos, *outPath->value, context)); + outputs.emplace(name, state->coerceToStorePath(*outPath->pos, *outPath->value, context, "While evaluating the outPath of an output path of a DrvInfo")); } } else outputs.emplace("out", queryOutPath()); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index e05e6d45a..14db24f68 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -103,7 +103,7 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea auto path = [&]() { try { - return state.coerceToPath(pos, v, context); + return state.coerceToPath(pos, v, context, "While realising the context of a path"); } catch (Error & e) { e.addTrace(pos, "while realising the context of a path"); throw; @@ -350,10 +350,12 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) }); } PathSet context; - auto program = state.coerceToString(pos, *elems[0], context, false, false).toOwned(); + auto program = state.coerceToString(pos, *elems[0], context, false, false, + "While evaluating the first element of the argument passed to builtins.exec").toOwned(); Strings commandArgs; for (unsigned int i = 1; i < args[0]->listSize(); ++i) { - commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false).toOwned()); + commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false, + "While evaluating an element of the argument passed to builtins.exec").toOwned()); } try { auto _ = state.realiseContext(context); // FIXME: Handle CA derivations @@ -714,7 +716,8 @@ static RegisterPrimOp primop_abort({ .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context).toOwned(); + auto s = state.coerceToString(pos, *args[0], context, + "While evaluating the error message passed to builtins.abort").toOwned(); throw Abort("evaluation aborted with the following error message: '%1%'", s); } }); @@ -732,7 +735,8 @@ static RegisterPrimOp primop_throw({ .fun = [](EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context).toOwned(); + auto s = state.coerceToString(pos, *args[0], context, + "While evaluating the error message passed to builtin.throw").toOwned(); throw ThrownError(s); } }); @@ -744,7 +748,8 @@ static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * a v = *args[1]; } catch (Error & e) { PathSet context; - e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context).toOwned()); + e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context, + "While evaluating the error message passed to builtins.addErrorContext").toOwned()); throw; } } @@ -757,7 +762,8 @@ static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info { static void prim_ceil(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "While evaluating the first argument passed to builtins.ceil"); + auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), + "While evaluating the first argument passed to builtins.ceil"); v.mkInt(ceil(value)); } @@ -1028,7 +1034,8 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * } if (i->name == state.sContentAddressed) { - contentAddressed = state.forceBool(*i->value, pos, "While evaluating the `__contentAddressed` attribute passed to builtins.derivationStrict"); + contentAddressed = state.forceBool(*i->value, pos, + "While evaluating the `__contentAddressed` attribute passed to builtins.derivationStrict"); if (contentAddressed) settings.requireExperimentalFeature(Xp::CaDerivations); } @@ -1036,9 +1043,11 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * /* The `args' attribute is special: it supplies the command-line arguments to the builder. */ else if (i->name == state.sArgs) { - state.forceList(*i->value, pos, "While evaluating the `args` attribute passed to builtins.derivationStrict"); + state.forceList(*i->value, pos, + "While evaluating the `args` attribute passed to builtins.derivationStrict"); for (auto elem : i->value->listItems()) { - auto s = state.coerceToString(posDrvName, *elem, context, true).toOwned(); + auto s = state.coerceToString(posDrvName, *elem, context, true, + "While evaluating an element of the `args` argument passed to builtins.derivationStrict").toOwned(); drv.args.push_back(s); } } @@ -1074,7 +1083,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * } } else { - auto s = state.coerceToString(*i->pos, *i->value, context, true).toOwned(); + auto s = state.coerceToString(*i->pos, *i->value, context, true, "While evaluating an attribute passed to builtins.derivationStrict").toOwned(); drv.env.emplace(key, s); if (i->name == state.sBuilder) drv.builder = std::move(s); else if (i->name == state.sSystem) drv.platform = std::move(s); @@ -1306,7 +1315,7 @@ static RegisterPrimOp primop_placeholder({ static void prim_toPath(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - Path path = state.coerceToPath(pos, *args[0], context); + Path path = state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.toPath"); v.mkString(canonPath(path), context); } @@ -1337,7 +1346,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V }); PathSet context; - Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context)); + Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.storePath")); /* Resolve symlinks in ‘path’, unless ‘path’ itself is a symlink directly in the store. The latter condition is necessary so e.g. nix-push does the right thing. */ @@ -1407,7 +1416,7 @@ static RegisterPrimOp primop_pathExists({ static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false)), context); + v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.baseNameOf")), context); } static RegisterPrimOp primop_baseNameOf({ @@ -1427,7 +1436,7 @@ static RegisterPrimOp primop_baseNameOf({ static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto path = state.coerceToString(pos, *args[0], context, false, false); + auto path = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.dirOf"); auto dir = dirOf(*path); if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context); } @@ -1484,7 +1493,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va std::string prefix; Bindings::iterator i = v2->attrs->find(state.sPrefix); if (i != v2->attrs->end()) - prefix = state.forceStringNoCtx(*i->value, pos, "While evaluating the `prefix` attribute passed to an element of the list passed to builtins.findFile"); + prefix = state.forceStringNoCtx(*i->value, pos, "While evaluating the `prefix` attribute of an element of the list passed to builtins.findFile"); i = getAttr( state, @@ -1495,7 +1504,8 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va ); PathSet context; - auto path = state.coerceToString(pos, *i->value, context, false, false).toOwned(); + auto path = state.coerceToString(pos, *i->value, context, false, false, + "While evaluating the `path` attribute of an element of the list passed to builtins.findFile").toOwned(); try { auto rewrites = state.realiseContext(context); @@ -1945,7 +1955,7 @@ static void addPath( static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - Path path = state.coerceToPath(pos, *args[1], context); + Path path = state.coerceToPath(pos, *args[1], context, "While evaluating the second argument (the path to filter) passed to builtins.filterSource"); state.forceValue(*args[0], pos); if (args[0]->type() != nFunction) @@ -2027,7 +2037,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value for (auto & attr : *args[0]->attrs) { auto & n(attr.name); if (n == "path") - path = state.coerceToPath(*attr.pos, *attr.value, context); + path = state.coerceToPath(*attr.pos, *attr.value, context, "While evaluating the `path` attribute passed to builtins.path"); else if (attr.name == state.sName) name = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the `name` attribute passed to builtins.path"); else if (n == "filter") { @@ -2045,7 +2055,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value } if (path.empty()) throw EvalError({ - .msg = hintfmt("'path' required"), + .msg = hintfmt("Missing required 'path' attribute in the first argument to builtins.path"), .errPos = pos }); if (name.empty()) @@ -3318,7 +3328,7 @@ static RegisterPrimOp primop_lessThan({ static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, true, false); + auto s = state.coerceToString(pos, *args[0], context, true, false, "While evaluating the first argument passed to builtins.toString"); v.mkString(*s, context); } @@ -3352,10 +3362,10 @@ static RegisterPrimOp primop_toString({ non-negative. */ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, Value & v) { - int start = state.forceInt(*args[0], pos, "While evaluating the first argument passed to builtins.substring"); - int len = state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.substring"); + int start = state.forceInt(*args[0], pos, "While evaluating the first argument (the start offset) passed to builtins.substring"); + int len = state.forceInt(*args[1], pos, "While evaluating the second argument (the substring length) passed to builtins.substring"); PathSet context; - auto s = state.coerceToString(pos, *args[2], context); + auto s = state.coerceToString(pos, *args[2], context, "While evaluating the third argument (the string) passed to builtins.substring"); if (start < 0) throw EvalError({ @@ -3389,7 +3399,7 @@ static RegisterPrimOp primop_substring({ static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context); + auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.stringLength"); v.mkInt(s->size()); } @@ -3641,8 +3651,8 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * { PathSet context; - auto sep = state.forceString(*args[0], context, pos, "While evaluating the first argument passed to builtins.concatStringsSep"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.concatStringsSep"); + auto sep = state.forceString(*args[0], context, pos, "While evaluating the first argument (the separator string) passed to builtins.concatStringsSep"); + state.forceList(*args[1], pos, "While evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep"); std::string res; res.reserve((args[1]->listSize() + 32) * sep.size()); @@ -3650,7 +3660,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * for (auto elem : args[1]->listItems()) { if (first) first = false; else res += sep; - res += *state.coerceToString(pos, *elem, context); + res += *state.coerceToString(pos, *elem, context, "While evaluating one element of the list of strings to concat passed to builtins.concatStringsSep"); } v.mkString(res, context); diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index f1feb4dc6..78320dc09 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -7,7 +7,7 @@ namespace nix { static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context); + auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardStringContext"); v.mkString(*s); } @@ -33,7 +33,7 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext); static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context); + auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardOutputDependency"); PathSet context2; for (auto & p : context) diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index 199f7508e..dcaf3d362 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -22,7 +22,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar for (auto & attr : *args[0]->attrs) { std::string_view n(attr.name); if (n == "url") - url = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned(); + url = state.coerceToString(*attr.pos, *attr.value, context, false, false, "While evaluating the `url` attribute passed to builtins.fetchMercurial").toOwned(); else if (n == "rev") { // Ugly: unlike fetchGit, here the "rev" attribute can // be both a revision or a branch/tag name. @@ -48,7 +48,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar }); } else - url = state.coerceToString(pos, *args[0], context, false, false).toOwned(); + url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.fetchMercurial").toOwned(); // FIXME: git externals probably can be used to bypass the URI // whitelist. Ah well. diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index a40eddfca..2baa272cb 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -125,7 +125,7 @@ static void fetchTree( if (attr.name == state.sType) continue; state.forceValue(*attr.value, *attr.pos); if (attr.value->type() == nPath || attr.value->type() == nString) { - auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false).toOwned(); + auto s = state.coerceToString(*attr.pos, *attr.value, context, false, false, "").toOwned(); attrs.emplace(attr.name, attr.name == "url" ? type == "git" @@ -151,7 +151,7 @@ static void fetchTree( input = fetchers::Input::fromAttrs(std::move(attrs)); } else { - auto url = state.coerceToString(pos, *args[0], context, false, false).toOwned(); + auto url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to the fetcher").toOwned(); if (type == "git") { fetchers::Attrs attrs; diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index d0fa93e92..858227018 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -85,7 +85,7 @@ class ExternalValueBase /* Coerce the value to a string. Defaults to uncoercable, i.e. throws an * error. */ - virtual std::string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore) const; + virtual std::string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, const std::string & errorCtx) const; /* Compare to another value of the same type. Defaults to uncomparable, * i.e. always false. diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index af4f350ff..b30ccc7f6 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -132,9 +132,9 @@ bool createUserEnv(EvalState & state, DrvInfos & elems, state.forceValue(topLevel, [&]() { return topLevel.determinePos(noPos); }); PathSet context; Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); - auto topLevelDrv = state.coerceToStorePath(*aDrvPath.pos, *aDrvPath.value, context); + auto topLevelDrv = state.coerceToStorePath(*aDrvPath.pos, *aDrvPath.value, context, ""); Attr & aOutPath(*topLevel.attrs->find(state.sOutPath)); - auto topLevelOut = state.coerceToStorePath(*aOutPath.pos, *aOutPath.value, context); + auto topLevelOut = state.coerceToStorePath(*aOutPath.pos, *aOutPath.value, context, ""); /* Realise the resulting store expression. */ debug("building user environment"); diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index aa074edc2..9ff66899e 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -97,13 +97,13 @@ struct CmdBundle : InstallableCommand throw Error("the bundler '%s' does not produce a derivation", bundler.what()); PathSet context2; - auto drvPath = evalState->coerceToStorePath(*attr1->pos, *attr1->value, context2); + auto drvPath = evalState->coerceToStorePath(*attr1->pos, *attr1->value, context2, ""); auto attr2 = vRes->attrs->get(evalState->sOutPath); if (!attr2) throw Error("the bundler '%s' does not produce a derivation", bundler.what()); - auto outPath = evalState->coerceToStorePath(*attr2->pos, *attr2->value, context2); + auto outPath = evalState->coerceToStorePath(*attr2->pos, *attr2->value, context2, ""); store->buildPaths({ DerivedPath::Built { drvPath } }); diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 8cd04d5fe..8ee3ba45b 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -107,7 +107,7 @@ struct CmdEval : MixJSON, InstallableCommand else if (raw) { stopProgressBar(); - std::cout << *state->coerceToString(noPos, *v, context); + std::cout << *state->coerceToString(noPos, *v, context, "While generating the eval command output"); } else if (json) { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 703c3beb2..356f89713 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -448,7 +448,7 @@ struct CmdFlakeCheck : FlakeCommand if (auto attr = v.attrs->get(state->symbols.create("path"))) { if (attr->name == state->symbols.create("path")) { PathSet context; - auto path = state->coerceToPath(*attr->pos, *attr->value, context); + auto path = state->coerceToPath(*attr->pos, *attr->value, context, ""); if (!store->isInStore(path)) throw Error("template '%s' has a bad 'path' attribute"); // TODO: recursively check the flake in 'path'. diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 7011ff939..0cb68552c 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -461,7 +461,7 @@ bool NixRepl::processLine(std::string line) if (v.type() == nPath || v.type() == nString) { PathSet context; - auto filename = state->coerceToString(noPos, v, context); + auto filename = state->coerceToString(noPos, v, context, "While evaluating the filename to edit"); pos.file = state->symbols.create(*filename); } else if (v.isLambda()) { pos = v.lambda.fun->pos; @@ -780,7 +780,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m Bindings::iterator i = v.attrs->find(state->sDrvPath); PathSet context; if (i != v.attrs->end()) - str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context)); + str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the drvPath of a derivation")); else str << "???"; str << "»"; From ed02fa3c403d4ab92c0e32c9d033231f46f87c85 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 4 Mar 2022 22:15:30 +0100 Subject: [PATCH 04/39] s/forceValue/forceFunction/ where applicable --- src/libexpr/primops.cc | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 14db24f68..b56293c30 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1957,15 +1957,7 @@ static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args PathSet context; Path path = state.coerceToPath(pos, *args[1], context, "While evaluating the second argument (the path to filter) passed to builtins.filterSource"); - state.forceValue(*args[0], pos); - if (args[0]->type() != nFunction) - throw TypeError({ - .msg = hintfmt( - "first argument in call to 'filterSource' is not a function but %1%", - showType(*args[0])), - .errPos = pos - }); - + state.forceFunction(*args[0], pos, "While evaluating the first argument to builtins.filterSource"); addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context); } @@ -2040,10 +2032,9 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value path = state.coerceToPath(*attr.pos, *attr.value, context, "While evaluating the `path` attribute passed to builtins.path"); else if (attr.name == state.sName) name = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the `name` attribute passed to builtins.path"); - else if (n == "filter") { - state.forceValue(*attr.value, pos); - filterFun = attr.value; - } else if (n == "recursive") + else if (n == "filter") + state.forceFunction(filterFun = *attr.value, *attr.pos, "While evaluating the `filter` parameter passed to builtins.path"); + else if (n == "recursive") method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos, "While evaluating the `recursive` attribute passed to builtins.path") }; else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `sha256` attribute passed to builtins.path"), htSHA256); From 57684d62475c9ec37e2abc2b7df2ec3581d0f011 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 4 Mar 2022 22:51:56 +0100 Subject: [PATCH 05/39] fixup! s/forceValue/forceFunction/ where applicable --- src/libexpr/eval.cc | 19 +++---------------- src/libexpr/primops.cc | 10 +++++----- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b2a8651d9..03329645f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -747,14 +747,6 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const }); } -LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s)) -{ - throw TypeError({ - .msg = hintfmt(s), - .errPos = pos - }); -} - LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2)) { throw TypeError({ @@ -763,11 +755,6 @@ LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const }); } -//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 AssertionError({ @@ -1371,7 +1358,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & for (auto & i : lambda.formals->formals) { auto j = args[0]->attrs->get(i.name); if (!j) { - if (!i.def) throwTypeError(pos, "%1% called without required argument '%2%'", + if (!i.def) throwTypeError(pos, "Function %1% called without required argument '%2%'", lambda, i.name); env2.values[displ++] = i.def->maybeThunk(*this, env2); } else { @@ -1387,7 +1374,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & user. */ for (auto & i : *args[0]->attrs) if (!lambda.formals->has(i.name)) - throwTypeError(pos, "%1% called with unexpected argument '%2%'", lambda, i.name); + throwTypeError(pos, "Function %1% called with unexpected argument '%2%'", lambda, i.name); abort(); // can't happen } } @@ -2059,7 +2046,7 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context, cons StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context, const std::string & errorCtx) { - auto path = coerceToString(pos, v, context, false, false, errorCtx).toOwned(); + auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (auto storePath = store->maybeParseStorePath(path)) return *storePath; throw EvalError({ diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index b56293c30..a72d0f12a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -683,12 +683,12 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar res.push_back(e); /* Call the `operator' function with `e' as argument. */ - Value call; - call.mkApp(op->value, e); - state.forceList(call, pos, "While evaluating the return value of the `operator` passed to builtins.genericClosure"); + Value res; + state.callFunction(*op->value, 1, &e, res, *op->pos); + state.forceList(res, pos, "While evaluating the return value of the `operator` passed to builtins.genericClosure"); /* Add the values returned by the operator to the work set. */ - for (auto elem : call.listItems()) { + for (auto elem : res.listItems()) { state.forceValue(*elem, pos); workSet.push_back(elem); } @@ -2033,7 +2033,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value else if (attr.name == state.sName) name = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the `name` attribute passed to builtins.path"); else if (n == "filter") - state.forceFunction(filterFun = *attr.value, *attr.pos, "While evaluating the `filter` parameter passed to builtins.path"); + state.forceFunction(*(filterFun = attr.value), *attr.pos, "While evaluating the `filter` parameter passed to builtins.path"); else if (n == "recursive") method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos, "While evaluating the `recursive` attribute passed to builtins.path") }; else if (n == "sha256") From cbbbf368818f147f9ce4562b497536a9e46fd657 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 4 Mar 2022 22:55:14 +0100 Subject: [PATCH 06/39] Use 'errorCtx' name everywhere --- src/libexpr/eval.cc | 8 ++++---- src/libexpr/eval.hh | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 03329645f..168b3582a 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1050,21 +1050,21 @@ void EvalState::eval(Expr * e, Value & v) } -inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, const std::string & location) +inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, const std::string & errorCtx) { Value v; e->eval(*this, env, v); if (v.type() != nBool) - throwTypeError(pos, "%2%: value is %1% while a Boolean was expected", v, location); + throwTypeError(pos, "%2%: value is %1% while a Boolean was expected", v, errorCtx); return v.boolean; } -inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string & location) +inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string & errorCtx) { e->eval(*this, env, v); if (v.type() != nAttrs) - throwTypeError(pos, "%2%: value is %1% while a set was expected", v, location); + throwTypeError(pos, "%2%: value is %1% while a set was expected", v, errorCtx); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 9e8ac4926..dadd46433 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -217,8 +217,8 @@ public: /* Evaluation the expression, then verify that it has the expected type. */ - inline bool evalBool(Env & env, Expr * e, const Pos & pos, const std::string & location); - inline void evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string & location); + inline bool evalBool(Env & env, Expr * e, const Pos & pos, const std::string & errorCtx); + inline void evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string & errorCtx); /* If `v' is a thunk, enter it and overwrite `v' with the result of the evaluation of the thunk. If `v' is a delayed function From 407801592719d77fcdad53b447e909e3ab255086 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Sat, 5 Mar 2022 21:18:30 +0100 Subject: [PATCH 07/39] DRY addPrimOp --- src/libexpr/eval.cc | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 168b3582a..d3ab5ad08 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -636,25 +636,7 @@ void EvalState::addConstant(const std::string & name, Value * v) Value * EvalState::addPrimOp(const std::string & name, size_t arity, PrimOpFun primOp) { - auto name2 = name.substr(0, 2) == "__" ? name.substr(2) : name; - Symbol sym = symbols.create(name2); - - /* Hack to make constants lazy: turn them into a application of - the primop to a dummy value. */ - if (arity == 0) { - auto vPrimOp = allocValue(); - vPrimOp->mkPrimOp(new PrimOp { .fun = primOp, .arity = 1, .name = sym }); - Value v; - v.mkApp(vPrimOp, vPrimOp); - return addConstant(name, v); - } - - Value * v = allocValue(); - v->mkPrimOp(new PrimOp { .fun = primOp, .arity = arity, .name = sym }); - staticBaseEnv.vars.emplace_back(symbols.create(name), baseEnvDispl); - baseEnv.values[baseEnvDispl++] = v; - baseEnv.values[0]->attrs->push_back(Attr(sym, v)); - return v; + return addPrimOp(PrimOp { .fun = primOp, .arity = arity, .name = symbols.create(name) }); } From 1b5a8db148dd4403c64bc058ce1a4a5e46d52031 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Sat, 5 Mar 2022 21:19:04 +0100 Subject: [PATCH 08/39] change error location for genericClosure operator errors --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index a72d0f12a..fff382c5e 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -684,7 +684,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar /* Call the `operator' function with `e' as argument. */ Value res; - state.callFunction(*op->value, 1, &e, res, *op->pos); + state.callFunction(*op->value, 1, &e, res, pos); state.forceList(res, pos, "While evaluating the return value of the `operator` passed to builtins.genericClosure"); /* Add the values returned by the operator to the work set. */ From 13c4dc65327c9654c47e6d80c0f4e1797b999f97 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Mon, 7 Mar 2022 11:33:03 +0100 Subject: [PATCH 09/39] more fixes --- src/libcmd/installables.cc | 2 +- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval-cache.cc | 2 +- src/libexpr/eval-inline.hh | 4 +- src/libexpr/eval.cc | 56 ++--- src/libexpr/eval.hh | 2 +- src/libexpr/flake/flake.cc | 2 +- src/libexpr/get-drvs.cc | 22 +- src/libexpr/primops.cc | 298 +++++++++++++------------- src/libexpr/primops/context.cc | 22 +- src/libexpr/primops/fetchMercurial.cc | 8 +- src/libexpr/primops/fetchTree.cc | 14 +- src/libexpr/primops/fromTOML.cc | 2 +- src/nix/eval.cc | 2 +- src/nix/flake.cc | 4 +- src/nix/main.cc | 2 +- src/nix/prefetch.cc | 16 +- src/nix/repl.cc | 8 +- src/nix/upgrade-nix.cc | 2 +- 19 files changed, 239 insertions(+), 231 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index e85482239..437380a4d 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -524,7 +524,7 @@ ref openEvalCache( auto vFlake = state.allocValue(); flake::callFlake(state, *lockedFlake, *vFlake); - state.forceAttrs(*vFlake, noPos, "While evaluating a cached flake"); + state.forceAttrs(*vFlake, noPos, "While evaluating a cached flake: "); auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); assert(aOutputs); diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index 63ee8b1ae..930400bd0 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -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 // toString + parsing? - auto pos = state.forceString(*v2, noPos, "While evaluating the meta.position attribute of a derivation"); + auto pos = state.forceString(*v2, noPos, "While evaluating the meta.position attribute of a derivation: "); auto colon = pos.rfind(':'); if (colon == std::string::npos) diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 66078b694..6528725e7 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -336,7 +336,7 @@ Value & AttrCursor::getValue() if (!_value) { if (parent) { auto & vParent = parent->first->getValue(); - root->state.forceAttrs(vParent, noPos, "While evaluating the parent attr set"); + root->state.forceAttrs(vParent, noPos, "While evaluating the parent attr set: "); auto attr = vParent.attrs->get(parent->second); if (!attr) throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 112c70e7b..ad53a74cb 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -63,7 +63,7 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos, const std::string { forceValue(v, getPos); if (v.type() != nAttrs) - throwTypeError(getPos(), "%2%: value is %1% while a set was expected", v, errorCtx); + throwTypeError(getPos(), "%2%value is %1% while a set was expected", v, errorCtx); } @@ -71,7 +71,7 @@ inline void EvalState::forceList(Value & v, const Pos & pos, const std::string & { forceValue(v, pos); if (!v.isList()) - throwTypeError(pos, "%2%: value is %1% while a list was expected", v, errorCtx); + throwTypeError(pos, "%2%value is %1% while a list was expected", v, errorCtx); } /* Note: Various places expect the allocated memory to be zeroed. */ diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d3ab5ad08..55c624cb9 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -297,7 +297,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) } else { Value nameValue; name.expr->eval(state, env, nameValue); - state.forceStringNoCtx(nameValue, noPos, "While evaluating an attribute name"); + state.forceStringNoCtx(nameValue, noPos, "While evaluating an attribute name: "); return state.symbols.create(nameValue.string.s); } } @@ -720,6 +720,14 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const }); } +LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3, const std::string & s4)) +{ + throw EvalError({ + .msg = hintfmt(s, s2, s3, s4), + .errPos = pos + }); +} + LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2)) { // p1 is where the error occurred; p2 is a position mentioned in the message. @@ -1037,7 +1045,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, const std: Value v; e->eval(*this, env, v); if (v.type() != nBool) - throwTypeError(pos, "%2%: value is %1% while a Boolean was expected", v, errorCtx); + throwTypeError(pos, "%2%value is %1% while a Boolean was expected", v, errorCtx); return v.boolean; } @@ -1046,7 +1054,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos { e->eval(*this, env, v); if (v.type() != nAttrs) - throwTypeError(pos, "%2%: value is %1% while a set was expected", v, errorCtx); + throwTypeError(pos, "%2%value is %1% while a set was expected", v, errorCtx); } @@ -1119,7 +1127,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) Hence we need __overrides.) */ if (hasOverrides) { Value * vOverrides = (*v.attrs)[overrides->second.displ].value; - state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "While evaluating the `__overrides` attribute"); + state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "While evaluating the `__overrides` attribute: "); Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size()); for (auto & i : *v.attrs) newBnds->push_back(i); @@ -1147,7 +1155,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) state.forceValue(nameVal, i.pos); if (nameVal.type() == nNull) continue; - state.forceStringNoCtx(nameVal, i.pos, "While evaluating the name of a dynamic attribute"); + state.forceStringNoCtx(nameVal, i.pos, "While evaluating the name of a dynamic attribute: "); Symbol nameSym = state.symbols.create(nameVal.string.s); Bindings::iterator j = v.attrs->find(nameSym); if (j != v.attrs->end()) @@ -1237,7 +1245,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) return; } } else { - state.forceAttrs(*vAttrs, pos, "While selecting an attribute"); + state.forceAttrs(*vAttrs, pos, "While selecting an attribute: "); if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) throwEvalError(pos, "attribute '%1%' missing", name); } @@ -1328,7 +1336,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & env2.values[displ++] = args[0]; else { - forceAttrs(*args[0], pos, "While evaluating the value passed as argument to a function expecting an attribute set"); + forceAttrs(*args[0], pos, "While evaluating the value passed as argument to a function expecting an attribute set: "); if (!lambda.arg.empty()) env2.values[displ++] = args[0]; @@ -1570,7 +1578,7 @@ void ExprOpEq::eval(EvalState & state, Env & env, Value & v) { Value v1; e1->eval(state, env, v1); Value v2; e2->eval(state, env, v2); - v.mkBool(state.eqValues(v1, v2)); + v.mkBool(state.eqValues(v1, v2, pos, "")); } @@ -1578,7 +1586,7 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v) { Value v1; e1->eval(state, env, v1); Value v2; e2->eval(state, env, v2); - v.mkBool(!state.eqValues(v1, v2)); + v.mkBool(!state.eqValues(v1, v2, pos, "")); } @@ -1806,7 +1814,7 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos, const std::string & error { forceValue(v, pos); if (v.type() != nInt) - throwTypeError(pos, "%2%: value is %1% while an integer was expected", v, errorCtx); + throwTypeError(pos, "%2%value is %1% while an integer was expected", v, errorCtx); return v.integer; } @@ -1817,7 +1825,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos, const std::string & e if (v.type() == nInt) return v.integer; else if (v.type() != nFloat) - throwTypeError(pos, "%2%: value is %1% while a float was expected", v, errorCtx); + throwTypeError(pos, "%2%value is %1% while a float was expected", v, errorCtx); return v.fpoint; } @@ -1826,7 +1834,7 @@ bool EvalState::forceBool(Value & v, const Pos & pos, const std::string & errorC { forceValue(v, pos); if (v.type() != nBool) - throwTypeError(pos, "%2%: value is %1% while a Boolean was expected", v, errorCtx); + throwTypeError(pos, "%2%value is %1% while a Boolean was expected", v, errorCtx); return v.boolean; } @@ -1841,7 +1849,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos, const std::string & er { forceValue(v, pos); if (v.type() != nFunction && !isFunctor(v)) - throwTypeError(pos, "%2%: value is %1% while a function was expected", v, errorCtx); + throwTypeError(pos, "%2%value is %1% while a function was expected", v, errorCtx); } @@ -1849,7 +1857,7 @@ std::string_view EvalState::forceString(Value & v, const Pos & pos, const std::s { forceValue(v, pos); if (v.type() != nString) { - throwTypeError(pos, "%2%: value is %1% while a string was expected", v, errorCtx); + throwTypeError(pos, "%2%value is %1% while a string was expected", v, errorCtx); } return v.string.s; } @@ -1958,7 +1966,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & if (maybeString) return std::move(*maybeString); auto i = v.attrs->find(sOutPath); - if (i == v.attrs->end()) throwTypeError(pos, "%2%: cannot coerce %1% to a string", v, errorCtx); + if (i == v.attrs->end()) throwTypeError(pos, "%2%cannot coerce %1% to a string", v, errorCtx); return coerceToString(pos, *i->value, context, coerceMore, copyToStore, canonicalizePath, errorCtx); } @@ -1989,7 +1997,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & } } - throwTypeError(pos, "%2%: cannot coerce %1% to a string", v, errorCtx); + throwTypeError(pos, "%2%cannot coerce %1% to a string", v, errorCtx); } @@ -2021,7 +2029,7 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context, cons { auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (path == "" || path[0] != '/') - throwEvalError(pos, "%2%: string '%1%' doesn't represent an absolute path", path, errorCtx); + throwEvalError(pos, "%2%string '%1%' doesn't represent an absolute path", path, errorCtx); return path; } @@ -2032,13 +2040,13 @@ StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & con if (auto storePath = store->maybeParseStorePath(path)) return *storePath; throw EvalError({ - .msg = hintfmt("%2%: path '%1%' is not in the Nix store", path, errorCtx), + .msg = hintfmt("%2%path '%1%' is not in the Nix store", path, errorCtx), .errPos = pos }); } -bool EvalState::eqValues(Value & v1, Value & v2) +bool EvalState::eqValues(Value & v1, Value & v2, const Pos & pos, const std::string & errorCtx) { forceValue(v1, noPos); forceValue(v2, noPos); @@ -2077,7 +2085,7 @@ bool EvalState::eqValues(Value & v1, Value & v2) case nList: if (v1.listSize() != v2.listSize()) return false; for (size_t n = 0; n < v1.listSize(); ++n) - if (!eqValues(*v1.listElems()[n], *v2.listElems()[n])) return false; + if (!eqValues(*v1.listElems()[n], *v2.listElems()[n], pos, errorCtx)) return false; return true; case nAttrs: { @@ -2087,7 +2095,7 @@ bool EvalState::eqValues(Value & v1, Value & v2) Bindings::iterator i = v1.attrs->find(sOutPath); Bindings::iterator j = v2.attrs->find(sOutPath); if (i != v1.attrs->end() && j != v2.attrs->end()) - return eqValues(*i->value, *j->value); + return eqValues(*i->value, *j->value, pos, errorCtx); } if (v1.attrs->size() != v2.attrs->size()) return false; @@ -2095,7 +2103,7 @@ bool EvalState::eqValues(Value & v1, Value & v2) /* Otherwise, compare the attributes one by one. */ Bindings::iterator i, j; for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) - if (i->name != j->name || !eqValues(*i->value, *j->value)) + if (i->name != j->name || !eqValues(*i->value, *j->value, pos, errorCtx)) return false; return true; @@ -2112,7 +2120,7 @@ bool EvalState::eqValues(Value & v1, Value & v2) return v1.fpoint == v2.fpoint; default: - throwEvalError("cannot compare %1% with %2%", showType(v1), showType(v2)); + throwEvalError(pos, "%3%cannot compare %1% with %2%", showType(v1), showType(v2), errorCtx); } } @@ -2237,7 +2245,7 @@ void EvalState::printStats() std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, const std::string & errorCtx) const { throw TypeError({ - .msg = hintfmt("%2%: cannot coerce %1% to a string", showType(), errorCtx), + .msg = hintfmt("%2%cannot coerce %1% to a string", showType(), errorCtx), .errPos = pos }); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index dadd46433..bd817c9fe 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -329,7 +329,7 @@ public: /* Do a deep equality test between two values. That is, list elements and attributes are compared recursively. */ - bool eqValues(Value & v1, Value & v2); + bool eqValues(Value & v1, Value & v2, const Pos & pos, const std::string & errorCtx); bool isFunctor(Value & fun); diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index fe2daaf21..560747210 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -708,7 +708,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va { state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos); - std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.getFlake")); + std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.getFlake: ")); auto flakeRef = parseFlakeRef(flakeRefS, {}, true); if (evalSettings.pureEval && !flakeRef.input.isLocked()) throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 9bb545430..94f26e925 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -50,7 +50,7 @@ std::string DrvInfo::queryName() const if (name == "" && attrs) { auto i = attrs->find(state->sName); if (i == attrs->end()) throw TypeError("derivation name missing"); - name = state->forceStringNoCtx(*i->value, noPos, "While evaluating the name of a DrvInfo"); + name = state->forceStringNoCtx(*i->value, noPos, "While evaluating the name of a DrvInfo: "); } return name; } @@ -60,7 +60,7 @@ std::string DrvInfo::querySystem() const { if (system == "" && attrs) { auto i = attrs->find(state->sSystem); - system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos, "While evaluating the system of a DrvInfo"); + system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos, "While evaluating the system of a DrvInfo: "); } return system; } @@ -74,7 +74,7 @@ std::optional DrvInfo::queryDrvPath() const if (i == attrs->end()) drvPath = {std::nullopt}; else - drvPath = {state->coerceToStorePath(*i->pos, *i->value, context, "Whyle evaluating the drv path of a DrvInfo")}; + drvPath = {state->coerceToStorePath(*i->pos, *i->value, context, "Whyle evaluating the drv path of a DrvInfo: ")}; } return drvPath.value_or(std::nullopt); } @@ -94,7 +94,7 @@ StorePath DrvInfo::queryOutPath() const Bindings::iterator i = attrs->find(state->sOutPath); PathSet context; if (i != attrs->end()) - outPath = state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the output path of a DrvInfo"); + outPath = state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the output path of a DrvInfo: "); } if (!outPath) throw UnimplementedError("CA derivations are not yet supported"); @@ -108,21 +108,21 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) /* Get the ‘outputs’ list. */ Bindings::iterator i; if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) { - state->forceList(*i->value, *i->pos, "While evaluating the outputs of a DrvInfo"); + state->forceList(*i->value, *i->pos, "While evaluating the outputs of a DrvInfo: "); /* For each output... */ for (auto elem : i->value->listItems()) { /* Evaluate the corresponding set. */ - std::string name(state->forceStringNoCtx(*elem, *i->pos, "While evaluating the name of one output of a DrvInfo")); + 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)); if (out == attrs->end()) continue; // FIXME: throw error? - state->forceAttrs(*out->value, *i->pos, "While evaluating the description of a DrvInfo output"); + state->forceAttrs(*out->value, *i->pos, "While evaluating the description of a DrvInfo output: "); /* And evaluate its ‘outPath’ attribute. */ Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? PathSet context; - outputs.emplace(name, state->coerceToStorePath(*outPath->pos, *outPath->value, context, "While evaluating the outPath of an output path of a DrvInfo")); + outputs.emplace(name, state->coerceToStorePath(*outPath->pos, *outPath->value, context, "While evaluating the outPath of an output path of a DrvInfo: ")); } } else outputs.emplace("out", queryOutPath()); @@ -151,7 +151,7 @@ std::string DrvInfo::queryOutputName() const { if (outputName == "" && attrs) { Bindings::iterator i = attrs->find(state->sOutputName); - outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "While evaluating the output name of a DrvInfo") : ""; + outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "While evaluating the output name of a DrvInfo: ") : ""; } return outputName; } @@ -163,7 +163,7 @@ Bindings * DrvInfo::getMeta() if (!attrs) return 0; Bindings::iterator a = attrs->find(state->sMeta); if (a == attrs->end()) return 0; - state->forceAttrs(*a->value, *a->pos, "While evaluating the `meta` attribute of a DrvInfo"); + state->forceAttrs(*a->value, *a->pos, "While evaluating the `meta` attribute of a DrvInfo: "); meta = a->value->attrs; return meta; } @@ -364,7 +364,7 @@ static void getDerivations(EvalState & state, Value & vIn, `recurseForDerivations = true' attribute. */ if (i->value->type() == nAttrs) { Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations); - if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos, "While evaluating the attribute `recurseForDerivations`")) + 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); } } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index fff382c5e..1e22044d7 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -103,7 +103,7 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea auto path = [&]() { try { - return state.coerceToPath(pos, v, context, "While realising the context of a path"); + return state.coerceToPath(pos, v, context, "While realising the context of a path: "); } catch (Error & e) { e.addTrace(pos, "while realising the context of a path"); throw; @@ -195,9 +195,9 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS , "/"), **state.vImportedDrvToDerivation); } - state.forceFunction(**state.vImportedDrvToDerivation, pos, "While evaluating imported-drv-to-derivation.nix.gen.hh"); + state.forceFunction(**state.vImportedDrvToDerivation, pos, "While evaluating imported-drv-to-derivation.nix.gen.hh: "); v.mkApp(*state.vImportedDrvToDerivation, w); - state.forceAttrs(v, pos, "While calling imported-drv-to-derivation.nix.gen.hh"); + state.forceAttrs(v, pos, "While calling imported-drv-to-derivation.nix.gen.hh: "); } else if (path == corepkgsPrefix + "fetchurl.nix") { @@ -210,7 +210,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS if (!vScope) state.evalFile(path, v); else { - state.forceAttrs(*vScope, pos, "While evaluating the first argument passed to builtins.scopedImport"); + state.forceAttrs(*vScope, pos, "While evaluating the first argument passed to builtins.scopedImport: "); Env * env = &state.allocEnv(vScope->attrs->size()); 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]); - std::string sym(state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.importNative")); + 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); if (!handle) @@ -340,7 +340,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value /* Execute a program and parse its output */ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.exec"); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.exec: "); auto elems = args[0]->listElems(); auto count = args[0]->listSize(); if (count == 0) { @@ -549,7 +549,7 @@ struct CompareValues return v1->integer < v2->fpoint; if (v1->type() != v2->type()) throw EvalError({ - .msg = hintfmt("%s: cannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)), + .msg = hintfmt("%scannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)), .errPos = pos, }); switch (v1->type()) { @@ -568,13 +568,13 @@ struct CompareValues return false; } else if (i == v1->listSize()) { return true; - } else if (!state.eqValues(*v1->listElems()[i], *v2->listElems()[i])) { + } else if (!state.eqValues(*v1->listElems()[i], *v2->listElems()[i], pos, errorCtx)) { return (*this)(v1->listElems()[i], v2->listElems()[i]); } } default: throw EvalError({ - .msg = hintfmt("%s: cannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)), + .msg = hintfmt("%scannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)), .errPos = pos, }); } @@ -628,7 +628,7 @@ static Bindings::iterator getAttr( static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.genericClosure"); + state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.genericClosure: "); /* Get the start set. */ Bindings::iterator startSet = getAttr( @@ -639,7 +639,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar pos ); - state.forceList(*startSet->value, pos, "While evaluating the `startSet` attribute passed to builtins.genericClosure"); + state.forceList(*startSet->value, pos, "While evaluating the `startSet` attribute passed to builtins.genericClosure: "); ValueList workSet; for (auto elem : startSet->value->listItems()) @@ -654,7 +654,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar pos ); - state.forceFunction(*op->value, pos, "While evaluating the `operator` attribute passed to builtins.genericClosure"); + state.forceFunction(*op->value, pos, "While evaluating the `operator` attribute passed to builtins.genericClosure: "); /* Construct the closure by applying the operator to element of `workSet', adding the result to `workSet', continuing until @@ -668,7 +668,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar Value * e = *(workSet.begin()); workSet.pop_front(); - state.forceAttrs(*e, pos, "While evaluating one item to be part of the genericClosure"); + state.forceAttrs(*e, pos, "While evaluating one item to be part of the genericClosure: "); Bindings::iterator key = e->attrs->find(state.sKey); @@ -685,7 +685,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar /* Call the `operator' function with `e' as argument. */ Value res; state.callFunction(*op->value, 1, &e, res, pos); - state.forceList(res, pos, "While evaluating the return value of the `operator` passed to builtins.genericClosure"); + state.forceList(res, pos, "While evaluating the return value of the `operator` passed to builtins.genericClosure: "); /* Add the values returned by the operator to the work set. */ for (auto elem : res.listItems()) { @@ -782,7 +782,7 @@ static RegisterPrimOp primop_ceil({ static void prim_floor(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "While evaluating the first argument passed to builtins.floor"); + auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "While evaluating the first argument passed to builtins.floor: "); v.mkInt(floor(value)); } @@ -839,7 +839,7 @@ static RegisterPrimOp primop_tryEval({ /* Return an environment variable. Use with care. */ static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v) { - std::string name(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.getEnv")); + 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("")); } @@ -937,7 +937,7 @@ static RegisterPrimOp primop_trace({ derivation. */ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.derivationStrict"); + state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.derivationStrict: "); /* Figure out the name first (for stack backtraces). */ Bindings::iterator attr = getAttr( @@ -951,7 +951,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * std::string drvName; Pos & posDrvName(*attr->pos); try { - drvName = state.forceStringNoCtx(*attr->value, pos, "While evaluating the `name` attribute passed to builtins.derivationStrict"); + drvName = state.forceStringNoCtx(*attr->value, pos, "While evaluating the `name` attribute passed to builtins.derivationStrict: "); } catch (Error & e) { e.addTrace(posDrvName, "while evaluating the derivation attribute 'name'"); throw; @@ -961,14 +961,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * std::ostringstream jsonBuf; std::unique_ptr jsonObject; attr = args[0]->attrs->find(state.sStructuredAttrs); - if (attr != args[0]->attrs->end() && state.forceBool(*attr->value, pos, "While evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict")) + if (attr != args[0]->attrs->end() && state.forceBool(*attr->value, pos, "While evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict: ")) jsonObject = std::make_unique(jsonBuf); /* Check whether null attributes should be ignored. */ bool ignoreNulls = false; attr = args[0]->attrs->find(state.sIgnoreNulls); if (attr != args[0]->attrs->end()) - ignoreNulls = state.forceBool(*attr->value, pos, "While evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict"); + ignoreNulls = state.forceBool(*attr->value, pos, "While evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict: "); /* Build the derivation expression by processing the attributes. */ Derivation drv; @@ -1064,26 +1064,26 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * printValueAsJSON(state, true, *i->value, pos, placeholder, context); if (i->name == state.sBuilder) - drv.builder = state.forceString(*i->value, context, posDrvName, "While evaluating the `builder` attribute passed to builtins.derivationStrict"); + drv.builder = state.forceString(*i->value, context, posDrvName, "While evaluating the `builder` attribute passed to builtins.derivationStrict: "); else if (i->name == state.sSystem) - drv.platform = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `system` attribute passed to builtins.derivationStrict"); + drv.platform = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `system` attribute passed to builtins.derivationStrict: "); else if (i->name == state.sOutputHash) - outputHash = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHash` attribute passed to builtins.derivationStrict"); + outputHash = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHash` attribute passed to builtins.derivationStrict: "); else if (i->name == state.sOutputHashAlgo) - outputHashAlgo = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashAlgo` attribute passed to builtins.derivationStrict"); + outputHashAlgo = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashAlgo` attribute passed to builtins.derivationStrict: "); else if (i->name == state.sOutputHashMode) - handleHashMode(state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashMode` attribute passed to builtins.derivationStrict")); + handleHashMode(state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashMode` attribute passed to builtins.derivationStrict: ")); else if (i->name == state.sOutputs) { /* Require ‘outputs’ to be a list of strings. */ - state.forceList(*i->value, posDrvName, "While evaluating the `outputs` attribute passed to builtins.derivationStrict"); + state.forceList(*i->value, posDrvName, "While evaluating the `outputs` attribute passed to builtins.derivationStrict: "); Strings ss; for (auto elem : i->value->listItems()) - ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName, "While evaluating an element of the `outputs` attribute passed to builtins.derivationStrict")); + ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName, "While evaluating an element of the `outputs` attribute passed to builtins.derivationStrict: ")); handleOutputs(ss); } } else { - auto s = state.coerceToString(*i->pos, *i->value, context, true, "While evaluating an attribute passed to builtins.derivationStrict").toOwned(); + auto s = state.coerceToString(*i->pos, *i->value, context, true, "While evaluating an attribute passed to builtins.derivationStrict: ").toOwned(); drv.env.emplace(key, s); if (i->name == state.sBuilder) drv.builder = std::move(s); else if (i->name == state.sSystem) drv.platform = std::move(s); @@ -1291,7 +1291,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info { ‘out’. */ static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v) { - v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.placeholder"))); + v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.placeholder: "))); } static RegisterPrimOp primop_placeholder({ @@ -1315,7 +1315,7 @@ static RegisterPrimOp primop_placeholder({ static void prim_toPath(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - Path path = state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.toPath"); + Path path = state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.toPath: "); v.mkString(canonPath(path), context); } @@ -1346,7 +1346,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V }); PathSet context; - Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.storePath")); + Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.storePath: ")); /* Resolve symlinks in ‘path’, unless ‘path’ itself is a symlink directly in the store. The latter condition is necessary so e.g. nix-push does the right thing. */ @@ -1416,7 +1416,7 @@ static RegisterPrimOp primop_pathExists({ static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.baseNameOf")), context); + v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.baseNameOf: ")), context); } static RegisterPrimOp primop_baseNameOf({ @@ -1436,7 +1436,7 @@ static RegisterPrimOp primop_baseNameOf({ static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto path = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.dirOf"); + auto path = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.dirOf: "); auto dir = dirOf(*path); if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context); } @@ -1483,17 +1483,17 @@ static RegisterPrimOp primop_readFile({ which are desugared to 'findFile __nixPath "x"'. */ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.findFile"); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.findFile: "); SearchPath searchPath; for (auto v2 : args[0]->listItems()) { - state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.findFile"); + state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.findFile: "); std::string prefix; Bindings::iterator i = v2->attrs->find(state.sPrefix); if (i != v2->attrs->end()) - prefix = state.forceStringNoCtx(*i->value, pos, "While evaluating the `prefix` attribute of an element of the list passed to builtins.findFile"); + prefix = state.forceStringNoCtx(*i->value, pos, "While evaluating the `prefix` attribute of an element of the list passed to builtins.findFile: "); i = getAttr( state, @@ -1521,7 +1521,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va searchPath.emplace_back(prefix, path); } - auto path = state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.findFile"); + 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))); } @@ -1535,7 +1535,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info { /* Return the cryptographic hash of a file in base-16. */ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashFile"); + auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashFile: "); std::optional ht = parseHashType(type); if (!ht) throw Error({ @@ -1742,7 +1742,7 @@ static RegisterPrimOp primop_toJSON({ /* Parse a JSON string to a value. */ static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto s = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.fromJSON"); + auto s = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.fromJSON: "); try { parseJSON(state, s, v); } catch (JSONParseError &e) { @@ -1771,8 +1771,8 @@ static RegisterPrimOp primop_fromJSON({ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - 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, "While evaluating the second argument passed to builtins.toFile")); + 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, "While evaluating the second argument passed to builtins.toFile: ")); StorePathSet refs; @@ -1929,7 +1929,7 @@ static void addPath( Value res; state.callFunction(*filterFun, 2, args, res, pos); - return state.forceBool(res, pos, "While evaluating the return value of the path filter function"); + return state.forceBool(res, pos, "While evaluating the return value of the path filter function: "); }) : defaultPathFilter; std::optional expectedStorePath; @@ -1955,9 +1955,9 @@ static void addPath( static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - Path path = state.coerceToPath(pos, *args[1], context, "While evaluating the second argument (the path to filter) passed to builtins.filterSource"); + Path path = state.coerceToPath(pos, *args[1], context, "While evaluating the second argument (the path to filter) passed to builtins.filterSource: "); - state.forceFunction(*args[0], pos, "While evaluating the first argument to builtins.filterSource"); + state.forceFunction(*args[0], pos, "While evaluating the first argument to builtins.filterSource: "); addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context); } @@ -2018,7 +2018,7 @@ static RegisterPrimOp primop_filterSource({ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.path"); + state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.path: "); Path path; std::string name; Value * filterFun = nullptr; @@ -2029,15 +2029,15 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value for (auto & attr : *args[0]->attrs) { auto & n(attr.name); if (n == "path") - path = state.coerceToPath(*attr.pos, *attr.value, context, "While evaluating the `path` attribute passed to builtins.path"); + path = state.coerceToPath(*attr.pos, *attr.value, context, "While evaluating the `path` attribute passed to builtins.path: "); else if (attr.name == state.sName) - name = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the `name` attribute passed to builtins.path"); + name = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the `name` attribute passed to builtins.path: "); else if (n == "filter") - state.forceFunction(*(filterFun = attr.value), *attr.pos, "While evaluating the `filter` parameter passed to builtins.path"); + state.forceFunction(*(filterFun = attr.value), *attr.pos, "While evaluating the `filter` parameter passed to builtins.path: "); else if (n == "recursive") - method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos, "While evaluating the `recursive` attribute passed to builtins.path") }; + method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos, "While evaluating the `recursive` attribute passed to builtins.path: ") }; else if (n == "sha256") - expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `sha256` attribute passed to builtins.path"), htSHA256); + expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `sha256` attribute passed to builtins.path: "), htSHA256); else throw EvalError({ .msg = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name), @@ -2100,7 +2100,7 @@ static RegisterPrimOp primop_path({ strings. */ static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrNames"); + state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrNames: "); state.mkList(v, args[0]->attrs->size()); @@ -2127,7 +2127,7 @@ static RegisterPrimOp primop_attrNames({ order as attrNames. */ static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrValues"); + state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrValues: "); state.mkList(v, args[0]->attrs->size()); @@ -2158,8 +2158,8 @@ static RegisterPrimOp primop_attrValues({ /* Dynamic version of the `.' operator. */ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.getAttr"); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.getAttr"); + auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.getAttr: "); + state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.getAttr: "); Bindings::iterator i = getAttr( state, "getAttr", @@ -2188,8 +2188,8 @@ static RegisterPrimOp primop_getAttr({ /* Return position information of the specified attribute. */ static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.unsafeGetAttrPos"); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.unsafeGetAttrPos"); + auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.unsafeGetAttrPos: "); + 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)); if (i == args[1]->attrs->end()) v.mkNull(); @@ -2206,8 +2206,8 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info { /* Dynamic version of the `?' operator. */ static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hasAttr"); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.hasAttr"); + auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hasAttr: "); + 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()); } @@ -2240,8 +2240,8 @@ static RegisterPrimOp primop_isAttrs({ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.removeAttrs"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.removeAttrs"); + state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.removeAttrs: "); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.removeAttrs: "); /* Get the attribute names to be removed. We keep them as Attrs instead of Symbols so std::set_difference @@ -2249,7 +2249,7 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, boost::container::small_vector names; names.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { - state.forceStringNoCtx(*elem, pos, "While evaluating the values of the second argument passed to builtins.removeAttrs"); + 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); } std::sort(names.begin(), names.end()); @@ -2288,14 +2288,14 @@ static RegisterPrimOp primop_removeAttrs({ name, the first takes precedence. */ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the argument passed to builtins.listToAttrs"); + state.forceList(*args[0], pos, "While evaluating the argument passed to builtins.listToAttrs: "); auto attrs = state.buildBindings(args[0]->listSize()); std::set seen; for (auto v2 : args[0]->listItems()) { - state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.listToAttrs"); + state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.listToAttrs: "); Bindings::iterator j = getAttr( state, @@ -2305,7 +2305,7 @@ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, pos ); - auto name = state.forceStringNoCtx(*j->value, *j->pos, "While evaluating the `name` attribute of an element of the list passed to builtins.listToAttrs"); + 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); if (seen.insert(sym).second) { @@ -2350,8 +2350,8 @@ static RegisterPrimOp primop_listToAttrs({ static void prim_intersectAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.intersectAttrs"); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.intersectAttrs"); + state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.intersectAttrs: "); + 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())); @@ -2376,14 +2376,14 @@ static RegisterPrimOp primop_intersectAttrs({ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.catAttrs")); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.catAttrs"); + Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.catAttrs: ")); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.catAttrs: "); Value * res[args[1]->listSize()]; unsigned int found = 0; for (auto v2 : args[1]->listItems()) { - state.forceAttrs(*v2, pos, "While evaluating an element in the list passed as second argument to builtins.catAttrs"); + 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); if (i != v2->attrs->end()) res[found++] = i->value; @@ -2456,7 +2456,7 @@ static RegisterPrimOp primop_functionArgs({ /* */ static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.mapAttrs"); + state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.mapAttrs: "); auto attrs = state.buildBindings(args[1]->attrs->size()); @@ -2497,15 +2497,15 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args std::map> attrsSeen; - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.zipAttrsWith"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.zipAttrsWith"); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.zipAttrsWith: "); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.zipAttrsWith: "); const auto listSize = args[1]->listSize(); const auto listElems = args[1]->listElems(); for (unsigned int n = 0; n < listSize; ++n) { Value * vElem = listElems[n]; try { - state.forceAttrs(*vElem, noPos, "While evaluating a value of the list passed as second argument to builtins.zipAttrsWith"); + state.forceAttrs(*vElem, noPos, "While evaluating a value of the list passed as second argument to builtins.zipAttrsWith: "); for (auto & attr : *vElem->attrs) attrsSeen[attr.name].first++; } catch (TypeError & e) { @@ -2595,7 +2595,7 @@ static RegisterPrimOp primop_isList({ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v) { - state.forceList(list, pos, "While evaluating the first argument passed to builtins.elemAt"); + state.forceList(list, pos, "While evaluating the first argument passed to builtins.elemAt: "); if (n < 0 || (unsigned int) n >= list.listSize()) throw Error({ .msg = hintfmt("list index %1% is out of bounds", n), @@ -2608,7 +2608,7 @@ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Valu /* Return the n-1'th element of a list. */ static void prim_elemAt(EvalState & state, const Pos & pos, Value * * args, Value & v) { - elemAt(state, pos, *args[0], state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.elemAt"), 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({ @@ -2643,7 +2643,7 @@ static RegisterPrimOp primop_head({ don't want to use it! */ static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.tail"); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.tail: "); if (args[0]->listSize() == 0) throw Error({ .msg = hintfmt("'tail' called on an empty list"), @@ -2674,12 +2674,12 @@ static RegisterPrimOp primop_tail({ /* Apply a function to every element of a list. */ static void prim_map(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.map"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.map: "); 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"); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.map: "); for (unsigned int n = 0; n < v.listSize(); ++n) (v.listElems()[n] = state.allocValue())->mkApp( @@ -2708,14 +2708,14 @@ static RegisterPrimOp primop_map({ returns true. */ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.filter"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.filter: "); if (args[1]->listSize() == 0) { v = *args[1]; return; } - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.filter"); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.filter: "); // FIXME: putting this on the stack is risky. Value * vs[args[1]->listSize()]; @@ -2725,7 +2725,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu for (unsigned int n = 0; n < args[1]->listSize(); ++n) { Value res; state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos); - if (state.forceBool(res, pos, "While evaluating the return value of the filtering function passed to builtins.filter")) + if (state.forceBool(res, pos, "While evaluating the return value of the filtering function passed to builtins.filter: ")) vs[k++] = args[1]->listElems()[n]; else same = false; @@ -2753,9 +2753,9 @@ static RegisterPrimOp primop_filter({ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value & v) { bool res = false; - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.elem"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.elem: "); for (auto elem : args[1]->listItems()) - if (state.eqValues(*args[0], *elem)) { + if (state.eqValues(*args[0], *elem, pos, "While searching for the presence of the given element in the list: ")) { res = true; break; } @@ -2775,7 +2775,7 @@ static RegisterPrimOp primop_elem({ /* Concatenate a list of lists. */ static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.concatLists"); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.concatLists: "); state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos, "While evaluating a value of the list passed to builtins.concatLists"); } @@ -2791,7 +2791,7 @@ static RegisterPrimOp primop_concatLists({ /* 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) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.length"); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.length: "); v.mkInt(args[0]->listSize()); } @@ -2808,8 +2808,8 @@ static RegisterPrimOp primop_length({ right. The operator is applied strictly. */ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.foldlStrict"); - state.forceList(*args[2], pos, "While evaluating the third argument passed to builtins.foldlStrict"); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.foldlStrict: "); + state.forceList(*args[2], pos, "While evaluating the third argument passed to builtins.foldlStrict: "); if (args[2]->listSize()) { Value * vCur = args[1]; @@ -2841,13 +2841,13 @@ static RegisterPrimOp primop_foldlStrict({ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, std::string("While evaluating the first argument passed to builtins.") + (any ? "any" : "all")); - state.forceList(*args[1], pos, std::string("While evaluating the second argument passed to builtins.") + (any ? "any" : "all")); + state.forceFunction(*args[0], pos, std::string("While evaluating the first argument passed to builtins.: ") + (any ? "any" : "all: ")); + state.forceList(*args[1], pos, std::string("While evaluating the second argument passed to builtins.: ") + (any ? "any" : "all")); Value vTmp; for (auto elem : args[1]->listItems()) { state.callFunction(*args[0], *elem, vTmp, pos); - bool res = state.forceBool(vTmp, pos, std::string("While evaluating the return value of the function passed to builtins.") + (any ? "any" : "all")); + 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) { v.mkBool(any); return; @@ -2890,7 +2890,7 @@ static RegisterPrimOp primop_all({ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto len = state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.genList"); + auto len = state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.genList: "); if (len < 0) throw EvalError({ @@ -2928,7 +2928,7 @@ 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) { - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.sort"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.sort: "); auto len = args[1]->listSize(); if (len == 0) { @@ -2936,7 +2936,7 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value return; } - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.sort"); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.sort: "); state.mkList(v, len); for (unsigned int n = 0; n < len; ++n) { @@ -2948,12 +2948,12 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value /* Optimization: if the comparator is lessThan, bypass callFunction. */ if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan) - return CompareValues(state, pos, "While evaluating the ordering function passed to builtins.sort")(a, b); + return CompareValues(state, pos, "While evaluating the ordering function passed to builtins.sort: ")(a, b); Value * vs[] = {a, b}; Value vBool; state.callFunction(*args[0], 2, vs, vBool, pos); - return state.forceBool(vBool, pos, "While evaluating the return value of the sorting function passed to builtins.sort"); + 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 @@ -2985,8 +2985,8 @@ static RegisterPrimOp primop_sort({ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.partition"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.partition"); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.partition: "); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.partition: "); auto len = args[1]->listSize(); @@ -2997,7 +2997,7 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V state.forceValue(*vElem, pos); Value res; state.callFunction(*args[0], *vElem, res, pos); - if (state.forceBool(res, pos, "While evaluating the return value of the partition function passed to builtins.partition")) + if (state.forceBool(res, pos, "While evaluating the return value of the partition function passed to builtins.partition: ")) right.push_back(vElem); else wrong.push_back(vElem); @@ -3045,15 +3045,15 @@ static RegisterPrimOp primop_partition({ static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.groupBy"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.groupBy"); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.groupBy: "); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.groupBy: "); ValueVectorMap attrs; for (auto vElem : args[1]->listItems()) { Value res; state.callFunction(*args[0], *vElem, res, pos); - auto name = state.forceStringNoCtx(res, pos, "While evaluating the return value of the grouping function passed to builtins.groupBy"); + 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); auto vector = attrs.try_emplace(sym, ValueVector()).first; vector->second.push_back(vElem); @@ -3097,8 +3097,8 @@ static RegisterPrimOp primop_groupBy({ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.concatMap"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.concatMap"); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.concatMap: "); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.concatMap: "); auto nrLists = args[1]->listSize(); Value lists[nrLists]; @@ -3108,7 +3108,7 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V Value * vElem = args[1]->listElems()[n]; state.callFunction(*args[0], *vElem, lists[n], pos); try { - state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos)), "While evaluating the return value of the function passed to buitlins.concatMap"); + 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) { e.addTrace(pos, hintfmt("while invoking '%s'", "concatMap")); throw; @@ -3147,11 +3147,11 @@ static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - 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")); + 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 - 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")); + 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({ @@ -3168,11 +3168,11 @@ static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - 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")); + 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 - 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")); + 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({ @@ -3189,11 +3189,11 @@ static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - 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")); + 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 - 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")); + 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({ @@ -3210,7 +3210,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); - NixFloat f2 = state.forceFloat(*args[1], pos, "While evaluating the second operand of the division"); + NixFloat f2 = state.forceFloat(*args[1], pos, "While evaluating the second operand of the division: "); if (f2 == 0) throw EvalError({ .msg = hintfmt("division by zero"), @@ -3218,10 +3218,10 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & }); if (args[0]->type() == nFloat || args[1]->type() == nFloat) { - v.mkFloat(state.forceFloat(*args[0], pos, "While evaluating the first operand of the division") / f2); + v.mkFloat(state.forceFloat(*args[0], pos, "While evaluating the first operand of the division: ") / f2); } else { - NixInt i1 = state.forceInt(*args[0], pos, "While evaluating the first operand of the division"); - NixInt i2 = state.forceInt(*args[1], pos, "While evaluating the second operand of the division"); + NixInt i1 = state.forceInt(*args[0], pos, "While evaluating the first operand of the division: "); + NixInt i2 = state.forceInt(*args[1], pos, "While evaluating the second operand of the division: "); /* Avoid division overflow as it might raise SIGFPE. */ if (i1 == std::numeric_limits::min() && i2 == -1) throw EvalError({ @@ -3244,8 +3244,8 @@ static RegisterPrimOp primop_div({ static void prim_bitAnd(EvalState & state, const Pos & pos, Value * * args, Value & v) { - 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")); + 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({ @@ -3259,8 +3259,8 @@ static RegisterPrimOp primop_bitAnd({ static void prim_bitOr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - 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")); + 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({ @@ -3274,8 +3274,8 @@ static RegisterPrimOp primop_bitOr({ static void prim_bitXor(EvalState & state, const Pos & pos, Value * * args, Value & v) { - 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")); + 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({ @@ -3319,7 +3319,7 @@ static RegisterPrimOp primop_lessThan({ static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, true, false, "While evaluating the first argument passed to builtins.toString"); + auto s = state.coerceToString(pos, *args[0], context, true, false, "While evaluating the first argument passed to builtins.toString: "); v.mkString(*s, context); } @@ -3353,10 +3353,10 @@ static RegisterPrimOp primop_toString({ non-negative. */ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, Value & v) { - int start = state.forceInt(*args[0], pos, "While evaluating the first argument (the start offset) passed to builtins.substring"); - int len = state.forceInt(*args[1], pos, "While evaluating the second argument (the substring length) passed to builtins.substring"); + int start = state.forceInt(*args[0], pos, "While evaluating the first argument (the start offset) passed to builtins.substring: "); + int len = state.forceInt(*args[1], pos, "While evaluating the second argument (the substring length) passed to builtins.substring: "); PathSet context; - auto s = state.coerceToString(pos, *args[2], context, "While evaluating the third argument (the string) passed to builtins.substring"); + auto s = state.coerceToString(pos, *args[2], context, "While evaluating the third argument (the string) passed to builtins.substring: "); if (start < 0) throw EvalError({ @@ -3390,7 +3390,7 @@ static RegisterPrimOp primop_substring({ static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.stringLength"); + auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.stringLength: "); v.mkInt(s->size()); } @@ -3407,7 +3407,7 @@ static RegisterPrimOp primop_stringLength({ /* Return the cryptographic hash of a string in base-16. */ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashString"); + auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashString: "); std::optional ht = parseHashType(type); if (!ht) throw Error({ @@ -3416,7 +3416,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, }); PathSet context; // discarded - auto s = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.hashString"); + 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)); } @@ -3455,14 +3455,14 @@ std::shared_ptr makeRegexCache() void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.match"); + auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.match: "); try { auto regex = state.regexCache->get(re); PathSet context; - const auto str = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.match"); + const auto str = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.match: "); std::cmatch match; if (!std::regex_match(str.begin(), str.end(), match, regex)) { @@ -3536,14 +3536,14 @@ static RegisterPrimOp primop_match({ non-matching parts interleaved by the lists of the matching groups. */ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.split"); + auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.split: "); try { auto regex = state.regexCache->get(re); PathSet context; - const auto str = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.split"); + 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 end = std::cregex_iterator(); @@ -3642,8 +3642,8 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * { PathSet context; - auto sep = state.forceString(*args[0], context, pos, "While evaluating the first argument (the separator string) passed to builtins.concatStringsSep"); - state.forceList(*args[1], pos, "While evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep"); + auto sep = state.forceString(*args[0], context, pos, "While evaluating the first argument (the separator string) passed to builtins.concatStringsSep: "); + state.forceList(*args[1], pos, "While evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep: "); std::string res; res.reserve((args[1]->listSize() + 32) * sep.size()); @@ -3651,7 +3651,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * for (auto elem : args[1]->listItems()) { if (first) first = false; else res += sep; - res += *state.coerceToString(pos, *elem, context, "While evaluating one element of the list of strings to concat passed to builtins.concatStringsSep"); + res += *state.coerceToString(pos, *elem, context, "While evaluating one element of the list of strings to concat passed to builtins.concatStringsSep: "); } v.mkString(res, context); @@ -3670,8 +3670,8 @@ static RegisterPrimOp primop_concatStringsSep({ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.replaceStrings"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.replaceStrings"); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.replaceStrings: "); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.replaceStrings: "); if (args[0]->listSize() != args[1]->listSize()) throw EvalError({ .msg = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"), @@ -3681,18 +3681,18 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar std::vector from; from.reserve(args[0]->listSize()); for (auto elem : args[0]->listItems()) - from.emplace_back(state.forceString(*elem, pos, "While evaluating one of the strings to replace in builtins.replaceStrings")); + from.emplace_back(state.forceString(*elem, pos, "While evaluating one of the strings to replace in builtins.replaceStrings: ")); std::vector> to; to.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { PathSet ctx; - auto s = state.forceString(*elem, ctx, pos, "While evaluating one of the replacement strings of builtins.replaceStrings"); + auto s = state.forceString(*elem, ctx, pos, "While evaluating one of the replacement strings of builtins.replaceStrings: "); to.emplace_back(s, std::move(ctx)); } PathSet context; - auto s = state.forceString(*args[2], context, pos, "While evaluating the third argument passed to builtins.replaceStrings"); + auto s = state.forceString(*args[2], context, pos, "While evaluating the third argument passed to builtins.replaceStrings: "); std::string res; // Loops one past last character to handle the case where 'from' contains an empty string. @@ -3750,7 +3750,7 @@ static RegisterPrimOp primop_replaceStrings({ static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto name = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.parseDrvName"); + auto name = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.parseDrvName: "); DrvName parsed(name); auto attrs = state.buildBindings(2); attrs.alloc(state.sName).mkString(parsed.name); @@ -3774,8 +3774,8 @@ static RegisterPrimOp primop_parseDrvName({ static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto version1 = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.compareVersions"); - auto version2 = state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.compareVersions"); + auto version1 = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.compareVersions: "); + auto version2 = state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.compareVersions: "); v.mkInt(compareVersions(version1, version2)); } @@ -3794,7 +3794,7 @@ static RegisterPrimOp primop_compareVersions({ static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto version = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.splitVersion"); + auto version = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.splitVersion: "); auto iter = version.cbegin(); Strings components; while (iter != version.cend()) { diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 78320dc09..222d183e4 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -7,7 +7,7 @@ namespace nix { static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardStringContext"); + auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardStringContext: "); v.mkString(*s); } @@ -17,7 +17,7 @@ static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringCo static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.hasContext"); + state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.hasContext: "); v.mkBool(!context.empty()); } @@ -33,7 +33,7 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext); static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardOutputDependency"); + auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardOutputDependency: "); PathSet context2; for (auto & p : context) @@ -72,7 +72,7 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, Strings outputs; }; PathSet context; - state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.getContext"); + state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.getContext: "); auto contextInfos = std::map(); for (const auto & p : context) { 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) { PathSet context; - auto orig = state.forceString(*args[0], context, pos, "while evaluating the first argument passed to builtins.appendContext"); + auto orig = state.forceString(*args[0], context, pos, "while evaluating the first argument passed to builtins.appendContext: "); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.appendContext"); + state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.appendContext: "); auto sPath = state.symbols.create("path"); auto sAllOutputs = state.symbols.create("allOutputs"); @@ -150,16 +150,16 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg }); if (!settings.readOnlyMode) state.store->ensurePath(state.store->parseStorePath(i.name)); - state.forceAttrs(*i.value, *i.pos, "While evaluating the value of a string context"); + state.forceAttrs(*i.value, *i.pos, "While evaluating the value of a string context: "); auto iter = i.value->attrs->find(sPath); if (iter != i.value->attrs->end()) { - if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `path` attribute of a string context")) + if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `path` attribute of a string context: ")) context.insert(i.name); } iter = i.value->attrs->find(sAllOutputs); if (iter != i.value->attrs->end()) { - if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `allOutputs` attribute of a string context")) { + if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `allOutputs` attribute of a string context: ")) { if (!isDerivation(i.name)) { throw EvalError({ .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); if (iter != i.value->attrs->end()) { - state.forceList(*iter->value, *iter->pos, "While evaluating the `outputs` attribute of a string context"); + state.forceList(*iter->value, *iter->pos, "While evaluating the `outputs` attribute of a string context: "); if (iter->value->listSize() && !isDerivation(i.name)) { throw EvalError({ .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()) { - auto name = state.forceStringNoCtx(*elem, *iter->pos, "While evaluating an output name within a string context"); + auto name = state.forceStringNoCtx(*elem, *iter->pos, "While evaluating an output name within a string context: "); context.insert(concatStrings("!", name, "!", i.name)); } } diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index dcaf3d362..7434b8101 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -22,18 +22,18 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar for (auto & attr : *args[0]->attrs) { std::string_view n(attr.name); if (n == "url") - url = state.coerceToString(*attr.pos, *attr.value, context, false, false, "While evaluating the `url` attribute passed to builtins.fetchMercurial").toOwned(); + url = state.coerceToString(*attr.pos, *attr.value, context, false, false, "While evaluating the `url` attribute passed to builtins.fetchMercurial: ").toOwned(); else if (n == "rev") { // Ugly: unlike fetchGit, here the "rev" attribute can // be both a revision or a branch/tag name. - auto value = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `rev` attribute passed to builtins.fetchMercurial"); + 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)) rev = Hash::parseAny(value, htSHA1); else ref = value; } else if (n == "name") - name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `name` attribute passed to builtins.fetchMercurial"); + name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `name` attribute passed to builtins.fetchMercurial: "); else throw EvalError({ .msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name), @@ -48,7 +48,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar }); } else - url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.fetchMercurial").toOwned(); + url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.fetchMercurial: ").toOwned(); // FIXME: git externals probably can be used to bypass the URI // whitelist. Ah well. diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 2baa272cb..31a8907f1 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -102,7 +102,7 @@ static void fetchTree( state.forceValue(*args[0], pos); if (args[0]->type() == nAttrs) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.fetchTree"); + state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.fetchTree: "); fetchers::Attrs attrs; @@ -112,7 +112,7 @@ static void fetchTree( .msg = hintfmt("unexpected attribute 'type'"), .errPos = pos }); - type = state.forceStringNoCtx(*aType->value, *aType->pos, "While evaluating the `type` attribute passed to builtins.fetchTree"); + type = state.forceStringNoCtx(*aType->value, *aType->pos, "While evaluating the `type` attribute passed to builtins.fetchTree: "); } else if (!type) throw Error({ .msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"), @@ -151,7 +151,7 @@ static void fetchTree( input = fetchers::Input::fromAttrs(std::move(attrs)); } else { - auto url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to the fetcher").toOwned(); + auto url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to the fetcher: ").toOwned(); if (type == "git") { fetchers::Attrs attrs; @@ -198,11 +198,11 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, for (auto & attr : *args[0]->attrs) { std::string n(attr.name); if (n == "url") - url = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the url we should fetch"); + url = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the url we should fetch: "); else if (n == "sha256") - expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the sha256 of the content we should fetch"), htSHA256); + expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the sha256 of the content we should fetch: "), htSHA256); else if (n == "name") - name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the name of the content we should fetch"); + name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the name of the content we should fetch: "); else throw EvalError({ .msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who), @@ -216,7 +216,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, .errPos = pos }); } else - url = state.forceStringNoCtx(*args[0], pos, "While evaluating the url we should fetch"); + url = state.forceStringNoCtx(*args[0], pos, "While evaluating the url we should fetch: "); url = resolveUri(*url); diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 6bfd8e285..2a038bb20 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -7,7 +7,7 @@ namespace nix { static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val) { - auto toml = state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.fromTOML"); + auto toml = state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.fromTOML: "); std::istringstream tomlStream(std::string{toml}); diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 8ee3ba45b..1d66d457c 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -107,7 +107,7 @@ struct CmdEval : MixJSON, InstallableCommand else if (raw) { stopProgressBar(); - std::cout << *state->coerceToString(noPos, *v, context, "While generating the eval command output"); + std::cout << *state->coerceToString(noPos, *v, context, "While generating the eval command output: "); } else if (json) { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 356f89713..9ba6e5be1 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -126,12 +126,12 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, std::function callback) { auto pos = vFlake.determinePos(noPos); - state.forceAttrs(vFlake, pos, "While evaluating a flake to get its outputs"); + state.forceAttrs(vFlake, pos, "While evaluating a flake to get its outputs: "); auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs")); assert(aOutputs); - state.forceAttrs(*aOutputs->value, pos, "While evaluating the outputs of a flake"); + state.forceAttrs(*aOutputs->value, pos, "While evaluating the outputs of a flake: "); auto sHydraJobs = state.symbols.create("hydraJobs"); diff --git a/src/nix/main.cc b/src/nix/main.cc index 0c6286686..3ba7ce8b6 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -196,7 +196,7 @@ static void showHelp(std::vector subcommand, MultiCommand & topleve if (!attr) throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); - auto markdown = state.forceString(*attr->value, noPos, "While evaluating the lowdown help text"); + auto markdown = state.forceString(*attr->value, noPos, "While evaluating the lowdown help text: "); RunPager pager; std::cout << renderMarkdownToTerminal(markdown) << "\n"; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 4376da896..a4a8ab605 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -28,17 +28,17 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url) Value vMirrors; // FIXME: use nixpkgs flake state.eval(state.parseExprFromString("import ", "."), vMirrors); - state.forceAttrs(vMirrors, noPos, "While evaluating the set of all mirrors"); + state.forceAttrs(vMirrors, noPos, "While evaluating the set of all mirrors: "); auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); if (mirrorList == vMirrors.attrs->end()) throw Error("unknown mirror name '%s'", mirrorName); - state.forceList(*mirrorList->value, noPos, "While evaluating this mirror configuration"); + state.forceList(*mirrorList->value, noPos, "While evaluating this mirror configuration: "); if (mirrorList->value->listSize() < 1) throw Error("mirror URL '%s' did not expand to anything", url); - std::string mirror(state.forceString(*mirrorList->value->listElems()[0], noPos, "While evaluating the first available mirror")); + std::string mirror(state.forceString(*mirrorList->value->listElems()[0], noPos, "While evaluating the first available mirror: ")); return mirror + (hasSuffix(mirror, "/") ? "" : "/") + s.substr(p + 1); } @@ -196,27 +196,27 @@ static int main_nix_prefetch_url(int argc, char * * argv) Value vRoot; state->evalFile(path, vRoot); Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first); - state->forceAttrs(v, noPos, "While evaluating the source attribute to prefetch"); + state->forceAttrs(v, noPos, "While evaluating the source attribute to prefetch: "); /* Extract the URL. */ auto & attr = v.attrs->need(state->symbols.create("urls")); - state->forceList(*attr.value, noPos, "While evaluating the urls to prefetch"); + state->forceList(*attr.value, noPos, "While evaluating the urls to prefetch: "); if (attr.value->listSize() < 1) throw Error("'urls' list is empty"); - url = state->forceString(*attr.value->listElems()[0], noPos, "While evaluating the first url from the urls list"); + url = state->forceString(*attr.value->listElems()[0], noPos, "While evaluating the first url from the urls list: "); /* Extract the hash mode. */ auto attr2 = v.attrs->get(state->symbols.create("outputHashMode")); if (!attr2) printInfo("warning: this does not look like a fetchurl call"); else - unpack = state->forceString(*attr2->value, noPos, "While evaluating the outputHashMode of the source to prefetch") == "recursive"; + unpack = state->forceString(*attr2->value, noPos, "While evaluating the outputHashMode of the source to prefetch: ") == "recursive"; /* Extract the name. */ if (!name) { auto attr3 = v.attrs->get(state->symbols.create("name")); if (!attr3) - name = state->forceString(*attr3->value, noPos, "While evaluating the name of the source to prefetch"); + name = state->forceString(*attr3->value, noPos, "While evaluating the name of the source to prefetch: "); } } diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 0cb68552c..038ed4911 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -342,7 +342,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) Expr * e = parseString(expr); Value v; e->eval(*state, *env, v); - state->forceAttrs(v, noPos, "nevermind, it is ignored anyway"); + state->forceAttrs(v, noPos, "nevermind, it is ignored anyway: "); for (auto & i : *v.attrs) { std::string name = i.name; @@ -461,7 +461,7 @@ bool NixRepl::processLine(std::string line) if (v.type() == nPath || v.type() == nString) { PathSet context; - auto filename = state->coerceToString(noPos, v, context, "While evaluating the filename to edit"); + auto filename = state->coerceToString(noPos, v, context, "While evaluating the filename to edit: "); pos.file = state->symbols.create(*filename); } else if (v.isLambda()) { pos = v.lambda.fun->pos; @@ -675,7 +675,7 @@ void NixRepl::reloadFiles() void NixRepl::addAttrsToScope(Value & attrs) { - state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "While evaluating an attribute set to be merged in the global scope"); + 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) throw Error("environment full; cannot add more variables"); @@ -780,7 +780,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m Bindings::iterator i = v.attrs->find(state->sDrvPath); PathSet context; if (i != v.attrs->end()) - str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the drvPath of a derivation")); + str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the drvPath of a derivation: ")); else str << "???"; str << "»"; diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 1f433a199..ae314a7c6 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -144,7 +144,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand Bindings & bindings(*state->allocBindings(0)); auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first; - return store->parseStorePath(state->forceString(*v2, noPos, "While evaluating the path tho latest nix version")); + return store->parseStorePath(state->forceString(*v2, noPos, "While evaluating the path tho latest nix version: ")); } }; From e6d07e0d89d964cde22894fca57a95177c085c8d Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 18 Mar 2022 00:58:09 +0100 Subject: [PATCH 10/39] Refactor to use more traces and less string manipulations --- src/libexpr/eval-inline.hh | 35 ++- src/libexpr/eval.cc | 297 ++++++++++++--------- src/libexpr/eval.hh | 34 +-- src/libexpr/parser.y | 2 +- src/libexpr/primops.cc | 514 +++++++++++++++++-------------------- src/libexpr/value.hh | 2 +- src/libutil/error.cc | 23 +- src/libutil/error.hh | 10 +- 8 files changed, 475 insertions(+), 442 deletions(-) diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index ad53a74cb..8980e8d4b 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -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, const std::string & s2)) +LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v)) { throw TypeError({ - .msg = hintfmt(s, showType(v), s2), + .msg = hintfmt(s, showType(v)), .errPos = pos }); } @@ -52,26 +52,39 @@ void EvalState::forceValue(Value & v, Callable getPos) } -inline void EvalState::forceAttrs(Value & v, const Pos & pos, const std::string & errorCtx) +inline void EvalState::forceAttrs(Value & v, const Pos & pos, const std::string_view & errorCtx) { forceAttrs(v, [&]() { return pos; }, errorCtx); } template -inline void EvalState::forceAttrs(Value & v, Callable getPos, const std::string & errorCtx) +inline void EvalState::forceAttrs(Value & v, Callable getPos, const std::string_view & errorCtx) { - forceValue(v, getPos); - if (v.type() != nAttrs) - throwTypeError(getPos(), "%2%value is %1% while a set was expected", v, errorCtx); + try { + forceValue(v, noPos); + if (v.type() != nAttrs) { + throwTypeError(noPos, "value is %1% while a set was expected", v); + } + } catch (Error & e) { + Pos pos = getPos(); + e.addTrace(pos, errorCtx); + throw; + } } -inline void EvalState::forceList(Value & v, const Pos & pos, const std::string & errorCtx) +inline void EvalState::forceList(Value & v, const Pos & pos, const std::string_view & errorCtx) { - forceValue(v, pos); - if (!v.isList()) - throwTypeError(pos, "%2%value is %1% while a list was expected", v, errorCtx); + try { + forceValue(v, noPos); + if (!v.isList()) { + throwTypeError(noPos, "value is %1% while a list was expected", v); + } + } catch (Error & e) { + e.addTrace(pos, errorCtx); + throw; + } } /* Note: Various places expect the allocated memory to be zeroed. */ diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 55c624cb9..33eeef902 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -694,9 +694,12 @@ std::optional EvalState::getDoc(Value & v) evaluator. So here are some helper functions for throwing exceptions. */ -LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2)) +LocalNoInlineNoReturn(void throwTypeErrorWithTrace(const Pos & pos, const char * s, const std::string & s2, const Symbol & sym, const Pos & p2, const std::string & s3)) { - throw EvalError(s, s2); + throw EvalError({ + .msg = hintfmt(s, s2, sym), + .errPos = pos + }).addTrace(p2, s3); } LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2)) @@ -707,23 +710,10 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const }); } -LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string & s2, const std::string & s3)) +LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const Value & v)) { - throw EvalError(s, s2, s3); -} - -LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3)) -{ - throw EvalError({ - .msg = hintfmt(s, s2, s3), - .errPos = pos - }); -} - -LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2, const std::string & s3, const std::string & s4)) -{ - throw EvalError({ - .msg = hintfmt(s, s2, s3, s4), + throw AssertionError({ + .msg = hintfmt(s, showType(v)), .errPos = pos }); } @@ -737,22 +727,6 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const }); } -LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2)) -{ - throw TypeError({ - .msg = hintfmt(s, fun.showNamePos(), s2), - .errPos = pos - }); -} - -LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const Value & v)) -{ - throw AssertionError({ - .msg = hintfmt(s, showType(v)), - .errPos = pos - }); -} - LocalNoInlineNoReturn(void throwAssertionError(const Pos & pos, const char * s, const std::string & s1)) { throw AssertionError({ @@ -1040,21 +1014,31 @@ void EvalState::eval(Expr * e, Value & v) } -inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, const std::string & errorCtx) +inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, const std::string_view & errorCtx) { - Value v; - e->eval(*this, env, v); - if (v.type() != nBool) - throwTypeError(pos, "%2%value is %1% while a Boolean was expected", v, errorCtx); - return v.boolean; + try { + Value v; + e->eval(*this, env, v); + if (v.type() != nBool) + throw TypeError("value is %1% while a Boolean was expected", showType(v)); + return v.boolean; + } catch (Error & e) { + e.addTrace(pos, errorCtx); + throw; + } } -inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string & errorCtx) +inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string_view & errorCtx) { - e->eval(*this, env, v); - if (v.type() != nAttrs) - throwTypeError(pos, "%2%value is %1% while a set was expected", v, errorCtx); + try { + e->eval(*this, env, v); + if (v.type() != nAttrs) + throw TypeError("value is %1% while a set was expected", showType(v)); + } catch (Error & e) { + e.addTrace(pos, errorCtx); + throw; + } } @@ -1297,6 +1281,10 @@ void ExprLambda::eval(EvalState & state, Env & env, Value & v) v.mkLambda(&env, this); } +const std::string prettyLambdaName(const ExprLambda & e) +{ + return e.name.set() ? std::string(e.name): "anonymous lambda"; +} void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const Pos & pos) { @@ -1336,7 +1324,12 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & env2.values[displ++] = args[0]; else { - forceAttrs(*args[0], pos, "While evaluating the value passed as argument to a function expecting an attribute set: "); + try { + forceAttrs(*args[0], lambda.pos, "While evaluating the value passed for this lambda parameter"); + } catch (Error & e) { + e.addTrace(pos, "from call site"); + throw; + } if (!lambda.arg.empty()) env2.values[displ++] = args[0]; @@ -1348,8 +1341,11 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & for (auto & i : lambda.formals->formals) { auto j = args[0]->attrs->get(i.name); if (!j) { - if (!i.def) throwTypeError(pos, "Function %1% called without required argument '%2%'", - lambda, i.name); + if (!i.def) { + throwTypeErrorWithTrace(lambda.pos, + "Function '%1%' called without required argument '%2%'", prettyLambdaName(lambda), i.name, + pos, "from call site"); + } env2.values[displ++] = i.def->maybeThunk(*this, env2); } else { attrsUsed++; @@ -1363,8 +1359,11 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & /* Nope, so show the first unexpected argument to the user. */ for (auto & i : *args[0]->attrs) - if (!lambda.formals->has(i.name)) - throwTypeError(pos, "Function %1% called with unexpected argument '%2%'", lambda, i.name); + if (!lambda.formals->has(i.name)) { + throwTypeErrorWithTrace(lambda.pos, + "Function '%1%' called with unexpected argument '%2%'", prettyLambdaName(lambda), i.name, + pos, "from call site"); + } abort(); // can't happen } } @@ -1377,11 +1376,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & lambda.body->eval(*this, env2, vCur); } catch (Error & e) { if (loggerSettings.showTrace.get()) { - addErrorTrace(e, lambda.pos, "while evaluating %s", - (lambda.name.set() - ? "'" + (const std::string &) lambda.name + "'" - : "anonymous lambda")); - addErrorTrace(e, pos, "from call site%s", ""); + addErrorTrace(e, lambda.pos, "While evaluating the '%s' function", prettyLambdaName(lambda)); + if (pos) e.addTrace(pos, "from call site"); } throw; } @@ -1400,9 +1396,17 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & return; } else { /* We have all the arguments, so call the primop. */ + Symbol name = vCur.primOp->name; + nrPrimOpCalls++; - if (countCalls) primOpCalls[vCur.primOp->name]++; - vCur.primOp->fun(*this, pos, args, vCur); + if (countCalls) primOpCalls[name]++; + + try { + vCur.primOp->fun(*this, pos, args, vCur); + } catch (Error & e) { + addErrorTrace(e, pos, "While calling the '%1%' builtin", name); + throw; + } nrArgs -= argsLeft; args += argsLeft; @@ -1437,9 +1441,16 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & for (size_t i = 0; i < argsLeft; ++i) vArgs[argsDone + i] = args[i]; + Symbol name = primOp->primOp->name; nrPrimOpCalls++; - if (countCalls) primOpCalls[primOp->primOp->name]++; - primOp->primOp->fun(*this, pos, vArgs, vCur); + if (countCalls) primOpCalls[name]++; + + try { + primOp->primOp->fun(*this, noPos, vArgs, vCur); + } catch (Error & e) { + addErrorTrace(e, pos, "While calling the '%1%' builtin", name); + throw; + } nrArgs -= argsLeft; args += argsLeft; @@ -1452,8 +1463,12 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & heap-allocate a copy and use that instead. */ Value * args2[] = {allocValue(), args[0]}; *args2[0] = vCur; - /* !!! Should we use the attr pos here? */ - callFunction(*functor->value, 2, args2, vCur, pos); + try { + callFunction(*functor->value, 2, args2, vCur, *functor->pos); + } catch (Error & e) { + e.addTrace(pos, "While calling a functor (an attribute set with a 'functor' attribute)"); + throw; + } nrArgs--; args++; } @@ -1553,7 +1568,7 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) void ExprIf::eval(EvalState & state, Env & env, Value & 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); + (state.evalBool(env, cond, pos, "While evaluating a branch condition") ? then : else_)->eval(state, env, v); } @@ -1578,7 +1593,7 @@ void ExprOpEq::eval(EvalState & state, Env & env, Value & v) { Value v1; e1->eval(state, env, v1); Value v2; e2->eval(state, env, v2); - v.mkBool(state.eqValues(v1, v2, pos, "")); + v.mkBool(state.eqValues(v1, v2, pos, "While testing two values for equality")); } @@ -1586,7 +1601,7 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v) { Value v1; e1->eval(state, env, v1); Value v2; e2->eval(state, env, v2); - v.mkBool(!state.eqValues(v1, v2, pos, "")); + v.mkBool(!state.eqValues(v1, v2, pos, "While testing two values for inequality")); } @@ -1655,7 +1670,7 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) } -void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos, const std::string & errorCtx) +void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos, const std::string_view & errorCtx) { nrListConcats++; @@ -1739,20 +1754,20 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) nf = n; nf += vTmp.fpoint; } else - throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp)); + throwEvalError(i_pos, "cannot add %1% to an integer", vTmp); } else if (firstType == nFloat) { if (vTmp.type() == nInt) { nf += vTmp.integer; } else if (vTmp.type() == nFloat) { nf += vTmp.fpoint; } else - throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp)); + throwEvalError(i_pos, "cannot add %1% to a float", vTmp); } else { if (s.empty()) s.reserve(es->size()); /* skip canonization of first path, which would only be not canonized in the first place if it's coming from a ./${foo} type path */ - auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first, ""); + auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first, "While evaluating a path segment"); sSize += part->size(); s.emplace_back(std::move(part)); } @@ -1810,32 +1825,47 @@ void EvalState::forceValueDeep(Value & v) } -NixInt EvalState::forceInt(Value & v, const Pos & pos, const std::string & errorCtx) +NixInt EvalState::forceInt(Value & v, const Pos & pos, const std::string_view & errorCtx) { - forceValue(v, pos); - if (v.type() != nInt) - throwTypeError(pos, "%2%value is %1% while an integer was expected", v, errorCtx); - return v.integer; -} - - -NixFloat EvalState::forceFloat(Value & v, const Pos & pos, const std::string & errorCtx) -{ - forceValue(v, pos); - if (v.type() == nInt) + try { + forceValue(v, pos); + if (v.type() != nInt) + throw TypeError("value is %1% while an integer was expected", showType(v)); return v.integer; - else if (v.type() != nFloat) - throwTypeError(pos, "%2%value is %1% while a float was expected", v, errorCtx); - return v.fpoint; + } catch (Error & e) { + e.addTrace(pos, errorCtx); + throw; + } } -bool EvalState::forceBool(Value & v, const Pos & pos, const std::string & errorCtx) +NixFloat EvalState::forceFloat(Value & v, const Pos & pos, const std::string_view & errorCtx) { - forceValue(v, pos); - if (v.type() != nBool) - throwTypeError(pos, "%2%value is %1% while a Boolean was expected", v, errorCtx); - return v.boolean; + try { + forceValue(v, pos); + if (v.type() == nInt) + return v.integer; + else if (v.type() != nFloat) + throw TypeError("value is %1% while a float was expected", showType(v)); + return v.fpoint; + } catch (Error & e) { + e.addTrace(pos, errorCtx); + throw; + } +} + + +bool EvalState::forceBool(Value & v, const Pos & pos, const std::string_view & errorCtx) +{ + try { + forceValue(v, pos); + if (v.type() != nBool) + throw TypeError("value is %1% while a Boolean was expected", showType(v)); + return v.boolean; + } catch (Error & e) { + e.addTrace(pos, errorCtx); + throw; + } } @@ -1845,21 +1875,30 @@ bool EvalState::isFunctor(Value & fun) } -void EvalState::forceFunction(Value & v, const Pos & pos, const std::string & errorCtx) +void EvalState::forceFunction(Value & v, const Pos & pos, const std::string_view & errorCtx) { - forceValue(v, pos); - if (v.type() != nFunction && !isFunctor(v)) - throwTypeError(pos, "%2%value is %1% while a function was expected", v, errorCtx); + try { + forceValue(v, pos); + if (v.type() != nFunction && !isFunctor(v)) + throw TypeError("value is %1% while a function was expected", showType(v)); + } catch (Error & e) { + e.addTrace(pos, errorCtx); + throw; + } } -std::string_view EvalState::forceString(Value & v, const Pos & pos, const std::string & errorCtx) +std::string_view EvalState::forceString(Value & v, const Pos & pos, const std::string_view & errorCtx) { - forceValue(v, pos); - if (v.type() != nString) { - throwTypeError(pos, "%2%value is %1% while a string was expected", v, errorCtx); + try { + forceValue(v, pos); + if (v.type() != nString) + throw TypeError("value is %1% while a string was expected", showType(v)); + return v.string.s; + } catch (Error & e) { + e.addTrace(pos, errorCtx); + throw; } - return v.string.s; } @@ -1894,7 +1933,7 @@ std::vector> Value::getContext() } -std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos, const std::string & errorCtx) +std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos, const std::string_view & errorCtx) { auto s = forceString(v, pos, errorCtx); copyContext(v, context); @@ -1902,18 +1941,21 @@ std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos } -std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos, const std::string & errorCtx) +std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos, const std::string_view & errorCtx) { - auto s = forceString(v, pos, errorCtx); - if (v.string.context) { - if (pos) - 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]); - else - 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]); + try { + auto s = forceString(v, pos, errorCtx); + if (v.string.context) { + if (pos) + throw EvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]); + else + throw EvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]); + } + return s; + } catch (Error & e) { + e.addTrace(pos, errorCtx); + throw; } - return s; } @@ -1943,7 +1985,7 @@ std::optional EvalState::tryAttrsToString(const Pos & pos, Value & } BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, - bool coerceMore, bool copyToStore, bool canonicalizePath, const std::string & errorCtx) + bool coerceMore, bool copyToStore, bool canonicalizePath, const std::string_view & errorCtx) { forceValue(v, pos); @@ -1966,7 +2008,9 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & if (maybeString) return std::move(*maybeString); auto i = v.attrs->find(sOutPath); - if (i == v.attrs->end()) throwTypeError(pos, "%2%cannot coerce %1% to a string", v, errorCtx); + if (i == v.attrs->end()) { + throw TypeError("cannot coerce %1% to a string", showType(v)).addTrace(pos, errorCtx); + } return coerceToString(pos, *i->value, context, coerceMore, copyToStore, canonicalizePath, errorCtx); } @@ -1986,8 +2030,13 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & if (v.isList()) { std::string result; for (auto [n, v2] : enumerate(v.listItems())) { - result += *coerceToString(pos, *v2, context, coerceMore, copyToStore, canonicalizePath, - errorCtx + ": While evaluating one element of the list"); + try { + result += *coerceToString(noPos, *v2, context, coerceMore, copyToStore, canonicalizePath, + "While evaluating one element of the list"); + } catch (Error & e) { + e.addTrace(pos, errorCtx); + throw; + } if (n < v.listSize() - 1 /* !!! not quite correct */ && (!v2->isList() || v2->listSize() != 0)) @@ -1997,14 +2046,14 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & } } - throwTypeError(pos, "%2%cannot coerce %1% to a string", v, errorCtx); + throw TypeError("cannot coerce %1% to a string", showType(v)).addTrace(pos, errorCtx); } std::string EvalState::copyPathToStore(PathSet & context, const Path & path) { if (nix::isDerivation(path)) - throwEvalError("file names are not allowed to end in '%1%'", drvExtension); + throw EvalError("file names are not allowed to end in '%1%'", drvExtension); Path dstPath; auto i = srcToStore.find(path); @@ -2025,28 +2074,25 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path) } -Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context, const std::string & errorCtx) +Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context, const std::string_view & errorCtx) { auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (path == "" || path[0] != '/') - throwEvalError(pos, "%2%string '%1%' doesn't represent an absolute path", path, errorCtx); + throw EvalError("string '%1%' doesn't represent an absolute path", path).addTrace(pos, errorCtx); return path; } -StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context, const std::string & errorCtx) +StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context, const std::string_view & errorCtx) { auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (auto storePath = store->maybeParseStorePath(path)) return *storePath; - throw EvalError({ - .msg = hintfmt("%2%path '%1%' is not in the Nix store", path, errorCtx), - .errPos = pos - }); + throw EvalError("path '%1%' is not in the Nix store", path).addTrace(pos, errorCtx); } -bool EvalState::eqValues(Value & v1, Value & v2, const Pos & pos, const std::string & errorCtx) +bool EvalState::eqValues(Value & v1, Value & v2, const Pos & pos, const std::string_view & errorCtx) { forceValue(v1, noPos); forceValue(v2, noPos); @@ -2120,7 +2166,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const Pos & pos, const std::str return v1.fpoint == v2.fpoint; default: - throwEvalError(pos, "%3%cannot compare %1% with %2%", showType(v1), showType(v2), errorCtx); + throw EvalError("cannot compare %1% with %2%", showType(v1), showType(v2)).addTrace(pos, errorCtx); } } @@ -2242,12 +2288,9 @@ void EvalState::printStats() } -std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, const std::string & errorCtx) const +std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, const std::string_view & errorCtx) const { - throw TypeError({ - .msg = hintfmt("%2%cannot coerce %1% to a string", showType(), errorCtx), - .errPos = pos - }); + throw TypeError("cannot coerce %1% to a string", showType()).addTrace(pos, errorCtx); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index bd817c9fe..3f987922a 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -217,8 +217,8 @@ public: /* Evaluation the expression, then verify that it has the expected type. */ - inline bool evalBool(Env & env, Expr * e, const Pos & pos, const std::string & errorCtx); - inline void evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string & errorCtx); + inline bool evalBool(Env & env, Expr * e, const Pos & pos, const std::string_view & errorCtx); + inline void evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string_view & errorCtx); /* If `v' is a thunk, enter it and overwrite `v' with the result of the evaluation of the thunk. If `v' is a delayed function @@ -234,20 +234,20 @@ public: void forceValueDeep(Value & v); /* Force `v', and then verify that it has the expected type. */ - NixInt forceInt(Value & v, const Pos & pos, const std::string & errorCtx); - NixFloat forceFloat(Value & v, const Pos & pos, const std::string & errorCtx); - bool forceBool(Value & v, const Pos & pos, const std::string & errorCtx); + NixInt forceInt(Value & v, const Pos & pos, const std::string_view & errorCtx); + NixFloat forceFloat(Value & v, const Pos & pos, const std::string_view & errorCtx); + bool forceBool(Value & v, const Pos & pos, const std::string_view & errorCtx); - void forceAttrs(Value & v, const Pos & pos, const std::string & errorCtx); + void forceAttrs(Value & v, const Pos & pos, const std::string_view & errorCtx); template - inline void forceAttrs(Value & v, Callable getPos, const std::string & errorCtx); + inline void forceAttrs(Value & v, Callable getPos, const std::string_view & errorCtx); - inline void forceList(Value & v, const Pos & pos, const std::string & errorCtx); - void forceFunction(Value & v, const Pos & pos, const std::string & errorCtx); // either lambda or primop - std::string_view forceString(Value & v, const Pos & pos, const std::string & errorCtx); - std::string_view forceString(Value & v, PathSet & context, const Pos & pos, const std::string & errorCtx); - std::string_view forceStringNoCtx(Value & v, const Pos & pos, const std::string & errorCtx); + inline void forceList(Value & v, const Pos & pos, const std::string_view & errorCtx); + void forceFunction(Value & v, const Pos & pos, const std::string_view & errorCtx); // either lambda or primop + std::string_view forceString(Value & v, const Pos & pos, const std::string_view & errorCtx); + std::string_view forceString(Value & v, PathSet & context, const Pos & pos, const std::string_view & errorCtx); + std::string_view forceStringNoCtx(Value & v, const Pos & pos, const std::string_view & errorCtx); /* Return true iff the value `v' denotes a derivation (i.e. a set with attribute `type = "derivation"'). */ @@ -263,17 +263,17 @@ public: BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context, bool coerceMore = false, bool copyToStore = true, bool canonicalizePath = true, - const std::string & errorCtx = ""); + const std::string_view & errorCtx = ""); std::string copyPathToStore(PathSet & context, const Path & path); /* Path coercion. Converts strings, paths and derivations to a path. The result is guaranteed to be a canonicalised, absolute path. Nothing is copied to the store. */ - Path coerceToPath(const Pos & pos, Value & v, PathSet & context, const std::string & errorCtx); + Path coerceToPath(const Pos & pos, Value & v, PathSet & context, const std::string_view & errorCtx); /* Like coerceToPath, but the result must be a store path. */ - StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context, const std::string & errorCtx); + StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context, const std::string_view & errorCtx); public: @@ -329,7 +329,7 @@ public: /* Do a deep equality test between two values. That is, list elements and attributes are compared recursively. */ - bool eqValues(Value & v1, Value & v2, const Pos & pos, const std::string & errorCtx); + bool eqValues(Value & v1, Value & v2, const Pos & pos, const std::string_view & errorCtx); bool isFunctor(Value & fun); @@ -364,7 +364,7 @@ public: void mkThunk_(Value & v, Expr * expr); void mkPos(Value & v, ptr pos); - void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos, const std::string & errorCtx); + void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos, const std::string_view & errorCtx); /* Print statistics. */ void printStats(); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 76d45180b..b236c038b 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -396,7 +396,7 @@ expr_function ; expr_if - : IF expr THEN expr ELSE expr { $$ = new ExprIf(makeCurPos(@2, data), $2, $4, $6); } + : IF expr THEN expr ELSE expr { $$ = new ExprIf(CUR_POS, $2, $4, $6); } | expr_op ; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 1e22044d7..40bb925ae 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -103,7 +103,7 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea auto path = [&]() { try { - return state.coerceToPath(pos, v, context, "While realising the context of a path: "); + return state.coerceToPath(pos, v, context, "While realising the context of a path"); } catch (Error & e) { e.addTrace(pos, "while realising the context of a path"); throw; @@ -195,9 +195,9 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS , "/"), **state.vImportedDrvToDerivation); } - state.forceFunction(**state.vImportedDrvToDerivation, pos, "While evaluating imported-drv-to-derivation.nix.gen.hh: "); + state.forceFunction(**state.vImportedDrvToDerivation, pos, "While evaluating imported-drv-to-derivation.nix.gen.hh"); v.mkApp(*state.vImportedDrvToDerivation, w); - state.forceAttrs(v, pos, "While calling imported-drv-to-derivation.nix.gen.hh: "); + state.forceAttrs(v, pos, "While calling imported-drv-to-derivation.nix.gen.hh"); } else if (path == corepkgsPrefix + "fetchurl.nix") { @@ -210,7 +210,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS if (!vScope) state.evalFile(path, v); else { - state.forceAttrs(*vScope, pos, "While evaluating the first argument passed to builtins.scopedImport: "); + state.forceAttrs(*vScope, pos, "While evaluating the first argument passed to builtins.scopedImport"); Env * env = &state.allocEnv(vScope->attrs->size()); 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]); - std::string sym(state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.importNative: ")); + 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); if (!handle) @@ -340,7 +340,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value /* Execute a program and parse its output */ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.exec: "); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.exec"); auto elems = args[0]->listElems(); auto count = args[0]->listSize(); if (count == 0) { @@ -533,50 +533,74 @@ static RegisterPrimOp primop_isPath({ .fun = prim_isPath, }); +template + static inline void withExceptionContext(Trace trace, Callable&& func) +{ + try + { + func(); + } + catch(Error & e) + { + e.pushTrace(trace); + throw; + } +} + struct CompareValues { EvalState & state; const Pos & pos; - const std::string errorCtx; + const std::string_view errorCtx; - CompareValues(EvalState & state, const Pos & pos, const std::string && errorCtx) : state(state), pos(pos), errorCtx(std::move(errorCtx)) { }; + CompareValues(EvalState & state, const Pos & pos, const std::string_view && errorCtx) : state(state), pos(pos), errorCtx(errorCtx) { }; bool operator () (Value * v1, Value * v2) const { - if (v1->type() == nFloat && v2->type() == nInt) - return v1->fpoint < v2->integer; - if (v1->type() == nInt && v2->type() == nFloat) - return v1->integer < v2->fpoint; - if (v1->type() != v2->type()) - throw EvalError({ - .msg = hintfmt("%scannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)), - .errPos = pos, - }); - switch (v1->type()) { - case nInt: - return v1->integer < v2->integer; - case nFloat: - return v1->fpoint < v2->fpoint; - case nString: - return strcmp(v1->string.s, v2->string.s) < 0; - case nPath: - return strcmp(v1->path, v2->path) < 0; - case nList: - // Lexicographic comparison - for (size_t i = 0;; i++) { - if (i == v2->listSize()) { - return false; - } else if (i == v1->listSize()) { - return true; - } else if (!state.eqValues(*v1->listElems()[i], *v2->listElems()[i], pos, errorCtx)) { - return (*this)(v1->listElems()[i], v2->listElems()[i]); - } - } - default: + return (*this)(v1, v2, errorCtx); + } + + bool operator () (Value * v1, Value * v2, const std::string_view & errorCtx) const + { + try { + if (v1->type() == nFloat && v2->type() == nInt) + return v1->fpoint < v2->integer; + if (v1->type() == nInt && v2->type() == nFloat) + return v1->integer < v2->fpoint; + if (v1->type() != v2->type()) throw EvalError({ .msg = hintfmt("%scannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)), - .errPos = pos, + .errPos = std::nullopt, }); + switch (v1->type()) { + case nInt: + return v1->integer < v2->integer; + case nFloat: + return v1->fpoint < v2->fpoint; + case nString: + return strcmp(v1->string.s, v2->string.s) < 0; + case nPath: + return strcmp(v1->path, v2->path) < 0; + case nList: + // Lexicographic comparison + for (size_t i = 0;; i++) { + if (i == v2->listSize()) { + return false; + } else if (i == v1->listSize()) { + return true; + } else if (!state.eqValues(*v1->listElems()[i], *v2->listElems()[i], pos, errorCtx)) { + return (*this)(v1->listElems()[i], v2->listElems()[i], "While comparing two lists"); + } + } + default: + throw EvalError({ + .msg = hintfmt("%scannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)), + .errPos = std::nullopt, + }); + } + } catch (Error & e) { + e.addTrace(noPos, errorCtx); + throw; } } }; @@ -590,106 +614,70 @@ typedef std::list ValueList; static Bindings::iterator getAttr( - EvalState & state, - std::string_view funcName, Symbol attrSym, Bindings * attrSet, - const Pos & pos) + std::string_view errorCtx) { Bindings::iterator value = attrSet->find(attrSym); if (value == attrSet->end()) { - hintformat errorMsg = hintfmt( - "attribute '%s' missing for call to '%s'", - attrSym, - funcName - ); - - Pos aPos = *attrSet->pos; - if (aPos == noPos) { - throw TypeError({ - .msg = errorMsg, - .errPos = pos, - }); - } else { - auto e = TypeError({ - .msg = errorMsg, - .errPos = aPos, - }); - - // Adding another trace for the function name to make it clear - // which call received wrong arguments. - e.addTrace(pos, hintfmt("while invoking '%s'", funcName)); - throw e; - } + throw TypeError({ + .msg = hintfmt("attribute '%s' missing %s", attrSym, errorCtx), + .errPos = *attrSet->pos + }); } - return value; } static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.genericClosure: "); + state.forceAttrs(*args[0], noPos, "While evaluating the first argument pased to builtins.genericClosure"); /* Get the start set. */ - Bindings::iterator startSet = getAttr( - state, - "genericClosure", - state.sStartSet, - args[0]->attrs, - pos - ); + Bindings::iterator startSet = getAttr(state.sStartSet, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure"); - state.forceList(*startSet->value, pos, "While evaluating the `startSet` attribute passed to builtins.genericClosure: "); + state.forceList(*startSet->value, noPos, "While evaluating the 'startSet' attribute passed as argument to builtins.genericClosure"); ValueList workSet; for (auto elem : startSet->value->listItems()) workSet.push_back(elem); + if (startSet->value->listSize() == 0) { + v = *startSet->value; + return; + } + /* Get the operator. */ - Bindings::iterator op = getAttr( - state, - "genericClosure", - state.sOperator, - args[0]->attrs, - pos - ); + Bindings::iterator op = getAttr(state.sOperator, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure"); + state.forceFunction(*op->value, noPos, "While evaluating the 'operator' attribute passed as argument to builtins.genericClosure"); - 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 elements of `workSet', adding the result to `workSet', continuing until no new elements are found. */ ValueList res; // `doneKeys' doesn't need to be a GC root, because its values are // reachable from res. - auto cmp = CompareValues(state, pos, "While comparing the `key` attributes of two genericClosure elements"); + auto cmp = CompareValues(state, noPos, "While comparing the `key` attributes of two genericClosure elements"); std::set doneKeys(cmp); while (!workSet.empty()) { Value * e = *(workSet.begin()); workSet.pop_front(); - state.forceAttrs(*e, pos, "While evaluating one item to be part of the genericClosure: "); + state.forceAttrs(*e, noPos, "While evaluating one of the elements generated by (or initially passed to) builtins.genericClosure"); - Bindings::iterator key = - e->attrs->find(state.sKey); - if (key == e->attrs->end()) - throw EvalError({ - .msg = hintfmt("While evaluating one of the attribute sets to be part of the genericClosure: attribute `key` required"), - .errPos = pos - }); - state.forceValue(*key->value, pos); + Bindings::iterator key = getAttr(state.sKey, e->attrs, "in one of the attrsets generated by (or initially passed to) builtins.genericClosure"); + state.forceValue(*key->value, noPos); if (!doneKeys.insert(key->value).second) continue; res.push_back(e); /* Call the `operator' function with `e' as argument. */ - Value res; - state.callFunction(*op->value, 1, &e, res, pos); - state.forceList(res, pos, "While evaluating the return value of the `operator` passed to builtins.genericClosure: "); + Value newElements; + state.callFunction(*op->value, 1, &e, newElements, noPos); + state.forceList(newElements, noPos, "While evaluating the return value of the `operator` passed to builtins.genericClosure"); /* Add the values returned by the operator to the work set. */ - for (auto elem : res.listItems()) { - state.forceValue(*elem, pos); + for (auto elem : newElements.listItems()) { + state.forceValue(*elem, noPos); // "While evaluating one one of the elements returned by the `operator` passed to builtins.genericClosure"); workSet.push_back(elem); } } @@ -782,7 +770,7 @@ static RegisterPrimOp primop_ceil({ static void prim_floor(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "While evaluating the first argument passed to builtins.floor: "); + auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "While evaluating the first argument passed to builtins.floor"); v.mkInt(floor(value)); } @@ -839,7 +827,7 @@ static RegisterPrimOp primop_tryEval({ /* Return an environment variable. Use with care. */ static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v) { - std::string name(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.getEnv: ")); + 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("")); } @@ -937,21 +925,15 @@ static RegisterPrimOp primop_trace({ derivation. */ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.derivationStrict: "); + state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.derivationStrict"); /* Figure out the name first (for stack backtraces). */ - Bindings::iterator attr = getAttr( - state, - "derivationStrict", - state.sName, - args[0]->attrs, - pos - ); + Bindings::iterator attr = getAttr(state.sName, args[0]->attrs, "in the attrset passed as argument to builtins.derivationStrict"); std::string drvName; Pos & posDrvName(*attr->pos); try { - drvName = state.forceStringNoCtx(*attr->value, pos, "While evaluating the `name` attribute passed to builtins.derivationStrict: "); + drvName = state.forceStringNoCtx(*attr->value, pos, "While evaluating the `name` attribute passed to builtins.derivationStrict"); } catch (Error & e) { e.addTrace(posDrvName, "while evaluating the derivation attribute 'name'"); throw; @@ -961,14 +943,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * std::ostringstream jsonBuf; std::unique_ptr jsonObject; attr = args[0]->attrs->find(state.sStructuredAttrs); - if (attr != args[0]->attrs->end() && state.forceBool(*attr->value, pos, "While evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict: ")) + if (attr != args[0]->attrs->end() && state.forceBool(*attr->value, pos, "While evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict")) jsonObject = std::make_unique(jsonBuf); /* Check whether null attributes should be ignored. */ bool ignoreNulls = false; attr = args[0]->attrs->find(state.sIgnoreNulls); if (attr != args[0]->attrs->end()) - ignoreNulls = state.forceBool(*attr->value, pos, "While evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict: "); + ignoreNulls = state.forceBool(*attr->value, pos, "While evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict"); /* Build the derivation expression by processing the attributes. */ Derivation drv; @@ -1064,26 +1046,26 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * printValueAsJSON(state, true, *i->value, pos, placeholder, context); if (i->name == state.sBuilder) - drv.builder = state.forceString(*i->value, context, posDrvName, "While evaluating the `builder` attribute passed to builtins.derivationStrict: "); + drv.builder = state.forceString(*i->value, context, posDrvName, "While evaluating the `builder` attribute passed to builtins.derivationStrict"); else if (i->name == state.sSystem) - drv.platform = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `system` attribute passed to builtins.derivationStrict: "); + drv.platform = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `system` attribute passed to builtins.derivationStrict"); else if (i->name == state.sOutputHash) - outputHash = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHash` attribute passed to builtins.derivationStrict: "); + outputHash = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHash` attribute passed to builtins.derivationStrict"); else if (i->name == state.sOutputHashAlgo) - outputHashAlgo = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashAlgo` attribute passed to builtins.derivationStrict: "); + outputHashAlgo = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashAlgo` attribute passed to builtins.derivationStrict"); else if (i->name == state.sOutputHashMode) - handleHashMode(state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashMode` attribute passed to builtins.derivationStrict: ")); + handleHashMode(state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashMode` attribute passed to builtins.derivationStrict")); else if (i->name == state.sOutputs) { /* Require ‘outputs’ to be a list of strings. */ - state.forceList(*i->value, posDrvName, "While evaluating the `outputs` attribute passed to builtins.derivationStrict: "); + state.forceList(*i->value, posDrvName, "While evaluating the `outputs` attribute passed to builtins.derivationStrict"); Strings ss; for (auto elem : i->value->listItems()) - ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName, "While evaluating an element of the `outputs` attribute passed to builtins.derivationStrict: ")); + ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName, "While evaluating an element of the `outputs` attribute passed to builtins.derivationStrict")); handleOutputs(ss); } } else { - auto s = state.coerceToString(*i->pos, *i->value, context, true, "While evaluating an attribute passed to builtins.derivationStrict: ").toOwned(); + auto s = state.coerceToString(*i->pos, *i->value, context, true, "While evaluating an attribute passed to builtins.derivationStrict").toOwned(); drv.env.emplace(key, s); if (i->name == state.sBuilder) drv.builder = std::move(s); else if (i->name == state.sSystem) drv.platform = std::move(s); @@ -1291,7 +1273,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info { ‘out’. */ static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v) { - v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.placeholder: "))); + v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.placeholder"))); } static RegisterPrimOp primop_placeholder({ @@ -1315,7 +1297,7 @@ static RegisterPrimOp primop_placeholder({ static void prim_toPath(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - Path path = state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.toPath: "); + Path path = state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.toPath"); v.mkString(canonPath(path), context); } @@ -1346,7 +1328,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V }); PathSet context; - Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.storePath: ")); + Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.storePath")); /* Resolve symlinks in ‘path’, unless ‘path’ itself is a symlink directly in the store. The latter condition is necessary so e.g. nix-push does the right thing. */ @@ -1416,7 +1398,7 @@ static RegisterPrimOp primop_pathExists({ static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.baseNameOf: ")), context); + v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.baseNameOf")), context); } static RegisterPrimOp primop_baseNameOf({ @@ -1436,7 +1418,7 @@ static RegisterPrimOp primop_baseNameOf({ static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto path = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.dirOf: "); + auto path = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.dirOf"); auto dir = dirOf(*path); if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context); } @@ -1483,25 +1465,19 @@ static RegisterPrimOp primop_readFile({ which are desugared to 'findFile __nixPath "x"'. */ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.findFile: "); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.findFile"); SearchPath searchPath; for (auto v2 : args[0]->listItems()) { - state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.findFile: "); + state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.findFile"); std::string prefix; Bindings::iterator i = v2->attrs->find(state.sPrefix); if (i != v2->attrs->end()) - prefix = state.forceStringNoCtx(*i->value, pos, "While evaluating the `prefix` attribute of an element of the list passed to builtins.findFile: "); + prefix = state.forceStringNoCtx(*i->value, pos, "While evaluating the `prefix` attribute of an element of the list passed to builtins.findFile"); - i = getAttr( - state, - "findFile", - state.sPath, - v2->attrs, - pos - ); + i = getAttr(state.sPath, v2->attrs, "in an element of the __nixPath"); PathSet context; auto path = state.coerceToString(pos, *i->value, context, false, false, @@ -1521,7 +1497,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va searchPath.emplace_back(prefix, path); } - auto path = state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.findFile: "); + 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))); } @@ -1535,7 +1511,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info { /* Return the cryptographic hash of a file in base-16. */ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashFile: "); + auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashFile"); std::optional ht = parseHashType(type); if (!ht) throw Error({ @@ -1742,7 +1718,7 @@ static RegisterPrimOp primop_toJSON({ /* Parse a JSON string to a value. */ static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto s = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.fromJSON: "); + auto s = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.fromJSON"); try { parseJSON(state, s, v); } catch (JSONParseError &e) { @@ -1771,8 +1747,8 @@ static RegisterPrimOp primop_fromJSON({ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - 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, "While evaluating the second argument passed to builtins.toFile: ")); + 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, "While evaluating the second argument passed to builtins.toFile")); StorePathSet refs; @@ -1929,7 +1905,7 @@ static void addPath( Value res; state.callFunction(*filterFun, 2, args, res, pos); - return state.forceBool(res, pos, "While evaluating the return value of the path filter function: "); + return state.forceBool(res, pos, "While evaluating the return value of the path filter function"); }) : defaultPathFilter; std::optional expectedStorePath; @@ -1955,9 +1931,9 @@ static void addPath( static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - Path path = state.coerceToPath(pos, *args[1], context, "While evaluating the second argument (the path to filter) passed to builtins.filterSource: "); + Path path = state.coerceToPath(pos, *args[1], context, "While evaluating the second argument (the path to filter) passed to builtins.filterSource"); - state.forceFunction(*args[0], pos, "While evaluating the first argument to builtins.filterSource: "); + state.forceFunction(*args[0], pos, "While evaluating the first argument to builtins.filterSource"); addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context); } @@ -2018,7 +1994,7 @@ static RegisterPrimOp primop_filterSource({ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.path: "); + state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.path"); Path path; std::string name; Value * filterFun = nullptr; @@ -2029,15 +2005,15 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value for (auto & attr : *args[0]->attrs) { auto & n(attr.name); if (n == "path") - path = state.coerceToPath(*attr.pos, *attr.value, context, "While evaluating the `path` attribute passed to builtins.path: "); + path = state.coerceToPath(*attr.pos, *attr.value, context, "While evaluating the `path` attribute passed to builtins.path"); else if (attr.name == state.sName) - name = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the `name` attribute passed to builtins.path: "); + name = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the `name` attribute passed to builtins.path"); else if (n == "filter") - state.forceFunction(*(filterFun = attr.value), *attr.pos, "While evaluating the `filter` parameter passed to builtins.path: "); + state.forceFunction(*(filterFun = attr.value), *attr.pos, "While evaluating the `filter` parameter passed to builtins.path"); else if (n == "recursive") - method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos, "While evaluating the `recursive` attribute passed to builtins.path: ") }; + method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos, "While evaluating the `recursive` attribute passed to builtins.path") }; else if (n == "sha256") - expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `sha256` attribute passed to builtins.path: "), htSHA256); + expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `sha256` attribute passed to builtins.path"), htSHA256); else throw EvalError({ .msg = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name), @@ -2100,7 +2076,7 @@ static RegisterPrimOp primop_path({ strings. */ static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrNames: "); + state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrNames"); state.mkList(v, args[0]->attrs->size()); @@ -2127,7 +2103,7 @@ static RegisterPrimOp primop_attrNames({ order as attrNames. */ static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrValues: "); + state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrValues"); state.mkList(v, args[0]->attrs->size()); @@ -2158,14 +2134,12 @@ static RegisterPrimOp primop_attrValues({ /* Dynamic version of the `.' operator. */ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.getAttr: "); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.getAttr: "); + auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.getAttr"); + state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.getAttr"); Bindings::iterator i = getAttr( - state, - "getAttr", state.symbols.create(attr), args[1]->attrs, - pos + "in the attribute set under consideration" ); // !!! add to stack trace? if (state.countCalls && *i->pos != noPos) state.attrSelects[*i->pos]++; @@ -2188,8 +2162,8 @@ static RegisterPrimOp primop_getAttr({ /* Return position information of the specified attribute. */ static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.unsafeGetAttrPos: "); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.unsafeGetAttrPos: "); + auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.unsafeGetAttrPos"); + 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)); if (i == args[1]->attrs->end()) v.mkNull(); @@ -2206,8 +2180,8 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info { /* Dynamic version of the `?' operator. */ static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hasAttr: "); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.hasAttr: "); + auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hasAttr"); + 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()); } @@ -2240,8 +2214,8 @@ static RegisterPrimOp primop_isAttrs({ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.removeAttrs: "); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.removeAttrs: "); + state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.removeAttrs"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.removeAttrs"); /* Get the attribute names to be removed. We keep them as Attrs instead of Symbols so std::set_difference @@ -2249,7 +2223,7 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, boost::container::small_vector names; names.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { - state.forceStringNoCtx(*elem, pos, "While evaluating the values of the second argument passed to builtins.removeAttrs: "); + 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); } std::sort(names.begin(), names.end()); @@ -2288,34 +2262,22 @@ static RegisterPrimOp primop_removeAttrs({ name, the first takes precedence. */ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the argument passed to builtins.listToAttrs: "); + state.forceList(*args[0], pos, "While evaluating the argument passed to builtins.listToAttrs"); auto attrs = state.buildBindings(args[0]->listSize()); std::set seen; for (auto v2 : args[0]->listItems()) { - state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.listToAttrs: "); + state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.listToAttrs"); - Bindings::iterator j = getAttr( - state, - "listToAttrs", - state.sName, - v2->attrs, - pos - ); + Bindings::iterator j = getAttr(state.sName, v2->attrs, "in a {name=...; value=...;} pair"); - auto name = state.forceStringNoCtx(*j->value, *j->pos, "While evaluating the `name` attribute of an element of the list passed to builtins.listToAttrs: "); + 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); if (seen.insert(sym).second) { - Bindings::iterator j2 = getAttr( - state, - "listToAttrs", - state.sValue, - v2->attrs, - pos - ); + Bindings::iterator j2 = getAttr(state.sValue, v2->attrs, "in a {name=...; value=...;} pair"); attrs.insert(sym, j2->value, j2->pos); } } @@ -2350,8 +2312,8 @@ static RegisterPrimOp primop_listToAttrs({ static void prim_intersectAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.intersectAttrs: "); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.intersectAttrs: "); + state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.intersectAttrs"); + 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())); @@ -2376,14 +2338,14 @@ static RegisterPrimOp primop_intersectAttrs({ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.catAttrs: ")); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.catAttrs: "); + Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.catAttrs")); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.catAttrs"); Value * res[args[1]->listSize()]; unsigned int found = 0; for (auto v2 : args[1]->listItems()) { - state.forceAttrs(*v2, pos, "While evaluating an element in the list passed as second argument to builtins.catAttrs: "); + 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); if (i != v2->attrs->end()) res[found++] = i->value; @@ -2456,7 +2418,7 @@ static RegisterPrimOp primop_functionArgs({ /* */ static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.mapAttrs: "); + state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.mapAttrs"); auto attrs = state.buildBindings(args[1]->attrs->size()); @@ -2497,15 +2459,15 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args std::map> attrsSeen; - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.zipAttrsWith: "); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.zipAttrsWith: "); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.zipAttrsWith"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.zipAttrsWith"); const auto listSize = args[1]->listSize(); const auto listElems = args[1]->listElems(); for (unsigned int n = 0; n < listSize; ++n) { Value * vElem = listElems[n]; try { - state.forceAttrs(*vElem, noPos, "While evaluating a value of the list passed as second argument to builtins.zipAttrsWith: "); + state.forceAttrs(*vElem, noPos, "While evaluating a value of the list passed as second argument to builtins.zipAttrsWith"); for (auto & attr : *vElem->attrs) attrsSeen[attr.name].first++; } catch (TypeError & e) { @@ -2595,7 +2557,7 @@ static RegisterPrimOp primop_isList({ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v) { - state.forceList(list, pos, "While evaluating the first argument passed to builtins.elemAt: "); + state.forceList(list, pos, "While evaluating the first argument passed to builtins.elemAt"); if (n < 0 || (unsigned int) n >= list.listSize()) throw Error({ .msg = hintfmt("list index %1% is out of bounds", n), @@ -2608,7 +2570,7 @@ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Valu /* Return the n-1'th element of a list. */ static void prim_elemAt(EvalState & state, const Pos & pos, Value * * args, Value & v) { - elemAt(state, pos, *args[0], state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.elemAt: "), 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({ @@ -2643,7 +2605,7 @@ static RegisterPrimOp primop_head({ don't want to use it! */ static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.tail: "); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.tail"); if (args[0]->listSize() == 0) throw Error({ .msg = hintfmt("'tail' called on an empty list"), @@ -2674,17 +2636,19 @@ static RegisterPrimOp primop_tail({ /* Apply a function to every element of a list. */ static void prim_map(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.map: "); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.map"); + + if (args[1]->listSize() == 0) { + v = *args[1]; + return; + } + + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.map"); 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) - (v.listElems()[n] = state.allocValue())->mkApp( - args[0], args[1]->listElems()[n]); - }; + for (unsigned int n = 0; n < v.listSize(); ++n) + (v.listElems()[n] = state.allocValue())->mkApp( + args[0], args[1]->listElems()[n]); } static RegisterPrimOp primop_map({ @@ -2695,7 +2659,7 @@ static RegisterPrimOp primop_map({ example, ```nix - map (x: "foo" + x) [ "bar" "bla" "abc" ] + map (x"foo" + x) [ "bar" "bla" "abc" ] ``` evaluates to `[ "foobar" "foobla" "fooabc" ]`. @@ -2708,14 +2672,14 @@ static RegisterPrimOp primop_map({ returns true. */ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.filter: "); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.filter"); if (args[1]->listSize() == 0) { v = *args[1]; return; } - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.filter: "); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.filter"); // FIXME: putting this on the stack is risky. Value * vs[args[1]->listSize()]; @@ -2725,7 +2689,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu for (unsigned int n = 0; n < args[1]->listSize(); ++n) { Value res; state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos); - if (state.forceBool(res, pos, "While evaluating the return value of the filtering function passed to builtins.filter: ")) + if (state.forceBool(res, pos, "While evaluating the return value of the filtering function passed to builtins.filter")) vs[k++] = args[1]->listElems()[n]; else same = false; @@ -2753,9 +2717,9 @@ static RegisterPrimOp primop_filter({ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value & v) { bool res = false; - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.elem: "); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.elem"); for (auto elem : args[1]->listItems()) - if (state.eqValues(*args[0], *elem, pos, "While searching for the presence of the given element in the list: ")) { + if (state.eqValues(*args[0], *elem, pos, "While searching for the presence of the given element in the list")) { res = true; break; } @@ -2775,7 +2739,7 @@ static RegisterPrimOp primop_elem({ /* Concatenate a list of lists. */ static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.concatLists: "); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.concatLists"); state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos, "While evaluating a value of the list passed to builtins.concatLists"); } @@ -2791,7 +2755,7 @@ static RegisterPrimOp primop_concatLists({ /* 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) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.length: "); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.length"); v.mkInt(args[0]->listSize()); } @@ -2808,8 +2772,8 @@ static RegisterPrimOp primop_length({ right. The operator is applied strictly. */ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.foldlStrict: "); - state.forceList(*args[2], pos, "While evaluating the third argument passed to builtins.foldlStrict: "); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.foldlStrict"); + state.forceList(*args[2], pos, "While evaluating the third argument passed to builtins.foldlStrict"); if (args[2]->listSize()) { Value * vCur = args[1]; @@ -2841,13 +2805,13 @@ static RegisterPrimOp primop_foldlStrict({ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, std::string("While evaluating the first argument passed to builtins.: ") + (any ? "any" : "all: ")); - state.forceList(*args[1], pos, std::string("While evaluating the second argument passed to builtins.: ") + (any ? "any" : "all")); + state.forceFunction(*args[0], pos, std::string("While evaluating the first argument passed to builtins.") + (any ? "any" : "all: ")); + state.forceList(*args[1], pos, std::string("While evaluating the second argument passed to builtins.") + (any ? "any" : "all")); Value vTmp; for (auto elem : args[1]->listItems()) { state.callFunction(*args[0], *elem, vTmp, pos); - bool res = state.forceBool(vTmp, pos, std::string("While evaluating the return value of the function passed to builtins.: ") + (any ? "any" : "all")); + 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) { v.mkBool(any); return; @@ -2890,7 +2854,7 @@ static RegisterPrimOp primop_all({ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto len = state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.genList: "); + auto len = state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.genList"); if (len < 0) throw EvalError({ @@ -2928,7 +2892,7 @@ 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) { - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.sort: "); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.sort"); auto len = args[1]->listSize(); if (len == 0) { @@ -2936,7 +2900,7 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value return; } - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.sort: "); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.sort"); state.mkList(v, len); for (unsigned int n = 0; n < len; ++n) { @@ -2948,12 +2912,12 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value /* Optimization: if the comparator is lessThan, bypass callFunction. */ if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan) - return CompareValues(state, pos, "While evaluating the ordering function passed to builtins.sort: ")(a, b); + return CompareValues(state, noPos, "While evaluating the ordering function passed to builtins.sort")(a, b); Value * vs[] = {a, b}; Value vBool; - state.callFunction(*args[0], 2, vs, vBool, pos); - return state.forceBool(vBool, pos, "While evaluating the return value of the sorting function passed to builtins.sort: "); + state.callFunction(*args[0], 2, vs, vBool, noPos); + 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 @@ -2985,8 +2949,8 @@ static RegisterPrimOp primop_sort({ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.partition: "); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.partition: "); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.partition"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.partition"); auto len = args[1]->listSize(); @@ -2997,7 +2961,7 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V state.forceValue(*vElem, pos); Value res; state.callFunction(*args[0], *vElem, res, pos); - if (state.forceBool(res, pos, "While evaluating the return value of the partition function passed to builtins.partition: ")) + if (state.forceBool(res, pos, "While evaluating the return value of the partition function passed to builtins.partition")) right.push_back(vElem); else wrong.push_back(vElem); @@ -3045,15 +3009,15 @@ static RegisterPrimOp primop_partition({ static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.groupBy: "); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.groupBy: "); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.groupBy"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.groupBy"); ValueVectorMap attrs; for (auto vElem : args[1]->listItems()) { Value res; state.callFunction(*args[0], *vElem, res, pos); - auto name = state.forceStringNoCtx(res, pos, "While evaluating the return value of the grouping function passed to builtins.groupBy: "); + 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); auto vector = attrs.try_emplace(sym, ValueVector()).first; vector->second.push_back(vElem); @@ -3097,8 +3061,8 @@ static RegisterPrimOp primop_groupBy({ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.concatMap: "); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.concatMap: "); + state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.concatMap"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.concatMap"); auto nrLists = args[1]->listSize(); Value lists[nrLists]; @@ -3108,7 +3072,7 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V Value * vElem = args[1]->listElems()[n]; state.callFunction(*args[0], *vElem, lists[n], pos); try { - state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos)), "While evaluating the return value of the function passed to buitlins.concatMap: "); + 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) { e.addTrace(pos, hintfmt("while invoking '%s'", "concatMap")); throw; @@ -3147,11 +3111,11 @@ static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - 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: ")); + 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 - 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: ")); + 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({ @@ -3168,11 +3132,11 @@ static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - 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: ")); + 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 - 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: ")); + 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({ @@ -3189,11 +3153,11 @@ static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - 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: ")); + 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 - 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: ")); + 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({ @@ -3210,7 +3174,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); - NixFloat f2 = state.forceFloat(*args[1], pos, "While evaluating the second operand of the division: "); + NixFloat f2 = state.forceFloat(*args[1], pos, "While evaluating the second operand of the division"); if (f2 == 0) throw EvalError({ .msg = hintfmt("division by zero"), @@ -3218,10 +3182,10 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & }); if (args[0]->type() == nFloat || args[1]->type() == nFloat) { - v.mkFloat(state.forceFloat(*args[0], pos, "While evaluating the first operand of the division: ") / f2); + v.mkFloat(state.forceFloat(*args[0], pos, "While evaluating the first operand of the division") / f2); } else { - NixInt i1 = state.forceInt(*args[0], pos, "While evaluating the first operand of the division: "); - NixInt i2 = state.forceInt(*args[1], pos, "While evaluating the second operand of the division: "); + NixInt i1 = state.forceInt(*args[0], pos, "While evaluating the first operand of the division"); + NixInt i2 = state.forceInt(*args[1], pos, "While evaluating the second operand of the division"); /* Avoid division overflow as it might raise SIGFPE. */ if (i1 == std::numeric_limits::min() && i2 == -1) throw EvalError({ @@ -3244,8 +3208,8 @@ static RegisterPrimOp primop_div({ static void prim_bitAnd(EvalState & state, const Pos & pos, Value * * args, Value & v) { - 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: ")); + 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({ @@ -3259,8 +3223,8 @@ static RegisterPrimOp primop_bitAnd({ static void prim_bitOr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - 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: ")); + 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({ @@ -3274,8 +3238,8 @@ static RegisterPrimOp primop_bitOr({ static void prim_bitXor(EvalState & state, const Pos & pos, Value * * args, Value & v) { - 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: ")); + 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({ @@ -3319,7 +3283,7 @@ static RegisterPrimOp primop_lessThan({ static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, true, false, "While evaluating the first argument passed to builtins.toString: "); + auto s = state.coerceToString(pos, *args[0], context, true, false, "While evaluating the first argument passed to builtins.toString"); v.mkString(*s, context); } @@ -3353,10 +3317,10 @@ static RegisterPrimOp primop_toString({ non-negative. */ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, Value & v) { - int start = state.forceInt(*args[0], pos, "While evaluating the first argument (the start offset) passed to builtins.substring: "); - int len = state.forceInt(*args[1], pos, "While evaluating the second argument (the substring length) passed to builtins.substring: "); + int start = state.forceInt(*args[0], pos, "While evaluating the first argument (the start offset) passed to builtins.substring"); + int len = state.forceInt(*args[1], pos, "While evaluating the second argument (the substring length) passed to builtins.substring"); PathSet context; - auto s = state.coerceToString(pos, *args[2], context, "While evaluating the third argument (the string) passed to builtins.substring: "); + auto s = state.coerceToString(pos, *args[2], context, "While evaluating the third argument (the string) passed to builtins.substring"); if (start < 0) throw EvalError({ @@ -3390,7 +3354,7 @@ static RegisterPrimOp primop_substring({ static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.stringLength: "); + auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.stringLength"); v.mkInt(s->size()); } @@ -3407,7 +3371,7 @@ static RegisterPrimOp primop_stringLength({ /* Return the cryptographic hash of a string in base-16. */ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashString: "); + auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashString"); std::optional ht = parseHashType(type); if (!ht) throw Error({ @@ -3416,7 +3380,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, }); PathSet context; // discarded - auto s = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.hashString: "); + 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)); } @@ -3455,14 +3419,14 @@ std::shared_ptr makeRegexCache() void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.match: "); + auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.match"); try { auto regex = state.regexCache->get(re); PathSet context; - const auto str = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.match: "); + const auto str = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.match"); std::cmatch match; if (!std::regex_match(str.begin(), str.end(), match, regex)) { @@ -3536,14 +3500,14 @@ static RegisterPrimOp primop_match({ non-matching parts interleaved by the lists of the matching groups. */ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.split: "); + auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.split"); try { auto regex = state.regexCache->get(re); PathSet context; - const auto str = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.split: "); + 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 end = std::cregex_iterator(); @@ -3642,8 +3606,8 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * { PathSet context; - auto sep = state.forceString(*args[0], context, pos, "While evaluating the first argument (the separator string) passed to builtins.concatStringsSep: "); - state.forceList(*args[1], pos, "While evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep: "); + auto sep = state.forceString(*args[0], context, pos, "While evaluating the first argument (the separator string) passed to builtins.concatStringsSep"); + state.forceList(*args[1], pos, "While evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep"); std::string res; res.reserve((args[1]->listSize() + 32) * sep.size()); @@ -3651,7 +3615,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * for (auto elem : args[1]->listItems()) { if (first) first = false; else res += sep; - res += *state.coerceToString(pos, *elem, context, "While evaluating one element of the list of strings to concat passed to builtins.concatStringsSep: "); + res += *state.coerceToString(pos, *elem, context, "While evaluating one element of the list of strings to concat passed to builtins.concatStringsSep"); } v.mkString(res, context); @@ -3670,8 +3634,8 @@ static RegisterPrimOp primop_concatStringsSep({ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.replaceStrings: "); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.replaceStrings: "); + state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.replaceStrings"); + state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.replaceStrings"); if (args[0]->listSize() != args[1]->listSize()) throw EvalError({ .msg = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"), @@ -3681,18 +3645,18 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar std::vector from; from.reserve(args[0]->listSize()); for (auto elem : args[0]->listItems()) - from.emplace_back(state.forceString(*elem, pos, "While evaluating one of the strings to replace in builtins.replaceStrings: ")); + from.emplace_back(state.forceString(*elem, pos, "While evaluating one of the strings to replace in builtins.replaceStrings")); std::vector> to; to.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { PathSet ctx; - auto s = state.forceString(*elem, ctx, pos, "While evaluating one of the replacement strings of builtins.replaceStrings: "); + auto s = state.forceString(*elem, ctx, pos, "While evaluating one of the replacement strings of builtins.replaceStrings"); to.emplace_back(s, std::move(ctx)); } PathSet context; - auto s = state.forceString(*args[2], context, pos, "While evaluating the third argument passed to builtins.replaceStrings: "); + auto s = state.forceString(*args[2], context, pos, "While evaluating the third argument passed to builtins.replaceStrings"); std::string res; // Loops one past last character to handle the case where 'from' contains an empty string. @@ -3750,7 +3714,7 @@ static RegisterPrimOp primop_replaceStrings({ static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto name = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.parseDrvName: "); + auto name = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.parseDrvName"); DrvName parsed(name); auto attrs = state.buildBindings(2); attrs.alloc(state.sName).mkString(parsed.name); @@ -3774,8 +3738,8 @@ static RegisterPrimOp primop_parseDrvName({ static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto version1 = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.compareVersions: "); - auto version2 = state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.compareVersions: "); + auto version1 = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.compareVersions"); + auto version2 = state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.compareVersions"); v.mkInt(compareVersions(version1, version2)); } @@ -3794,7 +3758,7 @@ static RegisterPrimOp primop_compareVersions({ static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto version = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.splitVersion: "); + auto version = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.splitVersion"); auto iter = version.cbegin(); Strings components; while (iter != version.cend()) { diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 858227018..403e38258 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -85,7 +85,7 @@ class ExternalValueBase /* Coerce the value to a string. Defaults to uncoercable, i.e. throws an * error. */ - virtual std::string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, const std::string & errorCtx) const; + virtual std::string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, const std::string_view & errorCtx) const; /* Compare to another value of the same type. Defaults to uncomparable, * i.e. always false. diff --git a/src/libutil/error.cc b/src/libutil/error.cc index dcd2f82a5..134b99893 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -185,15 +185,15 @@ void printAtPos(const ErrPos & pos, std::ostream & out) if (pos) { switch (pos.origin) { case foFile: { - out << fmt(ANSI_BLUE "at " ANSI_WARNING "%s:%s" ANSI_NORMAL ":", pos.file, showErrPos(pos)); + out << fmt(ANSI_BLUE " at " ANSI_WARNING "%s:%s" ANSI_NORMAL ":", pos.file, showErrPos(pos)); break; } case foString: { - out << fmt(ANSI_BLUE "at " ANSI_WARNING "«string»:%s" ANSI_NORMAL ":", showErrPos(pos)); + out << fmt(ANSI_BLUE " at " ANSI_WARNING "«string»:%s" ANSI_NORMAL ":", showErrPos(pos)); break; } case foStdin: { - out << fmt(ANSI_BLUE "at " ANSI_WARNING "«stdin»:%s" ANSI_NORMAL ":", showErrPos(pos)); + out << fmt(ANSI_BLUE " at " ANSI_WARNING "«stdin»:%s" ANSI_NORMAL ":", showErrPos(pos)); break; } default: @@ -269,7 +269,6 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s oss << einfo.msg << "\n"; if (einfo.errPos.has_value() && *einfo.errPos) { - oss << "\n"; printAtPos(*einfo.errPos, oss); auto loc = getCodeLines(*einfo.errPos); @@ -278,26 +277,34 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s if (loc.has_value()) { oss << "\n"; printCodeLines(oss, "", *einfo.errPos, *loc); - oss << "\n"; } + oss << "\n"; } // traces - if (showTrace && !einfo.traces.empty()) { + if (!einfo.traces.empty()) { + unsigned int count = 0; for (auto iter = einfo.traces.rbegin(); iter != einfo.traces.rend(); ++iter) { + if (!showTrace && count > 3) { + oss << "\n" << "(truncated)" << "\n"; + break; + } + + if (iter->hint.str().empty()) continue; + count++; oss << "\n" << "… " << iter->hint.str() << "\n"; if (iter->pos.has_value() && (*iter->pos)) { + count++; auto pos = iter->pos.value(); - oss << "\n"; printAtPos(pos, oss); auto loc = getCodeLines(pos); if (loc.has_value()) { oss << "\n"; printCodeLines(oss, "", pos, *loc); - oss << "\n"; } + oss << "\n"; } } } diff --git a/src/libutil/error.hh b/src/libutil/error.hh index d55e1d701..ad29f8d2a 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -130,6 +130,8 @@ protected: public: unsigned int status = 1; // exit status + BaseError(const BaseError &) = default; + template BaseError(unsigned int status, const Args & ... args) : err { .level = lvlError, .msg = hintfmt(args...) } @@ -165,10 +167,14 @@ public: const std::string & msg() const { return calcWhat(); } const ErrorInfo & info() const { calcWhat(); return err; } + void pushTrace(Trace trace) { + err.traces.push_front(trace); + } + template - BaseError & addTrace(std::optional e, const std::string & fs, const Args & ... args) + BaseError & addTrace(std::optional e, const std::string_view & fs, const Args & ... args) { - return addTrace(e, hintfmt(fs, args...)); + return addTrace(e, hintfmt(std::string(fs), args...)); } BaseError & addTrace(std::optional e, hintformat hint); From 1942fed6d9cee95775046c5ad3d253ab2e8ab210 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 18 Mar 2022 01:10:04 +0100 Subject: [PATCH 11/39] Revert extra colon at end os strings --- src/libcmd/installables.cc | 2 +- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval-cache.cc | 2 +- src/libexpr/eval.cc | 8 ++++---- src/libexpr/flake/flake.cc | 2 +- src/libexpr/get-drvs.cc | 22 +++++++++++----------- src/libexpr/primops.cc | 2 +- src/libexpr/primops/context.cc | 22 +++++++++++----------- src/libexpr/primops/fetchMercurial.cc | 8 ++++---- src/libexpr/primops/fetchTree.cc | 14 +++++++------- src/libexpr/primops/fromTOML.cc | 2 +- src/nix/eval.cc | 2 +- src/nix/flake.cc | 4 ++-- src/nix/main.cc | 2 +- src/nix/prefetch.cc | 16 ++++++++-------- src/nix/repl.cc | 8 ++++---- src/nix/upgrade-nix.cc | 2 +- 17 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 437380a4d..e85482239 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -524,7 +524,7 @@ ref openEvalCache( auto vFlake = state.allocValue(); flake::callFlake(state, *lockedFlake, *vFlake); - state.forceAttrs(*vFlake, noPos, "While evaluating a cached flake: "); + state.forceAttrs(*vFlake, noPos, "While evaluating a cached flake"); auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); assert(aOutputs); diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index 930400bd0..63ee8b1ae 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -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 // toString + parsing? - auto pos = state.forceString(*v2, noPos, "While evaluating the meta.position attribute of a derivation: "); + auto pos = state.forceString(*v2, noPos, "While evaluating the meta.position attribute of a derivation"); auto colon = pos.rfind(':'); if (colon == std::string::npos) diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 6528725e7..66078b694 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -336,7 +336,7 @@ Value & AttrCursor::getValue() if (!_value) { if (parent) { auto & vParent = parent->first->getValue(); - root->state.forceAttrs(vParent, noPos, "While evaluating the parent attr set: "); + root->state.forceAttrs(vParent, noPos, "While evaluating the parent attr set"); auto attr = vParent.attrs->get(parent->second); if (!attr) throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 33eeef902..e3dfe6718 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -297,7 +297,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) } else { Value nameValue; name.expr->eval(state, env, nameValue); - state.forceStringNoCtx(nameValue, noPos, "While evaluating an attribute name: "); + state.forceStringNoCtx(nameValue, noPos, "While evaluating an attribute name"); return state.symbols.create(nameValue.string.s); } } @@ -1111,7 +1111,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) Hence we need __overrides.) */ if (hasOverrides) { Value * vOverrides = (*v.attrs)[overrides->second.displ].value; - state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "While evaluating the `__overrides` attribute: "); + state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "While evaluating the `__overrides` attribute"); Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size()); for (auto & i : *v.attrs) newBnds->push_back(i); @@ -1139,7 +1139,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) state.forceValue(nameVal, i.pos); if (nameVal.type() == nNull) continue; - state.forceStringNoCtx(nameVal, i.pos, "While evaluating the name of a dynamic attribute: "); + state.forceStringNoCtx(nameVal, i.pos, "While evaluating the name of a dynamic attribute"); Symbol nameSym = state.symbols.create(nameVal.string.s); Bindings::iterator j = v.attrs->find(nameSym); if (j != v.attrs->end()) @@ -1229,7 +1229,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) return; } } else { - state.forceAttrs(*vAttrs, pos, "While selecting an attribute: "); + state.forceAttrs(*vAttrs, pos, "While selecting an attribute"); if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) throwEvalError(pos, "attribute '%1%' missing", name); } diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 560747210..fe2daaf21 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -708,7 +708,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va { state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos); - std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.getFlake: ")); + std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.getFlake")); auto flakeRef = parseFlakeRef(flakeRefS, {}, true); if (evalSettings.pureEval && !flakeRef.input.isLocked()) throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 94f26e925..9bb545430 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -50,7 +50,7 @@ std::string DrvInfo::queryName() const if (name == "" && attrs) { auto i = attrs->find(state->sName); if (i == attrs->end()) throw TypeError("derivation name missing"); - name = state->forceStringNoCtx(*i->value, noPos, "While evaluating the name of a DrvInfo: "); + name = state->forceStringNoCtx(*i->value, noPos, "While evaluating the name of a DrvInfo"); } return name; } @@ -60,7 +60,7 @@ std::string DrvInfo::querySystem() const { if (system == "" && attrs) { auto i = attrs->find(state->sSystem); - system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos, "While evaluating the system of a DrvInfo: "); + system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos, "While evaluating the system of a DrvInfo"); } return system; } @@ -74,7 +74,7 @@ std::optional DrvInfo::queryDrvPath() const if (i == attrs->end()) drvPath = {std::nullopt}; else - drvPath = {state->coerceToStorePath(*i->pos, *i->value, context, "Whyle evaluating the drv path of a DrvInfo: ")}; + drvPath = {state->coerceToStorePath(*i->pos, *i->value, context, "Whyle evaluating the drv path of a DrvInfo")}; } return drvPath.value_or(std::nullopt); } @@ -94,7 +94,7 @@ StorePath DrvInfo::queryOutPath() const Bindings::iterator i = attrs->find(state->sOutPath); PathSet context; if (i != attrs->end()) - outPath = state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the output path of a DrvInfo: "); + outPath = state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the output path of a DrvInfo"); } if (!outPath) throw UnimplementedError("CA derivations are not yet supported"); @@ -108,21 +108,21 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool onlyOutputsToInstall) /* Get the ‘outputs’ list. */ Bindings::iterator i; if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) { - state->forceList(*i->value, *i->pos, "While evaluating the outputs of a DrvInfo: "); + state->forceList(*i->value, *i->pos, "While evaluating the outputs of a DrvInfo"); /* For each output... */ for (auto elem : i->value->listItems()) { /* Evaluate the corresponding set. */ - std::string name(state->forceStringNoCtx(*elem, *i->pos, "While evaluating the name of one output of a DrvInfo: ")); + 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)); if (out == attrs->end()) continue; // FIXME: throw error? - state->forceAttrs(*out->value, *i->pos, "While evaluating the description of a DrvInfo output: "); + state->forceAttrs(*out->value, *i->pos, "While evaluating the description of a DrvInfo output"); /* And evaluate its ‘outPath’ attribute. */ Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? PathSet context; - outputs.emplace(name, state->coerceToStorePath(*outPath->pos, *outPath->value, context, "While evaluating the outPath of an output path of a DrvInfo: ")); + outputs.emplace(name, state->coerceToStorePath(*outPath->pos, *outPath->value, context, "While evaluating the outPath of an output path of a DrvInfo")); } } else outputs.emplace("out", queryOutPath()); @@ -151,7 +151,7 @@ std::string DrvInfo::queryOutputName() const { if (outputName == "" && attrs) { Bindings::iterator i = attrs->find(state->sOutputName); - outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "While evaluating the output name of a DrvInfo: ") : ""; + outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "While evaluating the output name of a DrvInfo") : ""; } return outputName; } @@ -163,7 +163,7 @@ Bindings * DrvInfo::getMeta() if (!attrs) return 0; Bindings::iterator a = attrs->find(state->sMeta); if (a == attrs->end()) return 0; - state->forceAttrs(*a->value, *a->pos, "While evaluating the `meta` attribute of a DrvInfo: "); + state->forceAttrs(*a->value, *a->pos, "While evaluating the `meta` attribute of a DrvInfo"); meta = a->value->attrs; return meta; } @@ -364,7 +364,7 @@ static void getDerivations(EvalState & state, Value & vIn, `recurseForDerivations = true' attribute. */ if (i->value->type() == nAttrs) { Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations); - if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos, "While evaluating the attribute `recurseForDerivations`: ")) + 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); } } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 40bb925ae..3ec5c0850 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2805,7 +2805,7 @@ static RegisterPrimOp primop_foldlStrict({ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, std::string("While evaluating the first argument passed to builtins.") + (any ? "any" : "all: ")); + state.forceFunction(*args[0], pos, std::string("While evaluating the first argument passed to builtins.") + (any ? "any" : "all")); state.forceList(*args[1], pos, std::string("While evaluating the second argument passed to builtins.") + (any ? "any" : "all")); Value vTmp; diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 222d183e4..78320dc09 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -7,7 +7,7 @@ namespace nix { static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardStringContext: "); + auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardStringContext"); v.mkString(*s); } @@ -17,7 +17,7 @@ static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringCo static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.hasContext: "); + state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.hasContext"); v.mkBool(!context.empty()); } @@ -33,7 +33,7 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext); static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardOutputDependency: "); + auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardOutputDependency"); PathSet context2; for (auto & p : context) @@ -72,7 +72,7 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, Strings outputs; }; PathSet context; - state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.getContext: "); + state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.getContext"); auto contextInfos = std::map(); for (const auto & p : context) { 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) { PathSet context; - auto orig = state.forceString(*args[0], context, pos, "while evaluating the first argument passed to builtins.appendContext: "); + auto orig = state.forceString(*args[0], context, pos, "while evaluating the first argument passed to builtins.appendContext"); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.appendContext: "); + state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.appendContext"); auto sPath = state.symbols.create("path"); auto sAllOutputs = state.symbols.create("allOutputs"); @@ -150,16 +150,16 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg }); if (!settings.readOnlyMode) state.store->ensurePath(state.store->parseStorePath(i.name)); - state.forceAttrs(*i.value, *i.pos, "While evaluating the value of a string context: "); + state.forceAttrs(*i.value, *i.pos, "While evaluating the value of a string context"); auto iter = i.value->attrs->find(sPath); if (iter != i.value->attrs->end()) { - if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `path` attribute of a string context: ")) + if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `path` attribute of a string context")) context.insert(i.name); } iter = i.value->attrs->find(sAllOutputs); if (iter != i.value->attrs->end()) { - if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `allOutputs` attribute of a string context: ")) { + if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `allOutputs` attribute of a string context")) { if (!isDerivation(i.name)) { throw EvalError({ .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); if (iter != i.value->attrs->end()) { - state.forceList(*iter->value, *iter->pos, "While evaluating the `outputs` attribute of a string context: "); + state.forceList(*iter->value, *iter->pos, "While evaluating the `outputs` attribute of a string context"); if (iter->value->listSize() && !isDerivation(i.name)) { throw EvalError({ .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()) { - auto name = state.forceStringNoCtx(*elem, *iter->pos, "While evaluating an output name within a string context: "); + auto name = state.forceStringNoCtx(*elem, *iter->pos, "While evaluating an output name within a string context"); context.insert(concatStrings("!", name, "!", i.name)); } } diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index 7434b8101..dcaf3d362 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -22,18 +22,18 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar for (auto & attr : *args[0]->attrs) { std::string_view n(attr.name); if (n == "url") - url = state.coerceToString(*attr.pos, *attr.value, context, false, false, "While evaluating the `url` attribute passed to builtins.fetchMercurial: ").toOwned(); + url = state.coerceToString(*attr.pos, *attr.value, context, false, false, "While evaluating the `url` attribute passed to builtins.fetchMercurial").toOwned(); else if (n == "rev") { // Ugly: unlike fetchGit, here the "rev" attribute can // be both a revision or a branch/tag name. - auto value = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `rev` attribute passed to builtins.fetchMercurial: "); + 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)) rev = Hash::parseAny(value, htSHA1); else ref = value; } else if (n == "name") - name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `name` attribute passed to builtins.fetchMercurial: "); + name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `name` attribute passed to builtins.fetchMercurial"); else throw EvalError({ .msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name), @@ -48,7 +48,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar }); } else - url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.fetchMercurial: ").toOwned(); + url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.fetchMercurial").toOwned(); // FIXME: git externals probably can be used to bypass the URI // whitelist. Ah well. diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 31a8907f1..2baa272cb 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -102,7 +102,7 @@ static void fetchTree( state.forceValue(*args[0], pos); if (args[0]->type() == nAttrs) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.fetchTree: "); + state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.fetchTree"); fetchers::Attrs attrs; @@ -112,7 +112,7 @@ static void fetchTree( .msg = hintfmt("unexpected attribute 'type'"), .errPos = pos }); - type = state.forceStringNoCtx(*aType->value, *aType->pos, "While evaluating the `type` attribute passed to builtins.fetchTree: "); + type = state.forceStringNoCtx(*aType->value, *aType->pos, "While evaluating the `type` attribute passed to builtins.fetchTree"); } else if (!type) throw Error({ .msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"), @@ -151,7 +151,7 @@ static void fetchTree( input = fetchers::Input::fromAttrs(std::move(attrs)); } else { - auto url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to the fetcher: ").toOwned(); + auto url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to the fetcher").toOwned(); if (type == "git") { fetchers::Attrs attrs; @@ -198,11 +198,11 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, for (auto & attr : *args[0]->attrs) { std::string n(attr.name); if (n == "url") - url = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the url we should fetch: "); + url = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the url we should fetch"); else if (n == "sha256") - expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the sha256 of the content we should fetch: "), htSHA256); + expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the sha256 of the content we should fetch"), htSHA256); else if (n == "name") - name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the name of the content we should fetch: "); + name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the name of the content we should fetch"); else throw EvalError({ .msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who), @@ -216,7 +216,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, .errPos = pos }); } else - url = state.forceStringNoCtx(*args[0], pos, "While evaluating the url we should fetch: "); + url = state.forceStringNoCtx(*args[0], pos, "While evaluating the url we should fetch"); url = resolveUri(*url); diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 2a038bb20..6bfd8e285 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -7,7 +7,7 @@ namespace nix { static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val) { - auto toml = state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.fromTOML: "); + auto toml = state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.fromTOML"); std::istringstream tomlStream(std::string{toml}); diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 1d66d457c..8ee3ba45b 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -107,7 +107,7 @@ struct CmdEval : MixJSON, InstallableCommand else if (raw) { stopProgressBar(); - std::cout << *state->coerceToString(noPos, *v, context, "While generating the eval command output: "); + std::cout << *state->coerceToString(noPos, *v, context, "While generating the eval command output"); } else if (json) { diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 9ba6e5be1..356f89713 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -126,12 +126,12 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, std::function callback) { auto pos = vFlake.determinePos(noPos); - state.forceAttrs(vFlake, pos, "While evaluating a flake to get its outputs: "); + state.forceAttrs(vFlake, pos, "While evaluating a flake to get its outputs"); auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs")); assert(aOutputs); - state.forceAttrs(*aOutputs->value, pos, "While evaluating the outputs of a flake: "); + state.forceAttrs(*aOutputs->value, pos, "While evaluating the outputs of a flake"); auto sHydraJobs = state.symbols.create("hydraJobs"); diff --git a/src/nix/main.cc b/src/nix/main.cc index 3ba7ce8b6..0c6286686 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -196,7 +196,7 @@ static void showHelp(std::vector subcommand, MultiCommand & topleve if (!attr) throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); - auto markdown = state.forceString(*attr->value, noPos, "While evaluating the lowdown help text: "); + auto markdown = state.forceString(*attr->value, noPos, "While evaluating the lowdown help text"); RunPager pager; std::cout << renderMarkdownToTerminal(markdown) << "\n"; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index a4a8ab605..4376da896 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -28,17 +28,17 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url) Value vMirrors; // FIXME: use nixpkgs flake state.eval(state.parseExprFromString("import ", "."), vMirrors); - state.forceAttrs(vMirrors, noPos, "While evaluating the set of all mirrors: "); + state.forceAttrs(vMirrors, noPos, "While evaluating the set of all mirrors"); auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); if (mirrorList == vMirrors.attrs->end()) throw Error("unknown mirror name '%s'", mirrorName); - state.forceList(*mirrorList->value, noPos, "While evaluating this mirror configuration: "); + state.forceList(*mirrorList->value, noPos, "While evaluating this mirror configuration"); if (mirrorList->value->listSize() < 1) throw Error("mirror URL '%s' did not expand to anything", url); - std::string mirror(state.forceString(*mirrorList->value->listElems()[0], noPos, "While evaluating the first available mirror: ")); + std::string mirror(state.forceString(*mirrorList->value->listElems()[0], noPos, "While evaluating the first available mirror")); return mirror + (hasSuffix(mirror, "/") ? "" : "/") + s.substr(p + 1); } @@ -196,27 +196,27 @@ static int main_nix_prefetch_url(int argc, char * * argv) Value vRoot; state->evalFile(path, vRoot); Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first); - state->forceAttrs(v, noPos, "While evaluating the source attribute to prefetch: "); + state->forceAttrs(v, noPos, "While evaluating the source attribute to prefetch"); /* Extract the URL. */ auto & attr = v.attrs->need(state->symbols.create("urls")); - state->forceList(*attr.value, noPos, "While evaluating the urls to prefetch: "); + state->forceList(*attr.value, noPos, "While evaluating the urls to prefetch"); if (attr.value->listSize() < 1) throw Error("'urls' list is empty"); - url = state->forceString(*attr.value->listElems()[0], noPos, "While evaluating the first url from the urls list: "); + url = state->forceString(*attr.value->listElems()[0], noPos, "While evaluating the first url from the urls list"); /* Extract the hash mode. */ auto attr2 = v.attrs->get(state->symbols.create("outputHashMode")); if (!attr2) printInfo("warning: this does not look like a fetchurl call"); else - unpack = state->forceString(*attr2->value, noPos, "While evaluating the outputHashMode of the source to prefetch: ") == "recursive"; + unpack = state->forceString(*attr2->value, noPos, "While evaluating the outputHashMode of the source to prefetch") == "recursive"; /* Extract the name. */ if (!name) { auto attr3 = v.attrs->get(state->symbols.create("name")); if (!attr3) - name = state->forceString(*attr3->value, noPos, "While evaluating the name of the source to prefetch: "); + name = state->forceString(*attr3->value, noPos, "While evaluating the name of the source to prefetch"); } } diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 038ed4911..0cb68552c 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -342,7 +342,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) Expr * e = parseString(expr); Value v; e->eval(*state, *env, v); - state->forceAttrs(v, noPos, "nevermind, it is ignored anyway: "); + state->forceAttrs(v, noPos, "nevermind, it is ignored anyway"); for (auto & i : *v.attrs) { std::string name = i.name; @@ -461,7 +461,7 @@ bool NixRepl::processLine(std::string line) if (v.type() == nPath || v.type() == nString) { PathSet context; - auto filename = state->coerceToString(noPos, v, context, "While evaluating the filename to edit: "); + auto filename = state->coerceToString(noPos, v, context, "While evaluating the filename to edit"); pos.file = state->symbols.create(*filename); } else if (v.isLambda()) { pos = v.lambda.fun->pos; @@ -675,7 +675,7 @@ void NixRepl::reloadFiles() void NixRepl::addAttrsToScope(Value & attrs) { - state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "While evaluating an attribute set to be merged in the global scope: "); + 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) throw Error("environment full; cannot add more variables"); @@ -780,7 +780,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m Bindings::iterator i = v.attrs->find(state->sDrvPath); PathSet context; if (i != v.attrs->end()) - str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the drvPath of a derivation: ")); + str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the drvPath of a derivation")); else str << "???"; str << "»"; diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index ae314a7c6..1f433a199 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -144,7 +144,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand Bindings & bindings(*state->allocBindings(0)); auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first; - return store->parseStorePath(state->forceString(*v2, noPos, "While evaluating the path tho latest nix version: ")); + return store->parseStorePath(state->forceString(*v2, noPos, "While evaluating the path tho latest nix version")); } }; From ad3fadb95aaae97910082335800c2be57942e154 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 18 Mar 2022 10:11:36 +0100 Subject: [PATCH 12/39] fixup! Merge remote-tracking branch 'origin/master' into coerce-string --- src/libexpr/eval.cc | 124 +++++++++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 54 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 3ad4df77e..320fd2597 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -706,31 +706,66 @@ std::optional EvalState::getDoc(Value & v) evaluator. So here are some helper functions for throwing exceptions. */ -LocalNoInlineNoReturn(void throwTypeErrorWithTrace(const Pos & pos, const char * s, const std::string & s2, const Symbol & sym, const Pos & p2, const std::string & s3)) +LocalNoInlineNoReturn(void throwTypeErrorWithTrace( + const Pos & pos, + const char * s, + const std::string & s2, + const Symbol & sym, + const Pos & p2, + const char * s3)) { throw EvalError({ .msg = hintfmt(s, s2, sym), - .errPos = pos + .errPos = pos, }).addTrace(p2, s3); } +LocalNoInlineNoReturn(void throwTypeErrorWithTrace( + const Pos & pos, + const Suggestions & suggestions, + const char * s, + const std::string & s2, + const Symbol & sym, + const Pos & p2, + const char * s3)) +{ + throw EvalError({ + .msg = hintfmt(s, s2, sym), + .errPos = pos, + .suggestions = suggestions + }).addTrace(p2, s3); +} + +LocalNoInlineNoReturn(void throwTypeErrorWithTrace(const char * s, const std::string & s2, const Pos & p2, const std::string_view s3)) +{ + throw TypeError(ErrorInfo { + .msg = hintfmt(s, s2), + }).addTrace(p2, s3); +} + +LocalNoInlineNoReturn(void throwEvalErrorWithTrace(const char * s, const std::string & s2, const Pos & p2, const std::string_view s3)) +{ + throw EvalError(ErrorInfo { + .msg = hintfmt(s, s2), + }).addTrace(p2, s3); +} + +LocalNoInlineNoReturn(void throwEvalErrorWithTrace(const char * s, const std::string_view & s2, const std::string_view & s3, const Pos & p2, const std::string_view s4)) +{ + throw EvalError(ErrorInfo { + .msg = hintfmt(s, s2, s3), + }).addTrace(p2, s4); +} + LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2)) { - throw EvalError(ErrorInfo { + throw EvalError({ .msg = hintfmt(s, s2), .errPos = pos, .suggestions = suggestions, }); } -LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const std::string & s2)) -{ - throw EvalError(ErrorInfo { - .msg = hintfmt(s, s2), - .errPos = pos - }); -} - LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const Value & v)) { throw AssertionError({ @@ -748,32 +783,16 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const }); } -LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s)) +LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string_view & s1)) { - throw TypeError({ - .msg = hintfmt(s), - .errPos = pos - }); + throw EvalError(s, s1); } -LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const char * s, const ExprLambda & fun, const Symbol & s2)) +LocalNoInlineNoReturn(void throwEvalError(const char * s, const std::string_view & s1, const std::string_view & s2)) { - throw TypeError({ - .msg = hintfmt(s, fun.showNamePos(), s2), - .errPos = pos - }); + throw EvalError(s, s1, s2); } -LocalNoInlineNoReturn(void throwTypeError(const Pos & pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol & s2)) -{ - throw TypeError(ErrorInfo { - .msg = hintfmt(s, fun.showNamePos(), s2), - .errPos = pos, - .suggestions = suggestions, - }); -} - - LocalNoInlineNoReturn(void throwTypeError(const char * s, const Value & v)) { throw TypeError(s, showType(v)); @@ -1008,11 +1027,9 @@ void EvalState::cacheFile( fileParseCache[resolvedPath] = e; try { - // Enforce that 'flake.nix' is a direct attrset, not a - // computation. - if (mustBeTrivial && - !(dynamic_cast(e))) - throw EvalError("file '%s' must be an attribute set", path); + // Enforce that 'flake.nix' is a direct attrset, not a computation. + if (mustBeTrivial && !(dynamic_cast(e))) + throwEvalError("file '%s' must be an attribute set", path); eval(e, v); } catch (Error & e) { addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath); @@ -1036,7 +1053,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, const std: Value v; e->eval(*this, env, v); if (v.type() != nBool) - throw TypeError("value is %1% while a Boolean was expected", showType(v)); + throwTypeError("value is %1% while a Boolean was expected", v); return v.boolean; } catch (Error & e) { e.addTrace(pos, errorCtx); @@ -1050,7 +1067,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos try { e->eval(*this, env, v); if (v.type() != nAttrs) - throw TypeError("value is %1% while a set was expected", showType(v)); + throwTypeError("value is %1% while a set was expected", v); } catch (Error & e) { e.addTrace(pos, errorCtx); throw; @@ -1857,7 +1874,7 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos, const std::string_view & try { forceValue(v, pos); if (v.type() != nInt) - throw TypeError("value is %1% while an integer was expected", showType(v)); + throwTypeError("value is %1% while an integer was expected", v); return v.integer; } catch (Error & e) { e.addTrace(pos, errorCtx); @@ -1873,7 +1890,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos, const std::string_vie if (v.type() == nInt) return v.integer; else if (v.type() != nFloat) - throw TypeError("value is %1% while a float was expected", showType(v)); + throwTypeError("value is %1% while a float was expected", v); return v.fpoint; } catch (Error & e) { e.addTrace(pos, errorCtx); @@ -1887,7 +1904,7 @@ bool EvalState::forceBool(Value & v, const Pos & pos, const std::string_view & e try { forceValue(v, pos); if (v.type() != nBool) - throw TypeError("value is %1% while a Boolean was expected", showType(v)); + throwTypeError("value is %1% while a Boolean was expected", v); return v.boolean; } catch (Error & e) { e.addTrace(pos, errorCtx); @@ -1907,7 +1924,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos, const std::string_view try { forceValue(v, pos); if (v.type() != nFunction && !isFunctor(v)) - throw TypeError("value is %1% while a function was expected", showType(v)); + throwTypeError("value is %1% while a function was expected", v); } catch (Error & e) { e.addTrace(pos, errorCtx); throw; @@ -1920,7 +1937,7 @@ std::string_view EvalState::forceString(Value & v, const Pos & pos, const std::s try { forceValue(v, pos); if (v.type() != nString) - throw TypeError("value is %1% while a string was expected", showType(v)); + throwTypeError("value is %1% while a string was expected", v); return v.string.s; } catch (Error & e) { e.addTrace(pos, errorCtx); @@ -1974,9 +1991,9 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos, const s auto s = forceString(v, pos, errorCtx); if (v.string.context) { if (pos) - throw EvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]); + throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]); else - throw EvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]); + throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]); } return s; } catch (Error & e) { @@ -2035,9 +2052,8 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & if (maybeString) return std::move(*maybeString); auto i = v.attrs->find(sOutPath); - if (i == v.attrs->end()) { - throw TypeError("cannot coerce %1% to a string", showType(v)).addTrace(pos, errorCtx); - } + if (i == v.attrs->end()) + throwTypeErrorWithTrace("cannot coerce %1% to a string", showType(v), pos, errorCtx); return coerceToString(pos, *i->value, context, coerceMore, copyToStore, canonicalizePath, errorCtx); } @@ -2073,14 +2089,14 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & } } - throw TypeError("cannot coerce %1% to a string", showType(v)).addTrace(pos, errorCtx); + throwTypeErrorWithTrace("cannot coerce %1% to a string", showType(v), pos, errorCtx); } std::string EvalState::copyPathToStore(PathSet & context, const Path & path) { if (nix::isDerivation(path)) - throw EvalError("file names are not allowed to end in '%1%'", drvExtension); + throwEvalError("file names are not allowed to end in '%1%'", drvExtension); Path dstPath; auto i = srcToStore.find(path); @@ -2105,7 +2121,7 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context, cons { auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (path == "" || path[0] != '/') - throw EvalError("string '%1%' doesn't represent an absolute path", path).addTrace(pos, errorCtx); + throwEvalErrorWithTrace("string '%1%' doesn't represent an absolute path", path, pos, errorCtx); return path; } @@ -2115,7 +2131,7 @@ StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & con auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (auto storePath = store->maybeParseStorePath(path)) return *storePath; - throw EvalError("path '%1%' is not in the Nix store", path).addTrace(pos, errorCtx); + throwEvalErrorWithTrace("path '%1%' is not in the Nix store", path, pos, errorCtx); } @@ -2193,7 +2209,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const Pos & pos, const std::str return v1.fpoint == v2.fpoint; default: - throw EvalError("cannot compare %1% with %2%", showType(v1), showType(v2)).addTrace(pos, errorCtx); + throwEvalErrorWithTrace("cannot compare %1% with %2%", showType(v1), showType(v2), pos, errorCtx); } } @@ -2317,7 +2333,7 @@ void EvalState::printStats() std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, const std::string_view & errorCtx) const { - throw TypeError("cannot coerce %1% to a string", showType()).addTrace(pos, errorCtx); + throwTypeErrorWithTrace("cannot coerce %1% to a string", showType(), pos, errorCtx); } From 9c42c005706a68f31a28398ce63b956ad0510549 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 18 Mar 2022 10:22:47 +0100 Subject: [PATCH 13/39] Fix some error kind mismatches --- src/libexpr/eval.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 320fd2597..7b843aeaf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -714,7 +714,7 @@ LocalNoInlineNoReturn(void throwTypeErrorWithTrace( const Pos & p2, const char * s3)) { - throw EvalError({ + throw TypeError({ .msg = hintfmt(s, s2, sym), .errPos = pos, }).addTrace(p2, s3); @@ -729,7 +729,7 @@ LocalNoInlineNoReturn(void throwTypeErrorWithTrace( const Pos & p2, const char * s3)) { - throw EvalError({ + throw TypeError({ .msg = hintfmt(s, s2, sym), .errPos = pos, .suggestions = suggestions @@ -768,7 +768,7 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & s LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const Value & v)) { - throw AssertionError({ + throw EvalError({ .msg = hintfmt(s, showType(v)), .errPos = pos }); From 37e84316c28662f23613896f3592260bdf358aca Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 18 Mar 2022 14:48:49 +0100 Subject: [PATCH 14/39] Try to fix issues with macos clang --- src/libexpr/eval.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 7b843aeaf..dc52f8db7 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -712,7 +712,7 @@ LocalNoInlineNoReturn(void throwTypeErrorWithTrace( const std::string & s2, const Symbol & sym, const Pos & p2, - const char * s3)) + const std::string_view & s3)) { throw TypeError({ .msg = hintfmt(s, s2, sym), @@ -727,7 +727,7 @@ LocalNoInlineNoReturn(void throwTypeErrorWithTrace( const std::string & s2, const Symbol & sym, const Pos & p2, - const char * s3)) + const std::string_view & s3)) { throw TypeError({ .msg = hintfmt(s, s2, sym), From 726f5836d85518fcf97d124ec0d4f84b6d4b7232 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 18 Mar 2022 15:22:25 +0100 Subject: [PATCH 15/39] Try to fix issues with macos clang, v2 --- src/libexpr/eval.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index dc52f8db7..990e98f9a 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -709,12 +709,12 @@ std::optional EvalState::getDoc(Value & v) LocalNoInlineNoReturn(void throwTypeErrorWithTrace( const Pos & pos, const char * s, - const std::string & s2, + const std::string_view & s2, const Symbol & sym, const Pos & p2, const std::string_view & s3)) { - throw TypeError({ + throw TypeError(ErrorInfo { .msg = hintfmt(s, s2, sym), .errPos = pos, }).addTrace(p2, s3); @@ -724,12 +724,12 @@ LocalNoInlineNoReturn(void throwTypeErrorWithTrace( const Pos & pos, const Suggestions & suggestions, const char * s, - const std::string & s2, + const std::string_view & s2, const Symbol & sym, const Pos & p2, const std::string_view & s3)) { - throw TypeError({ + throw TypeError(ErrorInfo { .msg = hintfmt(s, s2, sym), .errPos = pos, .suggestions = suggestions From c2b620f3ad80f3d5c091c955401445106d0579fe Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 18 Mar 2022 15:35:24 +0100 Subject: [PATCH 16/39] Try to fix issues with macos clang, v3 --- src/libexpr/eval.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 990e98f9a..ad879a85d 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -759,7 +759,7 @@ LocalNoInlineNoReturn(void throwEvalErrorWithTrace(const char * s, const std::st LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2)) { - throw EvalError({ + throw EvalError(ErrorInfo { .msg = hintfmt(s, s2), .errPos = pos, .suggestions = suggestions, @@ -768,7 +768,7 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & s LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const Value & v)) { - throw EvalError({ + throw EvalError(ErrorInfo { .msg = hintfmt(s, showType(v)), .errPos = pos }); @@ -777,7 +777,7 @@ LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const char * s, const LocalNoInlineNoReturn(void throwEvalError(const Pos & p1, const char * s, const Symbol & sym, const Pos & p2)) { // p1 is where the error occurred; p2 is a position mentioned in the message. - throw EvalError({ + throw EvalError(ErrorInfo { .msg = hintfmt(s, sym, p2), .errPos = p1 }); From 963b8aa39b169bf5c054449ddce39d60faacf298 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 18 Mar 2022 23:17:50 +0100 Subject: [PATCH 17/39] Explain current error trace impl --- src/libutil/error.cc | 95 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/src/libutil/error.cc b/src/libutil/error.cc index ae0bbc06f..c66dfa112 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -288,7 +288,100 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s "?" << std::endl; } - // traces + /* + * Traces + * ------ + * + * The semantics of traces is a bit weird. We have only one option to + * print them and to make them verbose (--show-trace). In the code they + * are always collected, but they are not printed by default. The code + * also collects more traces when the option is on. This means that there + * is no way to print the simplified traces at all. + * + * I (layus) designed the code to attach positions to a restricted set of + * messages. This means that we have a lot of traces with no position at + * all, including most of the base error messages. For example "type + * error: found a string while a set was expected" has no position, but + * will come with several traces detailing it's precise relation to the + * closest know position. This makes erroring without printing traces + * quite useless. + * + * This is why I introduced the idea to always print a few traces on + * error. The number 3 is quite arbitrary, and was selected so as not to + * clutter the console on error. For the same reason, a trace with an + * error position takes more space, and counts as two traces towards the + * limit. + * + * The rest is truncated, unless --show-trace is passed. This preserves + * the same bad semantics of --show-trace to both show the trace and + * augment it with new data. Not too sure what is the best course of + * action. + * + * The issue is that it is fundamentally hard to provide a trace for a + * lazy language. The trace will only cover the current spine of the + * evaluation, missing things that have been evaluated before. For + * example, most type errors are hard to inspect because there is not + * trace for the faulty value. These errors should really print the faulty + * value itself. + * + * In function calls, the --show-trace flag triggers extra traces for each + * function invocation. These work as scopes, allowing to follow the + * current spine of the evaluation graph. Without that flag, the error + * trace should restrict itself to a restricted prefix of that trace, + * until the first scope. If we ever get to such a precise error + * reporting, there would be no need to add an arbitrary limit here. We + * could always print the full trace, and it would just be small without + * the flag. + * + * One idea I had is for XxxError.addTrace() to perform nothing if one + * scope has already been traced. Alternatively, we could stop here when + * we encounter such a scope instead of after an arbitrary number of + * traces. This however requires to augment traces with the notion of + * "scope". + * + * This is particularly visible in code like evalAttrs(...) where we have + * to make a decision between the two following options. + * + * ``` long traces + * inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string_view & errorCtx) + * { + * try { + * e->eval(*this, env, v); + * if (v.type() != nAttrs) + * throwTypeError("value is %1% while a set was expected", v); + * } catch (Error & e) { + * e.addTrace(pos, errorCtx); + * throw; + * } + * } + * ``` + * + * ``` short traces + * inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string_view & errorCtx) + * { + * e->eval(*this, env, v); + * try { + * if (v.type() != nAttrs) + * throwTypeError("value is %1% while a set was expected", v); + * } catch (Error & e) { + * e.addTrace(pos, errorCtx); + * throw; + * } + * } + * ``` + * + * The second example can be rewritten more concisely, but kept in this + * form to highlight the symmetry. The first option adds more information, + * because whatever caused an error down the line, in the generic eval + * function, will get annotated with the code location that uses and + * required it. The second option is less verbose, but does not provide + * any context at all as to where and why a failing value was required. + * + * Scopes would fix that, by adding context only when --show-trace is + * passed, and keeping the trace terse otherwise. + * + */ + if (!einfo.traces.empty()) { unsigned int count = 0; for (auto iter = einfo.traces.rbegin(); iter != einfo.traces.rend(); ++iter) { From acf990c9ea9de913a500cf2b7a7492eef3bcd7b5 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 28 Apr 2022 12:54:14 +0200 Subject: [PATCH 18/39] fix errors case and wording --- src/libcmd/installables.cc | 2 +- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval-cache.cc | 2 +- src/libexpr/eval.cc | 112 +++++---- src/libexpr/flake/flake.cc | 2 +- src/libexpr/get-drvs.cc | 22 +- src/libexpr/primops.cc | 332 +++++++++++++------------- src/libexpr/primops/context.cc | 26 +- src/libexpr/primops/fetchMercurial.cc | 8 +- src/libexpr/primops/fetchTree.cc | 14 +- src/libexpr/primops/fromTOML.cc | 2 +- src/nix/flake.cc | 4 +- src/nix/main.cc | 2 +- src/nix/prefetch.cc | 16 +- src/nix/repl.cc | 6 +- src/nix/upgrade-nix.cc | 2 +- 16 files changed, 278 insertions(+), 276 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 40df12d1f..2f8283fd5 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -527,7 +527,7 @@ ref openEvalCache( auto vFlake = state.allocValue(); flake::callFlake(state, *lockedFlake, *vFlake); - state.forceAttrs(*vFlake, noPos, "While evaluating a cached flake"); + state.forceAttrs(*vFlake, noPos, "while parsing cached flake data"); auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); assert(aOutputs); diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index ea356b48d..7b7bc01f0 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -118,7 +118,7 @@ Pos findPackageFilename(EvalState & state, Value & v, std::string what) // FIXME: is it possible to extract the Pos object instead of doing this // toString + parsing? - auto pos = state.forceString(*v2, noPos, "While evaluating the meta.position attribute of a derivation"); + auto pos = state.forceString(*v2, noPos, "while evaluating the 'meta.position' attribute of a derivation"); auto colon = pos.rfind(':'); if (colon == std::string::npos) diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index aa2bb7435..fd6dd0e40 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -336,7 +336,7 @@ Value & AttrCursor::getValue() if (!_value) { if (parent) { auto & vParent = parent->first->getValue(); - root->state.forceAttrs(vParent, noPos, "While evaluating the parent attr set"); + root->state.forceAttrs(vParent, noPos, "while searching for an attribute"); auto attr = vParent.attrs->get(parent->second); if (!attr) throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ad879a85d..1feb0bf6f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -307,7 +307,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) } else { Value nameValue; name.expr->eval(state, env, nameValue); - state.forceStringNoCtx(nameValue, noPos, "While evaluating an attribute name"); + state.forceStringNoCtx(nameValue, noPos, "while evaluating an attribute name"); return state.symbols.create(nameValue.string.s); } } @@ -509,7 +509,7 @@ void EvalState::requireExperimentalFeatureOnEvaluation( if (!settings.isExperimentalFeatureEnabled(feature)) { throw EvalError({ .msg = hintfmt( - "Cannot call '%2%' because experimental Nix feature '%1%' is disabled. You can enable it via '--extra-experimental-features %1%'.", + "cannot call '%2%' because experimental Nix feature '%1%' is disabled. You can enable it via '--extra-experimental-features %1%'.", feature, fName ), @@ -714,10 +714,12 @@ LocalNoInlineNoReturn(void throwTypeErrorWithTrace( const Pos & p2, const std::string_view & s3)) { - throw TypeError(ErrorInfo { + auto e = TypeError(ErrorInfo { .msg = hintfmt(s, s2, sym), .errPos = pos, - }).addTrace(p2, s3); + }); + e.addTrace(p2, s3); + throw e; } LocalNoInlineNoReturn(void throwTypeErrorWithTrace( @@ -729,32 +731,40 @@ LocalNoInlineNoReturn(void throwTypeErrorWithTrace( const Pos & p2, const std::string_view & s3)) { - throw TypeError(ErrorInfo { + auto e = TypeError(ErrorInfo { .msg = hintfmt(s, s2, sym), .errPos = pos, .suggestions = suggestions - }).addTrace(p2, s3); + }); + e.addTrace(p2, s3); + throw e; } LocalNoInlineNoReturn(void throwTypeErrorWithTrace(const char * s, const std::string & s2, const Pos & p2, const std::string_view s3)) { - throw TypeError(ErrorInfo { + auto e = TypeError(ErrorInfo { .msg = hintfmt(s, s2), - }).addTrace(p2, s3); + }); + e.addTrace(p2, s3); + throw e; } LocalNoInlineNoReturn(void throwEvalErrorWithTrace(const char * s, const std::string & s2, const Pos & p2, const std::string_view s3)) { - throw EvalError(ErrorInfo { + auto e = EvalError(ErrorInfo { .msg = hintfmt(s, s2), - }).addTrace(p2, s3); + }) + e.addTrace(p2, s3); + throw e; } LocalNoInlineNoReturn(void throwEvalErrorWithTrace(const char * s, const std::string_view & s2, const std::string_view & s3, const Pos & p2, const std::string_view s4)) { - throw EvalError(ErrorInfo { + auto e = EvalError(ErrorInfo { .msg = hintfmt(s, s2, s3), - }).addTrace(p2, s4); + }); + e.addTrace(p2, s4); + throw e; } LocalNoInlineNoReturn(void throwEvalError(const Pos & pos, const Suggestions & suggestions, const char * s, const std::string & s2)) @@ -1047,7 +1057,7 @@ void EvalState::eval(Expr * e, Value & v) } -inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, const std::string_view & errorCtx) +inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, std::string_view errorCtx) { try { Value v; @@ -1062,7 +1072,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const Pos & pos, const std: } -inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string_view & errorCtx) +inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, std::string_view errorCtx) { try { e->eval(*this, env, v); @@ -1144,7 +1154,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) Hence we need __overrides.) */ if (hasOverrides) { Value * vOverrides = (*v.attrs)[overrides->second.displ].value; - state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "While evaluating the `__overrides` attribute"); + state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "while evaluating the `__overrides` attribute"); Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size()); for (auto & i : *v.attrs) newBnds->push_back(i); @@ -1172,7 +1182,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) state.forceValue(nameVal, i.pos); if (nameVal.type() == nNull) continue; - state.forceStringNoCtx(nameVal, i.pos, "While evaluating the name of a dynamic attribute"); + state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute"); Symbol nameSym = state.symbols.create(nameVal.string.s); Bindings::iterator j = v.attrs->find(nameSym); if (j != v.attrs->end()) @@ -1262,7 +1272,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) return; } } else { - state.forceAttrs(*vAttrs, pos, "While selecting an attribute"); + state.forceAttrs(*vAttrs, pos, "while selecting an attribute"); if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) { std::set allAttrNames; for (auto & attr : *vAttrs->attrs) @@ -1365,7 +1375,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & else { try { - forceAttrs(*args[0], lambda.pos, "While evaluating the value passed for this lambda parameter"); + forceAttrs(*args[0], lambda.pos, "while evaluating the value passed for the lambda argument"); } catch (Error & e) { e.addTrace(pos, "from call site"); throw; @@ -1383,7 +1393,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & if (!j) { if (!i.def) { throwTypeErrorWithTrace(lambda.pos, - "Function '%1%' called without required argument '%2%'", prettyLambdaName(lambda), i.name, + "function '%1%' called without required argument '%2%'", prettyLambdaName(lambda), i.name, pos, "from call site"); } env2.values[displ++] = i.def->maybeThunk(*this, env2); @@ -1405,7 +1415,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & formalNames.insert(formal.name); throwTypeErrorWithTrace(lambda.pos, Suggestions::bestMatches(formalNames, i.name), - "Function '%1%' called with unexpected argument '%2%'", prettyLambdaName(lambda), i.name, + "function '%1%' called with unexpected argument '%2%'", prettyLambdaName(lambda), i.name, pos, "from call site"); } abort(); // can't happen @@ -1420,7 +1430,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & lambda.body->eval(*this, env2, vCur); } catch (Error & e) { if (loggerSettings.showTrace.get()) { - addErrorTrace(e, lambda.pos, "While evaluating the '%s' function", prettyLambdaName(lambda)); + addErrorTrace(e, lambda.pos, "while evaluating the '%s' function", prettyLambdaName(lambda)); if (pos) e.addTrace(pos, "from call site"); } throw; @@ -1448,7 +1458,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & try { vCur.primOp->fun(*this, pos, args, vCur); } catch (Error & e) { - addErrorTrace(e, pos, "While calling the '%1%' builtin", name); + addErrorTrace(e, pos, "while calling the '%1%' builtin", name); throw; } @@ -1492,7 +1502,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & try { primOp->primOp->fun(*this, noPos, vArgs, vCur); } catch (Error & e) { - addErrorTrace(e, pos, "While calling the '%1%' builtin", name); + addErrorTrace(e, pos, "while calling the '%1%' builtin", name); throw; } @@ -1510,7 +1520,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & try { callFunction(*functor->value, 2, args2, vCur, *functor->pos); } catch (Error & e) { - e.addTrace(pos, "While calling a functor (an attribute set with a 'functor' attribute)"); + e.addTrace(pos, "while calling a functor (an attribute set with a '__functor' attribute)"); throw; } nrArgs--; @@ -1612,13 +1622,13 @@ void ExprWith::eval(EvalState & state, Env & env, Value & v) void ExprIf::eval(EvalState & state, Env & env, Value & 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, "While evaluating a branch condition") ? then : else_)->eval(state, env, v); + (state.evalBool(env, cond, pos, "while evaluating a branch condition") ? then : else_)->eval(state, env, v); } void ExprAssert::eval(EvalState & state, Env & env, Value & v) { - if (!state.evalBool(env, cond, pos, "In the condition of the assert statement")) { + if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) { std::ostringstream out; cond->show(out); throwAssertionError(pos, "assertion '%1%' failed", out.str()); @@ -1629,7 +1639,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v) void ExprOpNot::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(!state.evalBool(env, e, noPos, "In the argument of the not operator")); // XXX: FIXME: ! + v.mkBool(!state.evalBool(env, e, noPos, "in the argument of the not operator")); // XXX: FIXME: ! } @@ -1637,7 +1647,7 @@ void ExprOpEq::eval(EvalState & state, Env & env, Value & v) { Value v1; e1->eval(state, env, v1); Value v2; e2->eval(state, env, v2); - v.mkBool(state.eqValues(v1, v2, pos, "While testing two values for equality")); + v.mkBool(state.eqValues(v1, v2, pos, "while testing two values for equality")); } @@ -1645,33 +1655,33 @@ void ExprOpNEq::eval(EvalState & state, Env & env, Value & v) { Value v1; e1->eval(state, env, v1); Value v2; e2->eval(state, env, v2); - v.mkBool(!state.eqValues(v1, v2, pos, "While testing two values for inequality")); + v.mkBool(!state.eqValues(v1, v2, pos, "while testing two values for inequality")); } void ExprOpAnd::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(state.evalBool(env, e1, pos, "In the left operand of the AND (&&) operator") && state.evalBool(env, e2, pos, "In the right operand of the AND (&&) operator")); + v.mkBool(state.evalBool(env, e1, pos, "in the left operand of the AND (&&) operator") && state.evalBool(env, e2, pos, "in the right operand of the AND (&&) operator")); } void ExprOpOr::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(state.evalBool(env, e1, pos, "In the left operand of the OR (||) operator") || state.evalBool(env, e2, pos, "In the right operand of the OR (||) operator")); + v.mkBool(state.evalBool(env, e1, pos, "in the left operand of the OR (||) operator") || state.evalBool(env, e2, pos, "in the right operand of the OR (||) operator")); } void ExprOpImpl::eval(EvalState & state, Env & env, Value & v) { - v.mkBool(!state.evalBool(env, e1, pos, "In the left operand of the IMPL (->) operator") || state.evalBool(env, e2, pos, "In the right operand of the IMPL (->) operator")); + v.mkBool(!state.evalBool(env, e1, pos, "in the left operand of the IMPL (->) operator") || state.evalBool(env, e2, pos, "in the right operand of the IMPL (->) operator")); } void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) { Value v1, v2; - state.evalAttrs(env, e1, v1, pos, "In the left operand of the update (//) operator"); - state.evalAttrs(env, e2, v2, pos, "In the right operand of the update (//) operator"); + state.evalAttrs(env, e1, v1, pos, "in the left operand of the update (//) operator"); + state.evalAttrs(env, e2, v2, pos, "in the right operand of the update (//) operator"); state.nrOpUpdates++; @@ -1710,11 +1720,11 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) Value v1; e1->eval(state, env, v1); Value v2; e2->eval(state, env, v2); Value * lists[2] = { &v1, &v2 }; - state.concatLists(v, 2, lists, pos, "While evaluating one of the elements to concatenate"); + 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, const std::string_view & errorCtx) +void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos, std::string_view errorCtx) { nrListConcats++; @@ -1811,7 +1821,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) /* skip canonization of first path, which would only be not canonized in the first place if it's coming from a ./${foo} type path */ - auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first, "While evaluating a path segment"); + auto part = state.coerceToString(i_pos, vTmp, context, false, firstType == nString, !first, "while evaluating a path segment"); sSize += part->size(); s.emplace_back(std::move(part)); } @@ -1869,7 +1879,7 @@ void EvalState::forceValueDeep(Value & v) } -NixInt EvalState::forceInt(Value & v, const Pos & pos, const std::string_view & errorCtx) +NixInt EvalState::forceInt(Value & v, const Pos & pos, std::string_view errorCtx) { try { forceValue(v, pos); @@ -1883,7 +1893,7 @@ NixInt EvalState::forceInt(Value & v, const Pos & pos, const std::string_view & } -NixFloat EvalState::forceFloat(Value & v, const Pos & pos, const std::string_view & errorCtx) +NixFloat EvalState::forceFloat(Value & v, const Pos & pos, std::string_view errorCtx) { try { forceValue(v, pos); @@ -1899,7 +1909,7 @@ NixFloat EvalState::forceFloat(Value & v, const Pos & pos, const std::string_vie } -bool EvalState::forceBool(Value & v, const Pos & pos, const std::string_view & errorCtx) +bool EvalState::forceBool(Value & v, const Pos & pos, std::string_view errorCtx) { try { forceValue(v, pos); @@ -1919,7 +1929,7 @@ bool EvalState::isFunctor(Value & fun) } -void EvalState::forceFunction(Value & v, const Pos & pos, const std::string_view & errorCtx) +void EvalState::forceFunction(Value & v, const Pos & pos, std::string_view errorCtx) { try { forceValue(v, pos); @@ -1932,7 +1942,7 @@ void EvalState::forceFunction(Value & v, const Pos & pos, const std::string_view } -std::string_view EvalState::forceString(Value & v, const Pos & pos, const std::string_view & errorCtx) +std::string_view EvalState::forceString(Value & v, const Pos & pos, std::string_view errorCtx) { try { forceValue(v, pos); @@ -1977,7 +1987,7 @@ std::vector> Value::getContext() } -std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos, const std::string_view & errorCtx) +std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos & pos, std::string_view errorCtx) { auto s = forceString(v, pos, errorCtx); copyContext(v, context); @@ -1985,7 +1995,7 @@ std::string_view EvalState::forceString(Value & v, PathSet & context, const Pos } -std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos, const std::string_view & errorCtx) +std::string_view EvalState::forceStringNoCtx(Value & v, const Pos & pos, std::string_view errorCtx) { try { auto s = forceString(v, pos, errorCtx); @@ -2022,14 +2032,14 @@ std::optional EvalState::tryAttrsToString(const Pos & pos, Value & Value v1; callFunction(*i->value, v, v1, pos); return coerceToString(pos, v1, context, coerceMore, copyToStore, - "While evaluating the result of the `toString` attribute").toOwned(); + "while evaluating the result of the `toString` attribute").toOwned(); } return {}; } BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & context, - bool coerceMore, bool copyToStore, bool canonicalizePath, const std::string_view & errorCtx) + bool coerceMore, bool copyToStore, bool canonicalizePath, std::string_view errorCtx) { forceValue(v, pos); @@ -2075,7 +2085,7 @@ BackedStringView EvalState::coerceToString(const Pos & pos, Value & v, PathSet & for (auto [n, v2] : enumerate(v.listItems())) { try { result += *coerceToString(noPos, *v2, context, coerceMore, copyToStore, canonicalizePath, - "While evaluating one element of the list"); + "while evaluating one element of the list"); } catch (Error & e) { e.addTrace(pos, errorCtx); throw; @@ -2117,7 +2127,7 @@ std::string EvalState::copyPathToStore(PathSet & context, const Path & path) } -Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context, const std::string_view & errorCtx) +Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context, std::string_view errorCtx) { auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (path == "" || path[0] != '/') @@ -2126,7 +2136,7 @@ Path EvalState::coerceToPath(const Pos & pos, Value & v, PathSet & context, cons } -StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context, const std::string_view & errorCtx) +StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & context, std::string_view errorCtx) { auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (auto storePath = store->maybeParseStorePath(path)) @@ -2135,7 +2145,7 @@ StorePath EvalState::coerceToStorePath(const Pos & pos, Value & v, PathSet & con } -bool EvalState::eqValues(Value & v1, Value & v2, const Pos & pos, const std::string_view & errorCtx) +bool EvalState::eqValues(Value & v1, Value & v2, const Pos & pos, std::string_view errorCtx) { forceValue(v1, noPos); forceValue(v2, noPos); @@ -2331,7 +2341,7 @@ void EvalState::printStats() } -std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, const std::string_view & errorCtx) const +std::string ExternalValueBase::coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, std::string_view errorCtx) const { throwTypeErrorWithTrace("cannot coerce %1% to a string", showType(), pos, errorCtx); } diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index fe2daaf21..8427cf213 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -708,7 +708,7 @@ static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Va { state.requireExperimentalFeatureOnEvaluation(Xp::Flakes, "builtins.getFlake", pos); - std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.getFlake")); + std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); auto flakeRef = parseFlakeRef(flakeRefS, {}, true); if (evalSettings.pureEval && !flakeRef.input.isLocked()) throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, pos); diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 5ab920dcc..932ab7f1d 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -50,7 +50,7 @@ std::string DrvInfo::queryName() const if (name == "" && attrs) { auto i = attrs->find(state->sName); if (i == attrs->end()) throw TypeError("derivation name missing"); - name = state->forceStringNoCtx(*i->value, noPos, "While evaluating the name of a DrvInfo"); + name = state->forceStringNoCtx(*i->value, noPos, "while evaluating the 'name' attribute of a derivation"); } return name; } @@ -60,7 +60,7 @@ std::string DrvInfo::querySystem() const { if (system == "" && attrs) { auto i = attrs->find(state->sSystem); - system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos, "While evaluating the system of a DrvInfo"); + system = i == attrs->end() ? "unknown" : state->forceStringNoCtx(*i->value, *i->pos, "while evaluating the 'system' attribute of a derivation"); } return system; } @@ -74,7 +74,7 @@ std::optional DrvInfo::queryDrvPath() const if (i == attrs->end()) drvPath = {std::nullopt}; else - drvPath = {state->coerceToStorePath(*i->pos, *i->value, context, "Whyle evaluating the drv path of a DrvInfo")}; + drvPath = {state->coerceToStorePath(*i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")}; } return drvPath.value_or(std::nullopt); } @@ -94,7 +94,7 @@ StorePath DrvInfo::queryOutPath() const Bindings::iterator i = attrs->find(state->sOutPath); PathSet context; if (i != attrs->end()) - outPath = state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the output path of a DrvInfo"); + outPath = state->coerceToStorePath(*i->pos, *i->value, context, "while evaluating the output path of a derivation"); } if (!outPath) throw UnimplementedError("CA derivations are not yet supported"); @@ -108,23 +108,23 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall /* Get the ‘outputs’ list. */ Bindings::iterator i; if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) { - state->forceList(*i->value, *i->pos, "While evaluating the outputs of a DrvInfo"); + state->forceList(*i->value, *i->pos, "while evaluating the 'outputs' attribute of a derivation"); /* For each output... */ for (auto elem : i->value->listItems()) { - std::string output(state->forceStringNoCtx(*elem, *i->pos, "While evaluating the name of one output of a DrvInfo")); + std::string output(state->forceStringNoCtx(*elem, *i->pos, "while evaluating the name of an output of a derivation")); if (withPaths) { /* Evaluate the corresponding set. */ Bindings::iterator out = attrs->find(state->symbols.create(output)); if (out == attrs->end()) continue; // FIXME: throw error? - state->forceAttrs(*out->value, *i->pos, "While evaluating the description of a DrvInfo output"); + state->forceAttrs(*out->value, *i->pos, "while evaluating an output of a derivation"); /* And evaluate its ‘outPath’ attribute. */ Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? PathSet context; - outputs.emplace(output, state->coerceToStorePath(*outPath->pos, *outPath->value, context, "While evaluating the outPath of an output path of a DrvInfo")); + outputs.emplace(output, state->coerceToStorePath(*outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation")); } else outputs.emplace(output, std::nullopt); } @@ -155,7 +155,7 @@ std::string DrvInfo::queryOutputName() const { if (outputName == "" && attrs) { Bindings::iterator i = attrs->find(state->sOutputName); - outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "While evaluating the output name of a DrvInfo") : ""; + outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : ""; } return outputName; } @@ -167,7 +167,7 @@ Bindings * DrvInfo::getMeta() if (!attrs) return 0; Bindings::iterator a = attrs->find(state->sMeta); if (a == attrs->end()) return 0; - state->forceAttrs(*a->value, *a->pos, "While evaluating the `meta` attribute of a DrvInfo"); + state->forceAttrs(*a->value, *a->pos, "while evaluating the 'meta' attribute of a derivation"); meta = a->value->attrs; return meta; } @@ -368,7 +368,7 @@ static void getDerivations(EvalState & state, Value & vIn, `recurseForDerivations = true' attribute. */ if (i->value->type() == nAttrs) { Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations); - if (j != i->value->attrs->end() && state.forceBool(*j->value, *j->pos, "While evaluating the attribute `recurseForDerivations`")) + 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); } } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 88b87a433..f99ab8ca8 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -100,15 +100,7 @@ static Path realisePath(EvalState & state, const Pos & pos, Value & v, const Rea { PathSet context; - auto path = [&]() - { - try { - return state.coerceToPath(pos, v, context, "While realising the context of a path"); - } catch (Error & e) { - e.addTrace(pos, "while realising the context of a path"); - throw; - } - }(); + auto path = state.coerceToPath(noPos, v, context, "while realising the context of a path"); try { StringMap rewrites = state.realiseContext(context); @@ -195,9 +187,9 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS , "/"), **state.vImportedDrvToDerivation); } - state.forceFunction(**state.vImportedDrvToDerivation, pos, "While evaluating imported-drv-to-derivation.nix.gen.hh"); + state.forceFunction(**state.vImportedDrvToDerivation, pos, "while evaluating imported-drv-to-derivation.nix.gen.hh"); v.mkApp(*state.vImportedDrvToDerivation, w); - state.forceAttrs(v, pos, "While calling imported-drv-to-derivation.nix.gen.hh"); + state.forceAttrs(v, pos, "while calling imported-drv-to-derivation.nix.gen.hh"); } else if (path == corepkgsPrefix + "fetchurl.nix") { @@ -210,7 +202,7 @@ static void import(EvalState & state, const Pos & pos, Value & vPath, Value * vS if (!vScope) state.evalFile(path, v); else { - state.forceAttrs(*vScope, pos, "While evaluating the first argument passed to builtins.scopedImport"); + state.forceAttrs(*vScope, pos, "while evaluating the first argument passed to builtins.scopedImport"); Env * env = &state.allocEnv(vScope->attrs->size()); env->up = &state.baseEnv; @@ -314,7 +306,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value { auto path = realisePath(state, pos, *args[0]); - std::string sym(state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.importNative")); + 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); if (!handle) @@ -340,7 +332,7 @@ void prim_importNative(EvalState & state, const Pos & pos, Value * * args, Value /* Execute a program and parse its output */ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.exec"); + state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.exec"); auto elems = args[0]->listElems(); auto count = args[0]->listSize(); if (count == 0) { @@ -351,11 +343,11 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) } PathSet context; auto program = state.coerceToString(pos, *elems[0], context, false, false, - "While evaluating the first element of the argument passed to builtins.exec").toOwned(); + "while evaluating the first element of the argument passed to builtins.exec").toOwned(); Strings commandArgs; for (unsigned int i = 1; i < args[0]->listSize(); ++i) { commandArgs.push_back(state.coerceToString(pos, *elems[i], context, false, false, - "While evaluating an element of the argument passed to builtins.exec").toOwned()); + "while evaluating an element of the argument passed to builtins.exec").toOwned()); } try { auto _ = state.realiseContext(context); // FIXME: Handle CA derivations @@ -372,13 +364,13 @@ void prim_exec(EvalState & state, const Pos & pos, Value * * args, Value & v) try { parsed = state.parseExprFromString(std::move(output), pos.file); } catch (Error & e) { - e.addTrace(pos, "While parsing the output from '%1%'", program); + e.addTrace(pos, "while parsing the output from '%1%'", program); throw; } try { state.eval(parsed, v); } catch (Error & e) { - e.addTrace(pos, "While evaluating the output from '%1%'", program); + e.addTrace(pos, "while evaluating the output from '%1%'", program); throw; } } @@ -589,7 +581,7 @@ struct CompareValues } else if (i == v1->listSize()) { return true; } else if (!state.eqValues(*v1->listElems()[i], *v2->listElems()[i], pos, errorCtx)) { - return (*this)(v1->listElems()[i], v2->listElems()[i], "While comparing two lists"); + return (*this)(v1->listElems()[i], v2->listElems()[i], "while comparing two lists"); } } default: @@ -630,12 +622,12 @@ static Bindings::iterator getAttr( static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], noPos, "While evaluating the first argument pased to builtins.genericClosure"); + state.forceAttrs(*args[0], noPos, "while evaluating the first argument pased to builtins.genericClosure"); /* Get the start set. */ Bindings::iterator startSet = getAttr(state.sStartSet, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure"); - state.forceList(*startSet->value, noPos, "While evaluating the 'startSet' attribute passed as argument to builtins.genericClosure"); + state.forceList(*startSet->value, noPos, "while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure"); ValueList workSet; for (auto elem : startSet->value->listItems()) @@ -648,7 +640,7 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar /* Get the operator. */ Bindings::iterator op = getAttr(state.sOperator, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure"); - state.forceFunction(*op->value, noPos, "While evaluating the 'operator' attribute passed as argument to builtins.genericClosure"); + state.forceFunction(*op->value, noPos, "while evaluating the 'operator' attribute passed as argument to builtins.genericClosure"); /* Construct the closure by applying the operator to elements of `workSet', adding the result to `workSet', continuing until @@ -656,13 +648,13 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar ValueList res; // `doneKeys' doesn't need to be a GC root, because its values are // reachable from res. - auto cmp = CompareValues(state, noPos, "While comparing the `key` attributes of two genericClosure elements"); + auto cmp = CompareValues(state, noPos, "while comparing the `key` attributes of two genericClosure elements"); std::set doneKeys(cmp); while (!workSet.empty()) { Value * e = *(workSet.begin()); workSet.pop_front(); - state.forceAttrs(*e, noPos, "While evaluating one of the elements generated by (or initially passed to) builtins.genericClosure"); + state.forceAttrs(*e, noPos, "while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure"); Bindings::iterator key = getAttr(state.sKey, e->attrs, "in one of the attrsets generated by (or initially passed to) builtins.genericClosure"); state.forceValue(*key->value, noPos); @@ -673,11 +665,11 @@ static void prim_genericClosure(EvalState & state, const Pos & pos, Value * * ar /* Call the `operator' function with `e' as argument. */ Value newElements; state.callFunction(*op->value, 1, &e, newElements, noPos); - state.forceList(newElements, noPos, "While evaluating the return value of the `operator` passed to builtins.genericClosure"); + state.forceList(newElements, noPos, "while evaluating the return value of the `operator` passed to builtins.genericClosure"); /* Add the values returned by the operator to the work set. */ for (auto elem : newElements.listItems()) { - state.forceValue(*elem, noPos); // "While evaluating one one of the elements returned by the `operator` passed to builtins.genericClosure"); + state.forceValue(*elem, noPos); // "while evaluating one one of the elements returned by the `operator` passed to builtins.genericClosure"); workSet.push_back(elem); } } @@ -705,7 +697,7 @@ static RegisterPrimOp primop_abort({ { PathSet context; auto s = state.coerceToString(pos, *args[0], context, - "While evaluating the error message passed to builtins.abort").toOwned(); + "while evaluating the error message passed to builtins.abort").toOwned(); throw Abort("evaluation aborted with the following error message: '%1%'", s); } }); @@ -724,7 +716,7 @@ static RegisterPrimOp primop_throw({ { PathSet context; auto s = state.coerceToString(pos, *args[0], context, - "While evaluating the error message passed to builtin.throw").toOwned(); + "while evaluating the error message passed to builtin.throw").toOwned(); throw ThrownError(s); } }); @@ -737,7 +729,7 @@ static void prim_addErrorContext(EvalState & state, const Pos & pos, Value * * a } catch (Error & e) { PathSet context; e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context, - "While evaluating the error message passed to builtins.addErrorContext").toOwned()); + "while evaluating the error message passed to builtins.addErrorContext").toOwned()); throw; } } @@ -751,7 +743,7 @@ static RegisterPrimOp primop_addErrorContext(RegisterPrimOp::Info { static void prim_ceil(EvalState & state, const Pos & pos, Value * * args, Value & v) { auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), - "While evaluating the first argument passed to builtins.ceil"); + "while evaluating the first argument passed to builtins.ceil"); v.mkInt(ceil(value)); } @@ -770,7 +762,7 @@ static RegisterPrimOp primop_ceil({ static void prim_floor(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "While evaluating the first argument passed to builtins.floor"); + auto value = state.forceFloat(*args[0], args[0]->determinePos(pos), "while evaluating the first argument passed to builtins.floor"); v.mkInt(floor(value)); } @@ -827,7 +819,7 @@ static RegisterPrimOp primop_tryEval({ /* Return an environment variable. Use with care. */ static void prim_getEnv(EvalState & state, const Pos & pos, Value * * args, Value & v) { - std::string name(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.getEnv")); + 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("")); } @@ -925,7 +917,7 @@ static RegisterPrimOp primop_trace({ derivation. */ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.derivationStrict"); + state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.derivationStrict"); /* Figure out the name first (for stack backtraces). */ Bindings::iterator attr = getAttr(state.sName, args[0]->attrs, "in the attrset passed as argument to builtins.derivationStrict"); @@ -933,7 +925,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * std::string drvName; Pos & posDrvName(*attr->pos); try { - drvName = state.forceStringNoCtx(*attr->value, pos, "While evaluating the `name` attribute passed to builtins.derivationStrict"); + drvName = state.forceStringNoCtx(*attr->value, pos, "while evaluating the `name` attribute passed to builtins.derivationStrict"); } catch (Error & e) { e.addTrace(posDrvName, "while evaluating the derivation attribute 'name'"); throw; @@ -943,14 +935,14 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * std::ostringstream jsonBuf; std::unique_ptr jsonObject; attr = args[0]->attrs->find(state.sStructuredAttrs); - if (attr != args[0]->attrs->end() && state.forceBool(*attr->value, pos, "While evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict")) + if (attr != args[0]->attrs->end() && state.forceBool(*attr->value, pos, "while evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict")) jsonObject = std::make_unique(jsonBuf); /* Check whether null attributes should be ignored. */ bool ignoreNulls = false; attr = args[0]->attrs->find(state.sIgnoreNulls); if (attr != args[0]->attrs->end()) - ignoreNulls = state.forceBool(*attr->value, pos, "While evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict"); + ignoreNulls = state.forceBool(*attr->value, pos, "while evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict"); /* Build the derivation expression by processing the attributes. */ Derivation drv; @@ -1017,7 +1009,7 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * if (i->name == state.sContentAddressed) { contentAddressed = state.forceBool(*i->value, pos, - "While evaluating the `__contentAddressed` attribute passed to builtins.derivationStrict"); + "while evaluating the `__contentAddressed` attribute passed to builtins.derivationStrict"); if (contentAddressed) settings.requireExperimentalFeature(Xp::CaDerivations); } @@ -1026,10 +1018,10 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * command-line arguments to the builder. */ else if (i->name == state.sArgs) { state.forceList(*i->value, pos, - "While evaluating the `args` attribute passed to builtins.derivationStrict"); + "while evaluating the `args` attribute passed to builtins.derivationStrict"); for (auto elem : i->value->listItems()) { auto s = state.coerceToString(posDrvName, *elem, context, true, - "While evaluating an element of the `args` argument passed to builtins.derivationStrict").toOwned(); + "while evaluating an element of the `args` argument passed to builtins.derivationStrict").toOwned(); drv.args.push_back(s); } } @@ -1046,26 +1038,26 @@ static void prim_derivationStrict(EvalState & state, const Pos & pos, Value * * printValueAsJSON(state, true, *i->value, pos, placeholder, context); if (i->name == state.sBuilder) - drv.builder = state.forceString(*i->value, context, posDrvName, "While evaluating the `builder` attribute passed to builtins.derivationStrict"); + drv.builder = state.forceString(*i->value, context, posDrvName, "while evaluating the `builder` attribute passed to builtins.derivationStrict"); else if (i->name == state.sSystem) - drv.platform = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `system` attribute passed to builtins.derivationStrict"); + drv.platform = state.forceStringNoCtx(*i->value, posDrvName, "while evaluating the `system` attribute passed to builtins.derivationStrict"); else if (i->name == state.sOutputHash) - outputHash = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHash` attribute passed to builtins.derivationStrict"); + outputHash = state.forceStringNoCtx(*i->value, posDrvName, "while evaluating the `outputHash` attribute passed to builtins.derivationStrict"); else if (i->name == state.sOutputHashAlgo) - outputHashAlgo = state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashAlgo` attribute passed to builtins.derivationStrict"); + outputHashAlgo = state.forceStringNoCtx(*i->value, posDrvName, "while evaluating the `outputHashAlgo` attribute passed to builtins.derivationStrict"); else if (i->name == state.sOutputHashMode) - handleHashMode(state.forceStringNoCtx(*i->value, posDrvName, "While evaluating the `outputHashMode` attribute passed to builtins.derivationStrict")); + handleHashMode(state.forceStringNoCtx(*i->value, posDrvName, "while evaluating the `outputHashMode` attribute passed to builtins.derivationStrict")); else if (i->name == state.sOutputs) { /* Require ‘outputs’ to be a list of strings. */ - state.forceList(*i->value, posDrvName, "While evaluating the `outputs` attribute passed to builtins.derivationStrict"); + state.forceList(*i->value, posDrvName, "while evaluating the `outputs` attribute passed to builtins.derivationStrict"); Strings ss; for (auto elem : i->value->listItems()) - ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName, "While evaluating an element of the `outputs` attribute passed to builtins.derivationStrict")); + ss.emplace_back(state.forceStringNoCtx(*elem, posDrvName, "while evaluating an element of the `outputs` attribute passed to builtins.derivationStrict")); handleOutputs(ss); } } else { - auto s = state.coerceToString(*i->pos, *i->value, context, true, "While evaluating an attribute passed to builtins.derivationStrict").toOwned(); + auto s = state.coerceToString(*i->pos, *i->value, context, true, "while evaluating an attribute passed to builtins.derivationStrict").toOwned(); drv.env.emplace(key, s); if (i->name == state.sBuilder) drv.builder = std::move(s); else if (i->name == state.sSystem) drv.platform = std::move(s); @@ -1278,7 +1270,7 @@ static RegisterPrimOp primop_derivationStrict(RegisterPrimOp::Info { ‘out’. */ static void prim_placeholder(EvalState & state, const Pos & pos, Value * * args, Value & v) { - v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.placeholder"))); + v.mkString(hashPlaceholder(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.placeholder"))); } static RegisterPrimOp primop_placeholder({ @@ -1302,7 +1294,7 @@ static RegisterPrimOp primop_placeholder({ static void prim_toPath(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - Path path = state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.toPath"); + Path path = state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.toPath"); v.mkString(canonPath(path), context); } @@ -1333,7 +1325,7 @@ static void prim_storePath(EvalState & state, const Pos & pos, Value * * args, V }); PathSet context; - Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "While evaluating the first argument passed to builtins.storePath")); + Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.storePath")); /* Resolve symlinks in ‘path’, unless ‘path’ itself is a symlink directly in the store. The latter condition is necessary so e.g. nix-push does the right thing. */ @@ -1403,7 +1395,7 @@ static RegisterPrimOp primop_pathExists({ static void prim_baseNameOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.baseNameOf")), context); + v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, false, false, "while evaluating the first argument passed to builtins.baseNameOf")), context); } static RegisterPrimOp primop_baseNameOf({ @@ -1423,7 +1415,7 @@ static RegisterPrimOp primop_baseNameOf({ static void prim_dirOf(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto path = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.dirOf"); + auto path = state.coerceToString(pos, *args[0], context, false, false, "while evaluating the first argument passed to builtins.dirOf"); auto dir = dirOf(*path); if (args[0]->type() == nPath) v.mkPath(dir); else v.mkString(dir, context); } @@ -1470,23 +1462,23 @@ static RegisterPrimOp primop_readFile({ which are desugared to 'findFile __nixPath "x"'. */ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.findFile"); + state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.findFile"); SearchPath searchPath; for (auto v2 : args[0]->listItems()) { - state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.findFile"); + state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.findFile"); std::string prefix; Bindings::iterator i = v2->attrs->find(state.sPrefix); if (i != v2->attrs->end()) - prefix = state.forceStringNoCtx(*i->value, pos, "While evaluating the `prefix` attribute of an element of the list passed to builtins.findFile"); + prefix = state.forceStringNoCtx(*i->value, pos, "while evaluating the `prefix` attribute of an element of the list passed to builtins.findFile"); i = getAttr(state.sPath, v2->attrs, "in an element of the __nixPath"); PathSet context; auto path = state.coerceToString(pos, *i->value, context, false, false, - "While evaluating the `path` attribute of an element of the list passed to builtins.findFile").toOwned(); + "while evaluating the `path` attribute of an element of the list passed to builtins.findFile").toOwned(); try { auto rewrites = state.realiseContext(context); @@ -1502,7 +1494,7 @@ static void prim_findFile(EvalState & state, const Pos & pos, Value * * args, Va searchPath.emplace_back(prefix, path); } - auto path = state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.findFile"); + 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))); } @@ -1516,7 +1508,7 @@ static RegisterPrimOp primop_findFile(RegisterPrimOp::Info { /* Return the cryptographic hash of a file in base-16. */ static void prim_hashFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashFile"); + auto type = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hashFile"); std::optional ht = parseHashType(type); if (!ht) throw Error({ @@ -1723,7 +1715,7 @@ static RegisterPrimOp primop_toJSON({ /* Parse a JSON string to a value. */ static void prim_fromJSON(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto s = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.fromJSON"); + auto s = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.fromJSON"); try { parseJSON(state, s, v); } catch (JSONParseError &e) { @@ -1752,8 +1744,8 @@ static RegisterPrimOp primop_fromJSON({ static void prim_toFile(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - 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, "While evaluating the second argument passed to builtins.toFile")); + 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, "while evaluating the second argument passed to builtins.toFile")); StorePathSet refs; @@ -1910,7 +1902,7 @@ static void addPath( Value res; state.callFunction(*filterFun, 2, args, res, pos); - return state.forceBool(res, pos, "While evaluating the return value of the path filter function"); + return state.forceBool(res, pos, "while evaluating the return value of the path filter function"); }) : defaultPathFilter; std::optional expectedStorePath; @@ -1936,9 +1928,9 @@ static void addPath( static void prim_filterSource(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - Path path = state.coerceToPath(pos, *args[1], context, "While evaluating the second argument (the path to filter) passed to builtins.filterSource"); + Path path = state.coerceToPath(pos, *args[1], context, "while evaluating the second argument (the path to filter) passed to builtins.filterSource"); - state.forceFunction(*args[0], pos, "While evaluating the first argument to builtins.filterSource"); + state.forceFunction(*args[0], pos, "while evaluating the first argument to builtins.filterSource"); addPath(state, pos, std::string(baseNameOf(path)), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context); } @@ -1999,7 +1991,7 @@ static RegisterPrimOp primop_filterSource({ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.path"); + state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.path"); Path path; std::string name; Value * filterFun = nullptr; @@ -2010,15 +2002,15 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value for (auto & attr : *args[0]->attrs) { auto & n(attr.name); if (n == "path") - path = state.coerceToPath(*attr.pos, *attr.value, context, "While evaluating the `path` attribute passed to builtins.path"); + path = state.coerceToPath(*attr.pos, *attr.value, context, "while evaluating the `path` attribute passed to builtins.path"); else if (attr.name == state.sName) name = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the `name` attribute passed to builtins.path"); else if (n == "filter") - state.forceFunction(*(filterFun = attr.value), *attr.pos, "While evaluating the `filter` parameter passed to builtins.path"); + state.forceFunction(*(filterFun = attr.value), *attr.pos, "while evaluating the `filter` parameter passed to builtins.path"); else if (n == "recursive") - method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos, "While evaluating the `recursive` attribute passed to builtins.path") }; + method = FileIngestionMethod { state.forceBool(*attr.value, *attr.pos, "while evaluating the `recursive` attribute passed to builtins.path") }; else if (n == "sha256") - expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `sha256` attribute passed to builtins.path"), htSHA256); + expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the `sha256` attribute passed to builtins.path"), htSHA256); else throw EvalError({ .msg = hintfmt("unsupported argument '%1%' to 'addPath'", attr.name), @@ -2027,7 +2019,7 @@ static void prim_path(EvalState & state, const Pos & pos, Value * * args, Value } if (path.empty()) throw EvalError({ - .msg = hintfmt("Missing required 'path' attribute in the first argument to builtins.path"), + .msg = hintfmt("missing required 'path' attribute in the first argument to builtins.path"), .errPos = pos }); if (name.empty()) @@ -2081,7 +2073,7 @@ static RegisterPrimOp primop_path({ strings. */ static void prim_attrNames(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrNames"); + state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrNames"); state.mkList(v, args[0]->attrs->size()); @@ -2108,7 +2100,7 @@ static RegisterPrimOp primop_attrNames({ order as attrNames. */ static void prim_attrValues(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.attrValues"); + state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrValues"); state.mkList(v, args[0]->attrs->size()); @@ -2139,8 +2131,8 @@ static RegisterPrimOp primop_attrValues({ /* Dynamic version of the `.' operator. */ void prim_getAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.getAttr"); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.getAttr"); + auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getAttr"); + state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.getAttr"); Bindings::iterator i = getAttr( state.symbols.create(attr), args[1]->attrs, @@ -2167,8 +2159,8 @@ static RegisterPrimOp primop_getAttr({ /* Return position information of the specified attribute. */ static void prim_unsafeGetAttrPos(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.unsafeGetAttrPos"); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.unsafeGetAttrPos"); + auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.unsafeGetAttrPos"); + 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)); if (i == args[1]->attrs->end()) v.mkNull(); @@ -2185,8 +2177,8 @@ static RegisterPrimOp primop_unsafeGetAttrPos(RegisterPrimOp::Info { /* Dynamic version of the `?' operator. */ static void prim_hasAttr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto attr = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hasAttr"); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.hasAttr"); + auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hasAttr"); + 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()); } @@ -2219,8 +2211,8 @@ static RegisterPrimOp primop_isAttrs({ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.removeAttrs"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.removeAttrs"); + state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.removeAttrs"); + state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.removeAttrs"); /* Get the attribute names to be removed. We keep them as Attrs instead of Symbols so std::set_difference @@ -2228,7 +2220,7 @@ static void prim_removeAttrs(EvalState & state, const Pos & pos, Value * * args, boost::container::small_vector names; names.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { - state.forceStringNoCtx(*elem, pos, "While evaluating the values of the second argument passed to builtins.removeAttrs"); + 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); } std::sort(names.begin(), names.end()); @@ -2267,18 +2259,18 @@ static RegisterPrimOp primop_removeAttrs({ name, the first takes precedence. */ static void prim_listToAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the argument passed to builtins.listToAttrs"); + state.forceList(*args[0], pos, "while evaluating the argument passed to builtins.listToAttrs"); auto attrs = state.buildBindings(args[0]->listSize()); std::set seen; for (auto v2 : args[0]->listItems()) { - state.forceAttrs(*v2, pos, "While evaluating an element of the list passed to builtins.listToAttrs"); + state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.listToAttrs"); Bindings::iterator j = getAttr(state.sName, v2->attrs, "in a {name=...; value=...;} pair"); - auto name = state.forceStringNoCtx(*j->value, *j->pos, "While evaluating the `name` attribute of an element of the list passed to builtins.listToAttrs"); + 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); if (seen.insert(sym).second) { @@ -2317,8 +2309,8 @@ static RegisterPrimOp primop_listToAttrs({ static void prim_intersectAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], pos, "While evaluating the first argument passed to builtins.intersectAttrs"); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.intersectAttrs"); + state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.intersectAttrs"); + 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())); @@ -2343,14 +2335,14 @@ static RegisterPrimOp primop_intersectAttrs({ static void prim_catAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.catAttrs")); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.catAttrs"); + Symbol attrName = state.symbols.create(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.catAttrs")); + state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.catAttrs"); Value * res[args[1]->listSize()]; unsigned int found = 0; for (auto v2 : args[1]->listItems()) { - state.forceAttrs(*v2, pos, "While evaluating an element in the list passed as second argument to builtins.catAttrs"); + 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); if (i != v2->attrs->end()) res[found++] = i->value; @@ -2423,7 +2415,7 @@ static RegisterPrimOp primop_functionArgs({ /* */ static void prim_mapAttrs(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.mapAttrs"); + state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.mapAttrs"); auto attrs = state.buildBindings(args[1]->attrs->size()); @@ -2464,15 +2456,15 @@ static void prim_zipAttrsWith(EvalState & state, const Pos & pos, Value * * args std::map> attrsSeen; - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.zipAttrsWith"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.zipAttrsWith"); + state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.zipAttrsWith"); + state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.zipAttrsWith"); const auto listSize = args[1]->listSize(); const auto listElems = args[1]->listElems(); for (unsigned int n = 0; n < listSize; ++n) { Value * vElem = listElems[n]; try { - state.forceAttrs(*vElem, noPos, "While evaluating a value of the list passed as second argument to builtins.zipAttrsWith"); + state.forceAttrs(*vElem, noPos, "while evaluating a value of the list passed as second argument to builtins.zipAttrsWith"); for (auto & attr : *vElem->attrs) attrsSeen[attr.name].first++; } catch (TypeError & e) { @@ -2562,7 +2554,7 @@ static RegisterPrimOp primop_isList({ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Value & v) { - state.forceList(list, pos, "While evaluating the first argument passed to builtins.elemAt"); + state.forceList(list, pos, "while evaluating the first argument passed to builtins.elemAt"); if (n < 0 || (unsigned int) n >= list.listSize()) throw Error({ .msg = hintfmt("list index %1% is out of bounds", n), @@ -2575,7 +2567,7 @@ static void elemAt(EvalState & state, const Pos & pos, Value & list, int n, Valu /* Return the n-1'th element of a list. */ static void prim_elemAt(EvalState & state, const Pos & pos, Value * * args, Value & v) { - elemAt(state, pos, *args[0], state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.elemAt"), 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({ @@ -2610,7 +2602,7 @@ static RegisterPrimOp primop_head({ don't want to use it! */ static void prim_tail(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.tail"); + state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.tail"); if (args[0]->listSize() == 0) throw Error({ .msg = hintfmt("'tail' called on an empty list"), @@ -2641,14 +2633,14 @@ static RegisterPrimOp primop_tail({ /* Apply a function to every element of a list. */ static void prim_map(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.map"); + state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.map"); if (args[1]->listSize() == 0) { v = *args[1]; return; } - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.map"); + state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.map"); state.mkList(v, args[1]->listSize()); for (unsigned int n = 0; n < v.listSize(); ++n) @@ -2677,14 +2669,14 @@ static RegisterPrimOp primop_map({ returns true. */ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.filter"); + state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.filter"); if (args[1]->listSize() == 0) { v = *args[1]; return; } - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.filter"); + state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filter"); // FIXME: putting this on the stack is risky. Value * vs[args[1]->listSize()]; @@ -2694,7 +2686,7 @@ static void prim_filter(EvalState & state, const Pos & pos, Value * * args, Valu for (unsigned int n = 0; n < args[1]->listSize(); ++n) { Value res; state.callFunction(*args[0], *args[1]->listElems()[n], res, noPos); - if (state.forceBool(res, pos, "While evaluating the return value of the filtering function passed to builtins.filter")) + if (state.forceBool(res, pos, "while evaluating the return value of the filtering function passed to builtins.filter")) vs[k++] = args[1]->listElems()[n]; else same = false; @@ -2722,9 +2714,9 @@ static RegisterPrimOp primop_filter({ static void prim_elem(EvalState & state, const Pos & pos, Value * * args, Value & v) { bool res = false; - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.elem"); + state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.elem"); for (auto elem : args[1]->listItems()) - if (state.eqValues(*args[0], *elem, pos, "While searching for the presence of the given element in the list")) { + if (state.eqValues(*args[0], *elem, pos, "while searching for the presence of the given element in the list")) { res = true; break; } @@ -2744,8 +2736,8 @@ static RegisterPrimOp primop_elem({ /* Concatenate a list of lists. */ static void prim_concatLists(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.concatLists"); - state.concatLists(v, args[0]->listSize(), args[0]->listElems(), pos, "While evaluating a value of the list passed to builtins.concatLists"); + state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.concatLists"); + 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({ @@ -2760,7 +2752,7 @@ static RegisterPrimOp primop_concatLists({ /* 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) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.length"); + state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.length"); v.mkInt(args[0]->listSize()); } @@ -2777,8 +2769,8 @@ static RegisterPrimOp primop_length({ right. The operator is applied strictly. */ static void prim_foldlStrict(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.foldlStrict"); - state.forceList(*args[2], pos, "While evaluating the third argument passed to builtins.foldlStrict"); + state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.foldlStrict"); + state.forceList(*args[2], pos, "while evaluating the third argument passed to builtins.foldlStrict"); if (args[2]->listSize()) { Value * vCur = args[1]; @@ -2810,13 +2802,13 @@ static RegisterPrimOp primop_foldlStrict({ static void anyOrAll(bool any, EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, std::string("While evaluating the first argument passed to builtins.") + (any ? "any" : "all")); - state.forceList(*args[1], pos, std::string("While evaluating the second argument passed to builtins.") + (any ? "any" : "all")); + state.forceFunction(*args[0], pos, std::string("while evaluating the first argument passed to builtins.") + (any ? "any" : "all")); + state.forceList(*args[1], pos, std::string("while evaluating the second argument passed to builtins.") + (any ? "any" : "all")); Value vTmp; for (auto elem : args[1]->listItems()) { state.callFunction(*args[0], *elem, vTmp, pos); - bool res = state.forceBool(vTmp, pos, std::string("While evaluating the return value of the function passed to builtins.") + (any ? "any" : "all")); + 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) { v.mkBool(any); return; @@ -2859,7 +2851,7 @@ static RegisterPrimOp primop_all({ static void prim_genList(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto len = state.forceInt(*args[1], pos, "While evaluating the second argument passed to builtins.genList"); + auto len = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.genList"); if (len < 0) throw EvalError({ @@ -2897,7 +2889,7 @@ 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) { - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.sort"); + state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.sort"); auto len = args[1]->listSize(); if (len == 0) { @@ -2905,7 +2897,7 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value return; } - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.sort"); + state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.sort"); state.mkList(v, len); for (unsigned int n = 0; n < len; ++n) { @@ -2917,12 +2909,12 @@ static void prim_sort(EvalState & state, const Pos & pos, Value * * args, Value /* Optimization: if the comparator is lessThan, bypass callFunction. */ if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan) - return CompareValues(state, noPos, "While evaluating the ordering function passed to builtins.sort")(a, b); + return CompareValues(state, noPos, "while evaluating the ordering function passed to builtins.sort")(a, b); Value * vs[] = {a, b}; Value vBool; state.callFunction(*args[0], 2, vs, vBool, noPos); - return state.forceBool(vBool, pos, "While evaluating the return value of the sorting function passed to builtins.sort"); + 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 @@ -2954,8 +2946,8 @@ static RegisterPrimOp primop_sort({ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.partition"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.partition"); + state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.partition"); + state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.partition"); auto len = args[1]->listSize(); @@ -2966,7 +2958,7 @@ static void prim_partition(EvalState & state, const Pos & pos, Value * * args, V state.forceValue(*vElem, pos); Value res; state.callFunction(*args[0], *vElem, res, pos); - if (state.forceBool(res, pos, "While evaluating the return value of the partition function passed to builtins.partition")) + if (state.forceBool(res, pos, "while evaluating the return value of the partition function passed to builtins.partition")) right.push_back(vElem); else wrong.push_back(vElem); @@ -3014,15 +3006,15 @@ static RegisterPrimOp primop_partition({ static void prim_groupBy(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.groupBy"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.groupBy"); + state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.groupBy"); + state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.groupBy"); ValueVectorMap attrs; for (auto vElem : args[1]->listItems()) { Value res; state.callFunction(*args[0], *vElem, res, pos); - auto name = state.forceStringNoCtx(res, pos, "While evaluating the return value of the grouping function passed to builtins.groupBy"); + 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); auto vector = attrs.try_emplace(sym, ValueVector()).first; vector->second.push_back(vElem); @@ -3066,8 +3058,8 @@ static RegisterPrimOp primop_groupBy({ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceFunction(*args[0], pos, "While evaluating the first argument passed to builtins.concatMap"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.concatMap"); + state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.concatMap"); + state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.concatMap"); auto nrLists = args[1]->listSize(); Value lists[nrLists]; @@ -3077,7 +3069,7 @@ static void prim_concatMap(EvalState & state, const Pos & pos, Value * * args, V Value * vElem = args[1]->listElems()[n]; state.callFunction(*args[0], *vElem, lists[n], pos); try { - state.forceList(lists[n], lists[n].determinePos(args[0]->determinePos(pos)), "While evaluating the return value of the function passed to buitlins.concatMap"); + 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) { e.addTrace(pos, hintfmt("while invoking '%s'", "concatMap")); throw; @@ -3116,11 +3108,11 @@ static void prim_add(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - 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")); + 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 - 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")); + 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({ @@ -3137,11 +3129,11 @@ static void prim_sub(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - 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")); + 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 - 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")); + 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({ @@ -3158,11 +3150,11 @@ static void prim_mul(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); if (args[0]->type() == nFloat || args[1]->type() == nFloat) - 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")); + 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 - 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")); + 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({ @@ -3179,7 +3171,7 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & state.forceValue(*args[0], pos); state.forceValue(*args[1], pos); - NixFloat f2 = state.forceFloat(*args[1], pos, "While evaluating the second operand of the division"); + NixFloat f2 = state.forceFloat(*args[1], pos, "while evaluating the second operand of the division"); if (f2 == 0) throw EvalError({ .msg = hintfmt("division by zero"), @@ -3187,10 +3179,10 @@ static void prim_div(EvalState & state, const Pos & pos, Value * * args, Value & }); if (args[0]->type() == nFloat || args[1]->type() == nFloat) { - v.mkFloat(state.forceFloat(*args[0], pos, "While evaluating the first operand of the division") / f2); + v.mkFloat(state.forceFloat(*args[0], pos, "while evaluating the first operand of the division") / f2); } else { - NixInt i1 = state.forceInt(*args[0], pos, "While evaluating the first operand of the division"); - NixInt i2 = state.forceInt(*args[1], pos, "While evaluating the second operand of the division"); + NixInt i1 = state.forceInt(*args[0], pos, "while evaluating the first operand of the division"); + NixInt i2 = state.forceInt(*args[1], pos, "while evaluating the second operand of the division"); /* Avoid division overflow as it might raise SIGFPE. */ if (i1 == std::numeric_limits::min() && i2 == -1) throw EvalError({ @@ -3213,8 +3205,8 @@ static RegisterPrimOp primop_div({ static void prim_bitAnd(EvalState & state, const Pos & pos, Value * * args, Value & v) { - 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")); + 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({ @@ -3228,8 +3220,8 @@ static RegisterPrimOp primop_bitAnd({ static void prim_bitOr(EvalState & state, const Pos & pos, Value * * args, Value & v) { - 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")); + 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({ @@ -3243,8 +3235,8 @@ static RegisterPrimOp primop_bitOr({ static void prim_bitXor(EvalState & state, const Pos & pos, Value * * args, Value & v) { - 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")); + 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({ @@ -3288,7 +3280,7 @@ static RegisterPrimOp primop_lessThan({ static void prim_toString(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, true, false, "While evaluating the first argument passed to builtins.toString"); + auto s = state.coerceToString(pos, *args[0], context, true, false, "while evaluating the first argument passed to builtins.toString"); v.mkString(*s, context); } @@ -3322,10 +3314,10 @@ static RegisterPrimOp primop_toString({ non-negative. */ static void prim_substring(EvalState & state, const Pos & pos, Value * * args, Value & v) { - int start = state.forceInt(*args[0], pos, "While evaluating the first argument (the start offset) passed to builtins.substring"); - int len = state.forceInt(*args[1], pos, "While evaluating the second argument (the substring length) passed to builtins.substring"); + int start = state.forceInt(*args[0], pos, "while evaluating the first argument (the start offset) passed to builtins.substring"); + int len = state.forceInt(*args[1], pos, "while evaluating the second argument (the substring length) passed to builtins.substring"); PathSet context; - auto s = state.coerceToString(pos, *args[2], context, "While evaluating the third argument (the string) passed to builtins.substring"); + auto s = state.coerceToString(pos, *args[2], context, "while evaluating the third argument (the string) passed to builtins.substring"); if (start < 0) throw EvalError({ @@ -3359,7 +3351,7 @@ static RegisterPrimOp primop_substring({ static void prim_stringLength(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.stringLength"); + auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.stringLength"); v.mkInt(s->size()); } @@ -3376,7 +3368,7 @@ static RegisterPrimOp primop_stringLength({ /* Return the cryptographic hash of a string in base-16. */ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto type = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.hashString"); + auto type = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hashString"); std::optional ht = parseHashType(type); if (!ht) throw Error({ @@ -3385,7 +3377,7 @@ static void prim_hashString(EvalState & state, const Pos & pos, Value * * args, }); PathSet context; // discarded - auto s = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.hashString"); + 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)); } @@ -3424,14 +3416,14 @@ std::shared_ptr makeRegexCache() void prim_match(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.match"); + auto re = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.match"); try { auto regex = state.regexCache->get(re); PathSet context; - const auto str = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.match"); + const auto str = state.forceString(*args[1], context, pos, "while evaluating the second argument passed to builtins.match"); std::cmatch match; if (!std::regex_match(str.begin(), str.end(), match, regex)) { @@ -3505,14 +3497,14 @@ static RegisterPrimOp primop_match({ non-matching parts interleaved by the lists of the matching groups. */ void prim_split(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto re = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.split"); + auto re = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.split"); try { auto regex = state.regexCache->get(re); PathSet context; - const auto str = state.forceString(*args[1], context, pos, "While evaluating the second argument passed to builtins.split"); + 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 end = std::cregex_iterator(); @@ -3611,8 +3603,8 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * { PathSet context; - auto sep = state.forceString(*args[0], context, pos, "While evaluating the first argument (the separator string) passed to builtins.concatStringsSep"); - state.forceList(*args[1], pos, "While evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep"); + auto sep = state.forceString(*args[0], context, pos, "while evaluating the first argument (the separator string) passed to builtins.concatStringsSep"); + state.forceList(*args[1], pos, "while evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep"); std::string res; res.reserve((args[1]->listSize() + 32) * sep.size()); @@ -3620,7 +3612,7 @@ static void prim_concatStringsSep(EvalState & state, const Pos & pos, Value * * for (auto elem : args[1]->listItems()) { if (first) first = false; else res += sep; - res += *state.coerceToString(pos, *elem, context, "While evaluating one element of the list of strings to concat passed to builtins.concatStringsSep"); + res += *state.coerceToString(pos, *elem, context, "while evaluating one element of the list of strings to concat passed to builtins.concatStringsSep"); } v.mkString(res, context); @@ -3639,8 +3631,8 @@ static RegisterPrimOp primop_concatStringsSep({ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * args, Value & v) { - state.forceList(*args[0], pos, "While evaluating the first argument passed to builtins.replaceStrings"); - state.forceList(*args[1], pos, "While evaluating the second argument passed to builtins.replaceStrings"); + state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.replaceStrings"); + state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.replaceStrings"); if (args[0]->listSize() != args[1]->listSize()) throw EvalError({ .msg = hintfmt("'from' and 'to' arguments to 'replaceStrings' have different lengths"), @@ -3650,18 +3642,18 @@ static void prim_replaceStrings(EvalState & state, const Pos & pos, Value * * ar std::vector from; from.reserve(args[0]->listSize()); for (auto elem : args[0]->listItems()) - from.emplace_back(state.forceString(*elem, pos, "While evaluating one of the strings to replace in builtins.replaceStrings")); + from.emplace_back(state.forceString(*elem, pos, "while evaluating one of the strings to replace in builtins.replaceStrings")); std::vector> to; to.reserve(args[1]->listSize()); for (auto elem : args[1]->listItems()) { PathSet ctx; - auto s = state.forceString(*elem, ctx, pos, "While evaluating one of the replacement strings of builtins.replaceStrings"); + auto s = state.forceString(*elem, ctx, pos, "while evaluating one of the replacement strings of builtins.replaceStrings"); to.emplace_back(s, std::move(ctx)); } PathSet context; - auto s = state.forceString(*args[2], context, pos, "While evaluating the third argument passed to builtins.replaceStrings"); + auto s = state.forceString(*args[2], context, pos, "while evaluating the third argument passed to builtins.replaceStrings"); std::string res; // Loops one past last character to handle the case where 'from' contains an empty string. @@ -3719,7 +3711,7 @@ static RegisterPrimOp primop_replaceStrings({ static void prim_parseDrvName(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto name = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.parseDrvName"); + auto name = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.parseDrvName"); DrvName parsed(name); auto attrs = state.buildBindings(2); attrs.alloc(state.sName).mkString(parsed.name); @@ -3743,8 +3735,8 @@ static RegisterPrimOp primop_parseDrvName({ static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto version1 = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.compareVersions"); - auto version2 = state.forceStringNoCtx(*args[1], pos, "While evaluating the second argument passed to builtins.compareVersions"); + auto version1 = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.compareVersions"); + auto version2 = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.compareVersions"); v.mkInt(compareVersions(version1, version2)); } @@ -3763,7 +3755,7 @@ static RegisterPrimOp primop_compareVersions({ static void prim_splitVersion(EvalState & state, const Pos & pos, Value * * args, Value & v) { - auto version = state.forceStringNoCtx(*args[0], pos, "While evaluating the first argument passed to builtins.splitVersion"); + auto version = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.splitVersion"); auto iter = version.cbegin(); Strings components; while (iter != version.cend()) { diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 78320dc09..517b93897 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -7,7 +7,7 @@ namespace nix { static void prim_unsafeDiscardStringContext(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardStringContext"); + auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardStringContext"); v.mkString(*s); } @@ -17,7 +17,7 @@ static RegisterPrimOp primop_unsafeDiscardStringContext("__unsafeDiscardStringCo static void prim_hasContext(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.hasContext"); + state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.hasContext"); v.mkBool(!context.empty()); } @@ -33,7 +33,7 @@ static RegisterPrimOp primop_hasContext("__hasContext", 1, prim_hasContext); static void prim_unsafeDiscardOutputDependency(EvalState & state, const Pos & pos, Value * * args, Value & v) { PathSet context; - auto s = state.coerceToString(pos, *args[0], context, "While evaluating the argument passed to builtins.unsafeDiscardOutputDependency"); + auto s = state.coerceToString(pos, *args[0], context, "while evaluating the argument passed to builtins.unsafeDiscardOutputDependency"); PathSet context2; for (auto & p : context) @@ -72,7 +72,7 @@ static void prim_getContext(EvalState & state, const Pos & pos, Value * * args, Strings outputs; }; PathSet context; - state.forceString(*args[0], context, pos, "While evaluating the argument passed to builtins.getContext"); + state.forceString(*args[0], context, pos, "while evaluating the argument passed to builtins.getContext"); auto contextInfos = std::map(); for (const auto & p : context) { Path drv; @@ -138,31 +138,31 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg PathSet context; auto orig = state.forceString(*args[0], context, pos, "while evaluating the first argument passed to builtins.appendContext"); - state.forceAttrs(*args[1], pos, "While evaluating the second argument passed to builtins.appendContext"); + state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.appendContext"); auto sPath = state.symbols.create("path"); auto sAllOutputs = state.symbols.create("allOutputs"); for (auto & i : *args[1]->attrs) { if (!state.store->isStorePath(i.name)) throw EvalError({ - .msg = hintfmt("Context key '%s' is not a store path", i.name), + .msg = hintfmt("context key '%s' is not a store path", i.name), .errPos = *i.pos }); if (!settings.readOnlyMode) state.store->ensurePath(state.store->parseStorePath(i.name)); - state.forceAttrs(*i.value, *i.pos, "While evaluating the value of a string context"); + state.forceAttrs(*i.value, *i.pos, "while evaluating the value of a string context"); auto iter = i.value->attrs->find(sPath); if (iter != i.value->attrs->end()) { - if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `path` attribute of a string context")) + if (state.forceBool(*iter->value, *iter->pos, "while evaluating the `path` attribute of a string context")) context.insert(i.name); } iter = i.value->attrs->find(sAllOutputs); if (iter != i.value->attrs->end()) { - if (state.forceBool(*iter->value, *iter->pos, "While evaluating the `allOutputs` attribute of a string context")) { + if (state.forceBool(*iter->value, *iter->pos, "while evaluating the `allOutputs` attribute of a string context")) { if (!isDerivation(i.name)) { 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), .errPos = *i.pos }); } @@ -172,15 +172,15 @@ static void prim_appendContext(EvalState & state, const Pos & pos, Value * * arg iter = i.value->attrs->find(state.sOutputs); if (iter != i.value->attrs->end()) { - state.forceList(*iter->value, *iter->pos, "While evaluating the `outputs` attribute of a string context"); + state.forceList(*iter->value, *iter->pos, "while evaluating the `outputs` attribute of a string context"); if (iter->value->listSize() && !isDerivation(i.name)) { 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), .errPos = *i.pos }); } for (auto elem : iter->value->listItems()) { - auto name = state.forceStringNoCtx(*elem, *iter->pos, "While evaluating an output name within a string context"); + auto name = state.forceStringNoCtx(*elem, *iter->pos, "while evaluating an output name within a string context"); context.insert(concatStrings("!", name, "!", i.name)); } } diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index dcaf3d362..00c7e6b1d 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -22,18 +22,18 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar for (auto & attr : *args[0]->attrs) { std::string_view n(attr.name); if (n == "url") - url = state.coerceToString(*attr.pos, *attr.value, context, false, false, "While evaluating the `url` attribute passed to builtins.fetchMercurial").toOwned(); + url = state.coerceToString(*attr.pos, *attr.value, context, false, false, "while evaluating the `url` attribute passed to builtins.fetchMercurial").toOwned(); else if (n == "rev") { // Ugly: unlike fetchGit, here the "rev" attribute can // be both a revision or a branch/tag name. - auto value = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `rev` attribute passed to builtins.fetchMercurial"); + 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)) rev = Hash::parseAny(value, htSHA1); else ref = value; } else if (n == "name") - name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the `name` attribute passed to builtins.fetchMercurial"); + name = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the `name` attribute passed to builtins.fetchMercurial"); else throw EvalError({ .msg = hintfmt("unsupported argument '%s' to 'fetchMercurial'", attr.name), @@ -48,7 +48,7 @@ static void prim_fetchMercurial(EvalState & state, const Pos & pos, Value * * ar }); } else - url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to builtins.fetchMercurial").toOwned(); + url = state.coerceToString(pos, *args[0], context, false, false, "while evaluating the first argument passed to builtins.fetchMercurial").toOwned(); // FIXME: git externals probably can be used to bypass the URI // whitelist. Ah well. diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 2baa272cb..25fb8d939 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -102,7 +102,7 @@ static void fetchTree( state.forceValue(*args[0], pos); if (args[0]->type() == nAttrs) { - state.forceAttrs(*args[0], pos, "While evaluating the argument passed to builtins.fetchTree"); + state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.fetchTree"); fetchers::Attrs attrs; @@ -112,7 +112,7 @@ static void fetchTree( .msg = hintfmt("unexpected attribute 'type'"), .errPos = pos }); - type = state.forceStringNoCtx(*aType->value, *aType->pos, "While evaluating the `type` attribute passed to builtins.fetchTree"); + type = state.forceStringNoCtx(*aType->value, *aType->pos, "while evaluating the `type` attribute passed to builtins.fetchTree"); } else if (!type) throw Error({ .msg = hintfmt("attribute 'type' is missing in call to 'fetchTree'"), @@ -151,7 +151,7 @@ static void fetchTree( input = fetchers::Input::fromAttrs(std::move(attrs)); } else { - auto url = state.coerceToString(pos, *args[0], context, false, false, "While evaluating the first argument passed to the fetcher").toOwned(); + auto url = state.coerceToString(pos, *args[0], context, false, false, "while evaluating the first argument passed to the fetcher").toOwned(); if (type == "git") { fetchers::Attrs attrs; @@ -198,11 +198,11 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, for (auto & attr : *args[0]->attrs) { std::string n(attr.name); if (n == "url") - url = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the url we should fetch"); + url = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the url we should fetch"); else if (n == "sha256") - expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the sha256 of the content we should fetch"), htSHA256); + expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the sha256 of the content we should fetch"), htSHA256); else if (n == "name") - name = state.forceStringNoCtx(*attr.value, *attr.pos, "While evaluating the name of the content we should fetch"); + name = state.forceStringNoCtx(*attr.value, *attr.pos, "while evaluating the name of the content we should fetch"); else throw EvalError({ .msg = hintfmt("unsupported argument '%s' to '%s'", attr.name, who), @@ -216,7 +216,7 @@ static void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v, .errPos = pos }); } else - url = state.forceStringNoCtx(*args[0], pos, "While evaluating the url we should fetch"); + url = state.forceStringNoCtx(*args[0], pos, "while evaluating the url we should fetch"); url = resolveUri(*url); diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 6bfd8e285..336cfe5cc 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -7,7 +7,7 @@ namespace nix { static void prim_fromTOML(EvalState & state, const Pos & pos, Value * * args, Value & val) { - auto toml = state.forceStringNoCtx(*args[0], pos, "While evaluating the argument passed to builtins.fromTOML"); + auto toml = state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.fromTOML"); std::istringstream tomlStream(std::string{toml}); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3cf764a20..e835b8052 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -126,12 +126,12 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, std::function callback) { auto pos = vFlake.determinePos(noPos); - state.forceAttrs(vFlake, pos, "While evaluating a flake to get its outputs"); + state.forceAttrs(vFlake, pos, "while evaluating a flake to get its outputs"); auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs")); assert(aOutputs); - state.forceAttrs(*aOutputs->value, pos, "While evaluating the outputs of a flake"); + state.forceAttrs(*aOutputs->value, pos, "while evaluating the outputs of a flake"); auto sHydraJobs = state.symbols.create("hydraJobs"); diff --git a/src/nix/main.cc b/src/nix/main.cc index 0c6286686..0dc14e2e9 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -196,7 +196,7 @@ static void showHelp(std::vector subcommand, MultiCommand & topleve if (!attr) throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); - auto markdown = state.forceString(*attr->value, noPos, "While evaluating the lowdown help text"); + auto markdown = state.forceString(*attr->value, noPos, "while evaluating the lowdown help text"); RunPager pager; std::cout << renderMarkdownToTerminal(markdown) << "\n"; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 4376da896..782149575 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -28,17 +28,17 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url) Value vMirrors; // FIXME: use nixpkgs flake state.eval(state.parseExprFromString("import ", "."), vMirrors); - state.forceAttrs(vMirrors, noPos, "While evaluating the set of all mirrors"); + state.forceAttrs(vMirrors, noPos, "while evaluating the set of all mirrors"); auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); if (mirrorList == vMirrors.attrs->end()) throw Error("unknown mirror name '%s'", mirrorName); - state.forceList(*mirrorList->value, noPos, "While evaluating this mirror configuration"); + state.forceList(*mirrorList->value, noPos, "while evaluating one mirror configuration"); if (mirrorList->value->listSize() < 1) throw Error("mirror URL '%s' did not expand to anything", url); - std::string mirror(state.forceString(*mirrorList->value->listElems()[0], noPos, "While evaluating the first available mirror")); + std::string mirror(state.forceString(*mirrorList->value->listElems()[0], noPos, "while evaluating the first available mirror")); return mirror + (hasSuffix(mirror, "/") ? "" : "/") + s.substr(p + 1); } @@ -196,27 +196,27 @@ static int main_nix_prefetch_url(int argc, char * * argv) Value vRoot; state->evalFile(path, vRoot); Value & v(*findAlongAttrPath(*state, attrPath, autoArgs, vRoot).first); - state->forceAttrs(v, noPos, "While evaluating the source attribute to prefetch"); + state->forceAttrs(v, noPos, "while evaluating the source attribute to prefetch"); /* Extract the URL. */ auto & attr = v.attrs->need(state->symbols.create("urls")); - state->forceList(*attr.value, noPos, "While evaluating the urls to prefetch"); + state->forceList(*attr.value, noPos, "while evaluating the urls to prefetch"); if (attr.value->listSize() < 1) throw Error("'urls' list is empty"); - url = state->forceString(*attr.value->listElems()[0], noPos, "While evaluating the first url from the urls list"); + url = state->forceString(*attr.value->listElems()[0], noPos, "while evaluating the first url from the urls list"); /* Extract the hash mode. */ auto attr2 = v.attrs->get(state->symbols.create("outputHashMode")); if (!attr2) printInfo("warning: this does not look like a fetchurl call"); else - unpack = state->forceString(*attr2->value, noPos, "While evaluating the outputHashMode of the source to prefetch") == "recursive"; + unpack = state->forceString(*attr2->value, noPos, "while evaluating the outputHashMode of the source to prefetch") == "recursive"; /* Extract the name. */ if (!name) { auto attr3 = v.attrs->get(state->symbols.create("name")); if (!attr3) - name = state->forceString(*attr3->value, noPos, "While evaluating the name of the source to prefetch"); + name = state->forceString(*attr3->value, noPos, "while evaluating the name of the source to prefetch"); } } diff --git a/src/nix/repl.cc b/src/nix/repl.cc index b4a029ecc..7f7912de3 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -462,7 +462,7 @@ bool NixRepl::processLine(std::string line) if (v.type() == nPath || v.type() == nString) { PathSet context; - auto filename = state->coerceToString(noPos, v, context, "While evaluating the filename to edit"); + auto filename = state->coerceToString(noPos, v, context, "while evaluating the filename to edit"); pos.file = state->symbols.create(*filename); } else if (v.isLambda()) { pos = v.lambda.fun->pos; @@ -683,7 +683,7 @@ void NixRepl::reloadFiles() void NixRepl::addAttrsToScope(Value & attrs) { - state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "While evaluating an attribute set to be merged in the global scope"); + 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) throw Error("environment full; cannot add more variables"); @@ -788,7 +788,7 @@ std::ostream & NixRepl::printValue(std::ostream & str, Value & v, unsigned int m Bindings::iterator i = v.attrs->find(state->sDrvPath); PathSet context; if (i != v.attrs->end()) - str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context, "While evaluating the drvPath of a derivation")); + str << state->store->printStorePath(state->coerceToStorePath(*i->pos, *i->value, context, "while evaluating the drvPath of a derivation")); else str << "???"; str << "»"; diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 1f433a199..0c317de16 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -144,7 +144,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand Bindings & bindings(*state->allocBindings(0)); auto v2 = findAlongAttrPath(*state, settings.thisSystem, bindings, *v).first; - return store->parseStorePath(state->forceString(*v2, noPos, "While evaluating the path tho latest nix version")); + return store->parseStorePath(state->forceString(*v2, noPos, "while evaluating the path tho latest nix version")); } }; From 5ef88457b8bdef957fcbb3cd0e7740595fad1949 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 28 Apr 2022 13:00:24 +0200 Subject: [PATCH 19/39] Better document error location indent --- src/libutil/error.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libutil/error.cc b/src/libutil/error.cc index c66dfa112..7dc8c1941 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -180,20 +180,24 @@ void printCodeLines(std::ostream & out, } } +// Enough indent to align with with the `... ` +// prepended to each element of the trace +#define ELLIPSIS_INDENT " " + void printAtPos(const ErrPos & pos, std::ostream & out) { if (pos) { switch (pos.origin) { case foFile: { - out << fmt(ANSI_BLUE " at " ANSI_WARNING "%s:%s" ANSI_NORMAL ":", pos.file, showErrPos(pos)); + out << fmt(ELLIPSIS_INDENT "at " ANSI_WARNING "%s:%s" ANSI_NORMAL ":", pos.file, showErrPos(pos)); break; } case foString: { - out << fmt(ANSI_BLUE " at " ANSI_WARNING "«string»:%s" ANSI_NORMAL ":", showErrPos(pos)); + out << fmt(ELLIPSIS_INDENT "at " ANSI_WARNING "«string»:%s" ANSI_NORMAL ":", showErrPos(pos)); break; } case foStdin: { - out << fmt(ANSI_BLUE " at " ANSI_WARNING "«stdin»:%s" ANSI_NORMAL ":", showErrPos(pos)); + out << fmt(ELLIPSIS_INDENT "at " ANSI_WARNING "«stdin»:%s" ANSI_NORMAL ":", showErrPos(pos)); break; } default: From 402ee8ab64f9b11989cbdcf53f8ca513cb25e23f Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 28 Apr 2022 13:02:39 +0200 Subject: [PATCH 20/39] No point in passing string_views by reference --- src/libexpr/eval-inline.hh | 6 +++--- src/libexpr/eval.hh | 34 +++++++++++++++++----------------- src/libexpr/primops.cc | 2 +- src/libexpr/value.hh | 2 +- src/libutil/error.cc | 4 ++-- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 77ed07b2e..025459c00 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -128,7 +128,7 @@ void EvalState::forceValue(Value & v, Callable getPos) [[gnu::always_inline]] -inline void EvalState::forceAttrs(Value & v, const Pos & pos, const std::string_view & errorCtx) +inline void EvalState::forceAttrs(Value & v, const Pos & pos, std::string_view errorCtx) { forceAttrs(v, [&]() { return pos; }, errorCtx); } @@ -136,7 +136,7 @@ inline void EvalState::forceAttrs(Value & v, const Pos & pos, const std::string_ template [[gnu::always_inline]] -inline void EvalState::forceAttrs(Value & v, Callable getPos, const std::string_view & errorCtx) +inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view errorCtx) { try { forceValue(v, noPos); @@ -152,7 +152,7 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos, const std::string_ [[gnu::always_inline]] -inline void EvalState::forceList(Value & v, const Pos & pos, const std::string_view & errorCtx) +inline void EvalState::forceList(Value & v, const Pos & pos, std::string_view errorCtx) { try { forceValue(v, noPos); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 18dbfc5a4..fd0961bdc 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -222,8 +222,8 @@ public: /* Evaluation the expression, then verify that it has the expected type. */ - inline bool evalBool(Env & env, Expr * e, const Pos & pos, const std::string_view & errorCtx); - inline void evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string_view & errorCtx); + inline bool evalBool(Env & env, Expr * e, const Pos & pos, std::string_view errorCtx); + inline void evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, std::string_view errorCtx); /* If `v' is a thunk, enter it and overwrite `v' with the result of the evaluation of the thunk. If `v' is a delayed function @@ -239,20 +239,20 @@ public: void forceValueDeep(Value & v); /* Force `v', and then verify that it has the expected type. */ - NixInt forceInt(Value & v, const Pos & pos, const std::string_view & errorCtx); - NixFloat forceFloat(Value & v, const Pos & pos, const std::string_view & errorCtx); - bool forceBool(Value & v, const Pos & pos, const std::string_view & errorCtx); + NixInt forceInt(Value & v, const Pos & pos, std::string_view errorCtx); + NixFloat forceFloat(Value & v, const Pos & pos, std::string_view errorCtx); + bool forceBool(Value & v, const Pos & pos, std::string_view errorCtx); - void forceAttrs(Value & v, const Pos & pos, const std::string_view & errorCtx); + void forceAttrs(Value & v, const Pos & pos, std::string_view errorCtx); template - inline void forceAttrs(Value & v, Callable getPos, const std::string_view & errorCtx); + inline void forceAttrs(Value & v, Callable getPos, std::string_view errorCtx); - inline void forceList(Value & v, const Pos & pos, const std::string_view & errorCtx); - void forceFunction(Value & v, const Pos & pos, const std::string_view & errorCtx); // either lambda or primop - std::string_view forceString(Value & v, const Pos & pos, const std::string_view & errorCtx); - std::string_view forceString(Value & v, PathSet & context, const Pos & pos, const std::string_view & errorCtx); - std::string_view forceStringNoCtx(Value & v, const Pos & pos, const std::string_view & errorCtx); + inline void forceList(Value & v, const Pos & pos, std::string_view errorCtx); + void forceFunction(Value & v, const Pos & pos, std::string_view errorCtx); // either lambda or primop + std::string_view forceString(Value & v, const Pos & pos, std::string_view errorCtx); + std::string_view forceString(Value & v, PathSet & context, const Pos & pos, std::string_view errorCtx); + std::string_view forceStringNoCtx(Value & v, const Pos & pos, std::string_view errorCtx); /* Return true iff the value `v' denotes a derivation (i.e. a set with attribute `type = "derivation"'). */ @@ -268,17 +268,17 @@ public: BackedStringView coerceToString(const Pos & pos, Value & v, PathSet & context, bool coerceMore = false, bool copyToStore = true, bool canonicalizePath = true, - const std::string_view & errorCtx = ""); + std::string_view errorCtx = ""); std::string copyPathToStore(PathSet & context, const Path & path); /* Path coercion. Converts strings, paths and derivations to a path. The result is guaranteed to be a canonicalised, absolute path. Nothing is copied to the store. */ - Path coerceToPath(const Pos & pos, Value & v, PathSet & context, const std::string_view & errorCtx); + Path coerceToPath(const Pos & pos, Value & v, PathSet & context, std::string_view errorCtx); /* Like coerceToPath, but the result must be a store path. */ - StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context, const std::string_view & errorCtx); + StorePath coerceToStorePath(const Pos & pos, Value & v, PathSet & context, std::string_view errorCtx); public: @@ -334,7 +334,7 @@ public: /* Do a deep equality test between two values. That is, list elements and attributes are compared recursively. */ - bool eqValues(Value & v1, Value & v2, const Pos & pos, const std::string_view & errorCtx); + bool eqValues(Value & v1, Value & v2, const Pos & pos, std::string_view errorCtx); bool isFunctor(Value & fun); @@ -369,7 +369,7 @@ public: void mkThunk_(Value & v, Expr * expr); void mkPos(Value & v, ptr pos); - void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos, const std::string_view & errorCtx); + void concatLists(Value & v, size_t nrLists, Value * * lists, const Pos & pos, std::string_view errorCtx); /* Print statistics. */ void printStats(); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index f99ab8ca8..9360c2102 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -552,7 +552,7 @@ struct CompareValues return (*this)(v1, v2, errorCtx); } - bool operator () (Value * v1, Value * v2, const std::string_view & errorCtx) const + bool operator () (Value * v1, Value * v2, std::string_view errorCtx) const { try { if (v1->type() == nFloat && v2->type() == nInt) diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 403e38258..18e4218c5 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -85,7 +85,7 @@ class ExternalValueBase /* Coerce the value to a string. Defaults to uncoercable, i.e. throws an * error. */ - virtual std::string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, const std::string_view & errorCtx) const; + virtual std::string coerceToString(const Pos & pos, PathSet & context, bool copyMore, bool copyToStore, std::string_view errorCtx) const; /* Compare to another value of the same type. Defaults to uncomparable, * i.e. always false. diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 7dc8c1941..3644cada9 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -347,7 +347,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s * to make a decision between the two following options. * * ``` long traces - * inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string_view & errorCtx) + * inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, std::string_view errorCtx) * { * try { * e->eval(*this, env, v); @@ -361,7 +361,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s * ``` * * ``` short traces - * inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, const std::string_view & errorCtx) + * inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const Pos & pos, std::string_view errorCtx) * { * e->eval(*this, env, v); * try { From f6baa4d18845297f3f7fc2434b7ade93a45718e7 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 28 Apr 2022 13:18:19 +0200 Subject: [PATCH 21/39] fixup! fix errors case and wording --- src/libexpr/eval.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 1feb0bf6f..6d9129bd3 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -753,7 +753,7 @@ LocalNoInlineNoReturn(void throwEvalErrorWithTrace(const char * s, const std::st { auto e = EvalError(ErrorInfo { .msg = hintfmt(s, s2), - }) + }); e.addTrace(p2, s3); throw e; } From 9ff892aad4ee13532ea84cb6e4a2a53d70945efe Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 29 Apr 2022 11:24:48 +0200 Subject: [PATCH 22/39] Add release notes for error traces revamp --- doc/manual/src/release-notes/rl-next.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md index 7b3ad4e58..f312f2f86 100644 --- a/doc/manual/src/release-notes/rl-next.md +++ b/doc/manual/src/release-notes/rl-next.md @@ -14,3 +14,7 @@ * `nix build` has a new `--print-out-paths` flag to print the resulting output paths. This matches the default behaviour of `nix-build`. + +* Error traces have been reworked to provide detailed explanations and more + accurate error locations. A short excerpt of the trace is now shown by + default when an error occurs. From c7b901fd33e817bfd8306a0e1b4f021ee3a209c9 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Sun, 11 Sep 2022 01:34:19 +0200 Subject: [PATCH 23/39] Cleanup error strings rebase --- .gitignore | 1 + src/libexpr/eval-cache.cc | 18 +- src/libexpr/eval-inline.hh | 24 +- src/libexpr/eval.cc | 442 ++++++++----------------------------- src/libexpr/eval.hh | 155 +++---------- src/libexpr/get-drvs.cc | 2 +- src/libexpr/parser.y | 6 +- src/libexpr/primops.cc | 15 +- 8 files changed, 154 insertions(+), 509 deletions(-) diff --git a/.gitignore b/.gitignore index 0c1b89ace..174ea95e0 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,4 @@ nix-rust/target result .vscode/ +baseline diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 1a37f87f3..89afac0e8 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -571,14 +571,14 @@ std::string AttrCursor::getString() debug("using cached string attribute '%s'", getAttrPathStr()); return s->first; } else - root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr())); + throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr())); } } auto & v = forceValue(); if (v.type() != nString && v.type() != nPath) - root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()))); + throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()))); return v.type() == nString ? v.string.s : v.path; } @@ -602,7 +602,7 @@ string_t AttrCursor::getStringWithContext() return *s; } } else - root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr())); + throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr())); } } @@ -613,7 +613,7 @@ string_t AttrCursor::getStringWithContext() else if (v.type() == nPath) return {v.path, {}}; else - root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()))); + throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()))); } bool AttrCursor::getBool() @@ -626,14 +626,14 @@ bool AttrCursor::getBool() debug("using cached Boolean attribute '%s'", getAttrPathStr()); return *b; } else - root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr())); + throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr())); } } auto & v = forceValue(); if (v.type() != nBool) - root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr())); + throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr())); return v.boolean; } @@ -685,7 +685,7 @@ std::vector AttrCursor::getListOfStrings() std::vector res; for (auto & elem : v.listItems()) - res.push_back(std::string(root->state.forceStringNoCtx(*elem))); + res.push_back(std::string(root->state.forceStringNoCtx(*elem, noPos, "while evaluating an attribute for caching"))); if (root->db) cachedValue = {root->db->setListOfStrings(getKey(), res), res}; @@ -703,14 +703,14 @@ std::vector AttrCursor::getAttrs() debug("using cached attrset attribute '%s'", getAttrPathStr()); return *attrs; } else - root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr())); + throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr())); } } auto & v = forceValue(); if (v.type() != nAttrs) - root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr())); + throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr())); std::vector attrs; for (auto & attr : *getValue().attrs) diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 5997525e4..43a4e8fa6 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -103,7 +103,7 @@ void EvalState::forceValue(Value & v, Callable getPos) else if (v.isApp()) callFunction(*v.app.left, *v.app.right, v, noPos); else if (v.isBlackhole()) - throwEvalError(getPos(), "infinite recursion encountered"); + throwError(getPos(), "infinite recursion encountered", "", "", 0, 0, 0, 0, noPos, "", 0, 0, 0); } @@ -118,15 +118,10 @@ template [[gnu::always_inline]] inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view errorCtx) { - try { - forceValue(v, noPos); - if (v.type() != nAttrs) { - throwTypeError(noPos, "value is %1% while a set was expected", v); - } - } catch (Error & e) { + forceValue(v, noPos); + if (v.type() != nAttrs) { PosIdx pos = getPos(); - e.addTrace(positions[pos], errorCtx); - throw; + this->throwErrorWithTrace(noPos, "value is %1% while a set was expected", "", "", 0, 0, &v, 0, noPos, "", 0, pos, errorCtx, 0, 0); } } @@ -134,14 +129,9 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view e [[gnu::always_inline]] inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view errorCtx) { - try { - forceValue(v, noPos); - if (!v.isList()) { - throwTypeError(noPos, "value is %1% while a list was expected", v); - } - } catch (Error & e) { - e.addTrace(positions[pos], errorCtx); - throw; + forceValue(v, noPos); + if (!v.isList()) { + this->throwErrorWithTrace(noPos, "value is %1% while a list was expected", "", "", 0, 0, &v, 0, noPos, "", 0, pos, errorCtx, 0, 0); } } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 46e5dc41e..646e19bad 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -836,327 +836,64 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & evaluator. So here are some helper functions for throwing exceptions. */ -// *WithTrace -void EvalState::throwTypeErrorWithTrace( - const PosIdx pos, - const char * s, - const std::string_view s2, - const Symbol & sym, - const PosIdx p2, - const std::string_view s3) const +template +void EvalState::throwErrorWithTrace( + PosIdx pos, const char* format, + const std::string_view s1, const std::string_view s2, + const Symbol * sym1, const Symbol * sym2, + Value * val1, Value * val2, + PosIdx pos1, + const std::string_view s3, + const Suggestions * suggestions, + PosIdx tracePos, const std::string_view traceStr, + Env * env, Expr * expr) { - auto e = TypeError(ErrorInfo { - .msg = hintfmt(s, s2, symbols[sym]), + hintformat f(format); + if (!s1.empty()) { f = f % s1; } + if (!s2.empty()) { f = f % s2; } + if (sym1) { f = f % symbols[*sym1]; } + if (sym2) { f = f % symbols[*sym2]; } + if (val1) { f = f % showType(*val1); } + if (val2) { f = f % showType(*val2); } + if (pos1) { f = f % positions[pos1]; } + if (!s3.empty()) { f = f % s3; } + + auto e = ErrorType(ErrorInfo { + .msg = f, .errPos = positions[pos], + .suggestions = suggestions ? *suggestions : Suggestions(), }); - e.addTrace(positions[p2], s3); - throw e; + e.addTrace(positions[tracePos], traceStr); + debugThrow(e, env, expr); } -void EvalState::throwTypeErrorWithTrace( - const PosIdx pos, - const Suggestions & suggestions, - const char * s, - const std::string_view s2, - const Symbol & sym, - const PosIdx p2, - const std::string_view s3) const +template +void EvalState::throwError( + PosIdx pos, const char* format, + const std::string_view s1, const std::string_view s2, + const Symbol * sym1, const Symbol * sym2, + Value * val1, Value * val2, + PosIdx pos1, + const std::string_view s3, + const Suggestions * suggestions, + Env * env, Expr * expr) { - auto e = TypeError(ErrorInfo { - .msg = hintfmt(s, s2, symbols[sym]), + hintformat f(format); + if (!s1.empty()) { f = f % s1; } + if (!s2.empty()) { f = f % s2; } + if (sym1) { f = f % symbols[*sym1]; } + if (sym2) { f = f % symbols[*sym2]; } + if (val1) { f = f % showType(*val1); } + if (val2) { f = f % showType(*val2); } + if (pos1) { f = f % positions[pos1]; } + if (!s3.empty()) { f = f % s3; } + + auto e = ErrorType(ErrorInfo { + .msg = f, .errPos = positions[pos], - .suggestions = suggestions + .suggestions = suggestions ? *suggestions : Suggestions(), }); - e.addTrace(positions[p2], s3); - throw e; -} - -void EvalState::throwTypeErrorWithTrace(const char * s, const std::string_view s2, const PosIdx p2, const std::string_view s3) const -{ - auto e = TypeError(ErrorInfo { - .msg = hintfmt(s, s2), - }); - e.addTrace(positions[p2], s3); - throw e; -} - -void EvalState::throwEvalErrorWithTrace(const char * s, const std::string_view s2, const PosIdx p2, const std::string_view s3) const -{ - auto e = EvalError(ErrorInfo { - .msg = hintfmt(s, s2), - }); - e.addTrace(positions[p2], s3); - throw e; -} - -void EvalState::throwEvalErrorWithTrace(const char * s, const std::string_view s2, const std::string_view s3, const PosIdx p2, const std::string_view s4) const -{ - auto e = EvalError(ErrorInfo { - .msg = hintfmt(s, s2, s3), - }); - e.addTrace(positions[p2], s4); - throw e; -} - - -// *WithoutTrace coerce-strings - -void EvalState::throwEvalError(const PosIdx pos, const char * s, Env & env, Expr & expr) -{ - debugThrow(EvalError({ - .msg = hintfmt(s), - .errPos = positions[pos] - }), env, expr); -} - -void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, const std::string_view s2) const -{ - throw EvalError(ErrorInfo { - .msg = hintfmt(s, s2), - .errPos = positions[pos], - .suggestions = suggestions, - }); -} - -void EvalState::throwEvalError(const PosIdx pos, const char * s) const -{ - throw EvalError(ErrorInfo { - .msg = hintfmt(s), - .errPos = positions[pos] - }); -} - -void EvalState::throwEvalError(const PosIdx pos, const char * s, const Value & v) const -{ - throw EvalError(ErrorInfo { - .msg = hintfmt(s, showType(v)), - .errPos = positions[pos] - }); -} - -void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string_view s2) const -{ - throw EvalError(ErrorInfo { - .msg = hintfmt(s, s2), - .errPos = positions[pos] - }); -} - -void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2) const -{ - // p1 is where the error occurred; p2 is a position mentioned in the message. - throw EvalError(ErrorInfo { - .msg = hintfmt(s, symbols[sym], positions[p2]), - .errPos = positions[p1] - }); -} - -void EvalState::throwEvalError(const char * s, const std::string_view s1) const -{ - throw EvalError(s, s1); -} - -void EvalState::throwEvalError(const char * s, const std::string_view s1, const std::string_view s2) const -{ - throw EvalError(s, s1, s2); -} - -void EvalState::throwTypeError(const char * s, const Value & v) const -{ - throw TypeError(s, showType(v)); -} - -void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v) const -{ - throw TypeError(ErrorInfo { - .msg = hintfmt(s, showType(v)), - .errPos = positions[pos] - }); -} - -void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const -{ - throw AssertionError({ - .msg = hintfmt(s, s1), - .errPos = positions[pos] - }); -} - -void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const -{ - throw UndefinedVarError({ - .msg = hintfmt(s, s1), - .errPos = positions[pos] - }); -} - -void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const -{ - throw MissingArgumentError({ - .msg = hintfmt(s, s1), - .errPos = positions[pos] - }); -} - - -// master - -void EvalState::throwEvalError(const PosIdx pos, const char * s, Env & env, Expr & expr) -{ - debugThrow(EvalError({ - .msg = hintfmt(s), - .errPos = positions[pos] - }), env, expr); -} - -void EvalState::throwEvalError(const PosIdx pos, const char * s) -{ - debugThrowLastTrace(EvalError({ - .msg = hintfmt(s), - .errPos = positions[pos] - })); -} - -void EvalState::throwEvalError(const char * s, const std::string & s2) -{ - debugThrowLastTrace(EvalError(s, s2)); -} - -void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, - const std::string & s2, Env & env, Expr & expr) -{ - debugThrow(EvalError(ErrorInfo{ - .msg = hintfmt(s, s2), - .errPos = positions[pos], - .suggestions = suggestions, - }), env, expr); -} - -void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2) -{ - debugThrowLastTrace(EvalError({ - .msg = hintfmt(s, s2), - .errPos = positions[pos] - })); -} - -void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2, Env & env, Expr & expr) -{ - debugThrow(EvalError({ - .msg = hintfmt(s, s2), - .errPos = positions[pos] - }), env, expr); -} - -void EvalState::throwEvalError(const char * s, const std::string & s2, - const std::string & s3) -{ - debugThrowLastTrace(EvalError({ - .msg = hintfmt(s, s2), - .errPos = positions[noPos] - })); -} - -void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2, - const std::string & s3) -{ - debugThrowLastTrace(EvalError({ - .msg = hintfmt(s, s2), - .errPos = positions[pos] - })); -} - -void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2, - const std::string & s3, Env & env, Expr & expr) -{ - debugThrow(EvalError({ - .msg = hintfmt(s, s2), - .errPos = positions[pos] - }), env, expr); -} - -void EvalState::throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2, Env & env, Expr & expr) -{ - // p1 is where the error occurred; p2 is a position mentioned in the message. - debugThrow(EvalError({ - .msg = hintfmt(s, symbols[sym], positions[p2]), - .errPos = positions[p1] - }), env, expr); -} - -void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v) -{ - debugThrowLastTrace(TypeError({ - .msg = hintfmt(s, showType(v)), - .errPos = positions[pos] - })); -} - -void EvalState::throwTypeError(const PosIdx pos, const char * s, const Value & v, Env & env, Expr & expr) -{ - debugThrow(TypeError({ - .msg = hintfmt(s, showType(v)), - .errPos = positions[pos] - }), env, expr); -} - -void EvalState::throwTypeError(const PosIdx pos, const char * s) -{ - debugThrowLastTrace(TypeError({ - .msg = hintfmt(s), - .errPos = positions[pos] - })); -} - -void EvalState::throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, - const Symbol s2, Env & env, Expr &expr) -{ - debugThrow(TypeError({ - .msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]), - .errPos = positions[pos] - }), env, expr); -} - -void EvalState::throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, - const ExprLambda & fun, const Symbol s2, Env & env, Expr &expr) -{ - debugThrow(TypeError(ErrorInfo { - .msg = hintfmt(s, fun.showNamePos(*this), symbols[s2]), - .errPos = positions[pos], - .suggestions = suggestions, - }), env, expr); -} - -void EvalState::throwTypeError(const char * s, const Value & v, Env & env, Expr &expr) -{ - debugThrow(TypeError({ - .msg = hintfmt(s, showType(v)), - .errPos = positions[expr.getPos()], - }), env, expr); -} - -void EvalState::throwAssertionError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) -{ - debugThrow(AssertionError({ - .msg = hintfmt(s, s1), - .errPos = positions[pos] - }), env, expr); -} - -void EvalState::throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) -{ - debugThrow(UndefinedVarError({ - .msg = hintfmt(s, s1), - .errPos = positions[pos] - }), env, expr); -} - -void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1, Env & env, Expr &expr) -{ - debugThrow(MissingArgumentError({ - .msg = hintfmt(s, s1), - .errPos = positions[pos] - }), env, expr); + debugThrow(e, env, expr); } void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const @@ -1253,7 +990,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) return j->value; } if (!env->prevWith) - throwUndefinedVarError(var.pos, "undefined variable '%1%'", symbols[var.name], *env, const_cast(var)); + throwError(var.pos, "undefined variable '%1%'", "", "", &var.name, 0, 0, 0, noPos, "", 0, env, const_cast(&var)); for (size_t l = env->prevWith; l; --l, env = env->up) ; } } @@ -1403,7 +1140,7 @@ void EvalState::cacheFile( // computation. if (mustBeTrivial && !(dynamic_cast(e))) - throwEvalError("file '%s' must be an attribute set", path); + throwError(noPos, "file '%s' must be an attribute set", path, "", 0, 0, 0, 0, noPos, "", 0, 0, 0); eval(e, v); } catch (Error & e) { addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath); @@ -1427,7 +1164,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri Value v; e->eval(*this, env, v); if (v.type() != nBool) - throwTypeError("value is %1% while a Boolean was expected", v); + throwError(pos, "value is %1% while a Boolean was expected", "", "", 0, 0, &v, 0, noPos, "", 0, &env, e); return v.boolean; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -1441,7 +1178,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx po try { e->eval(*this, env, v); if (v.type() != nAttrs) - throwTypeError("value is %1% while a set was expected", v, env, *e); + throwError(pos, "value is %1% while a set was expected", "", "", 0, 0, &v, 0, noPos, "", 0, &env, e); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -1550,7 +1287,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) auto nameSym = state.symbols.create(nameVal.string.s); Bindings::iterator j = v.attrs->find(nameSym); if (j != v.attrs->end()) - state.throwEvalError(i.pos, "dynamic attribute '%1%' already defined at %2%", nameSym, j->pos, env, *this); + state.throwError(i.pos, "dynamic attribute '%1%' already defined at %2%", "", "", &nameSym, 0, 0, 0, j->pos, "", 0, &env, this); i.valueExpr->setName(nameSym); /* Keep sorted order so find can catch duplicates */ @@ -1652,10 +1389,11 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) std::set allAttrNames; for (auto & attr : *vAttrs->attrs) allAttrNames.insert(state.symbols[attr.name]); - state.throwEvalError( - pos, - Suggestions::bestMatches(allAttrNames, state.symbols[name]), - "attribute '%1%' missing", state.symbols[name], env, *this); + throw EvalError("foo"); + //tmp: state.throwEvalError( + // pos, + // Suggestions::bestMatches(allAttrNames, state.symbols[name]), + // "attribute '%1%' missing", state.symbols[name], env, *this); } } vAttrs = j->value; @@ -1750,7 +1488,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & try { forceAttrs(*args[0], lambda.pos, "while evaluating the value passed for the lambda argument"); } catch (Error & e) { - e.addTrace(positions[pos], "from call site"); + if (pos) e.addTrace(positions[pos], "from call site"); throw; } @@ -1765,10 +1503,13 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & auto j = args[0]->attrs->get(i.name); if (!j) { if (!i.def) { + throw EvalError("foo"); + /* throwTypeErrorWithTrace(lambda.pos, "function '%1%' called without required argument '%2%'", (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), i.name, pos, "from call site", *fun.lambda.env, lambda); + */ } env2.values[displ++] = i.def->maybeThunk(*this, env2); } else { @@ -1787,11 +1528,14 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & std::set formalNames; for (auto & formal : lambda.formals->formals) formalNames.insert(symbols[formal.name]); + throw EvalError("foo"); + /* throwTypeErrorWithTrace(lambda.pos, Suggestions::bestMatches(formalNames, symbols[i.name]), "function '%1%' called with unexpected argument '%2%'", (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), i.name, pos, "from call site"); + */ } abort(); // can't happen } @@ -1816,7 +1560,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & if (loggerSettings.showTrace.get()) { addErrorTrace(e, lambda.pos, "while evaluating the '%s' function", (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda")); - addErrorTrace(e, pos, "from call site%s", ""); + if (pos) addErrorTrace(e, pos, "from call site%s", ""); } throw; } @@ -1841,7 +1585,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & if (countCalls) primOpCalls[name]++; try { - vCur.primOp->fun(*this, pos, args, vCur); + vCur.primOp->fun(*this, noPos, args, vCur); } catch (Error & e) { addErrorTrace(e, pos, "while calling the '%1%' builtin", name); throw; @@ -1885,6 +1629,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & if (countCalls) primOpCalls[name]++; try { + // TODO: + // 1. Unify this and above code. Heavily redundant. + // 2. Create a fake env (arg1, arg2, etc.) and a fake expr (arg1: arg2: etc: builtins.name arg1 arg2 etc) + // so the debugger allows to inspect the wrong parameters passed to the builtin. primOp->primOp->fun(*this, noPos, vArgs, vCur); } catch (Error & e) { addErrorTrace(e, pos, "while calling the '%1%' builtin", name); @@ -1913,7 +1661,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } else - throwTypeError(pos, "attempt to call something which is not a function but %1%", vCur); + throwError(pos, "attempt to call something which is not a function but %1%", "", "", 0, 0, &vCur, 0, noPos, "", 0, 0, 0); } vRes = vCur; @@ -1977,13 +1725,12 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) if (j != args.end()) { attrs.insert(*j); } else if (!i.def) { - throwMissingArgumentError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%') - + throwError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%') Nix attempted to evaluate a function as a top level expression; in this case it must have its arguments supplied either by default values, or passed explicitly with '--arg' or '--argstr'. See -https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions.)", symbols[i.name], - *fun.lambda.env, *fun.lambda.fun); +https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions.)", "", "", &i.name, 0, 0, 0, noPos, "", 0, + fun.lambda.env, fun.lambda.fun); } } } @@ -2016,7 +1763,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v) if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) { std::ostringstream out; cond->show(state.symbols, out); - state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this); + //tmp: state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this); } body->eval(state, env, v); } @@ -2193,14 +1940,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) nf = n; nf += vTmp.fpoint; } else - state.throwEvalError(i_pos, "cannot add %1% to an integer", showType(vTmp), env, *this); + state.throwError(i_pos, "cannot add %1% to an integer", "", "", 0, 0, &vTmp, 0, noPos, "", 0, &env, this); } else if (firstType == nFloat) { if (vTmp.type() == nInt) { nf += vTmp.integer; } else if (vTmp.type() == nFloat) { nf += vTmp.fpoint; } else - state.throwEvalError(i_pos, "cannot add %1% to a float", showType(vTmp), env, *this); + state.throwError(i_pos, "cannot add %1% to a float", "", "", 0, 0, &vTmp, 0, noPos, "", 0, &env, this); } else { if (s.empty()) s.reserve(es->size()); /* skip canonization of first path, which would only be not @@ -2220,7 +1967,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) v.mkFloat(nf); else if (firstType == nPath) { if (!context.empty()) - state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path", env, *this); + state.throwError(pos, "a string that refers to a store path cannot be appended to a path", "", "", 0, 0, 0, 0, noPos, "", 0, &env, this); v.mkPath(canonPath(str())); } else v.mkStringMove(c_str(), context); @@ -2275,7 +2022,7 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCt try { forceValue(v, pos); if (v.type() != nInt) - throwTypeError("value is %1% while an integer was expected", v); + throwError(noPos, "value is %1% while an integer was expected", "", "", 0, 0, &v, 0, noPos, "", 0, 0, 0); return v.integer; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2291,7 +2038,7 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view err if (v.type() == nInt) return v.integer; else if (v.type() != nFloat) - throwTypeError("value is %1% while a float was expected", v); + throwError(noPos, "value is %1% while a float was expected", "", "", 0, 0, &v, 0, noPos, "", 0, 0, 0); return v.fpoint; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2305,7 +2052,7 @@ bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx try { forceValue(v, pos); if (v.type() != nBool) - throwTypeError("value is %1% while a Boolean was expected", v); + throwError(noPos, "value is %1% while a Boolean was expected", "", "", 0, 0, &v, 0, noPos, "", 0, 0, 0); return v.boolean; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2325,7 +2072,7 @@ void EvalState::forceFunction(Value & v, const PosIdx pos, std::string_view erro try { forceValue(v, pos); if (v.type() != nFunction && !isFunctor(v)) - throwTypeError("value is %1% while a function was expected", v); + throwError(noPos, "value is %1% while a function was expected", "", "", 0, 0, &v, 0, noPos, "", 0, 0, 0); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -2338,7 +2085,7 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string try { forceValue(v, pos); if (v.type() != nString) - throwTypeError("value is %1% while a string was expected", v); + throwError(noPos, "value is %1% while a string was expected", "", "", 0, 0, &v, 0, noPos, "", 0, 0, 0); return v.string.s; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2401,9 +2148,9 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s auto s = forceString(v, pos, errorCtx); if (v.string.context) { if (pos) - throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]); + throwError(noPos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0], 0, 0, 0, 0, noPos, "", 0, 0, 0); else - throwEvalError("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]); + throwError(noPos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0], 0, 0, 0, 0, noPos, "", 0, 0, 0); } return s; } catch (Error & e) { @@ -2463,7 +2210,7 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet return std::move(*maybeString); auto i = v.attrs->find(sOutPath); if (i == v.attrs->end()) - throwTypeErrorWithTrace("cannot coerce a set to a string", pos, errorCtx); + throwErrorWithTrace(noPos, "cannot coerce a set to a string", "", "", 0, 0, &v, 0, noPos, "", 0, pos, errorCtx, 0, 0); return coerceToString(pos, *i->value, context, coerceMore, copyToStore, canonicalizePath, errorCtx); } @@ -2498,14 +2245,14 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet } } - throwTypeErrorWithTrace("cannot coerce %1% to a string", showType(v), pos, errorCtx); + throwErrorWithTrace(noPos, "cannot coerce %1% to a string", "", "", 0, 0, &v, 0, noPos, "", 0, pos, errorCtx, 0, 0); } std::string EvalState::copyPathToStore(PathSet & context, const Path & path) { if (nix::isDerivation(path)) - throwEvalError("file names are not allowed to end in '%1%'", drvExtension); + throwError(noPos, "file names are not allowed to end in '%1%'", drvExtension, "", 0, 0, 0, 0, noPos, "", 0, 0, 0); Path dstPath; auto i = srcToStore.find(path); @@ -2530,7 +2277,7 @@ Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context, std { auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (path == "" || path[0] != '/') - throwEvalErrorWithTrace("string '%1%' doesn't represent an absolute path", path, pos, errorCtx); + throwErrorWithTrace(noPos, "string '%1%' doesn't represent an absolute path", path, "", 0, 0, 0, 0, noPos, "", 0, pos, errorCtx, 0, 0); return path; } @@ -2540,7 +2287,7 @@ StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & co auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (auto storePath = store->maybeParseStorePath(path)) return *storePath; - throwEvalErrorWithTrace("path '%1%' is not in the Nix store", path, pos, errorCtx); + throwErrorWithTrace(noPos, "path '%1%' is not in the Nix store", path, "", 0, 0, 0, 0, noPos, "", 0, pos, errorCtx, 0, 0); } @@ -2564,7 +2311,6 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v if (v1.type() != v2.type()) return false; switch (v1.type()) { - case nInt: return v1.integer == v2.integer; @@ -2618,7 +2364,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v return v1.fpoint == v2.fpoint; default: - throwEvalErrorWithTrace("cannot compare %1% with %2%", showType(v1), showType(v2), pos, errorCtx); + throwErrorWithTrace(noPos, "cannot compare %1% with %2%", "", "", 0, 0, &v1, &v2, noPos, "", 0, pos, errorCtx, 0, 0); } } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 5fe33025c..fd5ac3e77 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -146,30 +146,27 @@ public: template [[gnu::noinline, gnu::noreturn]] - void debugThrow(E && error, const Env & env, const Expr & expr) + void debugThrowLastTrace(E && error) { - if (debugRepl) - runDebugRepl(&error, env, expr); - - throw std::move(error); + debugThrow(error, nullptr, nullptr); } template [[gnu::noinline, gnu::noreturn]] - void debugThrowLastTrace(E && e) + void debugThrow(E && error, const Env * env, const Expr * expr) { - // Call this in the situation where Expr and Env are inaccessible. - // The debugger will start in the last context that's in the - // DebugTrace stack. - if (debugRepl && !debugTraces.empty()) { - const DebugTrace & last = debugTraces.front(); - runDebugRepl(&e, last.env, last.expr); + if (debugRepl && ((env && expr) || !debugTraces.empty())) { + if (!env || !expr) { + const DebugTrace & last = debugTraces.front(); + env = &last.env; + expr = &last.expr; + } + runDebugRepl(&error, *env, *expr); } - throw std::move(e); + throw std::move(error); } - private: SrcToStore srcToStore; @@ -315,118 +312,30 @@ public: std::string_view forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx); std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx); - // coerce-strings + template [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s) const; - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, const Value & v) const; - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const char * s, const std::string_view s2) const; - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, const std::string_view s2) const; - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, const std::string_view s2) const; - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const char * s, const std::string_view s2, const std::string_view s3) const; - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3) const; - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2) const; - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const char * s, const Value & v) const; - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s) const; - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s, const Value & v) const; - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2) const; - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol s2) const; - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const char * s, const Value & v) const; - [[gnu::noinline, gnu::noreturn]] - void throwTypeErrorWithTrace(const PosIdx, const char*, std::string_view, const nix::Symbol&, const PosIdx, std::string_view) const; - [[gnu::noinline, gnu::noreturn]] - void throwTypeErrorWithTrace(const PosIdx, const nix::Suggestions&, const char*, std::string_view, const nix::Symbol&, const PosIdx, std::string_view) const; - [[gnu::noinline, gnu::noreturn]] - void throwTypeErrorWithTrace(const char*, std::string_view, const Pos &, std::string_view) const; - [[gnu::noinline, gnu::noreturn]] - void throwTypeErrorWithTrace(const char*, std::string_view, const PosIdx, std::string_view) const; - [[gnu::noinline, gnu::noreturn]] - void throwEvalErrorWithTrace(const char*, std::string_view, const PosIdx, std::string_view) const; - [[gnu::noinline, gnu::noreturn]] - void throwEvalErrorWithTrace(const char*, std::string_view, std::string_view, nix::PosIdx, std::string_view) const; - [[gnu::noinline, gnu::noreturn]] - void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1) const; - [[gnu::noinline, gnu::noreturn]] - void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1) const; - [[gnu::noinline, gnu::noreturn]] - void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1) const; + void throwErrorWithTrace( + PosIdx pos, const char* format, + const std::string_view s1, const std::string_view s2, + const Symbol * sym1, const Symbol * sym2, + Value * val1, Value * val2, + PosIdx pos1, + const std::string_view s3, + const Suggestions * suggestions, + PosIdx tracePos, const std::string_view traceStr, + Env * env, Expr * expr); - // origin/master + template [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const char * s, const std::string & s2); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, const std::string & s2); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const char * s, const std::string & s2, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const char * s, const std::string & s2, const std::string & s3, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const char * s, const std::string & s2, const std::string & s3); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const char * s, const std::string & s2, const std::string & s3); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s, const std::string & s2, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwEvalError(const PosIdx p1, const char * s, const Symbol sym, const PosIdx p2, - Env & env, Expr & expr); - - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s, const Value & v); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s, const Value & v, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const char * s, const ExprLambda & fun, const Symbol s2, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const PosIdx pos, const Suggestions & suggestions, const char * s, const ExprLambda & fun, const Symbol s2, - Env & env, Expr & expr); - [[gnu::noinline, gnu::noreturn]] - void throwTypeError(const char * s, const Value & v, - Env & env, Expr & expr); - - [[gnu::noinline, gnu::noreturn]] - void throwAssertionError(const PosIdx pos, const char * s, const std::string & s1, - Env & env, Expr & expr); - - [[gnu::noinline, gnu::noreturn]] - void throwUndefinedVarError(const PosIdx pos, const char * s, const std::string & s1, - Env & env, Expr & expr); - - [[gnu::noinline, gnu::noreturn]] - void throwMissingArgumentError(const PosIdx pos, const char * s, const std::string & s1, - Env & env, Expr & expr); + void throwError( + PosIdx pos, const char* format, + const std::string_view s1, const std::string_view s2, + const Symbol * sym1, const Symbol * sym2, + Value * val1, Value * val2, + PosIdx pos1, + const std::string_view s3, + const Suggestions * suggestions, + Env * env, Expr * expr); [[gnu::noinline]] void addErrorTrace(Error & e, const char * s, const std::string & s2) const; diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 8a1f9311d..544f8efc4 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -137,7 +137,7 @@ DrvInfo::Outputs DrvInfo::queryOutputs(bool withPaths, bool onlyOutputsToInstall return outputs; Bindings::iterator i; - if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos)) { + if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) { Outputs result; auto out = outputs.find(queryOutputName()); if (out == outputs.end()) diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 7768ed8df..a91b8b41f 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -396,7 +396,7 @@ expr_function ; 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 ; @@ -788,13 +788,13 @@ Path EvalState::findFile(SearchPath & searchPath, const std::string_view path, c if (hasPrefix(path, "nix/")) return concatStrings(corepkgsPrefix, path.substr(4)); - debugThrowLastTrace(ThrownError({ + debugThrow(ThrownError({ .msg = hintfmt(evalSettings.pureEval ? "cannot look up '<%s>' in pure evaluation mode (use '--impure' to override)" : "file '%s' was not found in the Nix search path (add it using $NIX_PATH or -I)", path), .errPos = positions[pos] - })); + }), 0, 0); } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 6b7c20fd5..5ce0f4015 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -561,7 +561,7 @@ struct CompareValues return v1->integer < v2->fpoint; if (v1->type() != v2->type()) state.debugThrowLastTrace(EvalError({ - ."cannot compare %1% with %2%", showType(*v1), showType(*v2))); + .msg = hintfmt("%scannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)), .errPos = std::nullopt, })); switch (v1->type()) { @@ -586,7 +586,7 @@ struct CompareValues } default: state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("%scannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)), + .msg = hintfmt("%scannot compare %s with %s: nix does not define an ordering for that type", errorCtx, showType(*v1), showType(*v2)), .errPos = std::nullopt, })); } @@ -617,12 +617,11 @@ static Bindings::iterator getAttr( .msg = hintfmt("attribute '%s' missing %s", state.symbols[attrSym], errorCtx), .errPos = state.positions[attrSet->pos], }); - // TODO XXX - // Adding another trace for the function name to make it clear - // which call received wrong arguments. - //e.addTrace(state.positions[pos], hintfmt("while invoking '%s'", funcName)); - //state.debugThrowLastTrace(e); - } + // TODO XXX + // Adding another trace for the function name to make it clear + // which call received wrong arguments. + //e.addTrace(state.positions[pos], hintfmt("while invoking '%s'", funcName)); + //state.debugThrowLastTrace(e); } return value; } From e412bb6d30fb33e0b80928437a38ea5cf37ce78c Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Sun, 11 Sep 2022 22:58:59 +0200 Subject: [PATCH 24/39] fix remaining fixtures --- src/libexpr/eval.cc | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 646e19bad..27b333807 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1389,11 +1389,10 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) std::set allAttrNames; for (auto & attr : *vAttrs->attrs) allAttrNames.insert(state.symbols[attr.name]); - throw EvalError("foo"); - //tmp: state.throwEvalError( - // pos, - // Suggestions::bestMatches(allAttrNames, state.symbols[name]), - // "attribute '%1%' missing", state.symbols[name], env, *this); + auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]); + state.throwError(pos, "attribute '%1%' missing", + "", "", &name, 0, 0, 0, noPos, "", &suggestions, + &env, this); } } vAttrs = j->value; @@ -1503,13 +1502,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & auto j = args[0]->attrs->get(i.name); if (!j) { if (!i.def) { - throw EvalError("foo"); - /* - throwTypeErrorWithTrace(lambda.pos, + throwErrorWithTrace(lambda.pos, "function '%1%' called without required argument '%2%'", - (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), - i.name, pos, "from call site", *fun.lambda.env, lambda); - */ + (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), "", + &i.name, 0, 0, 0, noPos, "", 0, pos, "from call site", fun.lambda.env, &lambda); } env2.values[displ++] = i.def->maybeThunk(*this, env2); } else { @@ -1528,14 +1524,11 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & std::set formalNames; for (auto & formal : lambda.formals->formals) formalNames.insert(symbols[formal.name]); - throw EvalError("foo"); - /* - throwTypeErrorWithTrace(lambda.pos, - Suggestions::bestMatches(formalNames, symbols[i.name]), + auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]); + throwErrorWithTrace(lambda.pos, "function '%1%' called with unexpected argument '%2%'", - (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), - i.name, pos, "from call site"); - */ + (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), "", + &i.name, 0, 0, 0, noPos, "", &suggestions, pos, "from call site", fun.lambda.env, &lambda); } abort(); // can't happen } @@ -1763,7 +1756,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v) if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) { std::ostringstream out; cond->show(state.symbols, out); - //tmp: state.throwAssertionError(pos, "assertion '%1%' failed", out.str(), env, *this); + state.throwError(pos, "assertion '%1%' failed", out.str(), "", 0, 0, 0, 0, noPos, "", 0, &env, this); } body->eval(state, env, v); } From 96f2dd99d39da61706895b05ed864558679fac79 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Sun, 11 Sep 2022 23:09:36 +0200 Subject: [PATCH 25/39] fix remaining foo stuff --- src/libexpr/eval-cache.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 89afac0e8..4eb3fb53e 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -571,14 +571,14 @@ std::string AttrCursor::getString() debug("using cached string attribute '%s'", getAttrPathStr()); return s->first; } else - throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr())); + root->state.throwError(noPos, "'%s' is not a string", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); } } auto & v = forceValue(); if (v.type() != nString && v.type() != nPath) - throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()))); + root->state.throwError(noPos, "'%s' is not a string but %s", getAttrPathStr(), "", 0, 0, &v, 0, noPos, "", 0, 0, 0); return v.type() == nString ? v.string.s : v.path; } @@ -602,7 +602,7 @@ string_t AttrCursor::getStringWithContext() return *s; } } else - throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a string", getAttrPathStr())); + root->state.throwError(noPos, "'%s' is not a string", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); } } @@ -613,7 +613,7 @@ string_t AttrCursor::getStringWithContext() else if (v.type() == nPath) return {v.path, {}}; else - throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a string but %s", getAttrPathStr(), showType(v.type()))); + root->state.throwError(noPos, "'%s' is not a string but %s", getAttrPathStr(), "", 0, 0, &v, 0, noPos, "", 0, 0, 0); } bool AttrCursor::getBool() @@ -626,14 +626,14 @@ bool AttrCursor::getBool() debug("using cached Boolean attribute '%s'", getAttrPathStr()); return *b; } else - throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr())); + root->state.throwError(noPos, "'%s' is not a Boolean", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); } } auto & v = forceValue(); if (v.type() != nBool) - throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not a Boolean", getAttrPathStr())); + root->state.throwError(noPos, "'%s' is not a Boolean", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); return v.boolean; } @@ -703,14 +703,14 @@ std::vector AttrCursor::getAttrs() debug("using cached attrset attribute '%s'", getAttrPathStr()); return *attrs; } else - throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr())); + root->state.throwError(noPos, "'%s' is not an attribute set", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); } } auto & v = forceValue(); if (v.type() != nAttrs) - throw EvalError("foo"); //root->state.debugThrowLastTrace(TypeError("'%s' is not an attribute set", getAttrPathStr())); + root->state.throwError(noPos, "'%s' is not an attribute set", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); std::vector attrs; for (auto & attr : *getValue().attrs) From b945b844a9ce8479872f6280aedde27e2974b7f3 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Mon, 17 Oct 2022 03:05:02 +0200 Subject: [PATCH 26/39] Initial frames support --- src/libexpr/eval.cc | 43 ++++++++++++++++++++++++++++++++++++------ src/libexpr/eval.hh | 15 ++++++++++++++- src/libexpr/primops.cc | 6 +++--- src/libutil/error.cc | 9 +++++++-- src/libutil/error.hh | 3 ++- 5 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 27b333807..050b49833 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -836,6 +836,37 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & evaluator. So here are some helper functions for throwing exceptions. */ +template +void EvalState::throwFrameErrorWithTrace( + PosIdx pos, const char* format, + const std::string_view s1, const std::string_view s2, + const Symbol * sym1, const Symbol * sym2, + Value * val1, Value * val2, + PosIdx pos1, + const std::string_view s3, + const Suggestions * suggestions, + PosIdx tracePos, const std::string_view traceStr, + Env * env, Expr * expr) +{ + hintformat f(format); + if (!s1.empty()) { f = f % s1; } + if (!s2.empty()) { f = f % s2; } + if (sym1) { f = f % symbols[*sym1]; } + if (sym2) { f = f % symbols[*sym2]; } + if (val1) { f = f % showType(*val1); } + if (val2) { f = f % showType(*val2); } + if (pos1) { f = f % positions[pos1]; } + if (!s3.empty()) { f = f % s3; } + + auto e = ErrorType(ErrorInfo { + .msg = f, + .errPos = positions[pos], + .suggestions = suggestions ? *suggestions : Suggestions(), + }); + e.addTrace(positions[tracePos], traceStr, true); + debugThrow(e, env, expr); +} + template void EvalState::throwErrorWithTrace( PosIdx pos, const char* format, @@ -901,9 +932,9 @@ void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) e.addTrace(std::nullopt, s, s2); } -void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const +void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame) const { - e.addTrace(positions[pos], s, s2); + e.addTrace(positions[pos], hintfmt(s, s2), frame); } static std::unique_ptr makeDebugTraceStacker( @@ -1164,7 +1195,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri Value v; e->eval(*this, env, v); if (v.type() != nBool) - throwError(pos, "value is %1% while a Boolean was expected", "", "", 0, 0, &v, 0, noPos, "", 0, &env, e); + throwError(noPos, "value is %1% while a Boolean was expected", "", "", 0, 0, &v, 0, noPos, "", 0, &env, e); return v.boolean; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -1178,7 +1209,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx po try { e->eval(*this, env, v); if (v.type() != nAttrs) - throwError(pos, "value is %1% while a set was expected", "", "", 0, 0, &v, 0, noPos, "", 0, &env, e); + throwError(noPos, "value is %1% while a set was expected", "", "", 0, 0, &v, 0, noPos, "", 0, &env, e); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -1502,7 +1533,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & auto j = args[0]->attrs->get(i.name); if (!j) { if (!i.def) { - throwErrorWithTrace(lambda.pos, + throwFrameErrorWithTrace(lambda.pos, "function '%1%' called without required argument '%2%'", (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), "", &i.name, 0, 0, 0, noPos, "", 0, pos, "from call site", fun.lambda.env, &lambda); @@ -1525,7 +1556,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & for (auto & formal : lambda.formals->formals) formalNames.insert(symbols[formal.name]); auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]); - throwErrorWithTrace(lambda.pos, + throwFrameErrorWithTrace(lambda.pos, "function '%1%' called with unexpected argument '%2%'", (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), "", &i.name, 0, 0, 0, noPos, "", &suggestions, pos, "from call site", fun.lambda.env, &lambda); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index fd5ac3e77..3d72d3fe1 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -312,6 +312,19 @@ public: std::string_view forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx); std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx); + template + [[gnu::noinline, gnu::noreturn]] + void throwFrameErrorWithTrace( + PosIdx pos, const char* format, + const std::string_view s1, const std::string_view s2, + const Symbol * sym1, const Symbol * sym2, + Value * val1, Value * val2, + PosIdx pos1, + const std::string_view s3, + const Suggestions * suggestions, + PosIdx tracePos, const std::string_view traceStr, + Env * env, Expr * expr); + template [[gnu::noinline, gnu::noreturn]] void throwErrorWithTrace( @@ -340,7 +353,7 @@ public: [[gnu::noinline]] void addErrorTrace(Error & e, const char * s, const std::string & s2) const; [[gnu::noinline]] - void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const; + void addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2, bool frame = false) const; public: /* Return true iff the value `v' denotes a derivation (i.e. a diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9be4bbf6a..625b0aa4a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1171,9 +1171,9 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * } } catch (Error & e) { - e.addTrace(state.positions[posDrvName], - "while evaluating the attribute '%1%' of the derivation '%2%'", - key, drvName); + e.addTrace(state.positions[noPos], + hintfmt("while evaluating the attribute '%1%' of the derivation '%2%'", key, drvName), + true); throw; } } diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 5f86e1e76..cf4f4a56f 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -9,9 +9,9 @@ namespace nix { const std::string nativeSystem = SYSTEM; -void BaseError::addTrace(std::optional e, hintformat hint) +void BaseError::addTrace(std::optional e, hintformat hint, bool frame) { - err.traces.push_front(Trace { .pos = e, .hint = hint }); + err.traces.push_front(Trace { .pos = e, .hint = hint, .frame = frame }); } // c++ std::exception descendants must have a 'const char* what()' function. @@ -382,6 +382,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s * */ + bool frameOnly = false; if (!einfo.traces.empty()) { unsigned int count = 0; for (auto iter = einfo.traces.rbegin(); iter != einfo.traces.rend(); ++iter) { @@ -391,7 +392,11 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s } if (iter->hint.str().empty()) continue; + if (frameOnly && !iter->frame) continue; + count++; + frameOnly = iter->frame; + oss << "\n" << "… " << iter->hint.str() << "\n"; if (iter->pos.has_value() && (*iter->pos)) { diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 50335676e..bf99581e2 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -110,6 +110,7 @@ void printAtPos(const ErrPos & pos, std::ostream & out); struct Trace { std::optional pos; hintformat hint; + bool frame; }; struct ErrorInfo { @@ -188,7 +189,7 @@ public: addTrace(e, hintfmt(std::string(fs), args...)); } - void addTrace(std::optional e, hintformat hint); + void addTrace(std::optional e, hintformat hint, bool frame = false); bool hasTrace() const { return !err.traces.empty(); } }; From 520404f450e7a0dbaa77ef6352677d992920e34d Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 20 Oct 2022 13:55:15 +0200 Subject: [PATCH 27/39] Revert custom position of 'if' blocks --- src/libexpr/parser.y | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index a91b8b41f..0cf0d56f0 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -396,7 +396,7 @@ expr_function ; expr_if - : IF expr THEN expr ELSE expr { $$ = new ExprIf(makeCurPos(@2, data), $2, $4, $6); } + : IF expr THEN expr ELSE expr { $$ = new ExprIf(CUR_POS, $2, $4, $6); } | expr_op ; From 512f6be9b5dae74d3a6112f90090bec442d2a86a Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 20 Oct 2022 14:17:05 +0200 Subject: [PATCH 28/39] Reword incomparable types message --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 625b0aa4a..508608183 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -586,7 +586,7 @@ struct CompareValues } default: state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("%scannot compare %s with %s: nix does not define an ordering for that type", errorCtx, showType(*v1), showType(*v2)), + .msg = hintfmt("%scannot compare %s with %s; values of that type are incomparable", errorCtx, showType(*v1), showType(*v2)), .errPos = std::nullopt, })); } From 31ce52a045ac384b542f8409f20c0461ab6263ac Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 20 Oct 2022 14:18:35 +0200 Subject: [PATCH 29/39] Fix context message being printed twice with forceStringNoCtx --- src/libexpr/eval.cc | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 050b49833..0febff022 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2168,19 +2168,11 @@ std::string_view EvalState::forceString(Value & v, PathSet & context, const PosI std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx) { - try { - auto s = forceString(v, pos, errorCtx); - if (v.string.context) { - if (pos) - throwError(noPos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0], 0, 0, 0, 0, noPos, "", 0, 0, 0); - else - throwError(noPos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0], 0, 0, 0, 0, noPos, "", 0, 0, 0); - } - return s; - } catch (Error & e) { - e.addTrace(positions[pos], errorCtx); - throw; + auto s = forceString(v, pos, errorCtx); + if (v.string.context) { + throwErrorWithTrace(noPos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0], 0, 0, 0, 0, noPos, "", 0, pos, errorCtx, 0, 0); } + return s; } From 4a909c142c93f43a0d1913b54bc9f7b690f172aa Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 20 Oct 2022 14:25:11 +0200 Subject: [PATCH 30/39] Rollback unneeded throwFrameErrorWithTrace function --- src/libexpr/eval.cc | 44 ++++++++------------------------------------ src/libexpr/eval.hh | 13 ------------- 2 files changed, 8 insertions(+), 49 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0febff022..0a9ac68be 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -836,37 +836,6 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & evaluator. So here are some helper functions for throwing exceptions. */ -template -void EvalState::throwFrameErrorWithTrace( - PosIdx pos, const char* format, - const std::string_view s1, const std::string_view s2, - const Symbol * sym1, const Symbol * sym2, - Value * val1, Value * val2, - PosIdx pos1, - const std::string_view s3, - const Suggestions * suggestions, - PosIdx tracePos, const std::string_view traceStr, - Env * env, Expr * expr) -{ - hintformat f(format); - if (!s1.empty()) { f = f % s1; } - if (!s2.empty()) { f = f % s2; } - if (sym1) { f = f % symbols[*sym1]; } - if (sym2) { f = f % symbols[*sym2]; } - if (val1) { f = f % showType(*val1); } - if (val2) { f = f % showType(*val2); } - if (pos1) { f = f % positions[pos1]; } - if (!s3.empty()) { f = f % s3; } - - auto e = ErrorType(ErrorInfo { - .msg = f, - .errPos = positions[pos], - .suggestions = suggestions ? *suggestions : Suggestions(), - }); - e.addTrace(positions[tracePos], traceStr, true); - debugThrow(e, env, expr); -} - template void EvalState::throwErrorWithTrace( PosIdx pos, const char* format, @@ -1533,7 +1502,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & auto j = args[0]->attrs->get(i.name); if (!j) { if (!i.def) { - throwFrameErrorWithTrace(lambda.pos, + throwErrorWithTrace(lambda.pos, "function '%1%' called without required argument '%2%'", (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), "", &i.name, 0, 0, 0, noPos, "", 0, pos, "from call site", fun.lambda.env, &lambda); @@ -1556,7 +1525,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & for (auto & formal : lambda.formals->formals) formalNames.insert(symbols[formal.name]); auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]); - throwFrameErrorWithTrace(lambda.pos, + throwErrorWithTrace(lambda.pos, "function '%1%' called with unexpected argument '%2%'", (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), "", &i.name, 0, 0, 0, noPos, "", &suggestions, pos, "from call site", fun.lambda.env, &lambda); @@ -1582,9 +1551,12 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & lambda.body->eval(*this, env2, vCur); } catch (Error & e) { if (loggerSettings.showTrace.get()) { - addErrorTrace(e, lambda.pos, "while evaluating the '%s' function", - (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda")); - if (pos) addErrorTrace(e, pos, "from call site%s", ""); + addErrorTrace(e, + lambda.pos, + "while evaluating the '%s' function", + (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), + true); + if (pos) addErrorTrace(e, pos, "from call site%s", "", true); } throw; } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 3d72d3fe1..234b5c06b 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -312,19 +312,6 @@ public: std::string_view forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx); std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx); - template - [[gnu::noinline, gnu::noreturn]] - void throwFrameErrorWithTrace( - PosIdx pos, const char* format, - const std::string_view s1, const std::string_view s2, - const Symbol * sym1, const Symbol * sym2, - Value * val1, Value * val2, - PosIdx pos1, - const std::string_view s3, - const Suggestions * suggestions, - PosIdx tracePos, const std::string_view traceStr, - Env * env, Expr * expr); - template [[gnu::noinline, gnu::noreturn]] void throwErrorWithTrace( From 8c3afd2d685bdd616dcdd856803e571578685426 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Sat, 22 Oct 2022 23:37:54 +0200 Subject: [PATCH 31/39] Introduce an Error builder to tackle complexity --- mk/lib.mk | 2 + src/libexpr/eval-cache.cc | 16 +-- src/libexpr/eval-inline.hh | 6 +- src/libexpr/eval.cc | 202 ++++++++++++++++++------------------- src/libexpr/eval.hh | 73 +++++++++----- 5 files changed, 160 insertions(+), 139 deletions(-) diff --git a/mk/lib.mk b/mk/lib.mk index 92f0983d5..49f505ff8 100644 --- a/mk/lib.mk +++ b/mk/lib.mk @@ -87,6 +87,8 @@ endif # Pass -g if we want debug info. BUILD_DEBUG ?= 1 +GLOBAL_CXXFLAGS += -gdwarf-4 + ifeq ($(BUILD_DEBUG), 1) GLOBAL_CFLAGS += -g GLOBAL_CXXFLAGS += -g diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 4eb3fb53e..df5348148 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -571,14 +571,14 @@ std::string AttrCursor::getString() debug("using cached string attribute '%s'", getAttrPathStr()); return s->first; } else - root->state.throwError(noPos, "'%s' is not a string", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); + root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow(); } } auto & v = forceValue(); if (v.type() != nString && v.type() != nPath) - root->state.throwError(noPos, "'%s' is not a string but %s", getAttrPathStr(), "", 0, 0, &v, 0, noPos, "", 0, 0, 0); + root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow(); return v.type() == nString ? v.string.s : v.path; } @@ -602,7 +602,7 @@ string_t AttrCursor::getStringWithContext() return *s; } } else - root->state.throwError(noPos, "'%s' is not a string", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); + root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow(); } } @@ -613,7 +613,7 @@ string_t AttrCursor::getStringWithContext() else if (v.type() == nPath) return {v.path, {}}; else - root->state.throwError(noPos, "'%s' is not a string but %s", getAttrPathStr(), "", 0, 0, &v, 0, noPos, "", 0, 0, 0); + root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow(); } bool AttrCursor::getBool() @@ -626,14 +626,14 @@ bool AttrCursor::getBool() debug("using cached Boolean attribute '%s'", getAttrPathStr()); return *b; } else - root->state.throwError(noPos, "'%s' is not a Boolean", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); + root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); } } auto & v = forceValue(); if (v.type() != nBool) - root->state.throwError(noPos, "'%s' is not a Boolean", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); + root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); return v.boolean; } @@ -703,14 +703,14 @@ std::vector AttrCursor::getAttrs() debug("using cached attrset attribute '%s'", getAttrPathStr()); return *attrs; } else - root->state.throwError(noPos, "'%s' is not an attribute set", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); + root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); } } auto & v = forceValue(); if (v.type() != nAttrs) - root->state.throwError(noPos, "'%s' is not an attribute set", getAttrPathStr(), "", 0, 0, 0, 0, noPos, "", 0, 0, 0); + root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); std::vector attrs; for (auto & attr : *getValue().attrs) diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 43a4e8fa6..279f5b8be 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -103,7 +103,7 @@ void EvalState::forceValue(Value & v, Callable getPos) else if (v.isApp()) callFunction(*v.app.left, *v.app.right, v, noPos); else if (v.isBlackhole()) - throwError(getPos(), "infinite recursion encountered", "", "", 0, 0, 0, 0, noPos, "", 0, 0, 0); + error("infinite recursion encountered").atPos(getPos()).debugThrow(); } @@ -121,7 +121,7 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view e forceValue(v, noPos); if (v.type() != nAttrs) { PosIdx pos = getPos(); - this->throwErrorWithTrace(noPos, "value is %1% while a set was expected", "", "", 0, 0, &v, 0, noPos, "", 0, pos, errorCtx, 0, 0); + this->error("value is %1% while a set was expected", showType(v)).withTrace(pos, errorCtx).debugThrow(); } } @@ -131,7 +131,7 @@ inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view e { forceValue(v, noPos); if (!v.isList()) { - this->throwErrorWithTrace(noPos, "value is %1% while a list was expected", "", "", 0, 0, &v, 0, noPos, "", 0, pos, errorCtx, 0, 0); + this->error("value is %1% while a list was expected", showType(v)).withTrace(pos, errorCtx).debugThrow(); } } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0a9ac68be..bd102b186 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -418,6 +418,59 @@ static Strings parseNixPath(const std::string & s) return res; } +template +ErrorBuilder::ErrorBuilder(EvalState & s): + state(s), + info(ErrorInfo { .msg = hintformat(""), .errPos = std::nullopt, }) +{} + +template +ErrorBuilder & ErrorBuilder::atPos(PosIdx pos) +{ + info.errPos = state.positions[pos]; + return *this; +} + +template +ErrorBuilder & ErrorBuilder::withTrace(PosIdx pos, const std::string_view text) +{ + info.traces.push_back(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = false }); + return *this; +} + +template +ErrorBuilder & ErrorBuilder::withFrameTrace(PosIdx pos, const std::string_view text) +{ + info.traces.push_back(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = true }); + return *this; +} + +template +ErrorBuilder & ErrorBuilder::suggestions(Suggestions & s) { + info.suggestions = s; + return *this; +} + +template +ErrorBuilder & ErrorBuilder::withFrame(const Env & env, const Expr & expr) { + // NOTE: This is abusing side-effects. + // TODO: check compatibility with nested debugger calls. + state.debugTraces.push_front(DebugTrace { + .pos = std::nullopt, + .expr = expr, + .env = env, + .hint = hintformat("Fake frame for debugg{ing,er} purposes"), + .isError = true + }); + return *this; +} + +template +void ErrorBuilder::debugThrow() { + // NOTE: We always use the -LastTrace version as we push the new trace in withFrame() + state.debugThrowLastTrace(ErrorType(info)); +} + EvalState::EvalState( const Strings & _searchPath, @@ -831,71 +884,6 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & } } -/* Every "format" object (even temporary) takes up a few hundred bytes - of stack space, which is a real killer in the recursive - evaluator. So here are some helper functions for throwing - exceptions. */ - -template -void EvalState::throwErrorWithTrace( - PosIdx pos, const char* format, - const std::string_view s1, const std::string_view s2, - const Symbol * sym1, const Symbol * sym2, - Value * val1, Value * val2, - PosIdx pos1, - const std::string_view s3, - const Suggestions * suggestions, - PosIdx tracePos, const std::string_view traceStr, - Env * env, Expr * expr) -{ - hintformat f(format); - if (!s1.empty()) { f = f % s1; } - if (!s2.empty()) { f = f % s2; } - if (sym1) { f = f % symbols[*sym1]; } - if (sym2) { f = f % symbols[*sym2]; } - if (val1) { f = f % showType(*val1); } - if (val2) { f = f % showType(*val2); } - if (pos1) { f = f % positions[pos1]; } - if (!s3.empty()) { f = f % s3; } - - auto e = ErrorType(ErrorInfo { - .msg = f, - .errPos = positions[pos], - .suggestions = suggestions ? *suggestions : Suggestions(), - }); - e.addTrace(positions[tracePos], traceStr); - debugThrow(e, env, expr); -} - -template -void EvalState::throwError( - PosIdx pos, const char* format, - const std::string_view s1, const std::string_view s2, - const Symbol * sym1, const Symbol * sym2, - Value * val1, Value * val2, - PosIdx pos1, - const std::string_view s3, - const Suggestions * suggestions, - Env * env, Expr * expr) -{ - hintformat f(format); - if (!s1.empty()) { f = f % s1; } - if (!s2.empty()) { f = f % s2; } - if (sym1) { f = f % symbols[*sym1]; } - if (sym2) { f = f % symbols[*sym2]; } - if (val1) { f = f % showType(*val1); } - if (val2) { f = f % showType(*val2); } - if (pos1) { f = f % positions[pos1]; } - if (!s3.empty()) { f = f % s3; } - - auto e = ErrorType(ErrorInfo { - .msg = f, - .errPos = positions[pos], - .suggestions = suggestions ? *suggestions : Suggestions(), - }); - debugThrow(e, env, expr); -} - void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const { e.addTrace(std::nullopt, s, s2); @@ -990,7 +978,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) return j->value; } if (!env->prevWith) - throwError(var.pos, "undefined variable '%1%'", "", "", &var.name, 0, 0, 0, noPos, "", 0, env, const_cast(&var)); + error("undefined variable '%1%'", symbols[var.name]).atPos(var.pos).withFrame(*env, var).debugThrow(); for (size_t l = env->prevWith; l; --l, env = env->up) ; } } @@ -1140,7 +1128,7 @@ void EvalState::cacheFile( // computation. if (mustBeTrivial && !(dynamic_cast(e))) - throwError(noPos, "file '%s' must be an attribute set", path, "", 0, 0, 0, 0, noPos, "", 0, 0, 0); + error("file '%s' must be an attribute set", path).debugThrow(); eval(e, v); } catch (Error & e) { addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath); @@ -1164,7 +1152,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri Value v; e->eval(*this, env, v); if (v.type() != nBool) - throwError(noPos, "value is %1% while a Boolean was expected", "", "", 0, 0, &v, 0, noPos, "", 0, &env, e); + error("value is %1% while a Boolean was expected", showType(v)).withFrame(env, *e).debugThrow(); return v.boolean; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -1178,7 +1166,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx po try { e->eval(*this, env, v); if (v.type() != nAttrs) - throwError(noPos, "value is %1% while a set was expected", "", "", 0, 0, &v, 0, noPos, "", 0, &env, e); + error("value is %1% while a set was expected", showType(v)).withFrame(env, *e).debugThrow(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -1287,7 +1275,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) auto nameSym = state.symbols.create(nameVal.string.s); Bindings::iterator j = v.attrs->find(nameSym); if (j != v.attrs->end()) - state.throwError(i.pos, "dynamic attribute '%1%' already defined at %2%", "", "", &nameSym, 0, 0, 0, j->pos, "", 0, &env, this); + state.error("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow(); i.valueExpr->setName(nameSym); /* Keep sorted order so find can catch duplicates */ @@ -1390,9 +1378,8 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) for (auto & attr : *vAttrs->attrs) allAttrNames.insert(state.symbols[attr.name]); auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]); - state.throwError(pos, "attribute '%1%' missing", - "", "", &name, 0, 0, 0, noPos, "", &suggestions, - &env, this); + state.error("attribute '%1%' missing", state.symbols[name]) + .atPos(pos).suggestions(suggestions).withFrame(env, *this).debugThrow(); } } vAttrs = j->value; @@ -1502,10 +1489,13 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & auto j = args[0]->attrs->get(i.name); if (!j) { if (!i.def) { - throwErrorWithTrace(lambda.pos, - "function '%1%' called without required argument '%2%'", - (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), "", - &i.name, 0, 0, 0, noPos, "", 0, pos, "from call site", fun.lambda.env, &lambda); + error("function '%1%' called without required argument '%2%'", + (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), + symbols[i.name]) + .atPos(lambda.pos) + .withTrace(pos, "from call site") + .withFrame(*fun.lambda.env, lambda) + .debugThrow(); } env2.values[displ++] = i.def->maybeThunk(*this, env2); } else { @@ -1525,10 +1515,14 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & for (auto & formal : lambda.formals->formals) formalNames.insert(symbols[formal.name]); auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]); - throwErrorWithTrace(lambda.pos, - "function '%1%' called with unexpected argument '%2%'", - (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), "", - &i.name, 0, 0, 0, noPos, "", &suggestions, pos, "from call site", fun.lambda.env, &lambda); + error("function '%1%' called with unexpected argument '%2%'", + (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), + symbols[i.name]) + .atPos(lambda.pos) + .withTrace(pos, "from call site") + .suggestions(suggestions) + .withFrame(*fun.lambda.env, lambda) + .debugThrow(); } abort(); // can't happen } @@ -1553,8 +1547,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & if (loggerSettings.showTrace.get()) { addErrorTrace(e, lambda.pos, - "while evaluating the '%s' function", - (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), + "while evaluating %s", + lambda.name + ? concatStrings("'", symbols[lambda.name], "'") + : "anonymous lambda", true); if (pos) addErrorTrace(e, pos, "from call site%s", "", true); } @@ -1657,7 +1653,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } else - throwError(pos, "attempt to call something which is not a function but %1%", "", "", 0, 0, &vCur, 0, noPos, "", 0, 0, 0); + error("attempt to call something which is not a function but %1%", showType(vCur)).atPos(pos).debugThrow(); } vRes = vCur; @@ -1721,12 +1717,12 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) if (j != args.end()) { attrs.insert(*j); } else if (!i.def) { - throwError(i.pos, R"(cannot evaluate a function that has an argument without a value ('%1%') + error(R"(cannot evaluate a function that has an argument without a value ('%1%') Nix attempted to evaluate a function as a top level expression; in this case it must have its arguments supplied either by default values, or passed explicitly with '--arg' or '--argstr'. See -https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions.)", "", "", &i.name, 0, 0, 0, noPos, "", 0, - fun.lambda.env, fun.lambda.fun); +https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions.)", symbols[i.name]) + .atPos(i.pos).withFrame(*fun.lambda.env, *fun.lambda.fun).debugThrow(); } } } @@ -1759,7 +1755,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v) if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) { std::ostringstream out; cond->show(state.symbols, out); - state.throwError(pos, "assertion '%1%' failed", out.str(), "", 0, 0, 0, 0, noPos, "", 0, &env, this); + state.error("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow(); } body->eval(state, env, v); } @@ -1936,14 +1932,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) nf = n; nf += vTmp.fpoint; } else - state.throwError(i_pos, "cannot add %1% to an integer", "", "", 0, 0, &vTmp, 0, noPos, "", 0, &env, this); + state.error("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); } else if (firstType == nFloat) { if (vTmp.type() == nInt) { nf += vTmp.integer; } else if (vTmp.type() == nFloat) { nf += vTmp.fpoint; } else - state.throwError(i_pos, "cannot add %1% to a float", "", "", 0, 0, &vTmp, 0, noPos, "", 0, &env, this); + state.error("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); } else { if (s.empty()) s.reserve(es->size()); /* skip canonization of first path, which would only be not @@ -1963,7 +1959,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) v.mkFloat(nf); else if (firstType == nPath) { if (!context.empty()) - state.throwError(pos, "a string that refers to a store path cannot be appended to a path", "", "", 0, 0, 0, 0, noPos, "", 0, &env, this); + state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow(); v.mkPath(canonPath(str())); } else v.mkStringMove(c_str(), context); @@ -2018,7 +2014,7 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCt try { forceValue(v, pos); if (v.type() != nInt) - throwError(noPos, "value is %1% while an integer was expected", "", "", 0, 0, &v, 0, noPos, "", 0, 0, 0); + error("value is %1% while an integer was expected", showType(v)).debugThrow(); return v.integer; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2034,7 +2030,7 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view err if (v.type() == nInt) return v.integer; else if (v.type() != nFloat) - throwError(noPos, "value is %1% while a float was expected", "", "", 0, 0, &v, 0, noPos, "", 0, 0, 0); + error("value is %1% while a float was expected", showType(v)).debugThrow(); return v.fpoint; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2048,7 +2044,7 @@ bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx try { forceValue(v, pos); if (v.type() != nBool) - throwError(noPos, "value is %1% while a Boolean was expected", "", "", 0, 0, &v, 0, noPos, "", 0, 0, 0); + error("value is %1% while a Boolean was expected", showType(v)).debugThrow(); return v.boolean; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2068,7 +2064,7 @@ void EvalState::forceFunction(Value & v, const PosIdx pos, std::string_view erro try { forceValue(v, pos); if (v.type() != nFunction && !isFunctor(v)) - throwError(noPos, "value is %1% while a function was expected", "", "", 0, 0, &v, 0, noPos, "", 0, 0, 0); + error("value is %1% while a function was expected", showType(v)).debugThrow(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -2081,7 +2077,7 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string try { forceValue(v, pos); if (v.type() != nString) - throwError(noPos, "value is %1% while a string was expected", "", "", 0, 0, &v, 0, noPos, "", 0, 0, 0); + error("value is %1% while a string was expected", showType(v)).debugThrow(); return v.string.s; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2142,7 +2138,7 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s { auto s = forceString(v, pos, errorCtx); if (v.string.context) { - throwErrorWithTrace(noPos, "the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0], 0, 0, 0, 0, noPos, "", 0, pos, errorCtx, 0, 0); + error("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]).withTrace(pos, errorCtx).debugThrow(); } return s; } @@ -2198,7 +2194,7 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet return std::move(*maybeString); auto i = v.attrs->find(sOutPath); if (i == v.attrs->end()) - throwErrorWithTrace(noPos, "cannot coerce a set to a string", "", "", 0, 0, &v, 0, noPos, "", 0, pos, errorCtx, 0, 0); + error("cannot coerce a set to a string", showType(v)).withTrace(pos, errorCtx).debugThrow(); return coerceToString(pos, *i->value, context, coerceMore, copyToStore, canonicalizePath, errorCtx); } @@ -2233,14 +2229,14 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet } } - throwErrorWithTrace(noPos, "cannot coerce %1% to a string", "", "", 0, 0, &v, 0, noPos, "", 0, pos, errorCtx, 0, 0); + error("cannot coerce %1% to a string", showType(v)).withTrace(pos, errorCtx).debugThrow(); } std::string EvalState::copyPathToStore(PathSet & context, const Path & path) { if (nix::isDerivation(path)) - throwError(noPos, "file names are not allowed to end in '%1%'", drvExtension, "", 0, 0, 0, 0, noPos, "", 0, 0, 0); + error("file names are not allowed to end in '%1%'", drvExtension).debugThrow(); Path dstPath; auto i = srcToStore.find(path); @@ -2265,7 +2261,7 @@ Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context, std { auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (path == "" || path[0] != '/') - throwErrorWithTrace(noPos, "string '%1%' doesn't represent an absolute path", path, "", 0, 0, 0, 0, noPos, "", 0, pos, errorCtx, 0, 0); + error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow(); return path; } @@ -2275,7 +2271,7 @@ StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & co auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (auto storePath = store->maybeParseStorePath(path)) return *storePath; - throwErrorWithTrace(noPos, "path '%1%' is not in the Nix store", path, "", 0, 0, 0, 0, noPos, "", 0, pos, errorCtx, 0, 0); + error("path '%1%' is not in the Nix store", path).withTrace(pos, errorCtx).debugThrow(); } @@ -2352,7 +2348,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v return v1.fpoint == v2.fpoint; default: - throwErrorWithTrace(noPos, "cannot compare %1% with %2%", "", "", 0, 0, &v1, &v2, noPos, "", 0, pos, errorCtx, 0, 0); + error("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow(); } } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 234b5c06b..ff407d090 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -87,6 +87,47 @@ struct DebugTrace { void debugError(Error * e, Env & env, Expr & expr); +template +class ErrorBuilder +{ + + EvalState & state; + ErrorInfo info; + + public: + [[gnu::noinline]] + ErrorBuilder(EvalState & s); + + [[gnu::noinline]] + ErrorBuilder & atPos(PosIdx pos); + + template + [[gnu::noinline]] + ErrorBuilder & msg(const std::string & fs, const Args & ... args) + { + hintformat f(fs); + formatHelper(f, args...); + info.msg = f; + return *this; + } + + [[gnu::noinline]] + ErrorBuilder & withTrace(PosIdx pos, const std::string_view text); + + [[gnu::noinline]] + ErrorBuilder & withFrameTrace(PosIdx pos, const std::string_view text); + + [[gnu::noinline]] + ErrorBuilder & suggestions(Suggestions & s); + + [[gnu::noinline]] + ErrorBuilder & withFrame(const Env & e, const Expr & ex); + + [[gnu::noinline, gnu::noreturn]] + void debugThrow(); +}; + + class EvalState : public std::enable_shared_from_this { public: @@ -167,6 +208,13 @@ public: throw std::move(error); } + template + ErrorBuilder & error(const std::string & fs, const Args & ... args) { + ErrorBuilder * errorBuilder = new ErrorBuilder(*this); + errorBuilder->msg(fs, args ...); + return *errorBuilder; + } + private: SrcToStore srcToStore; @@ -312,31 +360,6 @@ public: std::string_view forceString(Value & v, PathSet & context, const PosIdx pos, std::string_view errorCtx); std::string_view forceStringNoCtx(Value & v, const PosIdx pos, std::string_view errorCtx); - template - [[gnu::noinline, gnu::noreturn]] - void throwErrorWithTrace( - PosIdx pos, const char* format, - const std::string_view s1, const std::string_view s2, - const Symbol * sym1, const Symbol * sym2, - Value * val1, Value * val2, - PosIdx pos1, - const std::string_view s3, - const Suggestions * suggestions, - PosIdx tracePos, const std::string_view traceStr, - Env * env, Expr * expr); - - template - [[gnu::noinline, gnu::noreturn]] - void throwError( - PosIdx pos, const char* format, - const std::string_view s1, const std::string_view s2, - const Symbol * sym1, const Symbol * sym2, - Value * val1, Value * val2, - PosIdx pos1, - const std::string_view s3, - const Suggestions * suggestions, - Env * env, Expr * expr); - [[gnu::noinline]] void addErrorTrace(Error & e, const char * s, const std::string & s2) const; [[gnu::noinline]] From 2a7348f986f8c2c9d6419ea44f7a6b19a667c7d4 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Sat, 22 Oct 2022 23:38:50 +0200 Subject: [PATCH 32/39] Fixup merge typo --- doc/manual/src/release-notes/rl-next.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md index 9c43132d4..a08dc9c9b 100644 --- a/doc/manual/src/release-notes/rl-next.md +++ b/doc/manual/src/release-notes/rl-next.md @@ -7,5 +7,3 @@ * Error traces have been reworked to provide detailed explanations and more accurate error locations. A short excerpt of the trace is now shown by default when an error occurs. - ->>>>>>> origin/master From 3cd1c3b9888ee4dfee0f0694061e6976949d5245 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Sat, 22 Oct 2022 23:41:05 +0200 Subject: [PATCH 33/39] Revert local settings merged by error --- .gitignore | 1 - mk/lib.mk | 2 -- 2 files changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index d7f310396..8e0db013f 100644 --- a/.gitignore +++ b/.gitignore @@ -129,4 +129,3 @@ nix-rust/target result .vscode/ -baseline diff --git a/mk/lib.mk b/mk/lib.mk index 49f505ff8..92f0983d5 100644 --- a/mk/lib.mk +++ b/mk/lib.mk @@ -87,8 +87,6 @@ endif # Pass -g if we want debug info. BUILD_DEBUG ?= 1 -GLOBAL_CXXFLAGS += -gdwarf-4 - ifeq ($(BUILD_DEBUG), 1) GLOBAL_CFLAGS += -g GLOBAL_CXXFLAGS += -g From 8bd8583bc7a430eeee0f5d5e5cb502158419a500 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Sun, 23 Oct 2022 00:11:44 +0200 Subject: [PATCH 34/39] Try to please clang with convoluted templates --- src/libexpr/eval.cc | 6 ------ src/libexpr/eval.hh | 6 +++++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index bd102b186..3b8c0b9c1 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -465,12 +465,6 @@ ErrorBuilder & ErrorBuilder::withFrame(const Env & env, co return *this; } -template -void ErrorBuilder::debugThrow() { - // NOTE: We always use the -LastTrace version as we push the new trace in withFrame() - state.debugThrowLastTrace(ErrorType(info)); -} - EvalState::EvalState( const Strings & _searchPath, diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index ff407d090..5e88eb950 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -124,7 +124,11 @@ class ErrorBuilder ErrorBuilder & withFrame(const Env & e, const Expr & ex); [[gnu::noinline, gnu::noreturn]] - void debugThrow(); + void ErrorBuilder::debugThrow() { + // NOTE: We always use the -LastTrace version as we push the new trace in withFrame() + state.debugThrowLastTrace(ErrorType(info)); + } + }; From e93bf69b448d4f4ce6c3fe7b7acfa904afe058c0 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Tue, 25 Oct 2022 01:46:10 +0200 Subject: [PATCH 35/39] Rework error throwing, and test it --- src/libexpr/eval-cache.cc | 16 +++--- src/libexpr/eval-inline.hh | 6 +- src/libexpr/eval.cc | 87 ++++++++++++---------------- src/libexpr/eval.hh | 63 ++++++++++---------- src/libexpr/primops.cc | 16 ++---- src/libexpr/tests/error_traces.cc | 96 +++++++++++++++++++++++++++++++ src/libexpr/tests/primops.cc | 6 ++ src/libutil/error.hh | 2 + 8 files changed, 190 insertions(+), 102 deletions(-) create mode 100644 src/libexpr/tests/error_traces.cc diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index df5348148..9a8a7fe62 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -571,14 +571,14 @@ std::string AttrCursor::getString() debug("using cached string attribute '%s'", getAttrPathStr()); return s->first; } else - root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow(); + root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow(); } } auto & v = forceValue(); if (v.type() != nString && v.type() != nPath) - root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow(); + root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow(); return v.type() == nString ? v.string.s : v.path; } @@ -602,7 +602,7 @@ string_t AttrCursor::getStringWithContext() return *s; } } else - root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow(); + root->state.error("'%s' is not a string", getAttrPathStr()).debugThrow(); } } @@ -613,7 +613,7 @@ string_t AttrCursor::getStringWithContext() else if (v.type() == nPath) return {v.path, {}}; else - root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow(); + root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow(); } bool AttrCursor::getBool() @@ -626,14 +626,14 @@ bool AttrCursor::getBool() debug("using cached Boolean attribute '%s'", getAttrPathStr()); return *b; } else - root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); + root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); } } auto & v = forceValue(); if (v.type() != nBool) - root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); + root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); return v.boolean; } @@ -703,14 +703,14 @@ std::vector AttrCursor::getAttrs() debug("using cached attrset attribute '%s'", getAttrPathStr()); return *attrs; } else - root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); + root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); } } auto & v = forceValue(); if (v.type() != nAttrs) - root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); + root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); std::vector attrs; for (auto & attr : *getValue().attrs) diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 279f5b8be..f0da688db 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -103,7 +103,7 @@ void EvalState::forceValue(Value & v, Callable getPos) else if (v.isApp()) callFunction(*v.app.left, *v.app.right, v, noPos); else if (v.isBlackhole()) - error("infinite recursion encountered").atPos(getPos()).debugThrow(); + error("infinite recursion encountered").atPos(getPos()).template debugThrow(); } @@ -121,7 +121,7 @@ inline void EvalState::forceAttrs(Value & v, Callable getPos, std::string_view e forceValue(v, noPos); if (v.type() != nAttrs) { PosIdx pos = getPos(); - this->error("value is %1% while a set was expected", showType(v)).withTrace(pos, errorCtx).debugThrow(); + error("value is %1% while a set was expected", showType(v)).withTrace(pos, errorCtx).debugThrow(); } } @@ -131,7 +131,7 @@ inline void EvalState::forceList(Value & v, const PosIdx pos, std::string_view e { forceValue(v, noPos); if (!v.isList()) { - this->error("value is %1% while a list was expected", showType(v)).withTrace(pos, errorCtx).debugThrow(); + error("value is %1% while a list was expected", showType(v)).withTrace(pos, errorCtx).debugThrow(); } } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 3b8c0b9c1..0ff8bba4d 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -418,41 +418,30 @@ static Strings parseNixPath(const std::string & s) return res; } -template -ErrorBuilder::ErrorBuilder(EvalState & s): - state(s), - info(ErrorInfo { .msg = hintformat(""), .errPos = std::nullopt, }) -{} - -template -ErrorBuilder & ErrorBuilder::atPos(PosIdx pos) +ErrorBuilder & ErrorBuilder::atPos(PosIdx pos) { info.errPos = state.positions[pos]; return *this; } -template -ErrorBuilder & ErrorBuilder::withTrace(PosIdx pos, const std::string_view text) +ErrorBuilder & ErrorBuilder::withTrace(PosIdx pos, const std::string_view text) { - info.traces.push_back(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = false }); + info.traces.push_front(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = false }); return *this; } -template -ErrorBuilder & ErrorBuilder::withFrameTrace(PosIdx pos, const std::string_view text) +ErrorBuilder & ErrorBuilder::withFrameTrace(PosIdx pos, const std::string_view text) { - info.traces.push_back(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = true }); + info.traces.push_front(Trace{ .pos = state.positions[pos], .hint = hintformat(std::string(text)), .frame = true }); return *this; } -template -ErrorBuilder & ErrorBuilder::suggestions(Suggestions & s) { +ErrorBuilder & ErrorBuilder::withSuggestions(Suggestions & s) { info.suggestions = s; return *this; } -template -ErrorBuilder & ErrorBuilder::withFrame(const Env & env, const Expr & expr) { +ErrorBuilder & ErrorBuilder::withFrame(const Env & env, const Expr & expr) { // NOTE: This is abusing side-effects. // TODO: check compatibility with nested debugger calls. state.debugTraces.push_front(DebugTrace { @@ -972,7 +961,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) return j->value; } if (!env->prevWith) - error("undefined variable '%1%'", symbols[var.name]).atPos(var.pos).withFrame(*env, var).debugThrow(); + error("undefined variable '%1%'", symbols[var.name]).atPos(var.pos).withFrame(*env, var).debugThrow(); for (size_t l = env->prevWith; l; --l, env = env->up) ; } } @@ -1122,7 +1111,7 @@ void EvalState::cacheFile( // computation. if (mustBeTrivial && !(dynamic_cast(e))) - error("file '%s' must be an attribute set", path).debugThrow(); + error("file '%s' must be an attribute set", path).debugThrow(); eval(e, v); } catch (Error & e) { addErrorTrace(e, "while evaluating the file '%1%':", resolvedPath); @@ -1146,7 +1135,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri Value v; e->eval(*this, env, v); if (v.type() != nBool) - error("value is %1% while a Boolean was expected", showType(v)).withFrame(env, *e).debugThrow(); + error("value is %1% while a Boolean was expected", showType(v)).withFrame(env, *e).debugThrow(); return v.boolean; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -1160,7 +1149,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx po try { e->eval(*this, env, v); if (v.type() != nAttrs) - error("value is %1% while a set was expected", showType(v)).withFrame(env, *e).debugThrow(); + error("value is %1% while a set was expected", showType(v)).withFrame(env, *e).debugThrow(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -1269,7 +1258,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) auto nameSym = state.symbols.create(nameVal.string.s); Bindings::iterator j = v.attrs->find(nameSym); if (j != v.attrs->end()) - state.error("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow(); + state.error("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow(); i.valueExpr->setName(nameSym); /* Keep sorted order so find can catch duplicates */ @@ -1372,8 +1361,8 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) for (auto & attr : *vAttrs->attrs) allAttrNames.insert(state.symbols[attr.name]); auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]); - state.error("attribute '%1%' missing", state.symbols[name]) - .atPos(pos).suggestions(suggestions).withFrame(env, *this).debugThrow(); + state.error("attribute '%1%' missing", state.symbols[name]) + .atPos(pos).withSuggestions(suggestions).withFrame(env, *this).debugThrow(); } } vAttrs = j->value; @@ -1483,13 +1472,13 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & auto j = args[0]->attrs->get(i.name); if (!j) { if (!i.def) { - error("function '%1%' called without required argument '%2%'", + error("function '%1%' called without required argument '%2%'", (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), symbols[i.name]) .atPos(lambda.pos) .withTrace(pos, "from call site") .withFrame(*fun.lambda.env, lambda) - .debugThrow(); + .debugThrow(); } env2.values[displ++] = i.def->maybeThunk(*this, env2); } else { @@ -1509,14 +1498,14 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & for (auto & formal : lambda.formals->formals) formalNames.insert(symbols[formal.name]); auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]); - error("function '%1%' called with unexpected argument '%2%'", + error("function '%1%' called with unexpected argument '%2%'", (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), symbols[i.name]) .atPos(lambda.pos) .withTrace(pos, "from call site") - .suggestions(suggestions) + .withSuggestions(suggestions) .withFrame(*fun.lambda.env, lambda) - .debugThrow(); + .debugThrow(); } abort(); // can't happen } @@ -1647,7 +1636,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } else - error("attempt to call something which is not a function but %1%", showType(vCur)).atPos(pos).debugThrow(); + error("attempt to call something which is not a function but %1%", showType(vCur)).atPos(pos).debugThrow(); } vRes = vCur; @@ -1711,12 +1700,12 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) if (j != args.end()) { attrs.insert(*j); } else if (!i.def) { - error(R"(cannot evaluate a function that has an argument without a value ('%1%') + error(R"(cannot evaluate a function that has an argument without a value ('%1%') Nix attempted to evaluate a function as a top level expression; in this case it must have its arguments supplied either by default values, or passed explicitly with '--arg' or '--argstr'. See https://nixos.org/manual/nix/stable/expressions/language-constructs.html#functions.)", symbols[i.name]) - .atPos(i.pos).withFrame(*fun.lambda.env, *fun.lambda.fun).debugThrow(); + .atPos(i.pos).withFrame(*fun.lambda.env, *fun.lambda.fun).debugThrow(); } } } @@ -1749,7 +1738,7 @@ void ExprAssert::eval(EvalState & state, Env & env, Value & v) if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) { std::ostringstream out; cond->show(state.symbols, out); - state.error("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow(); + state.error("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow(); } body->eval(state, env, v); } @@ -1926,14 +1915,14 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) nf = n; nf += vTmp.fpoint; } else - state.error("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); + state.error("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); } else if (firstType == nFloat) { if (vTmp.type() == nInt) { nf += vTmp.integer; } else if (vTmp.type() == nFloat) { nf += vTmp.fpoint; } else - state.error("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); + state.error("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); } else { if (s.empty()) s.reserve(es->size()); /* skip canonization of first path, which would only be not @@ -1953,7 +1942,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) v.mkFloat(nf); else if (firstType == nPath) { if (!context.empty()) - state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow(); + state.error("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow(); v.mkPath(canonPath(str())); } else v.mkStringMove(c_str(), context); @@ -2008,7 +1997,7 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCt try { forceValue(v, pos); if (v.type() != nInt) - error("value is %1% while an integer was expected", showType(v)).debugThrow(); + error("value is %1% while an integer was expected", showType(v)).debugThrow(); return v.integer; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2024,7 +2013,7 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view err if (v.type() == nInt) return v.integer; else if (v.type() != nFloat) - error("value is %1% while a float was expected", showType(v)).debugThrow(); + error("value is %1% while a float was expected", showType(v)).debugThrow(); return v.fpoint; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2038,7 +2027,7 @@ bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx try { forceValue(v, pos); if (v.type() != nBool) - error("value is %1% while a Boolean was expected", showType(v)).debugThrow(); + error("value is %1% while a Boolean was expected", showType(v)).debugThrow(); return v.boolean; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2058,7 +2047,7 @@ void EvalState::forceFunction(Value & v, const PosIdx pos, std::string_view erro try { forceValue(v, pos); if (v.type() != nFunction && !isFunctor(v)) - error("value is %1% while a function was expected", showType(v)).debugThrow(); + error("value is %1% while a function was expected", showType(v)).debugThrow(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -2071,7 +2060,7 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string try { forceValue(v, pos); if (v.type() != nString) - error("value is %1% while a string was expected", showType(v)).debugThrow(); + error("value is %1% while a string was expected", showType(v)).debugThrow(); return v.string.s; } catch (Error & e) { e.addTrace(positions[pos], errorCtx); @@ -2132,7 +2121,7 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s { auto s = forceString(v, pos, errorCtx); if (v.string.context) { - error("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]).withTrace(pos, errorCtx).debugThrow(); + error("the string '%1%' is not allowed to refer to a store path (such as '%2%')", v.string.s, v.string.context[0]).withTrace(pos, errorCtx).debugThrow(); } return s; } @@ -2188,7 +2177,7 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet return std::move(*maybeString); auto i = v.attrs->find(sOutPath); if (i == v.attrs->end()) - error("cannot coerce a set to a string", showType(v)).withTrace(pos, errorCtx).debugThrow(); + error("cannot coerce a set to a string", showType(v)).withTrace(pos, errorCtx).debugThrow(); return coerceToString(pos, *i->value, context, coerceMore, copyToStore, canonicalizePath, errorCtx); } @@ -2223,14 +2212,14 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet } } - error("cannot coerce %1% to a string", showType(v)).withTrace(pos, errorCtx).debugThrow(); + error("cannot coerce %1% to a string", showType(v)).withTrace(pos, errorCtx).debugThrow(); } std::string EvalState::copyPathToStore(PathSet & context, const Path & path) { if (nix::isDerivation(path)) - error("file names are not allowed to end in '%1%'", drvExtension).debugThrow(); + error("file names are not allowed to end in '%1%'", drvExtension).debugThrow(); Path dstPath; auto i = srcToStore.find(path); @@ -2255,7 +2244,7 @@ Path EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & context, std { auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (path == "" || path[0] != '/') - error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow(); + error("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow(); return path; } @@ -2265,7 +2254,7 @@ StorePath EvalState::coerceToStorePath(const PosIdx pos, Value & v, PathSet & co auto path = coerceToString(pos, v, context, false, false, true, errorCtx).toOwned(); if (auto storePath = store->maybeParseStorePath(path)) return *storePath; - error("path '%1%' is not in the Nix store", path).withTrace(pos, errorCtx).debugThrow(); + error("path '%1%' is not in the Nix store", path).withTrace(pos, errorCtx).debugThrow(); } @@ -2342,7 +2331,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v return v1.fpoint == v2.fpoint; default: - error("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow(); + error("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow(); } } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 5e88eb950..1f610a02f 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -87,48 +87,40 @@ struct DebugTrace { void debugError(Error * e, Env & env, Expr & expr); -template class ErrorBuilder { - + private: EvalState & state; ErrorInfo info; + ErrorBuilder(EvalState & s, ErrorInfo && i): state(s), info(i) { } + public: - [[gnu::noinline]] - ErrorBuilder(EvalState & s); - - [[gnu::noinline]] - ErrorBuilder & atPos(PosIdx pos); - template - [[gnu::noinline]] - ErrorBuilder & msg(const std::string & fs, const Args & ... args) + [[nodiscard, gnu::noinline]] + static ErrorBuilder * create(EvalState & s, const Args & ... args) { - hintformat f(fs); - formatHelper(f, args...); - info.msg = f; - return *this; + return new ErrorBuilder(s, ErrorInfo { .msg = hintfmt(args...) }); } - [[gnu::noinline]] - ErrorBuilder & withTrace(PosIdx pos, const std::string_view text); + [[nodiscard, gnu::noinline]] + ErrorBuilder & atPos(PosIdx pos); - [[gnu::noinline]] - ErrorBuilder & withFrameTrace(PosIdx pos, const std::string_view text); + [[nodiscard, gnu::noinline]] + ErrorBuilder & withTrace(PosIdx pos, const std::string_view text); - [[gnu::noinline]] - ErrorBuilder & suggestions(Suggestions & s); + [[nodiscard, gnu::noinline]] + ErrorBuilder & withFrameTrace(PosIdx pos, const std::string_view text); - [[gnu::noinline]] - ErrorBuilder & withFrame(const Env & e, const Expr & ex); + [[nodiscard, gnu::noinline]] + ErrorBuilder & withSuggestions(Suggestions & s); + [[nodiscard, gnu::noinline]] + ErrorBuilder & withFrame(const Env & e, const Expr & ex); + + template [[gnu::noinline, gnu::noreturn]] - void ErrorBuilder::debugThrow() { - // NOTE: We always use the -LastTrace version as we push the new trace in withFrame() - state.debugThrowLastTrace(ErrorType(info)); - } - + void debugThrow(); }; @@ -212,10 +204,12 @@ public: throw std::move(error); } - template - ErrorBuilder & error(const std::string & fs, const Args & ... args) { - ErrorBuilder * errorBuilder = new ErrorBuilder(*this); - errorBuilder->msg(fs, args ...); + ErrorBuilder * errorBuilder; + + template + [[nodiscard, gnu::noinline]] + ErrorBuilder & error(const Args & ... args) { + errorBuilder = ErrorBuilder::create(*this, args...); return *errorBuilder; } @@ -648,6 +642,13 @@ extern EvalSettings evalSettings; static const std::string corepkgsPrefix{"/__corepkgs__/"}; +template +void ErrorBuilder::debugThrow() +{ + // NOTE: We always use the -LastTrace version as we push the new trace in withFrame() + state.debugThrowLastTrace(ErrorType(info)); +} + } #include "eval-inline.hh" diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 508608183..b95f54851 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -560,10 +560,7 @@ struct CompareValues if (v1->type() == nInt && v2->type() == nFloat) return v1->integer < v2->fpoint; if (v1->type() != v2->type()) - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("%scannot compare %s with %s", errorCtx, showType(*v1), showType(*v2)), - .errPos = std::nullopt, - })); + state.error("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow(); switch (v1->type()) { case nInt: return v1->integer < v2->integer; @@ -581,14 +578,11 @@ struct CompareValues } else if (i == v1->listSize()) { return true; } else if (!state.eqValues(*v1->listElems()[i], *v2->listElems()[i], pos, errorCtx)) { - return (*this)(v1->listElems()[i], v2->listElems()[i], "while comparing two lists"); + return (*this)(v1->listElems()[i], v2->listElems()[i], "while comparing two list elements"); } } default: - state.debugThrowLastTrace(EvalError({ - .msg = hintfmt("%scannot compare %s with %s; values of that type are incomparable", errorCtx, showType(*v1), showType(*v2)), - .errPos = std::nullopt, - })); + state.error("cannot compare %s with %s; values of that type are incomparable", showType(*v1), showType(*v2)).debugThrow(); } } catch (Error & e) { e.addTrace(std::nullopt, errorCtx); @@ -614,7 +608,7 @@ static Bindings::iterator getAttr( Bindings::iterator value = attrSet->find(attrSym); if (value == attrSet->end()) { throw TypeError({ - .msg = hintfmt("attribute '%s' missing %s", state.symbols[attrSym], errorCtx), + .msg = hintfmt("attribute '%s' missing %s", state.symbols[attrSym], normaltxt(errorCtx)), .errPos = state.positions[attrSet->pos], }); // TODO XXX @@ -628,7 +622,7 @@ static Bindings::iterator getAttr( static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - state.forceAttrs(*args[0], noPos, "while evaluating the first argument pased to builtins.genericClosure"); + state.forceAttrs(*args[0], noPos, "while evaluating the first argument passed to builtins.genericClosure"); /* Get the start set. */ Bindings::iterator startSet = getAttr(state, state.sStartSet, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure"); diff --git a/src/libexpr/tests/error_traces.cc b/src/libexpr/tests/error_traces.cc new file mode 100644 index 000000000..6905dd561 --- /dev/null +++ b/src/libexpr/tests/error_traces.cc @@ -0,0 +1,96 @@ +#include +#include + +#include "libexprtests.hh" + +namespace nix { + + using namespace testing; + + // Testing eval of PrimOp's + class ErrorTraceTest : public LibExprTest { }; + +#define ASSERT_TRACE1(args, type, message) \ + ASSERT_THROW( \ + try { \ + eval("builtins." args); \ + } catch (BaseError & e) { \ + ASSERT_EQ(PrintToString(e.info().msg), \ + PrintToString(message)); \ + auto trace = e.info().traces.rbegin(); \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(hintfmt("while calling the '%s' builtin", "genericClosure"))); \ + throw; \ + } \ + , type \ + ) + +#define ASSERT_TRACE2(args, type, message, context) \ + ASSERT_THROW( \ + try { \ + eval("builtins." args); \ + } catch (BaseError & e) { \ + ASSERT_EQ(PrintToString(e.info().msg), \ + PrintToString(message)); \ + auto trace = e.info().traces.rbegin(); \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(context)); \ + ++trace; \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(hintfmt("while calling the '%s' builtin", "genericClosure"))); \ + throw; \ + } \ + , type \ + ) + + //TEST_F(ErrorTraceTest, genericClosure) { + //ASSERT_THROW( + //try { + //eval("builtins.genericClosure 1 1"); + //} catch (BaseError & e) { + //ASSERT_EQ(PrintToString(e.info().msg), PrintToString(hintfmt("value is %s while a set was expected", "an integer"))); + //throw; + //}, TypeError); + //} + + TEST_F(ErrorTraceTest, genericClosure) { \ + ASSERT_TRACE2("genericClosure 1", + TypeError, + hintfmt("value is %s while a set was expected", "an integer"), + hintfmt("while evaluating the first argument passed to builtins.genericClosure")); + + ASSERT_TRACE1("genericClosure {}", + TypeError, + hintfmt("attribute '%s' missing %s", "startSet", normaltxt("in the attrset passed as argument to builtins.genericClosure"))); + + ASSERT_TRACE2("genericClosure { startSet = 1; }", + TypeError, + hintfmt("value is %s while a list was expected", "an integer"), + hintfmt("while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure")); + // Okay: "genericClosure { startSet = []; }" + ASSERT_TRACE2("genericClosure { startSet = [{ key = 1;}]; operator = true; }", + TypeError, + hintfmt("value is %s while a function was expected", "a Boolean"), + hintfmt("while evaluating the 'operator' attribute passed as argument to builtins.genericClosure")); + + ASSERT_TRACE2("genericClosure { startSet = [{ key = 1;}]; operator = item: true; }", + TypeError, + hintfmt("value is %s while a list was expected", "a Boolean"), + hintfmt("while evaluating the return value of the `operator` passed to builtins.genericClosure")); // TODO: inconsistent naming + + ASSERT_TRACE2("genericClosure { startSet = [{ key = 1;}]; operator = item: [ true ]; }", + TypeError, + hintfmt("value is %s while a set was expected", "a Boolean"), + hintfmt("while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure")); + + ASSERT_TRACE1("genericClosure { startSet = [{ key = 1;}]; operator = item: [ {} ]; }", + TypeError, + hintfmt("attribute '%s' missing %s", "key", normaltxt("in one of the attrsets generated by (or initially passed to) builtins.genericClosure"))); + + ASSERT_TRACE2("genericClosure { startSet = [{ key = 1;}]; operator = item: [{ key = ''a''; }]; }", + EvalError, + hintfmt("cannot compare %s with %s", "a string", "an integer"), + hintfmt("while comparing the `key` attributes of two genericClosure elements")); + } + +} /* namespace nix */ diff --git a/src/libexpr/tests/primops.cc b/src/libexpr/tests/primops.cc index 16cf66d2c..a4102753c 100644 --- a/src/libexpr/tests/primops.cc +++ b/src/libexpr/tests/primops.cc @@ -836,4 +836,10 @@ namespace nix { for (const auto [n, elem] : enumerate(v.listItems())) ASSERT_THAT(*elem, IsStringEq(expected[n])); } + + TEST_F(PrimOpTest, genericClosure_not_strict) { + // Operator should not be used when startSet is empty + auto v = eval("builtins.genericClosure { startSet = []; }"); + ASSERT_THAT(v, IsListOfSize(0)); + } } /* namespace nix */ diff --git a/src/libutil/error.hh b/src/libutil/error.hh index bf99581e2..6db77bcbf 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -192,6 +192,8 @@ public: void addTrace(std::optional e, hintformat hint, bool frame = false); bool hasTrace() const { return !err.traces.empty(); } + + const ErrorInfo & info() { return err; }; }; #define MakeError(newClass, superClass) \ From d6f5734c630f893e8695bd37ca2b0dbf2bb24daa Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Tue, 25 Oct 2022 01:57:18 +0200 Subject: [PATCH 36/39] Complete genericClosure tests --- src/libexpr/tests/error_traces.cc | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/libexpr/tests/error_traces.cc b/src/libexpr/tests/error_traces.cc index 6905dd561..8741ecdd2 100644 --- a/src/libexpr/tests/error_traces.cc +++ b/src/libexpr/tests/error_traces.cc @@ -43,16 +43,6 @@ namespace nix { , type \ ) - //TEST_F(ErrorTraceTest, genericClosure) { - //ASSERT_THROW( - //try { - //eval("builtins.genericClosure 1 1"); - //} catch (BaseError & e) { - //ASSERT_EQ(PrintToString(e.info().msg), PrintToString(hintfmt("value is %s while a set was expected", "an integer"))); - //throw; - //}, TypeError); - //} - TEST_F(ErrorTraceTest, genericClosure) { \ ASSERT_TRACE2("genericClosure 1", TypeError, @@ -67,7 +57,9 @@ namespace nix { TypeError, hintfmt("value is %s while a list was expected", "an integer"), hintfmt("while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure")); + // Okay: "genericClosure { startSet = []; }" + ASSERT_TRACE2("genericClosure { startSet = [{ key = 1;}]; operator = true; }", TypeError, hintfmt("value is %s while a function was expected", "a Boolean"), @@ -91,6 +83,12 @@ namespace nix { EvalError, hintfmt("cannot compare %s with %s", "a string", "an integer"), hintfmt("while comparing the `key` attributes of two genericClosure elements")); + + ASSERT_TRACE2("genericClosure { startSet = [ true ]; operator = item: [{ key = ''a''; }]; }", + TypeError, + hintfmt("value is %s while a set was expected", "a Boolean"), + hintfmt("while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure")); + } } /* namespace nix */ From e0ab2069c975abf80db9fdca23f0c164df921829 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 5 Dec 2022 16:55:55 +0100 Subject: [PATCH 37/39] Consistent capitalisation --- src/nix/eval.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 5dedf02c2..23fef7c68 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -111,7 +111,7 @@ struct CmdEval : MixJSON, InstallableCommand else if (raw) { stopProgressBar(); - std::cout << *state->coerceToString(noPos, *v, context, "While generating the eval command output"); + std::cout << *state->coerceToString(noPos, *v, context, "while generating the eval command output"); } else if (json) { From c548e3549850f224a232f8a74b5a9e2276070896 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Jan 2023 20:01:28 +0100 Subject: [PATCH 38/39] Don't use state.positions[noPos] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This caused traces 'at «none»:0: (source not available)'. --- src/libexpr/primops.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5142d79e8..9356d307e 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -737,7 +737,7 @@ static RegisterPrimOp primop_break({ throw Error(ErrorInfo{ .level = lvlInfo, .msg = hintfmt("quit the debugger"), - .errPos = state.positions[noPos], + .errPos = nullptr, }); } } @@ -1164,7 +1164,7 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * } } catch (Error & e) { - e.addTrace(state.positions[noPos], + e.addTrace(nullptr, hintfmt("while evaluating the attribute '%1%' of the derivation '%2%'", key, drvName), true); throw; From d33d15a48b55eb81270a39b001c04bc61c42105f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 2 Jan 2023 20:40:39 +0100 Subject: [PATCH 39/39] Put the --show-trace hint in the logical place --- src/libmain/shared.cc | 2 -- src/libutil/error.cc | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 99e3d6f56..648e21a25 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -403,8 +403,6 @@ int handleExceptions(const std::string & programName, std::function fun) return 1; } catch (BaseError & e) { logError(e.info()); - if (e.hasTrace() && !loggerSettings.showTrace.get()) - printError("(use '--show-trace' to show detailed location information)"); return e.status; } catch (std::bad_alloc & e) { printError(error + "out of memory"); diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 7f7c27267..e4f0d4677 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -303,7 +303,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s size_t count = 0; for (const auto & trace : einfo.traces) { if (!showTrace && count > 3) { - oss << "\n" << ANSI_ITALIC "(stack trace truncated)" ANSI_NORMAL << "\n"; + oss << "\n" << ANSI_WARNING "(stack trace truncated; use '--show-trace' to show the full trace)" ANSI_NORMAL << "\n"; break; }