1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-06 09:11:47 +02:00

Never update values after setting the type

Thunks are now overwritten by a helper function
`Value::finishValue(newType, payload)` (where `payload` is the
original anonymous union inside `Value`). This helps to ensure we
never update a value elsewhere, since that would be incompatible with
parallel evaluation (i.e. after a value has transitioned from being a
thunk to being a non-thunk, it should be immutable).

There were two places where this happened: `Value::mkString()` and
`ExprAttrs::eval()`.

This PR also adds a bunch of accessor functions for value contents,
like `Value::integer()` to access the integer field in the union.
This commit is contained in:
Eelco Dolstra 2024-03-25 18:20:18 +01:00
parent 6d90287f5a
commit 8c0590fa32
35 changed files with 530 additions and 556 deletions

View file

@ -234,14 +234,14 @@ public:
ExprLambda * fun;
};
union
using Payload = union
{
NixInt integer;
bool boolean;
StringWithContext string;
Path _path;
Path path;
Bindings * attrs;
struct {
@ -258,6 +258,8 @@ public:
NixFloat fpoint;
};
Payload payload;
/**
* Returns the normal type of a Value. This only returns nThunk if
* the Value hasn't been forceValue'd
@ -286,34 +288,31 @@ public:
abort();
}
/**
* After overwriting an app node, be sure to clear pointers in the
* Value to ensure that the target isn't kept alive unnecessarily.
*/
inline void clearValue()
inline void finishValue(InternalType newType, Payload newPayload)
{
app.left = app.right = 0;
/* After overwriting thunk/app values, be sure to clear
pointers in the Value to ensure that the target isn't kept
alive unnecessarily. */
payload.app.left = payload.app.right = 0;
payload = newPayload;
internalType = newType;
}
inline void mkInt(NixInt n)
{
clearValue();
internalType = tInt;
integer = n;
finishValue(tInt, { .integer = n });
}
inline void mkBool(bool b)
{
clearValue();
internalType = tBool;
boolean = b;
finishValue(tBool, { .boolean = b });
}
inline void mkString(const char * s, const char * * context = 0)
{
internalType = tString;
string.c_str = s;
string.context = context;
finishValue(tString, { .string = { .c_str = s, .context = context } });
}
void mkString(std::string_view s);
@ -331,63 +330,44 @@ public:
inline void mkPath(InputAccessor * accessor, const char * path)
{
clearValue();
internalType = tPath;
_path.accessor = accessor;
_path.path = path;
finishValue(tPath, { .path = { .accessor = accessor, .path = path } });
}
inline void mkNull()
{
clearValue();
internalType = tNull;
finishValue(tNull, {});
}
inline void mkAttrs(Bindings * a)
{
clearValue();
internalType = tAttrs;
attrs = a;
finishValue(tAttrs, { .attrs = a });
}
Value & mkAttrs(BindingsBuilder & bindings);
void mkList(const ListBuilder & builder)
{
clearValue();
if (builder.size == 1) {
smallList[0] = builder.inlineElems[0];
internalType = tList1;
} else if (builder.size == 2) {
smallList[0] = builder.inlineElems[0];
smallList[1] = builder.inlineElems[1];
internalType = tList2;
} else {
bigList.size = builder.size;
bigList.elems = builder.elems;
internalType = tListN;
}
if (builder.size == 1)
finishValue(tList1, { .smallList = { builder.inlineElems[0] } });
else if (builder.size == 2)
finishValue(tList2, { .smallList = { builder.inlineElems[0], builder.inlineElems[1] } });
else
finishValue(tListN, { .bigList = { .size = builder.size, .elems = builder.elems } });
}
inline void mkThunk(Env * e, Expr * ex)
{
internalType = tThunk;
thunk.env = e;
thunk.expr = ex;
finishValue(tThunk, { .thunk = { .env = e, .expr = ex } });
}
inline void mkApp(Value * l, Value * r)
{
internalType = tApp;
app.left = l;
app.right = r;
finishValue(tApp, { .app = { .left = l, .right = r } });
}
inline void mkLambda(Env * e, ExprLambda * f)
{
internalType = tLambda;
lambda.env = e;
lambda.fun = f;
finishValue(tLambda, { .lambda = { .env = e, .fun = f } });
}
inline void mkBlackhole();
@ -396,28 +376,22 @@ public:
inline void mkPrimOpApp(Value * l, Value * r)
{
internalType = tPrimOpApp;
primOpApp.left = l;
primOpApp.right = r;
finishValue(tPrimOpApp, { .primOpApp = { .left = l, .right = r } });
}
/**
* For a `tPrimOpApp` value, get the original `PrimOp` value.
*/
PrimOp * primOpAppPrimOp() const;
const PrimOp * primOpAppPrimOp() const;
inline void mkExternal(ExternalValueBase * e)
{
clearValue();
internalType = tExternal;
external = e;
finishValue(tExternal, { .external = e });
}
inline void mkFloat(NixFloat n)
{
clearValue();
internalType = tFloat;
fpoint = n;
finishValue(tFloat, { .fpoint = n });
}
bool isList() const
@ -427,7 +401,7 @@ public:
Value * const * listElems()
{
return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems;
return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems;
}
std::span<Value * const> listItems() const
@ -438,12 +412,12 @@ public:
Value * const * listElems() const
{
return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems;
return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems;
}
size_t listSize() const
{
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size;
return internalType == tList1 ? 1 : internalType == tList2 ? 2 : payload.bigList.size;
}
PosIdx determinePos(const PosIdx pos) const;
@ -459,26 +433,44 @@ public:
{
assert(internalType == tPath);
return SourcePath(
ref(_path.accessor->shared_from_this()),
CanonPath(CanonPath::unchecked_t(), _path.path));
ref(payload.path.accessor->shared_from_this()),
CanonPath(CanonPath::unchecked_t(), payload.path.path));
}
std::string_view string_view() const
{
assert(internalType == tString);
return std::string_view(string.c_str);
return std::string_view(payload.string.c_str);
}
const char * const c_str() const
{
assert(internalType == tString);
return string.c_str;
return payload.string.c_str;
}
const char * * context() const
{
return string.context;
return payload.string.context;
}
ExternalValueBase * external() const
{ return payload.external; }
const Bindings * attrs() const
{ return payload.attrs; }
const PrimOp * primOp() const
{ return payload.primOp; }
bool boolean() const
{ return payload.boolean; }
NixInt integer() const
{ return payload.integer; }
NixFloat fpoint() const
{ return payload.fpoint; }
};
@ -486,13 +478,12 @@ extern ExprBlackHole eBlackHole;
bool Value::isBlackhole() const
{
return internalType == tThunk && thunk.expr == (Expr*) &eBlackHole;
return internalType == tThunk && payload.thunk.expr == (Expr*) &eBlackHole;
}
void Value::mkBlackhole()
{
internalType = tThunk;
thunk.expr = (Expr*) &eBlackHole;
mkThunk(nullptr, (Expr *) &eBlackHole);
}