From 3a3c0629826781e4e51266e016e9e964e0ba5ff3 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 27 Oct 2025 21:29:17 +0100 Subject: [PATCH 1/5] libexpr: store ExprLambda data in Expr::alloc --- src/libexpr-tests/value/print.cc | 4 +- src/libexpr/eval.cc | 16 ++--- src/libexpr/include/nix/expr/nixexpr.hh | 64 ++++++++++++++------ src/libexpr/include/nix/expr/parser-state.hh | 16 +++-- src/libexpr/nixexpr.cc | 11 ++-- src/libexpr/parser.y | 28 +++++---- src/libexpr/primops.cc | 2 +- src/libexpr/value-to-xml.cc | 4 +- src/libflake/flake.cc | 2 +- src/nix/nix-build/nix-build.cc | 2 +- 10 files changed, 88 insertions(+), 61 deletions(-) diff --git a/src/libexpr-tests/value/print.cc b/src/libexpr-tests/value/print.cc index 6cadbc70a..20a6f8614 100644 --- a/src/libexpr-tests/value/print.cc +++ b/src/libexpr-tests/value/print.cc @@ -112,7 +112,7 @@ TEST_F(ValuePrintingTests, vLambda) auto body = ExprInt(0); auto formals = Formals{}; - ExprLambda eLambda(posIdx, createSymbol("a"), &formals, &body); + ExprLambda eLambda(state.mem.exprs.alloc, posIdx, createSymbol("a"), formals, &body); Value vLambda; vLambda.mkLambda(&env, &eLambda); @@ -502,7 +502,7 @@ TEST_F(ValuePrintingTests, ansiColorsLambda) auto body = ExprInt(0); auto formals = Formals{}; - ExprLambda eLambda(posIdx, createSymbol("a"), &formals, &body); + ExprLambda eLambda(state.mem.exprs.alloc, posIdx, createSymbol("a"), formals, &body); Value vLambda; vLambda.mkLambda(&env, &eLambda); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 8a03084ee..7f16ddf8f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1496,7 +1496,7 @@ void EvalState::callFunction(Value & fun, std::span args, Value & vRes, ExprLambda & lambda(*vCur.lambda().fun); - auto size = (!lambda.arg ? 0 : 1) + (lambda.hasFormals() ? lambda.formals->formals.size() : 0); + auto size = (!lambda.arg ? 0 : 1) + lambda.nFormals; Env & env2(mem.allocEnv(size)); env2.up = vCur.lambda().env; @@ -1520,7 +1520,7 @@ void EvalState::callFunction(Value & fun, std::span args, Value & vRes, there is no matching actual argument but the formal argument has a default, use the default. */ size_t attrsUsed = 0; - for (auto & i : lambda.formals->formals) { + for (auto & i : lambda.getFormals()) { auto j = args[0]->attrs()->get(i.name); if (!j) { if (!i.def) { @@ -1542,13 +1542,13 @@ void EvalState::callFunction(Value & fun, std::span args, Value & vRes, /* Check that each actual argument is listed as a formal argument (unless the attribute match specifies a `...'). */ - if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs()->size()) { + if (!lambda.ellipsis && attrsUsed != args[0]->attrs()->size()) { /* Nope, so show the first unexpected argument to the user. */ for (auto & i : *args[0]->attrs()) - if (!lambda.formals->has(i.name)) { + if (!lambda.hasFormal(i.name)) { StringSet formalNames; - for (auto & formal : lambda.formals->formals) + for (auto & formal : lambda.getFormals()) formalNames.insert(std::string(symbols[formal.name])); auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]); error( @@ -1752,9 +1752,9 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res return; } - auto attrs = buildBindings(std::max(static_cast(fun.lambda().fun->formals->formals.size()), args.size())); + auto attrs = buildBindings(std::max(static_cast(fun.lambda().fun->nFormals), args.size())); - if (fun.lambda().fun->formals->ellipsis) { + if (fun.lambda().fun->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) @@ -1762,7 +1762,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.lambda().fun->formals->formals) { + for (auto & i : fun.lambda().fun->getFormals()) { auto j = args.get(i.name); if (j) { attrs.insert(*j); diff --git a/src/libexpr/include/nix/expr/nixexpr.hh b/src/libexpr/include/nix/expr/nixexpr.hh index 26d5addd5..56c378660 100644 --- a/src/libexpr/include/nix/expr/nixexpr.hh +++ b/src/libexpr/include/nix/expr/nixexpr.hh @@ -481,16 +481,6 @@ struct Formals formals.begin(), formals.end(), arg, [](const Formal & f, const Symbol & sym) { return f.name < sym; }); return it != formals.end() && it->name == arg; } - - std::vector lexicographicOrder(const SymbolTable & symbols) const - { - std::vector result(formals.begin(), formals.end()); - std::sort(result.begin(), result.end(), [&](const Formal & a, const Formal & b) { - std::string_view sa = symbols[a.name], sb = symbols[b.name]; - return sa < sb; - }); - return result; - } }; struct ExprLambda : Expr @@ -498,21 +488,42 @@ struct ExprLambda : Expr PosIdx pos; Symbol name; Symbol arg; - Formals * formals; + + bool ellipsis; + uint16_t nFormals; + Formal * formalsStart; + Expr * body; DocComment docComment; - ExprLambda(PosIdx pos, Symbol arg, Formals * formals, Expr * body) + ExprLambda( + std::pmr::polymorphic_allocator & alloc, PosIdx pos, Symbol arg, const Formals & formals, Expr * body) : pos(pos) , arg(arg) - , formals(formals) - , body(body) {}; - - ExprLambda(PosIdx pos, Formals * formals, Expr * body) - : pos(pos) - , formals(formals) + , ellipsis(formals.ellipsis) + , nFormals(formals.formals.size()) + , formalsStart(alloc.allocate_object(nFormals)) , body(body) { + std::ranges::copy(formals.formals, formalsStart); + }; + + ExprLambda(std::pmr::polymorphic_allocator & alloc, PosIdx pos, Symbol arg, Expr * body) + : pos(pos) + , arg(arg) + , nFormals(0) + , formalsStart(nullptr) + , body(body) {}; + + ExprLambda(std::pmr::polymorphic_allocator & alloc, PosIdx pos, Formals formals, Expr * body) + : ExprLambda(alloc, pos, Symbol(), formals, body) {}; + + bool hasFormal(Symbol arg) const + { + auto formals = getFormals(); + auto it = std::lower_bound( + formals.begin(), formals.end(), arg, [](const Formal & f, const Symbol & sym) { return f.name < sym; }); + return it != formals.end() && it->name == arg; } void setName(Symbol name) override; @@ -520,7 +531,17 @@ struct ExprLambda : Expr inline bool hasFormals() const { - return formals != nullptr; + return nFormals > 0; + } + + std::vector getFormalsLexicographic(const SymbolTable & symbols) const + { + std::vector result(getFormals().begin(), getFormals().end()); + std::sort(result.begin(), result.end(), [&](const Formal & a, const Formal & b) { + std::string_view sa = symbols[a.name], sb = symbols[b.name]; + return sa < sb; + }); + return result; } PosIdx getPos() const override @@ -528,6 +549,11 @@ struct ExprLambda : Expr return pos; } + std::span getFormals() const + { + return {formalsStart, nFormals}; + } + virtual void setDocComment(DocComment docComment) override; COMMON_METHODS }; diff --git a/src/libexpr/include/nix/expr/parser-state.hh b/src/libexpr/include/nix/expr/parser-state.hh index 18d2051d0..4d1312443 100644 --- a/src/libexpr/include/nix/expr/parser-state.hh +++ b/src/libexpr/include/nix/expr/parser-state.hh @@ -93,7 +93,7 @@ struct ParserState void addAttr( ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc); void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def); - Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {}); + void validateFormals(Formals & formals, PosIdx pos = noPos, Symbol arg = {}); Expr * stripIndentation(const PosIdx pos, std::vector>> && es); PosIdx at(const ParserLocation & loc); }; @@ -213,17 +213,17 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb } } -inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg) +inline void ParserState::validateFormals(Formals & formals, PosIdx pos, Symbol arg) { - std::sort(formals->formals.begin(), formals->formals.end(), [](const auto & a, const auto & b) { + std::sort(formals.formals.begin(), formals.formals.end(), [](const auto & a, const auto & b) { return std::tie(a.name, a.pos) < std::tie(b.name, b.pos); }); std::optional> duplicate; - for (size_t i = 0; i + 1 < formals->formals.size(); i++) { - if (formals->formals[i].name != formals->formals[i + 1].name) + for (size_t i = 0; i + 1 < formals.formals.size(); i++) { + if (formals.formals[i].name != formals.formals[i + 1].name) continue; - std::pair thisDup{formals->formals[i].name, formals->formals[i + 1].pos}; + std::pair thisDup{formals.formals[i].name, formals.formals[i + 1].pos}; duplicate = std::min(thisDup, duplicate.value_or(thisDup)); } if (duplicate) @@ -231,11 +231,9 @@ inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Sym {.msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]), .pos = positions[duplicate->second]}); - if (arg && formals->has(arg)) + if (arg && formals.has(arg)) throw ParseError( {.msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]), .pos = positions[pos]}); - - return formals; } inline Expr * diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index a77e42356..2fadc5e33 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -160,7 +160,7 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const // the natural Symbol ordering is by creation time, which can lead to the // same expression being printed in two different ways depending on its // context. always use lexicographic ordering to avoid this. - for (auto & i : formals->lexicographicOrder(symbols)) { + for (auto & i : getFormalsLexicographic(symbols)) { if (first) first = false; else @@ -171,7 +171,7 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const i.def->show(symbols, str); } } - if (formals->ellipsis) { + if (ellipsis) { if (!first) str << ", "; str << "..."; @@ -451,8 +451,7 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); - auto newEnv = - std::make_shared(nullptr, env, (hasFormals() ? formals->formals.size() : 0) + (!arg ? 0 : 1)); + auto newEnv = std::make_shared(nullptr, env, nFormals + (!arg ? 0 : 1)); Displacement displ = 0; @@ -460,12 +459,12 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr newEnv->vars.emplace_back(arg, displ++); if (hasFormals()) { - for (auto & i : formals->formals) + for (auto & i : getFormals()) newEnv->vars.emplace_back(i.name, displ++); newEnv->sort(); - for (auto & i : formals->formals) + for (auto & i : getFormals()) if (i.def) i.def->bindVars(es, newEnv); } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 51c82efe5..49cd534d7 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -131,7 +131,7 @@ static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) { %type expr_pipe_from expr_pipe_into %type > list %type binds binds1 -%type formals formal_set +%type formals formal_set %type formal %type > attrpath %type >> attrs @@ -179,26 +179,30 @@ expr: expr_function; expr_function : ID ':' expr_function - { auto me = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); + { auto me = new ExprLambda(state->alloc, CUR_POS, state->symbols.create($1), $3); $$ = me; SET_DOC_POS(me, @1); } | formal_set ':' expr_function[body] - { auto me = new ExprLambda(CUR_POS, state->validateFormals($formal_set), $body); + { + state->validateFormals($formal_set); + auto me = new ExprLambda(state->alloc, CUR_POS, std::move($formal_set), $body); $$ = me; SET_DOC_POS(me, @1); } | formal_set '@' ID ':' expr_function[body] { auto arg = state->symbols.create($ID); - auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($formal_set, CUR_POS, arg), $body); + state->validateFormals($formal_set, CUR_POS, arg); + auto me = new ExprLambda(state->alloc, CUR_POS, arg, std::move($formal_set), $body); $$ = me; SET_DOC_POS(me, @1); } | ID '@' formal_set ':' expr_function[body] { auto arg = state->symbols.create($ID); - auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($formal_set, CUR_POS, arg), $body); + state->validateFormals($formal_set, CUR_POS, arg); + auto me = new ExprLambda(state->alloc, CUR_POS, arg, std::move($formal_set), $body); $$ = me; SET_DOC_POS(me, @1); } @@ -490,18 +494,18 @@ list ; formal_set - : '{' formals ',' ELLIPSIS '}' { $$ = $formals; $$->ellipsis = true; } - | '{' ELLIPSIS '}' { $$ = new Formals; $$->ellipsis = true; } - | '{' formals ',' '}' { $$ = $formals; $$->ellipsis = false; } - | '{' formals '}' { $$ = $formals; $$->ellipsis = false; } - | '{' '}' { $$ = new Formals; $$->ellipsis = false; } + : '{' formals ',' ELLIPSIS '}' { $$ = std::move($formals); $$.ellipsis = true; } + | '{' ELLIPSIS '}' { $$.ellipsis = true; } + | '{' formals ',' '}' { $$ = std::move($formals); $$.ellipsis = false; } + | '{' formals '}' { $$ = std::move($formals); $$.ellipsis = false; } + | '{' '}' { $$.ellipsis = false; } ; formals : formals[accum] ',' formal - { $$ = $accum; $$->formals.emplace_back(std::move($formal)); } + { $$ = std::move($accum); $$.formals.emplace_back(std::move($formal)); } | formal - { $$ = new Formals; $$->formals.emplace_back(std::move($formal)); } + { $$.formals.emplace_back(std::move($formal)); } ; formal diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index c8e49cd29..35670f451 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -3368,7 +3368,7 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value ** args return; } - const auto & formals = args[0]->lambda().fun->formals->formals; + const auto & formals = args[0]->lambda().fun->getFormals(); auto attrs = state.buildBindings(formals.size()); for (auto & i : formals) attrs.insert(i.name, state.getBool(i.def), i.pos); diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 31400e439..7865e25dc 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -149,10 +149,10 @@ static void printValueAsXML( XMLAttrs attrs; if (v.lambda().fun->arg) attrs["name"] = state.symbols[v.lambda().fun->arg]; - if (v.lambda().fun->formals->ellipsis) + if (v.lambda().fun->ellipsis) attrs["ellipsis"] = "1"; XMLOpenElement _(doc, "attrspat", attrs); - for (auto & i : v.lambda().fun->formals->lexicographicOrder(state.symbols)) + for (auto & i : v.lambda().fun->getFormalsLexicographic(state.symbols)) doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name])); } else doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda().fun->arg])); diff --git a/src/libflake/flake.cc b/src/libflake/flake.cc index 2aec0fac4..dec39f738 100644 --- a/src/libflake/flake.cc +++ b/src/libflake/flake.cc @@ -282,7 +282,7 @@ static Flake readFlake( expectType(state, nFunction, *outputs->value, outputs->pos); if (outputs->value->isLambda() && outputs->value->lambda().fun->hasFormals()) { - for (auto & formal : outputs->value->lambda().fun->formals->formals) { + for (auto & formal : outputs->value->lambda().fun->getFormals()) { if (formal.name != state.s.self) flake.inputs.emplace( state.symbols[formal.name], diff --git a/src/nix/nix-build/nix-build.cc b/src/nix/nix-build/nix-build.cc index ab65fba7e..f5c7884a9 100644 --- a/src/nix/nix-build/nix-build.cc +++ b/src/nix/nix-build/nix-build.cc @@ -416,7 +416,7 @@ static void main_nix_build(int argc, char ** argv) } bool add = false; if (v.type() == nFunction && v.lambda().fun->hasFormals()) { - for (auto & i : v.lambda().fun->formals->formals) { + for (auto & i : v.lambda().fun->getFormals()) { if (state->symbols[i.name] == "inNixShell") { add = true; break; From 4a80c92a4dcff9cde9892383c4e5d2c101eb6cda Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 27 Oct 2025 23:05:24 +0100 Subject: [PATCH 2/5] add test for no formals case --- .../functional/lang/eval-fail-empty-formals.err.exp | 12 ++++++++++++ tests/functional/lang/eval-fail-empty-formals.nix | 1 + 2 files changed, 13 insertions(+) create mode 100644 tests/functional/lang/eval-fail-empty-formals.err.exp create mode 100644 tests/functional/lang/eval-fail-empty-formals.nix diff --git a/tests/functional/lang/eval-fail-empty-formals.err.exp b/tests/functional/lang/eval-fail-empty-formals.err.exp new file mode 100644 index 000000000..5cd4829f7 --- /dev/null +++ b/tests/functional/lang/eval-fail-empty-formals.err.exp @@ -0,0 +1,12 @@ +error: + … from call site + at /pwd/lang/eval-fail-empty-formals.nix:1:1: + 1| (foo@{ }: 1) { a = 3; } + | ^ + 2| + + error: function 'anonymous lambda' called with unexpected argument 'a' + at /pwd/lang/eval-fail-empty-formals.nix:1:2: + 1| (foo@{ }: 1) { a = 3; } + | ^ + 2| diff --git a/tests/functional/lang/eval-fail-empty-formals.nix b/tests/functional/lang/eval-fail-empty-formals.nix new file mode 100644 index 000000000..597f40496 --- /dev/null +++ b/tests/functional/lang/eval-fail-empty-formals.nix @@ -0,0 +1 @@ +(foo@{ }: 1) { a = 3; } From e43888890f39007ca4f0574d97026bc146756fd2 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Mon, 27 Oct 2025 22:58:37 +0100 Subject: [PATCH 3/5] restore proper handling of no formals vs. 0 formals e.g. (foo@{}: 1) { a = 3; } should error, but wasn't with the previous commit --- src/libexpr-tests/primops.cc | 2 +- src/libexpr/eval.cc | 6 +++--- src/libexpr/include/nix/expr/nixexpr.hh | 10 ++++------ src/libexpr/nixexpr.cc | 6 +++--- src/libexpr/primops.cc | 2 +- src/libexpr/value-to-xml.cc | 2 +- src/libflake/flake.cc | 2 +- src/nix/flake.cc | 2 +- src/nix/nix-build/nix-build.cc | 2 +- 9 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/libexpr-tests/primops.cc b/src/libexpr-tests/primops.cc index 74d676844..2c2adf5d6 100644 --- a/src/libexpr-tests/primops.cc +++ b/src/libexpr-tests/primops.cc @@ -771,7 +771,7 @@ TEST_F(PrimOpTest, derivation) ASSERT_EQ(v.type(), nFunction); ASSERT_TRUE(v.isLambda()); ASSERT_NE(v.lambda().fun, nullptr); - ASSERT_TRUE(v.lambda().fun->hasFormals()); + ASSERT_TRUE(v.lambda().fun->hasFormals); } TEST_F(PrimOpTest, currentTime) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 7f16ddf8f..ec203a0d9 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1496,13 +1496,13 @@ void EvalState::callFunction(Value & fun, std::span args, Value & vRes, ExprLambda & lambda(*vCur.lambda().fun); - auto size = (!lambda.arg ? 0 : 1) + lambda.nFormals; + auto size = (!lambda.arg ? 0 : 1) + (lambda.hasFormals ? lambda.getFormals().size() : 0); Env & env2(mem.allocEnv(size)); env2.up = vCur.lambda().env; Displacement displ = 0; - if (!lambda.hasFormals()) + if (!lambda.hasFormals) env2.values[displ++] = args[0]; else { try { @@ -1747,7 +1747,7 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res } } - if (!fun.isLambda() || !fun.lambda().fun->hasFormals()) { + if (!fun.isLambda() || !fun.lambda().fun->hasFormals) { res = fun; return; } diff --git a/src/libexpr/include/nix/expr/nixexpr.hh b/src/libexpr/include/nix/expr/nixexpr.hh index 56c378660..fbe8747c8 100644 --- a/src/libexpr/include/nix/expr/nixexpr.hh +++ b/src/libexpr/include/nix/expr/nixexpr.hh @@ -490,6 +490,7 @@ struct ExprLambda : Expr Symbol arg; bool ellipsis; + bool hasFormals; uint16_t nFormals; Formal * formalsStart; @@ -501,6 +502,7 @@ struct ExprLambda : Expr : pos(pos) , arg(arg) , ellipsis(formals.ellipsis) + , hasFormals(true) , nFormals(formals.formals.size()) , formalsStart(alloc.allocate_object(nFormals)) , body(body) @@ -511,7 +513,7 @@ struct ExprLambda : Expr ExprLambda(std::pmr::polymorphic_allocator & alloc, PosIdx pos, Symbol arg, Expr * body) : pos(pos) , arg(arg) - , nFormals(0) + , hasFormals(false) , formalsStart(nullptr) , body(body) {}; @@ -529,11 +531,6 @@ struct ExprLambda : Expr void setName(Symbol name) override; std::string showNamePos(const EvalState & state) const; - inline bool hasFormals() const - { - return nFormals > 0; - } - std::vector getFormalsLexicographic(const SymbolTable & symbols) const { std::vector result(getFormals().begin(), getFormals().end()); @@ -551,6 +548,7 @@ struct ExprLambda : Expr std::span getFormals() const { + assert(hasFormals); return {formalsStart, nFormals}; } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 2fadc5e33..ce1ea19e1 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -154,7 +154,7 @@ void ExprList::show(const SymbolTable & symbols, std::ostream & str) const void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const { str << "("; - if (hasFormals()) { + if (hasFormals) { str << "{ "; bool first = true; // the natural Symbol ordering is by creation time, which can lead to the @@ -451,14 +451,14 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); - auto newEnv = std::make_shared(nullptr, env, nFormals + (!arg ? 0 : 1)); + auto newEnv = std::make_shared(nullptr, env, (hasFormals ? getFormals().size() : 0) + (!arg ? 0 : 1)); Displacement displ = 0; if (arg) newEnv->vars.emplace_back(arg, displ++); - if (hasFormals()) { + if (hasFormals) { for (auto & i : getFormals()) newEnv->vars.emplace_back(i.name, displ++); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 35670f451..d95c999ff 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -3363,7 +3363,7 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value ** args if (!args[0]->isLambda()) state.error("'functionArgs' requires a function").atPos(pos).debugThrow(); - if (!args[0]->lambda().fun->hasFormals()) { + if (!args[0]->lambda().fun->hasFormals) { v.mkAttrs(&Bindings::emptyBindings); return; } diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 7865e25dc..547597ca4 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -145,7 +145,7 @@ static void printValueAsXML( posToXML(state, xmlAttrs, state.positions[v.lambda().fun->pos]); XMLOpenElement _(doc, "function", xmlAttrs); - if (v.lambda().fun->hasFormals()) { + if (v.lambda().fun->hasFormals) { XMLAttrs attrs; if (v.lambda().fun->arg) attrs["name"] = state.symbols[v.lambda().fun->arg]; diff --git a/src/libflake/flake.cc b/src/libflake/flake.cc index dec39f738..e2fe9805e 100644 --- a/src/libflake/flake.cc +++ b/src/libflake/flake.cc @@ -281,7 +281,7 @@ static Flake readFlake( if (auto outputs = vInfo.attrs()->get(sOutputs)) { expectType(state, nFunction, *outputs->value, outputs->pos); - if (outputs->value->isLambda() && outputs->value->lambda().fun->hasFormals()) { + if (outputs->value->isLambda() && outputs->value->lambda().fun->hasFormals) { for (auto & formal : outputs->value->lambda().fun->getFormals()) { if (formal.name != state.s.self) flake.inputs.emplace( diff --git a/src/nix/flake.cc b/src/nix/flake.cc index a7e7d0039..0e499fed4 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -468,7 +468,7 @@ struct CmdFlakeCheck : FlakeCommand if (!v.isLambda()) { throw Error("overlay is not a function, but %s instead", showType(v)); } - if (v.lambda().fun->hasFormals() || !argHasName(v.lambda().fun->arg, "final")) + if (v.lambda().fun->hasFormals || !argHasName(v.lambda().fun->arg, "final")) throw Error("overlay does not take an argument named 'final'"); // FIXME: if we have a 'nixpkgs' input, use it to // evaluate the overlay. diff --git a/src/nix/nix-build/nix-build.cc b/src/nix/nix-build/nix-build.cc index f5c7884a9..75bae5208 100644 --- a/src/nix/nix-build/nix-build.cc +++ b/src/nix/nix-build/nix-build.cc @@ -415,7 +415,7 @@ static void main_nix_build(int argc, char ** argv) return false; } bool add = false; - if (v.type() == nFunction && v.lambda().fun->hasFormals()) { + if (v.type() == nFunction && v.lambda().fun->hasFormals) { for (auto & i : v.lambda().fun->getFormals()) { if (state->symbols[i.name] == "inNixShell") { add = true; From 34f780d747d8de444b8f4b24767c073bdf393e36 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Fri, 31 Oct 2025 16:21:50 +0100 Subject: [PATCH 4/5] safer interface for ExprLambda's formals --- src/libexpr-tests/primops.cc | 2 +- src/libexpr-tests/value/print.cc | 6 +- src/libexpr/eval.cc | 25 ++++--- src/libexpr/include/nix/expr/nixexpr.hh | 77 ++++++++++++-------- src/libexpr/include/nix/expr/parser-state.hh | 4 +- src/libexpr/nixexpr.cc | 13 ++-- src/libexpr/parser.y | 2 +- src/libexpr/primops.cc | 23 +++--- src/libexpr/value-to-xml.cc | 6 +- src/libflake/flake.cc | 15 ++-- src/nix/flake.cc | 2 +- src/nix/nix-build/nix-build.cc | 12 +-- 12 files changed, 105 insertions(+), 82 deletions(-) diff --git a/src/libexpr-tests/primops.cc b/src/libexpr-tests/primops.cc index 2c2adf5d6..f00b4f475 100644 --- a/src/libexpr-tests/primops.cc +++ b/src/libexpr-tests/primops.cc @@ -771,7 +771,7 @@ TEST_F(PrimOpTest, derivation) ASSERT_EQ(v.type(), nFunction); ASSERT_TRUE(v.isLambda()); ASSERT_NE(v.lambda().fun, nullptr); - ASSERT_TRUE(v.lambda().fun->hasFormals); + ASSERT_TRUE(v.lambda().fun->getFormals()); } TEST_F(PrimOpTest, currentTime) diff --git a/src/libexpr-tests/value/print.cc b/src/libexpr-tests/value/print.cc index 20a6f8614..aa66c55fb 100644 --- a/src/libexpr-tests/value/print.cc +++ b/src/libexpr-tests/value/print.cc @@ -110,9 +110,8 @@ TEST_F(ValuePrintingTests, vLambda) PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1); auto posIdx = state.positions.add(origin, 0); auto body = ExprInt(0); - auto formals = Formals{}; - ExprLambda eLambda(state.mem.exprs.alloc, posIdx, createSymbol("a"), formals, &body); + ExprLambda eLambda(state.mem.exprs.alloc, posIdx, createSymbol("a"), &body); Value vLambda; vLambda.mkLambda(&env, &eLambda); @@ -500,9 +499,8 @@ TEST_F(ValuePrintingTests, ansiColorsLambda) PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1); auto posIdx = state.positions.add(origin, 0); auto body = ExprInt(0); - auto formals = Formals{}; - ExprLambda eLambda(state.mem.exprs.alloc, posIdx, createSymbol("a"), formals, &body); + ExprLambda eLambda(state.mem.exprs.alloc, posIdx, createSymbol("a"), &body); Value vLambda; vLambda.mkLambda(&env, &eLambda); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ec203a0d9..35a01101b 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1496,15 +1496,13 @@ void EvalState::callFunction(Value & fun, std::span args, Value & vRes, ExprLambda & lambda(*vCur.lambda().fun); - auto size = (!lambda.arg ? 0 : 1) + (lambda.hasFormals ? lambda.getFormals().size() : 0); + auto size = (!lambda.arg ? 0 : 1) + (lambda.getFormals() ? lambda.getFormals()->formals.size() : 0); Env & env2(mem.allocEnv(size)); env2.up = vCur.lambda().env; Displacement displ = 0; - if (!lambda.hasFormals) - env2.values[displ++] = args[0]; - else { + if (auto formals = lambda.getFormals()) { try { forceAttrs(*args[0], lambda.pos, "while evaluating the value passed for the lambda argument"); } catch (Error & e) { @@ -1520,7 +1518,7 @@ void EvalState::callFunction(Value & fun, std::span args, Value & vRes, there is no matching actual argument but the formal argument has a default, use the default. */ size_t attrsUsed = 0; - for (auto & i : lambda.getFormals()) { + for (auto & i : formals->formals) { auto j = args[0]->attrs()->get(i.name); if (!j) { if (!i.def) { @@ -1542,13 +1540,13 @@ void EvalState::callFunction(Value & fun, std::span args, Value & vRes, /* Check that each actual argument is listed as a formal argument (unless the attribute match specifies a `...'). */ - if (!lambda.ellipsis && attrsUsed != args[0]->attrs()->size()) { + if (!formals->ellipsis && attrsUsed != args[0]->attrs()->size()) { /* Nope, so show the first unexpected argument to the user. */ for (auto & i : *args[0]->attrs()) - if (!lambda.hasFormal(i.name)) { + if (!formals->has(i.name)) { StringSet formalNames; - for (auto & formal : lambda.getFormals()) + for (auto & formal : formals->formals) formalNames.insert(std::string(symbols[formal.name])); auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]); error( @@ -1563,6 +1561,8 @@ void EvalState::callFunction(Value & fun, std::span args, Value & vRes, } unreachable(); } + } else { + env2.values[displ++] = args[0]; } nrFunctionCalls++; @@ -1747,14 +1747,15 @@ void EvalState::autoCallFunction(const Bindings & args, Value & fun, Value & res } } - if (!fun.isLambda() || !fun.lambda().fun->hasFormals) { + if (!fun.isLambda() || !fun.lambda().fun->getFormals()) { res = fun; return; } + auto formals = fun.lambda().fun->getFormals(); - auto attrs = buildBindings(std::max(static_cast(fun.lambda().fun->nFormals), args.size())); + auto attrs = buildBindings(std::max(static_cast(formals->formals.size()), args.size())); - if (fun.lambda().fun->ellipsis) { + if (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) @@ -1762,7 +1763,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.lambda().fun->getFormals()) { + for (auto & i : formals->formals) { auto j = args.get(i.name); if (j) { attrs.insert(*j); diff --git a/src/libexpr/include/nix/expr/nixexpr.hh b/src/libexpr/include/nix/expr/nixexpr.hh index fbe8747c8..9df0fb1b6 100644 --- a/src/libexpr/include/nix/expr/nixexpr.hh +++ b/src/libexpr/include/nix/expr/nixexpr.hh @@ -466,7 +466,7 @@ struct Formal Expr * def; }; -struct Formals +struct FormalsBuilder { typedef std::vector Formals_; /** @@ -483,26 +483,67 @@ struct Formals } }; +struct Formals +{ + std::span formals; + bool ellipsis; + + Formals(std::span formals, bool ellipsis) + : formals(formals) + , ellipsis(ellipsis) {}; + + bool has(Symbol arg) const + { + auto it = std::lower_bound( + formals.begin(), formals.end(), arg, [](const Formal & f, const Symbol & sym) { return f.name < sym; }); + return it != formals.end() && it->name == arg; + } + + std::vector lexicographicOrder(const SymbolTable & symbols) const + { + std::vector result(formals.begin(), formals.end()); + std::sort(result.begin(), result.end(), [&](const Formal & a, const Formal & b) { + std::string_view sa = symbols[a.name], sb = symbols[b.name]; + return sa < sb; + }); + return result; + } +}; + struct ExprLambda : Expr { PosIdx pos; Symbol name; Symbol arg; - bool ellipsis; +private: bool hasFormals; + bool ellipsis; uint16_t nFormals; Formal * formalsStart; +public: + + std::optional getFormals() const + { + if (hasFormals) + return Formals{{formalsStart, nFormals}, ellipsis}; + else + return std::nullopt; + } Expr * body; DocComment docComment; ExprLambda( - std::pmr::polymorphic_allocator & alloc, PosIdx pos, Symbol arg, const Formals & formals, Expr * body) + std::pmr::polymorphic_allocator & alloc, + PosIdx pos, + Symbol arg, + const FormalsBuilder & formals, + Expr * body) : pos(pos) , arg(arg) - , ellipsis(formals.ellipsis) , hasFormals(true) + , ellipsis(formals.ellipsis) , nFormals(formals.formals.size()) , formalsStart(alloc.allocate_object(nFormals)) , body(body) @@ -514,44 +555,22 @@ struct ExprLambda : Expr : pos(pos) , arg(arg) , hasFormals(false) + , ellipsis(false) + , nFormals(0) , formalsStart(nullptr) , body(body) {}; - ExprLambda(std::pmr::polymorphic_allocator & alloc, PosIdx pos, Formals formals, Expr * body) + ExprLambda(std::pmr::polymorphic_allocator & alloc, PosIdx pos, FormalsBuilder formals, Expr * body) : ExprLambda(alloc, pos, Symbol(), formals, body) {}; - bool hasFormal(Symbol arg) const - { - auto formals = getFormals(); - auto it = std::lower_bound( - formals.begin(), formals.end(), arg, [](const Formal & f, const Symbol & sym) { return f.name < sym; }); - return it != formals.end() && it->name == arg; - } - void setName(Symbol name) override; std::string showNamePos(const EvalState & state) const; - std::vector getFormalsLexicographic(const SymbolTable & symbols) const - { - std::vector result(getFormals().begin(), getFormals().end()); - std::sort(result.begin(), result.end(), [&](const Formal & a, const Formal & b) { - std::string_view sa = symbols[a.name], sb = symbols[b.name]; - return sa < sb; - }); - return result; - } - PosIdx getPos() const override { return pos; } - std::span getFormals() const - { - assert(hasFormals); - return {formalsStart, nFormals}; - } - virtual void setDocComment(DocComment docComment) override; COMMON_METHODS }; diff --git a/src/libexpr/include/nix/expr/parser-state.hh b/src/libexpr/include/nix/expr/parser-state.hh index 4d1312443..4cffaa497 100644 --- a/src/libexpr/include/nix/expr/parser-state.hh +++ b/src/libexpr/include/nix/expr/parser-state.hh @@ -93,7 +93,7 @@ struct ParserState void addAttr( ExprAttrs * attrs, AttrPath && attrPath, const ParserLocation & loc, Expr * e, const ParserLocation & exprLoc); void addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def); - void validateFormals(Formals & formals, PosIdx pos = noPos, Symbol arg = {}); + void validateFormals(FormalsBuilder & formals, PosIdx pos = noPos, Symbol arg = {}); Expr * stripIndentation(const PosIdx pos, std::vector>> && es); PosIdx at(const ParserLocation & loc); }; @@ -213,7 +213,7 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb } } -inline void ParserState::validateFormals(Formals & formals, PosIdx pos, Symbol arg) +inline void ParserState::validateFormals(FormalsBuilder & formals, PosIdx pos, Symbol arg) { std::sort(formals.formals.begin(), formals.formals.end(), [](const auto & a, const auto & b) { return std::tie(a.name, a.pos) < std::tie(b.name, b.pos); diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index ce1ea19e1..b183f1bbf 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -154,13 +154,13 @@ void ExprList::show(const SymbolTable & symbols, std::ostream & str) const void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const { str << "("; - if (hasFormals) { + if (auto formals = getFormals()) { str << "{ "; bool first = true; // the natural Symbol ordering is by creation time, which can lead to the // same expression being printed in two different ways depending on its // context. always use lexicographic ordering to avoid this. - for (auto & i : getFormalsLexicographic(symbols)) { + for (auto & i : formals->lexicographicOrder(symbols)) { if (first) first = false; else @@ -451,20 +451,21 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); - auto newEnv = std::make_shared(nullptr, env, (hasFormals ? getFormals().size() : 0) + (!arg ? 0 : 1)); + auto newEnv = + std::make_shared(nullptr, env, (getFormals() ? getFormals()->formals.size() : 0) + (!arg ? 0 : 1)); Displacement displ = 0; if (arg) newEnv->vars.emplace_back(arg, displ++); - if (hasFormals) { - for (auto & i : getFormals()) + if (auto formals = getFormals()) { + for (auto & i : formals->formals) newEnv->vars.emplace_back(i.name, displ++); newEnv->sort(); - for (auto & i : getFormals()) + for (auto & i : formals->formals) if (i.def) i.def->bindVars(es, newEnv); } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 49cd534d7..a9d440d23 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -131,7 +131,7 @@ static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) { %type expr_pipe_from expr_pipe_into %type > list %type binds binds1 -%type formals formal_set +%type formals formal_set %type formal %type > attrpath %type >> attrs diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index d95c999ff..04196bc1f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -3363,21 +3363,20 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value ** args if (!args[0]->isLambda()) state.error("'functionArgs' requires a function").atPos(pos).debugThrow(); - if (!args[0]->lambda().fun->hasFormals) { + if (const auto & formals = args[0]->lambda().fun->getFormals()) { + auto attrs = state.buildBindings(formals->formals.size()); + for (auto & i : formals->formals) + attrs.insert(i.name, state.getBool(i.def), i.pos); + /* Optimization: avoid sorting bindings. `formals` must already be sorted according to + (std::tie(a.name, a.pos) < std::tie(b.name, b.pos)) predicate, so the following assertion + always holds: + assert(std::is_sorted(attrs.alreadySorted()->begin(), attrs.alreadySorted()->end())); + .*/ + v.mkAttrs(attrs.alreadySorted()); + } else { v.mkAttrs(&Bindings::emptyBindings); return; } - - const auto & formals = args[0]->lambda().fun->getFormals(); - auto attrs = state.buildBindings(formals.size()); - for (auto & i : formals) - attrs.insert(i.name, state.getBool(i.def), i.pos); - /* Optimization: avoid sorting bindings. `formals` must already be sorted according to - (std::tie(a.name, a.pos) < std::tie(b.name, b.pos)) predicate, so the following assertion - always holds: - assert(std::is_sorted(attrs.alreadySorted()->begin(), attrs.alreadySorted()->end())); - .*/ - v.mkAttrs(attrs.alreadySorted()); } static RegisterPrimOp primop_functionArgs({ diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 547597ca4..d5959e894 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -145,14 +145,14 @@ static void printValueAsXML( posToXML(state, xmlAttrs, state.positions[v.lambda().fun->pos]); XMLOpenElement _(doc, "function", xmlAttrs); - if (v.lambda().fun->hasFormals) { + if (auto formals = v.lambda().fun->getFormals()) { XMLAttrs attrs; if (v.lambda().fun->arg) attrs["name"] = state.symbols[v.lambda().fun->arg]; - if (v.lambda().fun->ellipsis) + if (formals->ellipsis) attrs["ellipsis"] = "1"; XMLOpenElement _(doc, "attrspat", attrs); - for (auto & i : v.lambda().fun->getFormalsLexicographic(state.symbols)) + for (auto & i : formals->lexicographicOrder(state.symbols)) doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name])); } else doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda().fun->arg])); diff --git a/src/libflake/flake.cc b/src/libflake/flake.cc index e2fe9805e..42385712c 100644 --- a/src/libflake/flake.cc +++ b/src/libflake/flake.cc @@ -281,12 +281,15 @@ static Flake readFlake( if (auto outputs = vInfo.attrs()->get(sOutputs)) { expectType(state, nFunction, *outputs->value, outputs->pos); - if (outputs->value->isLambda() && outputs->value->lambda().fun->hasFormals) { - for (auto & formal : outputs->value->lambda().fun->getFormals()) { - if (formal.name != state.s.self) - flake.inputs.emplace( - state.symbols[formal.name], - FlakeInput{.ref = parseFlakeRef(state.fetchSettings, std::string(state.symbols[formal.name]))}); + if (outputs->value->isLambda()) { + if (auto formals = outputs->value->lambda().fun->getFormals()) { + for (auto & formal : formals->formals) { + if (formal.name != state.s.self) + flake.inputs.emplace( + state.symbols[formal.name], + FlakeInput{ + .ref = parseFlakeRef(state.fetchSettings, std::string(state.symbols[formal.name]))}); + } } } diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 0e499fed4..b826e943c 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -468,7 +468,7 @@ struct CmdFlakeCheck : FlakeCommand if (!v.isLambda()) { throw Error("overlay is not a function, but %s instead", showType(v)); } - if (v.lambda().fun->hasFormals || !argHasName(v.lambda().fun->arg, "final")) + if (v.lambda().fun->getFormals() || !argHasName(v.lambda().fun->arg, "final")) throw Error("overlay does not take an argument named 'final'"); // FIXME: if we have a 'nixpkgs' input, use it to // evaluate the overlay. diff --git a/src/nix/nix-build/nix-build.cc b/src/nix/nix-build/nix-build.cc index 75bae5208..4d876c9eb 100644 --- a/src/nix/nix-build/nix-build.cc +++ b/src/nix/nix-build/nix-build.cc @@ -415,11 +415,13 @@ static void main_nix_build(int argc, char ** argv) return false; } bool add = false; - if (v.type() == nFunction && v.lambda().fun->hasFormals) { - for (auto & i : v.lambda().fun->getFormals()) { - if (state->symbols[i.name] == "inNixShell") { - add = true; - break; + if (v.type() == nFunction) { + if (auto formals = v.lambda().fun->getFormals()) { + for (auto & i : formals->formals) { + if (state->symbols[i.name] == "inNixShell") { + add = true; + break; + } } } } From 67be2df174c7e87d06650dfe3d81e53c29c95436 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Fri, 31 Oct 2025 16:33:02 +0100 Subject: [PATCH 5/5] remove unnecessary constructor argument --- src/libexpr-tests/value/print.cc | 4 ++-- src/libexpr/include/nix/expr/nixexpr.hh | 2 +- src/libexpr/parser.y | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr-tests/value/print.cc b/src/libexpr-tests/value/print.cc index aa66c55fb..0006da2ff 100644 --- a/src/libexpr-tests/value/print.cc +++ b/src/libexpr-tests/value/print.cc @@ -111,7 +111,7 @@ TEST_F(ValuePrintingTests, vLambda) auto posIdx = state.positions.add(origin, 0); auto body = ExprInt(0); - ExprLambda eLambda(state.mem.exprs.alloc, posIdx, createSymbol("a"), &body); + ExprLambda eLambda(posIdx, createSymbol("a"), &body); Value vLambda; vLambda.mkLambda(&env, &eLambda); @@ -500,7 +500,7 @@ TEST_F(ValuePrintingTests, ansiColorsLambda) auto posIdx = state.positions.add(origin, 0); auto body = ExprInt(0); - ExprLambda eLambda(state.mem.exprs.alloc, posIdx, createSymbol("a"), &body); + ExprLambda eLambda(posIdx, createSymbol("a"), &body); Value vLambda; vLambda.mkLambda(&env, &eLambda); diff --git a/src/libexpr/include/nix/expr/nixexpr.hh b/src/libexpr/include/nix/expr/nixexpr.hh index 9df0fb1b6..4962c7857 100644 --- a/src/libexpr/include/nix/expr/nixexpr.hh +++ b/src/libexpr/include/nix/expr/nixexpr.hh @@ -551,7 +551,7 @@ public: std::ranges::copy(formals.formals, formalsStart); }; - ExprLambda(std::pmr::polymorphic_allocator & alloc, PosIdx pos, Symbol arg, Expr * body) + ExprLambda(PosIdx pos, Symbol arg, Expr * body) : pos(pos) , arg(arg) , hasFormals(false) diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index a9d440d23..02fc4c3b6 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -179,7 +179,7 @@ expr: expr_function; expr_function : ID ':' expr_function - { auto me = new ExprLambda(state->alloc, CUR_POS, state->symbols.create($1), $3); + { auto me = new ExprLambda(CUR_POS, state->symbols.create($1), $3); $$ = me; SET_DOC_POS(me, @1); }