diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index bba058cca..385d4c05f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -205,7 +205,7 @@ bool Value::isTrivial() const { return !isa() && (!isa() - || (dynamic_cast(thunk().expr) && ((ExprAttrs *) thunk().expr)->dynamicAttrs.empty()) + || (dynamic_cast(thunk().expr) && ((ExprAttrs *) thunk().expr)->dynamicAttrs->empty()) || dynamic_cast(thunk().expr) || dynamic_cast(thunk().expr)); } @@ -1226,26 +1226,26 @@ Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up) void ExprAttrs::eval(EvalState & state, Env & env, Value & v) { - auto bindings = state.buildBindings(attrs.size() + dynamicAttrs.size()); + auto bindings = state.buildBindings(attrs->size() + dynamicAttrs->size()); auto dynamicEnv = &env; bool sort = false; if (recursive) { /* Create a new environment that contains the attributes in this `rec'. */ - Env & env2(state.mem.allocEnv(attrs.size())); + Env & env2(state.mem.allocEnv(attrs->size())); env2.up = &env; dynamicEnv = &env2; Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env2) : nullptr; - AttrDefs::iterator overrides = attrs.find(state.s.overrides); - bool hasOverrides = overrides != attrs.end(); + AttrDefs::iterator overrides = attrs->find(state.s.overrides); + bool hasOverrides = overrides != attrs->end(); /* The recursive attributes are evaluated in the new environment, while the inherited attributes are evaluated in the original environment. */ Displacement displ = 0; - for (auto & i : attrs) { + for (auto & i : *attrs) { Value * vAttr; if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) { vAttr = state.allocValue(); @@ -1272,8 +1272,8 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) "while evaluating the `__overrides` attribute"); bindings.grow(state.buildBindings(bindings.capacity() + vOverrides->attrs()->size())); for (auto & i : *vOverrides->attrs()) { - AttrDefs::iterator j = attrs.find(i.name); - if (j != attrs.end()) { + AttrDefs::iterator j = attrs->find(i.name); + if (j != attrs->end()) { (*bindings.bindings)[j->second.displ] = i; env2.values[j->second.displ] = i.value; } else @@ -1285,13 +1285,13 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) else { Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr; - for (auto & i : attrs) + for (auto & i : *attrs) bindings.insert( i.first, i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)), i.second.pos); } /* Dynamic attrs apply *after* rec and __overrides. */ - for (auto & i : dynamicAttrs) { + for (auto & i : *dynamicAttrs) { Value nameVal; i.nameExpr->eval(state, *dynamicEnv, nameVal); state.forceValue(nameVal, i.pos); @@ -1325,7 +1325,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) { /* Create a new environment that contains the attributes in this `let'. */ - Env & env2(state.mem.allocEnv(attrs->attrs.size())); + Env & env2(state.mem.allocEnv(attrs->attrs->size())); env2.up = &env; Env * inheritEnv = attrs->inheritFromExprs ? attrs->buildInheritFromEnv(state, env2) : nullptr; @@ -1334,7 +1334,7 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) while the inherited attributes are evaluated in the original environment. */ Displacement displ = 0; - for (auto & i : attrs->attrs) { + for (auto & i : *attrs->attrs) { env2.values[displ++] = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv)); } diff --git a/src/libexpr/include/nix/expr/nixexpr.hh b/src/libexpr/include/nix/expr/nixexpr.hh index 673c14cb3..392545069 100644 --- a/src/libexpr/include/nix/expr/nixexpr.hh +++ b/src/libexpr/include/nix/expr/nixexpr.hh @@ -395,9 +395,13 @@ struct ExprAttrs : Expr } }; - typedef std::map AttrDefs; - AttrDefs attrs; - std::unique_ptr> inheritFromExprs; + typedef std::pmr::map AttrDefs; + /** + * attrs will never be null. we use std::optional so that we can call emplace() to re-initialize the value with a + * new pmr::map using a different allocator (move assignment will copy into the old allocator) + */ + std::optional attrs; + std::unique_ptr> inheritFromExprs; struct DynamicAttrDef { @@ -409,13 +413,20 @@ struct ExprAttrs : Expr , pos(pos) {}; }; - typedef std::vector DynamicAttrDefs; - DynamicAttrDefs dynamicAttrs; + typedef std::pmr::vector DynamicAttrDefs; + /** + * dynamicAttrs will never be null. See comment on AttrDefs above. + */ + std::optional dynamicAttrs; ExprAttrs(const PosIdx & pos) : recursive(false) - , pos(pos) {}; + , pos(pos) + , attrs(AttrDefs{}) + , dynamicAttrs(DynamicAttrDefs{}) {}; ExprAttrs() - : recursive(false) {}; + : recursive(false) + , attrs(AttrDefs{}) + , dynamicAttrs(DynamicAttrDefs{}) {}; PosIdx getPos() const override { diff --git a/src/libexpr/include/nix/expr/parser-state.hh b/src/libexpr/include/nix/expr/parser-state.hh index 9e1d17b53..c2a49a3d3 100644 --- a/src/libexpr/include/nix/expr/parser-state.hh +++ b/src/libexpr/include/nix/expr/parser-state.hh @@ -126,8 +126,8 @@ inline void ParserState::addAttr( for (i = attrPath.begin(); i + 1 < attrPath.end(); i++) { ExprAttrs * nested; if (i->symbol) { - ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol); - if (j != attrs->attrs.end()) { + ExprAttrs::AttrDefs::iterator j = attrs->attrs->find(i->symbol); + if (j != attrs->attrs->end()) { nested = dynamic_cast(j->second.e); if (!nested) { attrPath.erase(i + 1, attrPath.end()); @@ -135,11 +135,11 @@ inline void ParserState::addAttr( } } else { nested = exprs.add(); - attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos); + (*attrs->attrs)[i->symbol] = ExprAttrs::AttrDef(nested, pos); } } else { nested = exprs.add(); - attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos)); + attrs->dynamicAttrs->push_back(ExprAttrs::DynamicAttrDef(i->expr, nested, pos)); } attrs = nested; } @@ -148,7 +148,7 @@ inline void ParserState::addAttr( if (i->symbol) { addAttr(attrs, attrPath, i->symbol, ExprAttrs::AttrDef(e, pos)); } else { - attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos)); + attrs->dynamicAttrs->push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos)); } auto it = lexerState.positionToDocComment.find(pos); @@ -165,8 +165,8 @@ inline void ParserState::addAttr( inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symbol, ExprAttrs::AttrDef && def) { - ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(symbol); - if (j != attrs->attrs.end()) { + ExprAttrs::AttrDefs::iterator j = attrs->attrs->find(symbol); + if (j != attrs->attrs->end()) { // This attr path is already defined. However, if both // e and the expr pointed by the attr path are two attribute sets, // we want to merge them. @@ -181,8 +181,8 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb // See https://github.com/NixOS/nix/issues/9020. if (jAttrs && ae) { if (ae->inheritFromExprs && !jAttrs->inheritFromExprs) - jAttrs->inheritFromExprs = std::make_unique>(); - for (auto & ad : ae->attrs) { + jAttrs->inheritFromExprs = std::make_unique>(); + for (auto & ad : *ae->attrs) { if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) { auto & sel = dynamic_cast(*ad.second.e); auto & from = dynamic_cast(*sel.e); @@ -192,12 +192,12 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb addAttr(jAttrs, attrPath, ad.first, std::move(ad.second)); attrPath.pop_back(); } - ae->attrs.clear(); - jAttrs->dynamicAttrs.insert( - jAttrs->dynamicAttrs.end(), - std::make_move_iterator(ae->dynamicAttrs.begin()), - std::make_move_iterator(ae->dynamicAttrs.end())); - ae->dynamicAttrs.clear(); + ae->attrs->clear(); + jAttrs->dynamicAttrs->insert( + jAttrs->dynamicAttrs->end(), + std::make_move_iterator(ae->dynamicAttrs->begin()), + std::make_move_iterator(ae->dynamicAttrs->end())); + ae->dynamicAttrs->clear(); if (ae->inheritFromExprs) { jAttrs->inheritFromExprs->insert( jAttrs->inheritFromExprs->end(), @@ -210,7 +210,7 @@ ParserState::addAttr(ExprAttrs * attrs, AttrPath & attrPath, const Symbol & symb } } else { // This attr path is not defined. Let's create it. - attrs->attrs.emplace(symbol, def); + attrs->attrs->emplace(symbol, def); def.e->setName(symbol); } } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index db5e52c3a..3de5bdcb8 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -74,9 +74,9 @@ void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const { - typedef const decltype(attrs)::value_type * Attr; + typedef const AttrDefs::value_type * Attr; std::vector sorted; - for (auto & i : attrs) + for (auto & i : *attrs) sorted.push_back(&i); std::sort(sorted.begin(), sorted.end(), [&](Attr a, Attr b) { std::string_view sa = symbols[a->first], sb = symbols[b->first]; @@ -122,7 +122,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co str << "; "; } } - for (auto & i : dynamicAttrs) { + for (auto & i : *dynamicAttrs) { str << "\"${"; i.nameExpr->show(symbols, str); str << "}\" = "; @@ -401,15 +401,26 @@ ExprAttrs::bindInheritSources(EvalState & es, const std::shared_ptr & env) { + // Move storage into the Exprs arena + { + auto arena = es.mem.exprs.alloc; + AttrDefs newAttrs{std::move(*attrs), arena}; + attrs.emplace(std::move(newAttrs), arena); + DynamicAttrDefs newDynamicAttrs{std::move(*dynamicAttrs), arena}; + dynamicAttrs.emplace(std::move(newDynamicAttrs), arena); + if (inheritFromExprs) + inheritFromExprs = std::make_unique>(std::move(*inheritFromExprs), arena); + } + if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); if (recursive) { auto newEnv = [&]() -> std::shared_ptr { - auto newEnv = std::make_shared(nullptr, env, attrs.size()); + auto newEnv = std::make_shared(nullptr, env, attrs->size()); Displacement displ = 0; - for (auto & i : attrs) + for (auto & i : *attrs) newEnv->vars.emplace_back(i.first, i.second.displ = displ++); return newEnv; }(); @@ -417,20 +428,20 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr // No need to sort newEnv since attrs is in sorted order. auto inheritFromEnv = bindInheritSources(es, newEnv); - for (auto & i : attrs) + for (auto & i : *attrs) i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv)); - for (auto & i : dynamicAttrs) { + for (auto & i : *dynamicAttrs) { i.nameExpr->bindVars(es, newEnv); i.valueExpr->bindVars(es, newEnv); } } else { auto inheritFromEnv = bindInheritSources(es, env); - for (auto & i : attrs) + for (auto & i : *attrs) i.second.e->bindVars(es, i.second.chooseByKind(env, env, inheritFromEnv)); - for (auto & i : dynamicAttrs) { + for (auto & i : *dynamicAttrs) { i.nameExpr->bindVars(es, env); i.valueExpr->bindVars(es, env); } @@ -486,10 +497,10 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr & void ExprLet::bindVars(EvalState & es, const std::shared_ptr & env) { auto newEnv = [&]() -> std::shared_ptr { - auto newEnv = std::make_shared(nullptr, env, attrs->attrs.size()); + auto newEnv = std::make_shared(nullptr, env, attrs->attrs->size()); Displacement displ = 0; - for (auto & i : attrs->attrs) + for (auto & i : *attrs->attrs) newEnv->vars.emplace_back(i.first, i.second.displ = displ++); return newEnv; }(); @@ -497,7 +508,7 @@ void ExprLet::bindVars(EvalState & es, const std::shared_ptr & // No need to sort newEnv since attrs->attrs is in sorted order. auto inheritFromEnv = attrs->bindInheritSources(es, newEnv); - for (auto & i : attrs->attrs) + for (auto & i : *attrs->attrs) i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv)); if (es.debugRepl) diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index df700cab2..3cfc6f936 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -211,7 +211,7 @@ expr_function | WITH expr ';' expr_function { $$ = state->exprs.add(CUR_POS, $2, $4); } | LET binds IN_KW expr_function - { if (!$2->dynamicAttrs.empty()) + { if (!$2->dynamicAttrs->empty()) throw ParseError({ .msg = HintFmt("dynamic attributes not allowed in let"), .pos = state->positions[CUR_POS] @@ -413,9 +413,9 @@ binds1 | binds[accum] INHERIT attrs ';' { $$ = $accum; for (auto & [i, iPos] : $attrs) { - if ($accum->attrs.find(i.symbol) != $accum->attrs.end()) - state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos); - $accum->attrs.emplace( + if ($accum->attrs->find(i.symbol) != $accum->attrs->end()) + state->dupAttr(i.symbol, iPos, (*$accum->attrs)[i.symbol].pos); + $accum->attrs->emplace( i.symbol, ExprAttrs::AttrDef(state->exprs.add(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited)); } @@ -423,13 +423,13 @@ binds1 | binds[accum] INHERIT '(' expr ')' attrs ';' { $$ = $accum; if (!$accum->inheritFromExprs) - $accum->inheritFromExprs = std::make_unique>(); + $accum->inheritFromExprs = std::make_unique>(); $accum->inheritFromExprs->push_back($expr); auto from = state->exprs.add(state->at(@expr), $accum->inheritFromExprs->size() - 1); for (auto & [i, iPos] : $attrs) { - if ($accum->attrs.find(i.symbol) != $accum->attrs.end()) - state->dupAttr(i.symbol, iPos, $accum->attrs[i.symbol].pos); - $accum->attrs.emplace( + if ($accum->attrs->find(i.symbol) != $accum->attrs->end()) + state->dupAttr(i.symbol, iPos, (*$accum->attrs)[i.symbol].pos); + $accum->attrs->emplace( i.symbol, ExprAttrs::AttrDef( state->exprs.add(state->exprs.alloc, iPos, from, i.symbol),