1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-24 11:19:35 +01:00

Merge pull request #14623 from Radvendii/exprcall-alloc-shvach

libexpr: plug ExprCall memory leak
This commit is contained in:
John Ericson 2025-11-23 00:08:10 +00:00 committed by GitHub
commit bd11043c67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 21 additions and 12 deletions

View file

@ -1751,9 +1751,9 @@ void ExprCall::eval(EvalState & state, Env & env, Value & v)
// 4: about 60 // 4: about 60
// 5: under 10 // 5: under 10
// This excluded attrset lambdas (`{...}:`). Contributions of mixed lambdas appears insignificant at ~150 total. // This excluded attrset lambdas (`{...}:`). Contributions of mixed lambdas appears insignificant at ~150 total.
SmallValueVector<4> vArgs(args.size()); SmallValueVector<4> vArgs(args->size());
for (size_t i = 0; i < args.size(); ++i) for (size_t i = 0; i < args->size(); ++i)
vArgs[i] = args[i]->maybeThunk(state, env); vArgs[i] = (*args)[i]->maybeThunk(state, env);
state.callFunction(vFun, vArgs, v, pos); state.callFunction(vFun, vArgs, v, pos);
} }

View file

@ -592,11 +592,14 @@ public:
struct ExprCall : Expr struct ExprCall : Expr
{ {
Expr * fun; Expr * fun;
std::vector<Expr *> args; /**
* args will never be null. See comment on ExprAttrs::AttrDefs below.
*/
std::optional<std::pmr::vector<Expr *>> args;
PosIdx pos; PosIdx pos;
std::optional<PosIdx> cursedOrEndPos; // used during parsing to warn about https://github.com/NixOS/nix/issues/11118 std::optional<PosIdx> cursedOrEndPos; // used during parsing to warn about https://github.com/NixOS/nix/issues/11118
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args) ExprCall(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args)
: fun(fun) : fun(fun)
, args(args) , args(args)
, pos(pos) , pos(pos)
@ -604,7 +607,7 @@ struct ExprCall : Expr
{ {
} }
ExprCall(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args, PosIdx && cursedOrEndPos) ExprCall(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args, PosIdx && cursedOrEndPos)
: fun(fun) : fun(fun)
, args(args) , args(args)
, pos(pos) , pos(pos)
@ -836,7 +839,7 @@ public:
// we define some calls to add explicitly so that the argument can be passed in as initializer lists // we define some calls to add explicitly so that the argument can be passed in as initializer lists
template<class C> template<class C>
[[gnu::always_inline]] [[gnu::always_inline]]
C * add(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args) C * add(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args)
requires(std::same_as<C, ExprCall>) requires(std::same_as<C, ExprCall>)
{ {
return alloc.new_object<C>(pos, fun, std::move(args)); return alloc.new_object<C>(pos, fun, std::move(args));
@ -844,7 +847,7 @@ public:
template<class C> template<class C>
[[gnu::always_inline]] [[gnu::always_inline]]
C * add(const PosIdx & pos, Expr * fun, std::vector<Expr *> && args, PosIdx && cursedOrEndPos) C * add(const PosIdx & pos, Expr * fun, std::pmr::vector<Expr *> && args, PosIdx && cursedOrEndPos)
requires(std::same_as<C, ExprCall>) requires(std::same_as<C, ExprCall>)
{ {
return alloc.new_object<C>(pos, fun, std::move(args), std::move(cursedOrEndPos)); return alloc.new_object<C>(pos, fun, std::move(args), std::move(cursedOrEndPos));

View file

@ -191,7 +191,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const
{ {
str << '('; str << '(';
fun->show(symbols, str); fun->show(symbols, str);
for (auto e : args) { for (auto e : *args) {
str << ' '; str << ' ';
e->show(symbols, str); e->show(symbols, str);
} }
@ -486,11 +486,17 @@ void ExprLambda::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv>
void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env)
{ {
// Move storage into the Exprs arena
{
auto arena = es.mem.exprs.alloc;
std::pmr::vector<Expr *> newArgs{std::move(*args), arena};
args.emplace(std::move(newArgs), arena);
}
if (es.debugRepl) if (es.debugRepl)
es.exprEnvs.insert(std::make_pair(this, env)); es.exprEnvs.insert(std::make_pair(this, env));
fun->bindVars(es, env); fun->bindVars(es, env);
for (auto e : args) for (auto e : *args)
e->bindVars(es, env); e->bindVars(es, env);
} }

View file

@ -115,7 +115,7 @@ static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, P
static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) { static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
if (auto e2 = dynamic_cast<ExprCall *>(fn)) { if (auto e2 = dynamic_cast<ExprCall *>(fn)) {
e2->args.push_back(arg); e2->args->push_back(arg);
return fn; return fn;
} }
return exprs.add<ExprCall>(pos, fn, {arg}); return exprs.add<ExprCall>(pos, fn, {arg});
@ -129,7 +129,7 @@ static Expr * makeCall(Exprs & exprs, PosIdx pos, Expr * fn, Expr * arg) {
%type <Expr *> start expr expr_function expr_if expr_op %type <Expr *> start expr expr_function expr_if expr_op
%type <Expr *> expr_select expr_simple expr_app %type <Expr *> expr_select expr_simple expr_app
%type <Expr *> expr_pipe_from expr_pipe_into %type <Expr *> expr_pipe_from expr_pipe_into
%type <std::vector<Expr *>> list %type <std::pmr::vector<Expr *>> list
%type <ExprAttrs *> binds binds1 %type <ExprAttrs *> binds binds1
%type <FormalsBuilder> formals formal_set %type <FormalsBuilder> formals formal_set
%type <Formal> formal %type <Formal> formal