libexpr: add multi-arg app nodes
these behave like the old chains of app nodes, but they can store more than one argument per node. for tApp values themselves this is not all that useful, but if we could share tApp and tPrimOpApp backing storage we could avoid creating and traversing the linked lists of values that are currently needed to represent partially applied builtin functions. Change-Id: I5a2a02d9733e1e0be5443459e2998d62fd3b9a5b
This commit is contained in:
@@ -5,9 +5,27 @@
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libexpr/eval-error.hh"
|
||||
#include "lix/libexpr/gc-alloc.hh"
|
||||
#include "value.hh"
|
||||
#include <cstdint>
|
||||
|
||||
namespace nix {
|
||||
|
||||
inline Value::Value(app_t, EvalMemory & mem, Value & lhs, std::span<Value *> args)
|
||||
: internalType(tApp)
|
||||
{
|
||||
if (args.size() == 1) {
|
||||
_app._left = reinterpret_cast<uintptr_t>(&lhs);
|
||||
_app._right = args[0];
|
||||
} else {
|
||||
auto app =
|
||||
static_cast<Value::AppN *>(mem.allocBytes(sizeof(Value::AppN) + args.size_bytes()));
|
||||
app->nargs = args.size();
|
||||
memcpy(app->args, args.data(), args.size_bytes());
|
||||
_app._left = reinterpret_cast<uintptr_t>(&lhs) | 1;
|
||||
_app._appn = app;
|
||||
}
|
||||
}
|
||||
|
||||
[[gnu::always_inline]]
|
||||
void * EvalMemory::allocBytes(size_t size)
|
||||
{
|
||||
@@ -89,12 +107,11 @@ void EvalState::forceValue(Value & v, const PosIdx pos)
|
||||
tryFixupBlackHolePos(v, pos);
|
||||
throw;
|
||||
}
|
||||
} else if (v.isApp()) {
|
||||
callFunction(*v.app().left(), v.app().args(), v, pos);
|
||||
}
|
||||
else if (v.isApp())
|
||||
callFunction(*v.app().left, *v.app().right, v, pos);
|
||||
}
|
||||
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline void EvalState::forceAttrs(Value & v, const PosIdx pos, std::string_view errorCtx)
|
||||
{
|
||||
|
||||
@@ -157,6 +157,7 @@ void initLibExpr()
|
||||
/* Don't look for interior pointers. This reduces the odds of
|
||||
misdetection a bit. */
|
||||
GC_set_all_interior_pointers(0);
|
||||
GC_REGISTER_DISPLACEMENT(1);
|
||||
|
||||
/* We don't have any roots in data segments, so don't scan from
|
||||
there. */
|
||||
|
||||
@@ -1955,10 +1955,9 @@ static void prim_mapAttrs(EvalState & state, Value * * args, Value & v)
|
||||
auto attrs = state.ctx.buildBindings(args[1]->attrs()->size());
|
||||
|
||||
for (auto & i : *args[1]->attrs()) {
|
||||
Value * vFun2 = state.ctx.mem.allocValue();
|
||||
auto vName = const_cast<Value *>(state.ctx.symbols[i.name].toValuePtr());
|
||||
vFun2->mkApp(args[0], vName);
|
||||
attrs.alloc(i.name).mkApp(vFun2, i.value);
|
||||
Value * appArgs[] = {vName, i.value};
|
||||
attrs.alloc(i.name) = {NewValueAs::app, state.ctx.mem, *args[0], appArgs};
|
||||
}
|
||||
|
||||
v.mkAttrs(attrs.alreadySorted());
|
||||
@@ -1997,13 +1996,12 @@ static void prim_zipAttrsWith(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
/* Construct a `fn name list` function call value. */
|
||||
auto name = const_cast<Value *>(state.ctx.symbols[sym].toValuePtr());
|
||||
auto call1 = state.ctx.mem.allocValue();
|
||||
call1->mkApp(args[0], name);
|
||||
auto call2 = state.ctx.mem.allocValue();
|
||||
call2->mkApp(call1, list);
|
||||
Value * callArgs[] = {name, list};
|
||||
auto call = state.ctx.mem.allocValue();
|
||||
*call = {NewValueAs::app, state.ctx.mem, *args[0], callArgs};
|
||||
|
||||
/* Insert it inside the returned attribute set. */
|
||||
attrs.insert(sym, call2);
|
||||
attrs.insert(sym, call);
|
||||
}
|
||||
|
||||
/* Populate the lists inside the attribute set */
|
||||
|
||||
+41
-9
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <ranges>
|
||||
#include <span>
|
||||
@@ -19,6 +20,7 @@
|
||||
namespace nix {
|
||||
|
||||
class BindingsBuilder;
|
||||
class EvalMemory;
|
||||
|
||||
typedef enum {
|
||||
tInt = 1,
|
||||
@@ -440,8 +442,13 @@ public:
|
||||
/// lazy partial application of another lambda.
|
||||
Value(app_t, Value & lhs, Value & rhs)
|
||||
: internalType(tApp)
|
||||
, _app({ .left = &lhs, .right = &rhs })
|
||||
{ }
|
||||
, _app{._left = reinterpret_cast<uintptr_t>(&lhs), ._right = &rhs}
|
||||
{
|
||||
}
|
||||
|
||||
/// Constructs a nix language value of type "lambda", which represents a
|
||||
/// lazy partial application of another lambda.
|
||||
Value(app_t, EvalMemory & mem, Value & lhs, std::span<Value *> args);
|
||||
|
||||
/// Constructs a nix language value of type "external", which is only used
|
||||
/// by plugins. Do any existing plugins even use this mechanism?
|
||||
@@ -562,6 +569,17 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct AppN
|
||||
{
|
||||
size_t nargs;
|
||||
Value * args[0];
|
||||
|
||||
std::span<Value *> argsSpan()
|
||||
{
|
||||
return {args, nargs};
|
||||
}
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
/// Dummy field, which takes up as much space as the largest union variants
|
||||
@@ -589,9 +607,25 @@ public:
|
||||
Expr * expr;
|
||||
} _thunk;
|
||||
struct {
|
||||
Value * left, * right;
|
||||
uintptr_t _left;
|
||||
union
|
||||
{
|
||||
Value * _right;
|
||||
AppN * _appn;
|
||||
};
|
||||
|
||||
Value * left() const
|
||||
{
|
||||
return reinterpret_cast<Value *>(_left & ~uintptr_t(1));
|
||||
}
|
||||
|
||||
std::span<Value *> args()
|
||||
{
|
||||
return _left & 1 ? _appn->argsSpan() : std::span{&_right, 1};
|
||||
}
|
||||
} _app;
|
||||
struct {
|
||||
struct
|
||||
{
|
||||
Env * env;
|
||||
ExprLambda * fun;
|
||||
} _lambda;
|
||||
@@ -647,7 +681,7 @@ public:
|
||||
*/
|
||||
inline void clearValue()
|
||||
{
|
||||
_app.left = _app.right = 0;
|
||||
_empty[0] = _empty[1] = 0;
|
||||
}
|
||||
|
||||
inline void mkInt(NixInt::Inner n)
|
||||
@@ -716,9 +750,7 @@ public:
|
||||
|
||||
inline void mkApp(Value * l, Value * r)
|
||||
{
|
||||
internalType = tApp;
|
||||
_app.left = l;
|
||||
_app.right = r;
|
||||
*this = {NewValueAs::app, *l, *r};
|
||||
}
|
||||
|
||||
inline void mkLambda(Env * e, ExprLambda * f)
|
||||
@@ -849,7 +881,7 @@ public:
|
||||
return _thunk;
|
||||
}
|
||||
|
||||
const auto & app() const
|
||||
auto & app()
|
||||
{
|
||||
return _app;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ TEST_F(ValuePrintingTests, vThunk)
|
||||
TEST_F(ValuePrintingTests, vApp)
|
||||
{
|
||||
Value vApp;
|
||||
vApp.mkApp(nullptr, nullptr);
|
||||
vApp.mkApp(&vApp, &vApp);
|
||||
|
||||
test(vApp, "«thunk»");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user