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:
parent
6d90287f5a
commit
8c0590fa32
35 changed files with 530 additions and 556 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue