diff --git a/lix/libexpr/eval-inline.hh b/lix/libexpr/eval-inline.hh index 3d414928d..a11df926e 100644 --- a/lix/libexpr/eval-inline.hh +++ b/lix/libexpr/eval-inline.hh @@ -20,11 +20,21 @@ inline Value::Value(app_t, EvalMemory & mem, Value & lhs, Value & rhs) } inline Value::Value(app_t, EvalMemory & mem, Value & lhs, std::span args) + : Value(app_t{}, mem, lhs, args, {}) { - auto app = static_cast(mem.allocBytes(sizeof(Value::App) + args.size_bytes())); +} + +inline Value::Value( + app_t, EvalMemory & mem, const Value & lhs, std::span baseArgs, std::span moreArgs +) +{ + auto app = static_cast( + mem.allocBytes(sizeof(Value::App) + baseArgs.size_bytes() + moreArgs.size_bytes()) + ); app->_left = lhs; - app->_n = args.size(); - std::copy(args.begin(), args.end(), app->_args); + app->_n = baseArgs.size() + moreArgs.size(); + std::copy(baseArgs.begin(), baseArgs.end(), app->_args); + std::copy(moreArgs.begin(), moreArgs.end(), app->_args + baseArgs.size()); raw = tag(tApp, app); } diff --git a/lix/libexpr/eval.cc b/lix/libexpr/eval.cc index d532d5da9..4e12801c3 100644 --- a/lix/libexpr/eval.cc +++ b/lix/libexpr/eval.cc @@ -1639,7 +1639,14 @@ void EvalState::callFunction(Value & fun, std::span args, Value & vRes, c Value vCur(fun); - auto makeAppChain = [&]() { vRes = {NewValueAs::app, ctx.mem, vCur, args}; }; + auto makeAppChain = [&]() { + if (vCur.isApp()) { + auto & app = vCur.app(); + vRes = {NewValueAs::app, ctx.mem, app.left(), app.args(), args}; + } else { + vRes = {NewValueAs::app, ctx.mem, vCur, args}; + } + }; const Attr * functor; diff --git a/lix/libexpr/value.hh b/lix/libexpr/value.hh index 1f3d80f8f..75a16fae6 100644 --- a/lix/libexpr/value.hh +++ b/lix/libexpr/value.hh @@ -544,6 +544,10 @@ public: /// lazy and/or partial application of a function. Value(app_t, EvalMemory & mem, Value & lhs, std::span args); + /// Constructs a nix language value of type "lambda", which represents a + /// lazy and/or partial application of a function. + Value(app_t, EvalMemory & mem, const Value & lhs, std::span baseArgs, std::span moreArgs); + /// Constructs a nix language value of type "external", which is only used /// by plugins. Do any existing plugins even use this mechanism? Value(external_t, ExternalValueBase & external) diff --git a/tests/functional2/lang/call-primop/eval-fail.err.exp b/tests/functional2/lang/call-primop/eval-fail-incomplete.err.exp similarity index 100% rename from tests/functional2/lang/call-primop/eval-fail.err.exp rename to tests/functional2/lang/call-primop/eval-fail-incomplete.err.exp diff --git a/tests/functional2/lang/call-primop/eval-okay-app-extension.out.exp b/tests/functional2/lang/call-primop/eval-okay-app-extension.out.exp new file mode 100644 index 000000000..ce291fa51 --- /dev/null +++ b/tests/functional2/lang/call-primop/eval-okay-app-extension.out.exp @@ -0,0 +1 @@ +"234" diff --git a/tests/functional2/lang/call-primop/in-app-extension.nix b/tests/functional2/lang/call-primop/in-app-extension.nix new file mode 100644 index 000000000..bc627c0f2 --- /dev/null +++ b/tests/functional2/lang/call-primop/in-app-extension.nix @@ -0,0 +1,5 @@ +let + a = builtins.substring 1; + b = a 3; +in +b "1234567890" diff --git a/tests/functional2/lang/call-primop/in.nix b/tests/functional2/lang/call-primop/in-incomplete.nix similarity index 100% rename from tests/functional2/lang/call-primop/in.nix rename to tests/functional2/lang/call-primop/in-incomplete.nix