mirror of
https://github.com/NixOS/nix.git
synced 2025-11-12 05:26:02 +01:00
Merge remote-tracking branch 'upstream/master' into messages-present-tense
This commit is contained in:
commit
d6710b4c04
43 changed files with 984 additions and 170 deletions
|
|
@ -204,7 +204,7 @@ FrameInfo SampleStack::getFrameInfoFromValueAndPos(const Value & v, std::span<Va
|
|||
/* NOTE: No actual references to garbage collected values are not held in
|
||||
the profiler. */
|
||||
if (v.isLambda())
|
||||
return LambdaFrameInfo{.expr = v.payload.lambda.fun, .callPos = pos};
|
||||
return LambdaFrameInfo{.expr = v.lambda().fun, .callPos = pos};
|
||||
else if (v.isPrimOp()) {
|
||||
return getPrimOpFrameInfo(*v.primOp(), args, pos);
|
||||
} else if (v.isPrimOpApp())
|
||||
|
|
|
|||
|
|
@ -147,8 +147,8 @@ PosIdx Value::determinePos(const PosIdx pos) const
|
|||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (internalType) {
|
||||
case tAttrs: return attrs()->pos;
|
||||
case tLambda: return payload.lambda.fun->pos;
|
||||
case tApp: return payload.app.left->determinePos(pos);
|
||||
case tLambda: return lambda().fun->pos;
|
||||
case tApp: return app().left->determinePos(pos);
|
||||
default: return pos;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
|
@ -160,10 +160,10 @@ bool Value::isTrivial() const
|
|||
internalType != tApp
|
||||
&& internalType != tPrimOpApp
|
||||
&& (internalType != tThunk
|
||||
|| (dynamic_cast<ExprAttrs *>(payload.thunk.expr)
|
||||
&& ((ExprAttrs *) payload.thunk.expr)->dynamicAttrs.empty())
|
||||
|| dynamic_cast<ExprLambda *>(payload.thunk.expr)
|
||||
|| dynamic_cast<ExprList *>(payload.thunk.expr));
|
||||
|| (dynamic_cast<ExprAttrs *>(thunk().expr)
|
||||
&& ((ExprAttrs *) thunk().expr)->dynamicAttrs.empty())
|
||||
|| dynamic_cast<ExprLambda *>(thunk().expr)
|
||||
|| dynamic_cast<ExprList *>(thunk().expr));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -525,9 +525,9 @@ std::ostream & operator<<(std::ostream & output, const PrimOp & primOp)
|
|||
|
||||
const PrimOp * Value::primOpAppPrimOp() const
|
||||
{
|
||||
Value * left = payload.primOpApp.left;
|
||||
Value * left = primOpApp().left;
|
||||
while (left && !left->isPrimOp()) {
|
||||
left = left->payload.primOpApp.left;
|
||||
left = left->primOpApp().left;
|
||||
}
|
||||
|
||||
if (!left)
|
||||
|
|
@ -610,7 +610,7 @@ std::optional<EvalState::Doc> EvalState::getDoc(Value & v)
|
|||
};
|
||||
}
|
||||
if (v.isLambda()) {
|
||||
auto exprLambda = v.payload.lambda.fun;
|
||||
auto exprLambda = v.lambda().fun;
|
||||
|
||||
std::ostringstream s;
|
||||
std::string name;
|
||||
|
|
@ -1567,13 +1567,13 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
|||
|
||||
if (vCur.isLambda()) {
|
||||
|
||||
ExprLambda & lambda(*vCur.payload.lambda.fun);
|
||||
ExprLambda & lambda(*vCur.lambda().fun);
|
||||
|
||||
auto size =
|
||||
(!lambda.arg ? 0 : 1) +
|
||||
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
|
||||
Env & env2(allocEnv(size));
|
||||
env2.up = vCur.payload.lambda.env;
|
||||
env2.up = vCur.lambda().env;
|
||||
|
||||
Displacement displ = 0;
|
||||
|
||||
|
|
@ -1603,7 +1603,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
|||
symbols[i.name])
|
||||
.atPos(lambda.pos)
|
||||
.withTrace(pos, "from call site")
|
||||
.withFrame(*fun.payload.lambda.env, lambda)
|
||||
.withFrame(*fun.lambda().env, lambda)
|
||||
.debugThrow();
|
||||
}
|
||||
env2.values[displ++] = i.def->maybeThunk(*this, env2);
|
||||
|
|
@ -1630,7 +1630,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
|||
.atPos(lambda.pos)
|
||||
.withTrace(pos, "from call site")
|
||||
.withSuggestions(suggestions)
|
||||
.withFrame(*fun.payload.lambda.env, lambda)
|
||||
.withFrame(*fun.lambda().env, lambda)
|
||||
.debugThrow();
|
||||
}
|
||||
unreachable();
|
||||
|
|
@ -1702,7 +1702,7 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
|||
Value * primOp = &vCur;
|
||||
while (primOp->isPrimOpApp()) {
|
||||
argsDone++;
|
||||
primOp = primOp->payload.primOpApp.left;
|
||||
primOp = primOp->primOpApp().left;
|
||||
}
|
||||
assert(primOp->isPrimOp());
|
||||
auto arity = primOp->primOp()->arity;
|
||||
|
|
@ -1718,8 +1718,8 @@ void EvalState::callFunction(Value & fun, std::span<Value *> args, Value & vRes,
|
|||
|
||||
Value * vArgs[maxPrimOpArity];
|
||||
auto n = argsDone;
|
||||
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->payload.primOpApp.left)
|
||||
vArgs[--n] = arg->payload.primOpApp.right;
|
||||
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp().left)
|
||||
vArgs[--n] = arg->primOpApp().right;
|
||||
|
||||
for (size_t i = 0; i < argsLeft; ++i)
|
||||
vArgs[argsDone + i] = args[i];
|
||||
|
|
@ -1825,14 +1825,14 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res
|
|||
}
|
||||
}
|
||||
|
||||
if (!fun.isLambda() || !fun.payload.lambda.fun->hasFormals()) {
|
||||
if (!fun.isLambda() || !fun.lambda().fun->hasFormals()) {
|
||||
res = fun;
|
||||
return;
|
||||
}
|
||||
|
||||
auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.payload.lambda.fun->formals->formals.size()), args.size()));
|
||||
auto attrs = buildBindings(std::max(static_cast<uint32_t>(fun.lambda().fun->formals->formals.size()), args.size()));
|
||||
|
||||
if (fun.payload.lambda.fun->formals->ellipsis) {
|
||||
if (fun.lambda().fun->formals->ellipsis) {
|
||||
// If the formals have an ellipsis (eg the function accepts extra args) pass
|
||||
// all available automatic arguments (which includes arguments specified on
|
||||
// the command line via --arg/--argstr)
|
||||
|
|
@ -1840,7 +1840,7 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res
|
|||
attrs.insert(v);
|
||||
} else {
|
||||
// Otherwise, only pass the arguments that the function accepts
|
||||
for (auto & i : fun.payload.lambda.fun->formals->formals) {
|
||||
for (auto & i : fun.lambda().fun->formals->formals) {
|
||||
auto j = args.get(i.name);
|
||||
if (j) {
|
||||
attrs.insert(*j);
|
||||
|
|
@ -1850,7 +1850,7 @@ Nix attempted to evaluate a function as a top level expression; in
|
|||
this case it must have its arguments supplied either by default
|
||||
values, or passed explicitly with '--arg' or '--argstr'. See
|
||||
https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name])
|
||||
.atPos(i.pos).withFrame(*fun.payload.lambda.env, *fun.payload.lambda.fun).debugThrow();
|
||||
.atPos(i.pos).withFrame(*fun.lambda().env, *fun.lambda().fun).debugThrow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2163,7 +2163,7 @@ void EvalState::forceValueDeep(Value & v)
|
|||
try {
|
||||
// If the value is a thunk, we're evaling. Otherwise no trace necessary.
|
||||
auto dts = debugRepl && i.value->isThunk()
|
||||
? makeDebugTraceStacker(*this, *i.value->payload.thunk.expr, *i.value->payload.thunk.env, i.pos,
|
||||
? makeDebugTraceStacker(*this, *i.value->thunk().expr, *i.value->thunk().env, i.pos,
|
||||
"while evaluating the attribute '%1%'", symbols[i.name])
|
||||
: nullptr;
|
||||
|
||||
|
|
@ -2368,7 +2368,7 @@ BackedStringView EvalState::coerceToString(
|
|||
!canonicalizePath && !copyToStore
|
||||
? // FIXME: hack to preserve path literals that end in a
|
||||
// slash, as in /foo/${x}.
|
||||
v.payload.path.path
|
||||
v.pathStr()
|
||||
: copyToStore
|
||||
? store->printStorePath(copyPathToStore(context, v.path()))
|
||||
: std::string(v.path().path.abs());
|
||||
|
|
@ -2636,14 +2636,14 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st
|
|||
return;
|
||||
|
||||
case nPath:
|
||||
if (v1.payload.path.accessor != v2.payload.path.accessor) {
|
||||
if (v1.pathAccessor() != v2.pathAccessor()) {
|
||||
error<AssertionError>(
|
||||
"path '%s' is not equal to path '%s' because their accessors are different",
|
||||
ValuePrinter(*this, v1, errorPrintOptions),
|
||||
ValuePrinter(*this, v2, errorPrintOptions))
|
||||
.debugThrow();
|
||||
}
|
||||
if (strcmp(v1.payload.path.path, v2.payload.path.path) != 0) {
|
||||
if (strcmp(v1.pathStr(), v2.pathStr()) != 0) {
|
||||
error<AssertionError>(
|
||||
"path '%s' is not equal to path '%s'",
|
||||
ValuePrinter(*this, v1, errorPrintOptions),
|
||||
|
|
@ -2810,8 +2810,8 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
|||
case nPath:
|
||||
return
|
||||
// FIXME: compare accessors by their fingerprint.
|
||||
v1.payload.path.accessor == v2.payload.path.accessor
|
||||
&& strcmp(v1.payload.path.path, v2.payload.path.path) == 0;
|
||||
v1.pathAccessor() == v2.pathAccessor()
|
||||
&& strcmp(v1.pathStr(), v2.pathStr()) == 0;
|
||||
|
||||
case nNull:
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -89,9 +89,9 @@ Env & EvalState::allocEnv(size_t size)
|
|||
void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||
{
|
||||
if (v.isThunk()) {
|
||||
Env * env = v.payload.thunk.env;
|
||||
Env * env = v.thunk().env;
|
||||
assert(env || v.isBlackhole());
|
||||
Expr * expr = v.payload.thunk.expr;
|
||||
Expr * expr = v.thunk().expr;
|
||||
try {
|
||||
v.mkBlackhole();
|
||||
//checkInterrupt();
|
||||
|
|
@ -106,7 +106,7 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
|
|||
}
|
||||
}
|
||||
else if (v.isApp())
|
||||
callFunction(*v.payload.app.left, *v.payload.app.right, v, pos);
|
||||
callFunction(*v.app().left, *v.app().right, v, pos);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -410,11 +410,6 @@ public:
|
|||
return internalType == tList1 || internalType == tList2 || internalType == tListN;
|
||||
}
|
||||
|
||||
Value * const * listElems()
|
||||
{
|
||||
return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems;
|
||||
}
|
||||
|
||||
std::span<Value * const> listItems() const
|
||||
{
|
||||
assert(isList());
|
||||
|
|
@ -444,8 +439,8 @@ public:
|
|||
{
|
||||
assert(internalType == tPath);
|
||||
return SourcePath(
|
||||
ref(payload.path.accessor->shared_from_this()),
|
||||
CanonPath(CanonPath::unchecked_t(), payload.path.path));
|
||||
ref(pathAccessor()->shared_from_this()),
|
||||
CanonPath(CanonPath::unchecked_t(), pathStr()));
|
||||
}
|
||||
|
||||
std::string_view string_view() const
|
||||
|
|
@ -482,6 +477,24 @@ public:
|
|||
|
||||
NixFloat fpoint() const
|
||||
{ return payload.fpoint; }
|
||||
|
||||
Lambda lambda() const
|
||||
{ return payload.lambda; }
|
||||
|
||||
ClosureThunk thunk() const
|
||||
{ return payload.thunk; }
|
||||
|
||||
FunctionApplicationThunk primOpApp() const
|
||||
{ return payload.primOpApp; }
|
||||
|
||||
FunctionApplicationThunk app() const
|
||||
{ return payload.app; }
|
||||
|
||||
const char * pathStr() const
|
||||
{ return payload.path.path; }
|
||||
|
||||
SourceAccessor * pathAccessor() const
|
||||
{ return payload.path.accessor; }
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -489,7 +502,7 @@ extern ExprBlackHole eBlackHole;
|
|||
|
||||
bool Value::isBlackhole() const
|
||||
{
|
||||
return internalType == tThunk && payload.thunk.expr == (Expr*) &eBlackHole;
|
||||
return internalType == tThunk && thunk().expr == (Expr*) &eBlackHole;
|
||||
}
|
||||
|
||||
void Value::mkBlackhole()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "nix/expr/value-to-xml.hh"
|
||||
#include "nix/expr/primops.hh"
|
||||
#include "nix/fetchers/fetch-to-store.hh"
|
||||
#include "nix/util/sort.hh"
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
|
@ -651,7 +652,7 @@ struct CompareValues
|
|||
// Note: we don't take the accessor into account
|
||||
// since it's not obvious how to compare them in a
|
||||
// reproducible way.
|
||||
return strcmp(v1->payload.path.path, v2->payload.path.path) < 0;
|
||||
return strcmp(v1->pathStr(), v2->pathStr()) < 0;
|
||||
case nList:
|
||||
// Lexicographic comparison
|
||||
for (size_t i = 0;; i++) {
|
||||
|
|
@ -3138,12 +3139,12 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg
|
|||
if (!args[0]->isLambda())
|
||||
state.error<TypeError>("'functionArgs' requires a function").atPos(pos).debugThrow();
|
||||
|
||||
if (!args[0]->payload.lambda.fun->hasFormals()) {
|
||||
if (!args[0]->lambda().fun->hasFormals()) {
|
||||
v.mkAttrs(&state.emptyBindings);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &formals = args[0]->payload.lambda.fun->formals->formals;
|
||||
const auto &formals = args[0]->lambda().fun->formals->formals;
|
||||
auto attrs = state.buildBindings(formals.size());
|
||||
for (auto & i : formals)
|
||||
attrs.insert(i.name, state.getBool(i.def), i.pos);
|
||||
|
|
@ -3695,10 +3696,14 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value
|
|||
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
|
||||
weak ordering. What to do? std::stable_sort() seems more
|
||||
resilient, but no guarantees... */
|
||||
std::stable_sort(list.begin(), list.end(), comparator);
|
||||
/* NOTE: Using custom implementation because std::sort and std::stable_sort
|
||||
are not resilient to comparators that violate strict weak ordering. Diagnosing
|
||||
incorrect implementations is a O(n^3) problem, so doing the checks is much more
|
||||
expensive that doing the sorting. For this reason we choose to use sorting algorithms
|
||||
that are can't be broken by invalid comprators. peeksort (mergesort)
|
||||
doesn't misbehave when any of the strict weak order properties is
|
||||
violated - output is always a reordering of the input. */
|
||||
peeksort(list.begin(), list.end(), comparator);
|
||||
|
||||
v.mkList(list);
|
||||
}
|
||||
|
|
@ -3720,6 +3725,32 @@ static RegisterPrimOp primop_sort({
|
|||
|
||||
This is a stable sort: it preserves the relative order of elements
|
||||
deemed equal by the comparator.
|
||||
|
||||
*comparator* must impose a strict weak ordering on the set of values
|
||||
in the *list*. This means that for any elements *a*, *b* and *c* from the
|
||||
*list*, *comparator* must satisfy the following relations:
|
||||
|
||||
1. Transitivity
|
||||
|
||||
```nix
|
||||
comparator a b && comparator b c -> comparator a c
|
||||
```
|
||||
|
||||
1. Irreflexivity
|
||||
|
||||
```nix
|
||||
comparator a a == false
|
||||
```
|
||||
|
||||
1. Transitivity of equivalence
|
||||
|
||||
```nix
|
||||
let equiv = a: b: (!comparator a b && !comparator b a); in
|
||||
equiv a b && equiv b c -> equiv a c
|
||||
```
|
||||
|
||||
If the *comparator* violates any of these properties, then `builtins.sort`
|
||||
reorders elements in an unspecified manner.
|
||||
)",
|
||||
.fun = prim_sort,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
generated_headers += gen_header.process(
|
||||
'derivation.nix',
|
||||
preserve_path_from: meson.project_source_root(),
|
||||
preserve_path_from : meson.project_source_root(),
|
||||
)
|
||||
|
||||
sources += files(
|
||||
|
|
|
|||
|
|
@ -453,13 +453,13 @@ private:
|
|||
|
||||
if (v.isLambda()) {
|
||||
output << "lambda";
|
||||
if (v.payload.lambda.fun) {
|
||||
if (v.payload.lambda.fun->name) {
|
||||
output << " " << state.symbols[v.payload.lambda.fun->name];
|
||||
if (v.lambda().fun) {
|
||||
if (v.lambda().fun->name) {
|
||||
output << " " << state.symbols[v.lambda().fun->name];
|
||||
}
|
||||
|
||||
std::ostringstream s;
|
||||
s << state.positions[v.payload.lambda.fun->pos];
|
||||
s << state.positions[v.lambda().fun->pos];
|
||||
output << " @ " << filterANSIEscapes(toView(s));
|
||||
}
|
||||
} else if (v.isPrimOp()) {
|
||||
|
|
|
|||
|
|
@ -126,18 +126,18 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||
break;
|
||||
}
|
||||
XMLAttrs xmlAttrs;
|
||||
if (location) posToXML(state, xmlAttrs, state.positions[v.payload.lambda.fun->pos]);
|
||||
if (location) posToXML(state, xmlAttrs, state.positions[v.lambda().fun->pos]);
|
||||
XMLOpenElement _(doc, "function", xmlAttrs);
|
||||
|
||||
if (v.payload.lambda.fun->hasFormals()) {
|
||||
if (v.lambda().fun->hasFormals()) {
|
||||
XMLAttrs attrs;
|
||||
if (v.payload.lambda.fun->arg) attrs["name"] = state.symbols[v.payload.lambda.fun->arg];
|
||||
if (v.payload.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
if (v.lambda().fun->arg) attrs["name"] = state.symbols[v.lambda().fun->arg];
|
||||
if (v.lambda().fun->formals->ellipsis) attrs["ellipsis"] = "1";
|
||||
XMLOpenElement _(doc, "attrspat", attrs);
|
||||
for (auto & i : v.payload.lambda.fun->formals->lexicographicOrder(state.symbols))
|
||||
for (auto & i : v.lambda().fun->formals->lexicographicOrder(state.symbols))
|
||||
doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name]));
|
||||
} else
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.payload.lambda.fun->arg]));
|
||||
doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda().fun->arg]));
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue