mirror of
https://github.com/NixOS/nix.git
synced 2025-11-20 17:29:36 +01:00
libexpr: improve error messages for builtins.genericClosure
Show which element(s) are involved at each error point: - When an element is missing the "key" attribute, show the element - When an element is not an attribute set, show the element - When comparing keys fails, show both elements being compared - When calling operator fails, show which element was being processed This provides concrete context using ValuePrinter with errorPrintOptions. Note: errorPrintOptions uses maxDepth=10 by default, which may print quite deeply nested structures in error messages. This could potentially be overwhelming, but follows the existing default for error contexts.
This commit is contained in:
parent
ca787bc3e0
commit
d262efc240
10 changed files with 188 additions and 46 deletions
|
|
@ -681,7 +681,14 @@ struct CompareValues
|
|||
if (v1->type() == nInt && v2->type() == nFloat)
|
||||
return v1->integer().value < v2->fpoint();
|
||||
if (v1->type() != v2->type())
|
||||
state.error<EvalError>("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow();
|
||||
state
|
||||
.error<EvalError>(
|
||||
"cannot compare %s with %s; values are %s and %s",
|
||||
showType(*v1),
|
||||
showType(*v2),
|
||||
ValuePrinter(state, *v1, errorPrintOptions),
|
||||
ValuePrinter(state, *v2, errorPrintOptions))
|
||||
.debugThrow();
|
||||
// Allow selecting a subset of enum values
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
|
|
@ -711,7 +718,11 @@ struct CompareValues
|
|||
default:
|
||||
state
|
||||
.error<EvalError>(
|
||||
"cannot compare %s with %s; values of that type are incomparable", showType(*v1), showType(*v2))
|
||||
"cannot compare %s with %s; values of that type are incomparable (values are %s and %s)",
|
||||
showType(*v1),
|
||||
showType(*v2),
|
||||
ValuePrinter(state, *v1, errorPrintOptions),
|
||||
ValuePrinter(state, *v2, errorPrintOptions))
|
||||
.debugThrow();
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
|
@ -757,42 +768,79 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value ** ar
|
|||
`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, noPos, "while comparing the `key` attributes of two genericClosure elements");
|
||||
std::set<Value *, decltype(cmp)> doneKeys(cmp);
|
||||
// Track which element each key came from
|
||||
auto cmp = CompareValues(state, noPos, "");
|
||||
std::map<Value *, Value *, decltype(cmp)> keyToElem(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");
|
||||
try {
|
||||
state.forceAttrs(*e, noPos, "");
|
||||
} catch (Error & err) {
|
||||
err.addTrace(nullptr, "in genericClosure element %s", ValuePrinter(state, *e, errorPrintOptions));
|
||||
throw;
|
||||
}
|
||||
|
||||
auto key = state.getAttr(
|
||||
state.s.key,
|
||||
e->attrs(),
|
||||
"in one of the attrsets generated by (or initially passed to) builtins.genericClosure");
|
||||
const Attr * key;
|
||||
try {
|
||||
key = state.getAttr(state.s.key, e->attrs(), "");
|
||||
} catch (Error & err) {
|
||||
err.addTrace(nullptr, "in genericClosure element %s", ValuePrinter(state, *e, errorPrintOptions));
|
||||
throw;
|
||||
}
|
||||
state.forceValue(*key->value, noPos);
|
||||
|
||||
if (!doneKeys.insert(key->value).second)
|
||||
continue;
|
||||
try {
|
||||
auto [it, inserted] = keyToElem.insert({key->value, e});
|
||||
if (!inserted)
|
||||
continue;
|
||||
} catch (Error & err) {
|
||||
// Try to find which element we're comparing against
|
||||
Value * otherElem = nullptr;
|
||||
for (auto & [otherKey, elem] : keyToElem) {
|
||||
try {
|
||||
cmp(key->value, otherKey);
|
||||
} catch (Error &) {
|
||||
// Found the element we're comparing against
|
||||
otherElem = elem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (otherElem) {
|
||||
// Traces are printed in reverse order; pre-swap them.
|
||||
err.addTrace(nullptr, "with element %s", ValuePrinter(state, *otherElem, errorPrintOptions));
|
||||
err.addTrace(nullptr, "while comparing element %s", ValuePrinter(state, *e, errorPrintOptions));
|
||||
} else {
|
||||
// Couldn't find the specific element, just show current
|
||||
err.addTrace(nullptr, "while checking key of element %s", ValuePrinter(state, *e, errorPrintOptions));
|
||||
}
|
||||
throw;
|
||||
}
|
||||
res.push_back(e);
|
||||
|
||||
/* Call the `operator' function with `e' as argument. */
|
||||
Value newElements;
|
||||
state.callFunction(*op->value, {&e, 1}, newElements, noPos);
|
||||
state.forceList(
|
||||
newElements,
|
||||
noPos,
|
||||
"while evaluating the return value of the `operator` passed to builtins.genericClosure");
|
||||
try {
|
||||
state.callFunction(*op->value, {&e, 1}, newElements, noPos);
|
||||
state.forceList(
|
||||
newElements,
|
||||
noPos,
|
||||
"while evaluating the return value of the `operator` passed to builtins.genericClosure");
|
||||
|
||||
/* Add the values returned by the operator to the work set. */
|
||||
for (auto elem : newElements.listView()) {
|
||||
state.forceValue(*elem, noPos); // "while evaluating one one of the elements returned by the `operator`
|
||||
// passed to builtins.genericClosure");
|
||||
workSet.push_back(elem);
|
||||
/* Add the values returned by the operator to the work set. */
|
||||
for (auto elem : newElements.listView()) {
|
||||
state.forceValue(*elem, noPos); // "while evaluating one one of the elements returned by the `operator`
|
||||
// passed to builtins.genericClosure");
|
||||
workSet.push_back(elem);
|
||||
}
|
||||
} catch (Error & err) {
|
||||
err.addTrace(
|
||||
nullptr,
|
||||
"while calling %s on genericClosure element %s",
|
||||
state.symbols[state.s.operator_],
|
||||
ValuePrinter(state, *e, errorPrintOptions));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue