Files
lix/lix/libexpr/print-ambiguous.cc
T
eldritch horrors 80654b84b6 libexpr: de-ptr-ize Value references
thunk values are shareable, and we can represent invalid/uninitialized
values with a special bit pattern that makes no sense otherwise. there
is no need to keep allocating values on the heap, instead we can treat
values like reference-counted smart pointers to heap objects, which in
turn lets us save a lot of allocations and, ultimately, gc heap space.

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

Change-Id: Ib6c47acdbe2fac4f76a83c2269f16f30ef66b2e1
2025-10-05 16:23:05 +02:00

106 lines
2.9 KiB
C++

#include "lix/libexpr/print-ambiguous.hh"
#include "lix/libexpr/attr-set.hh"
#include "lix/libutil/logging.hh"
#include "lix/libexpr/print.hh"
#include "lix/libutil/signals.hh"
#include "lix/libutil/escape-string.hh"
namespace nix {
// See: https://github.com/NixOS/nix/issues/9730
void printAmbiguous(
const Value & v,
const SymbolTable & symbols,
std::ostream & str,
std::set<const void *> * seen,
int depth
)
{
checkInterrupt();
if (depth <= 0) {
str << "«too deep»";
return;
}
if (v.isInvalid()) {
str << "<INVALID>";
return;
}
switch (v.type()) {
case nInt:
str << v.integer();
break;
case nBool:
printLiteralBool(str, v.boolean());
break;
case nString:
escapeString(str, v.str());
break;
case nPath:
str << v.path().to_string(); // !!! escaping?
break;
case nNull:
str << "null";
break;
case nAttrs: {
if (seen && !v.attrs()->empty() && !seen->insert(v.attrs()).second)
str << "«repeated»";
else {
str << "{ ";
for (auto & i : v.attrs()->lexicographicOrder(symbols)) {
str << symbols[i->name] << " = ";
printAmbiguous(i->value, symbols, str, seen, depth - 1);
str << "; ";
}
str << "}";
}
break;
}
case nList:
if (seen && v.listSize() && !seen->insert(v.listElems()).second)
str << "«repeated»";
else {
str << "[ ";
for (auto & v2 : v.listItems()) {
printAmbiguous(v2, symbols, str, seen, depth - 1);
str << " ";
}
str << "]";
}
break;
case nThunk:
if (!v.isBlackhole()) {
str << "<CODE>";
} else {
// Although we know for sure that it's going to be an infinite recursion
// when this value is accessed _in the current context_, it's likely
// that the user will misinterpret a simpler «infinite recursion» output
// as a definitive statement about the value, while in fact it may be
// a valid value after `builtins.trace` and perhaps some other steps
// have completed.
str << "«potential infinite recursion»";
}
break;
case nFunction:
if (v.isLambda()) {
str << "<LAMBDA>";
} else if (v.isPrimOp()) {
str << "<PRIMOP>";
} else if (v.isPrimOpApp()) {
str << "<PRIMOP-APP>";
}
break;
case nExternal:
str << *v.external();
break;
case nFloat:
str << v.fpoint();
break;
default:
printError("Lix evaluator internal error: printAmbiguous: invalid value type");
abort();
}
}
}