From 8c0590fa32395e8f66bad11049fb40458d94424b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 25 Mar 2024 18:20:18 +0100 Subject: [PATCH] Never update values after setting the type Thunks are now overwritten by a helper function `Value::finishValue(newType, payload)` (where `payload` is the original anonymous union inside `Value`). This helps to ensure we never update a value elsewhere, since that would be incompatible with parallel evaluation (i.e. after a value has transitioned from being a thunk to being a non-thunk, it should be immutable). There were two places where this happened: `Value::mkString()` and `ExprAttrs::eval()`. This PR also adds a bunch of accessor functions for value contents, like `Value::integer()` to access the integer field in the union. --- src/libcmd/installable-flake.cc | 2 +- src/libcmd/installables.cc | 4 +- src/libcmd/repl.cc | 10 +- src/libexpr/attr-path.cc | 6 +- src/libexpr/attr-set.cc | 17 -- src/libexpr/attr-set.hh | 34 ++- src/libexpr/eval-cache.cc | 14 +- src/libexpr/eval-inline.hh | 6 +- src/libexpr/eval.cc | 303 ++++++++++---------- src/libexpr/eval.hh | 5 +- src/libexpr/flake/flake.cc | 34 +-- src/libexpr/get-drvs.cc | 68 +++-- src/libexpr/get-drvs.hh | 6 +- src/libexpr/nixexpr.cc | 4 +- src/libexpr/primops.cc | 125 ++++---- src/libexpr/primops/context.cc | 24 +- src/libexpr/primops/fetchClosure.cc | 2 +- src/libexpr/primops/fetchMercurial.cc | 2 +- src/libexpr/primops/fetchTree.cc | 10 +- src/libexpr/print-ambiguous.cc | 12 +- src/libexpr/print.cc | 27 +- src/libexpr/value-to-json.cc | 28 +- src/libexpr/value-to-xml.cc | 47 ++- src/libexpr/value.hh | 129 ++++----- src/nix-build/nix-build.cc | 4 +- src/nix-env/nix-env.cc | 14 +- src/nix-env/user-env.cc | 4 +- src/nix/bundle.cc | 6 +- src/nix/eval.cc | 2 +- src/nix/flake.cc | 54 ++-- src/nix/main.cc | 7 +- src/nix/prefetch.cc | 10 +- tests/unit/libexpr-support/tests/libexpr.hh | 12 +- tests/unit/libexpr/primops.cc | 42 +-- tests/unit/libexpr/trivial.cc | 12 +- 35 files changed, 530 insertions(+), 556 deletions(-) diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index ddec7537bab..6ff837ddc4b 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -49,7 +49,7 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked callFlake(state, lockedFlake, *vFlake); - auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); + auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs")); assert(aOutputs); state.forceValue(*aOutputs->value, aOutputs->value->determinePos(noPos)); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 6db9bf9a129..ed67723778f 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -289,7 +289,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s state->autoCallFunction(*autoArgs, v1, v2); if (v2.type() == nAttrs) { - for (auto & i : *v2.attrs) { + for (auto & i : *v2.attrs()) { std::string name = state->symbols[i.name]; if (name.find(searchWord) == 0) { if (prefix_ == "") @@ -461,7 +461,7 @@ ref openEvalCache( state.forceAttrs(*vFlake, noPos, "while parsing cached flake data"); - auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); + auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs")); assert(aOutputs); return aOutputs->value; diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 7cecc60b77c..a97d8aaf4f2 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -290,7 +290,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) e->eval(*state, *env, v); state->forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)"); - for (auto & i : *v.attrs) { + for (auto & i : *v.attrs()) { std::string_view name = state->symbols[i.name]; if (name.substr(0, cur2.size()) != cur2) continue; completions.insert(concatStrings(prev, expr, ".", name)); @@ -490,7 +490,7 @@ ProcessLineResult NixRepl::processLine(std::string line) auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit"); return {path, 0}; } else if (v.isLambda()) { - auto pos = state->positions[v.lambda.fun->pos]; + auto pos = state->positions[v.payload.lambda.fun->pos]; if (auto path = std::get_if(&pos.origin)) return {*path, pos.line}; else @@ -742,17 +742,17 @@ void NixRepl::loadFiles() void NixRepl::addAttrsToScope(Value & attrs) { state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "while evaluating an attribute set to be merged in the global scope"); - if (displ + attrs.attrs->size() >= envSize) + if (displ + attrs.attrs()->size() >= envSize) throw Error("environment full; cannot add more variables"); - for (auto & i : *attrs.attrs) { + for (auto & i : *attrs.attrs()) { staticEnv->vars.emplace_back(i.name, displ); env->values[displ++] = i.value; varNames.emplace(state->symbols[i.name]); } staticEnv->sort(); staticEnv->deduplicate(); - notice("Added %1% variables.", attrs.attrs->size()); + notice("Added %1% variables.", attrs.attrs()->size()); } diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index d6befd362e4..9ad201b63ba 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -72,10 +72,10 @@ std::pair findAlongAttrPath(EvalState & state, const std::strin if (attr.empty()) throw Error("empty attribute name in selection path '%1%'", attrPath); - Bindings::iterator a = v->attrs->find(state.symbols.create(attr)); - if (a == v->attrs->end()) { + auto a = v->attrs()->get(state.symbols.create(attr)); + if (!a) { std::set attrNames; - for (auto & attr : *v->attrs) + for (auto & attr : *v->attrs()) attrNames.insert(state.symbols[attr.name]); auto suggestions = Suggestions::bestMatches(attrNames, attr); diff --git a/src/libexpr/attr-set.cc b/src/libexpr/attr-set.cc index 877116f1f9c..866ef817aa4 100644 --- a/src/libexpr/attr-set.cc +++ b/src/libexpr/attr-set.cc @@ -23,23 +23,6 @@ Bindings * EvalState::allocBindings(size_t capacity) } -/* Create a new attribute named 'name' on an existing attribute set stored - in 'vAttrs' and return the newly allocated Value which is associated with - this attribute. */ -Value * EvalState::allocAttr(Value & vAttrs, Symbol name) -{ - Value * v = allocValue(); - vAttrs.attrs->push_back(Attr(name, v)); - return v; -} - - -Value * EvalState::allocAttr(Value & vAttrs, std::string_view name) -{ - return allocAttr(vAttrs, symbols.create(name)); -} - - Value & BindingsBuilder::alloc(Symbol name, PosIdx pos) { auto value = state.allocValue(); diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index 31215f880ad..c90bb0633f7 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -65,24 +65,26 @@ public: typedef Attr * iterator; + typedef const Attr * const_iterator; + void push_back(const Attr & attr) { assert(size_ < capacity_); attrs[size_++] = attr; } - iterator find(Symbol name) + const_iterator find(Symbol name) const { Attr key(name, 0); - iterator i = std::lower_bound(begin(), end(), key); + const_iterator i = std::lower_bound(begin(), end(), key); if (i != end() && i->name == name) return i; return end(); } - Attr * get(Symbol name) + const Attr * get(Symbol name) const { Attr key(name, 0); - iterator i = std::lower_bound(begin(), end(), key); + const_iterator i = std::lower_bound(begin(), end(), key); if (i != end() && i->name == name) return &*i; return nullptr; } @@ -90,14 +92,22 @@ public: iterator begin() { return &attrs[0]; } iterator end() { return &attrs[size_]; } + const_iterator begin() const { return &attrs[0]; } + const_iterator end() const { return &attrs[size_]; } + Attr & operator[](size_t pos) { return attrs[pos]; } + const Attr & operator[](size_t pos) const + { + return attrs[pos]; + } + void sort(); - size_t capacity() { return capacity_; } + size_t capacity() const { return capacity_; } /** * Returns the attributes in lexicographically sorted order. @@ -166,6 +176,20 @@ public: { return bindings; } + + size_t capacity() + { + return bindings->capacity(); + } + + void grow(Bindings * newBindings) + { + for (auto & i : *bindings) + newBindings->push_back(i); + bindings = newBindings; + } + + friend class ExprAttrs; }; } diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 1538eb05679..d60967a14a7 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -387,7 +387,7 @@ Value & AttrCursor::getValue() if (parent) { auto & vParent = parent->first->getValue(); root->state.forceAttrs(vParent, noPos, "while searching for an attribute"); - auto attr = vParent.attrs->get(parent->second); + auto attr = vParent.attrs()->get(parent->second); if (!attr) throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); _value = allocRootValue(attr->value); @@ -448,9 +448,9 @@ Value & AttrCursor::forceValue() cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}}; } else if (v.type() == nBool) - cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean}; + cachedValue = {root->db->setBool(getKey(), v.boolean()), v.boolean()}; else if (v.type() == nInt) - cachedValue = {root->db->setInt(getKey(), v.integer), int_t{v.integer}}; + cachedValue = {root->db->setInt(getKey(), v.integer()), int_t{v.integer()}}; else if (v.type() == nAttrs) ; // FIXME: do something? else @@ -510,7 +510,7 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro return nullptr; //error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); - auto attr = v.attrs->get(name); + auto attr = v.attrs()->get(name); if (!attr) { if (root->db) { @@ -652,7 +652,7 @@ bool AttrCursor::getBool() if (v.type() != nBool) root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); - return v.boolean; + return v.boolean(); } NixInt AttrCursor::getInt() @@ -674,7 +674,7 @@ NixInt AttrCursor::getInt() if (v.type() != nInt) root->state.error("'%s' is not an integer", getAttrPathStr()).debugThrow(); - return v.integer; + return v.integer(); } std::vector AttrCursor::getListOfStrings() @@ -730,7 +730,7 @@ std::vector AttrCursor::getAttrs() root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); std::vector attrs; - for (auto & attr : *getValue().attrs) + for (auto & attr : *getValue().attrs()) attrs.push_back(attr.name); std::sort(attrs.begin(), attrs.end(), [&](Symbol a, Symbol b) { std::string_view sa = root->state.symbols[a], sb = root->state.symbols[b]; diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 03320c7c977..6fa34b06279 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -85,8 +85,8 @@ Env & EvalState::allocEnv(size_t size) void EvalState::forceValue(Value & v, const PosIdx pos) { if (v.isThunk()) { - Env * env = v.thunk.env; - Expr * expr = v.thunk.expr; + Env * env = v.payload.thunk.env; + Expr * expr = v.payload.thunk.expr; try { v.mkBlackhole(); //checkInterrupt(); @@ -98,7 +98,7 @@ void EvalState::forceValue(Value & v, const PosIdx pos) } } else if (v.isApp()) - callFunction(*v.app.left, *v.app.right, v, pos); + callFunction(*v.payload.app.left, *v.payload.app.right, v, pos); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 5e2f716491d..7147e799754 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -131,7 +131,7 @@ void Value::print(EvalState & state, std::ostream & str, PrintOptions options) const Value * getPrimOp(const Value &v) { const Value * primOp = &v; while (primOp->isPrimOpApp()) { - primOp = primOp->primOpApp.left; + primOp = primOp->payload.primOpApp.left; } assert(primOp->isPrimOp()); return primOp; @@ -163,12 +163,12 @@ std::string showType(const Value & v) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" switch (v.internalType) { - case tString: return v.string.context ? "a string with context" : "a string"; + case tString: return v.payload.string.context ? "a string with context" : "a string"; case tPrimOp: - return fmt("the built-in function '%s'", std::string(v.primOp->name)); + return fmt("the built-in function '%s'", std::string(v.payload.primOp->name)); case tPrimOpApp: - return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->primOp->name)); - case tExternal: return v.external->showType(); + return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->payload.primOp->name)); + case tExternal: return v.external()->showType(); case tThunk: return v.isBlackhole() ? "a black hole" : "a thunk"; case tApp: return "a function application"; default: @@ -183,9 +183,9 @@ PosIdx Value::determinePos(const PosIdx pos) const #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" switch (internalType) { - case tAttrs: return attrs->pos; - case tLambda: return lambda.fun->pos; - case tApp: return app.left->determinePos(pos); + case tAttrs: return attrs()->pos; + case tLambda: return payload.lambda.fun->pos; + case tApp: return payload.app.left->determinePos(pos); default: return pos; } #pragma GCC diagnostic pop @@ -197,10 +197,10 @@ bool Value::isTrivial() const internalType != tApp && internalType != tPrimOpApp && (internalType != tThunk - || (dynamic_cast(thunk.expr) - && ((ExprAttrs *) thunk.expr)->dynamicAttrs.empty()) - || dynamic_cast(thunk.expr) - || dynamic_cast(thunk.expr)); + || (dynamic_cast(payload.thunk.expr) + && ((ExprAttrs *) payload.thunk.expr)->dynamicAttrs.empty()) + || dynamic_cast(payload.thunk.expr) + || dynamic_cast(payload.thunk.expr)); } @@ -584,7 +584,7 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info) /* Install value the base environment. */ staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl); baseEnv.values[baseEnvDispl++] = v; - baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v)); + baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(name2), v)); } } @@ -597,32 +597,30 @@ void PrimOp::check() } -std::ostream & operator<<(std::ostream & output, PrimOp & primOp) +std::ostream & operator<<(std::ostream & output, const PrimOp & primOp) { output << "primop " << primOp.name; return output; } -PrimOp * Value::primOpAppPrimOp() const +const PrimOp * Value::primOpAppPrimOp() const { - Value * left = primOpApp.left; + Value * left = payload.primOpApp.left; while (left && !left->isPrimOp()) { - left = left->primOpApp.left; + left = left->payload.primOpApp.left; } if (!left) return nullptr; - return left->primOp; + return left->primOp(); } void Value::mkPrimOp(PrimOp * p) { p->check(); - clearValue(); - internalType = tPrimOp; - primOp = p; + finishValue(tPrimOp, { .primOp = p }); } @@ -650,14 +648,14 @@ Value * EvalState::addPrimOp(PrimOp && primOp) v->mkPrimOp(new PrimOp(primOp)); staticBaseEnv->vars.emplace_back(envName, baseEnvDispl); baseEnv.values[baseEnvDispl++] = v; - baseEnv.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v)); + baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(primOp.name), v)); return v; } Value & EvalState::getBuiltin(const std::string & name) { - return *baseEnv.values[0]->attrs->find(symbols.create(name))->value; + return *baseEnv.values[0]->attrs()->find(symbols.create(name))->value; } @@ -665,12 +663,12 @@ std::optional EvalState::getDoc(Value & v) { if (v.isPrimOp()) { auto v2 = &v; - if (auto * doc = v2->primOp->doc) + if (auto * doc = v2->primOp()->doc) return Doc { .pos = {}, - .name = v2->primOp->name, - .arity = v2->primOp->arity, - .args = v2->primOp->args, + .name = v2->primOp()->name, + .arity = v2->primOp()->arity, + .args = v2->primOp()->args, .doc = doc, }; } @@ -694,8 +692,8 @@ void printWithBindings(const SymbolTable & st, const Env & env) if (!env.values[0]->isThunk()) { std::cout << "with: "; std::cout << ANSI_MAGENTA; - Bindings::iterator j = env.values[0]->attrs->begin(); - while (j != env.values[0]->attrs->end()) { + auto j = env.values[0]->attrs()->begin(); + while (j != env.values[0]->attrs()->end()) { std::cout << st[j->name] << " "; ++j; } @@ -749,11 +747,8 @@ void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const En if (se.isWith && !env.values[0]->isThunk()) { // add 'with' bindings. - Bindings::iterator j = env.values[0]->attrs->begin(); - while (j != env.values[0]->attrs->end()) { - vm[st[j->name]] = j->value; - ++j; - } + for (auto & j : *env.values[0]->attrs()) + vm[st[j.name]] = j.value; } else { // iterate through staticenv bindings and add them. for (auto & i : se.vars) @@ -873,28 +868,28 @@ void Value::mkString(std::string_view s) } -static void copyContextToValue(Value & v, const NixStringContext & context) +static const char * * encodeContext(const NixStringContext & context) { if (!context.empty()) { size_t n = 0; - v.string.context = (const char * *) + auto ctx = (const char * *) allocBytes((context.size() + 1) * sizeof(char *)); for (auto & i : context) - v.string.context[n++] = dupString(i.to_string().c_str()); - v.string.context[n] = 0; - } + ctx[n++] = dupString(i.to_string().c_str()); + ctx[n] = 0; + return ctx; + } else + return nullptr; } void Value::mkString(std::string_view s, const NixStringContext & context) { - mkString(s); - copyContextToValue(*this, context); + mkString(makeImmutableString(s), encodeContext(context)); } void Value::mkStringMove(const char * s, const NixStringContext & context) { - mkString(s); - copyContextToValue(*this, context); + mkString(s, encodeContext(context)); } @@ -918,8 +913,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) auto * fromWith = var.fromWith; while (1) { forceAttrs(*env->values[0], fromWith->pos, "while evaluating the first subexpression of a with expression"); - Bindings::iterator j = env->values[0]->attrs->find(var.name); - if (j != env->values[0]->attrs->end()) { + if (auto j = env->values[0]->attrs()->get(var.name)) { if (countCalls) attrSelects[j->pos]++; return j->value; } @@ -1167,7 +1161,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri showType(v), ValuePrinter(*this, v, errorPrintOptions) ).atPos(pos).withFrame(env, *e).debugThrow(); - return v.boolean; + return v.boolean(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -1235,8 +1229,9 @@ Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up) void ExprAttrs::eval(EvalState & state, Env & env, Value & v) { - v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish()); + 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 @@ -1261,7 +1256,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) } else vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv)); env2.values[displ++] = vAttr; - v.attrs->push_back(Attr(i.first, vAttr, i.second.pos)); + bindings.insert(i.first, vAttr, i.second.pos); } /* If the rec contains an attribute called `__overrides', then @@ -1273,32 +1268,28 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) been substituted into the bodies of the other attributes. Hence we need __overrides.) */ if (hasOverrides) { - Value * vOverrides = (*v.attrs)[overrides->second.displ].value; + Value * vOverrides = (*bindings.bindings)[overrides->second.displ].value; state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "while evaluating the `__overrides` attribute"); - Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size()); - for (auto & i : *v.attrs) - newBnds->push_back(i); - for (auto & i : *vOverrides->attrs) { + bindings.grow(state.allocBindings(bindings.capacity() + vOverrides->attrs()->size())); + for (auto & i : *vOverrides->attrs()) { AttrDefs::iterator j = attrs.find(i.name); if (j != attrs.end()) { - (*newBnds)[j->second.displ] = i; + (*bindings.bindings)[j->second.displ] = i; env2.values[j->second.displ] = i.value; } else - newBnds->push_back(i); + bindings.push_back(i); } - newBnds->sort(); - v.attrs = newBnds; + sort = true; } } else { Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr; - for (auto & i : attrs) { - v.attrs->push_back(Attr( - i.first, - i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)), - i.second.pos)); - } + 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. */ @@ -1310,17 +1301,21 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) continue; state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute"); auto nameSym = state.symbols.create(nameVal.string_view()); - Bindings::iterator j = v.attrs->find(nameSym); - if (j != v.attrs->end()) + if (sort) + // FIXME: inefficient + bindings.bindings->sort(); + if (auto j = bindings.bindings->get(nameSym)) state.error("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow(); i.valueExpr->setName(nameSym); /* Keep sorted order so find can catch duplicates */ - v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos)); - v.attrs->sort(); // FIXME: inefficient + bindings.insert(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos); + sort = true; } - v.attrs->pos = pos; + bindings.bindings->pos = pos; + + v.mkAttrs(sort ? bindings.finish() : bindings.alreadySorted()); } @@ -1426,21 +1421,21 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) for (auto & i : attrPath) { state.nrLookups++; - Bindings::iterator j; + const Attr * j; auto name = getName(i, state, env); if (def) { state.forceValue(*vAttrs, pos); if (vAttrs->type() != nAttrs || - (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) + !(j = vAttrs->attrs()->get(name))) { def->eval(state, env, v); return; } } else { state.forceAttrs(*vAttrs, pos, "while selecting an attribute"); - if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) { + if (!(j = vAttrs->attrs()->get(name))) { std::set allAttrNames; - for (auto & attr : *vAttrs->attrs) + for (auto & attr : *vAttrs->attrs()) allAttrNames.insert(state.symbols[attr.name]); auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]); state.error("attribute '%1%' missing", state.symbols[name]) @@ -1478,15 +1473,15 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) for (auto & i : attrPath) { state.forceValue(*vAttrs, getPos()); - Bindings::iterator j; + const Attr * j; auto name = getName(i, state, env); - if (vAttrs->type() != nAttrs || - (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) + if (vAttrs->type() == nAttrs && + (j = vAttrs->attrs()->get(name))) { + vAttrs = j->value; + } else { v.mkBool(false); return; - } else { - vAttrs = j->value; } } @@ -1538,19 +1533,19 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } }; - Attr * functor; + const Attr * functor; while (nrArgs > 0) { if (vCur.isLambda()) { - ExprLambda & lambda(*vCur.lambda.fun); + ExprLambda & lambda(*vCur.payload.lambda.fun); auto size = (!lambda.arg ? 0 : 1) + (lambda.hasFormals() ? lambda.formals->formals.size() : 0); Env & env2(allocEnv(size)); - env2.up = vCur.lambda.env; + env2.up = vCur.payload.lambda.env; Displacement displ = 0; @@ -1572,7 +1567,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & argument has a default, use the default. */ size_t attrsUsed = 0; for (auto & i : lambda.formals->formals) { - auto j = args[0]->attrs->get(i.name); + auto j = args[0]->attrs()->get(i.name); if (!j) { if (!i.def) { error("function '%1%' called without required argument '%2%'", @@ -1580,7 +1575,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & symbols[i.name]) .atPos(lambda.pos) .withTrace(pos, "from call site") - .withFrame(*fun.lambda.env, lambda) + .withFrame(*fun.payload.lambda.env, lambda) .debugThrow(); } env2.values[displ++] = i.def->maybeThunk(*this, env2); @@ -1592,10 +1587,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & /* 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.formals->ellipsis && attrsUsed != args[0]->attrs()->size()) { /* Nope, so show the first unexpected argument to the user. */ - for (auto & i : *args[0]->attrs) + for (auto & i : *args[0]->attrs()) if (!lambda.formals->has(i.name)) { std::set formalNames; for (auto & formal : lambda.formals->formals) @@ -1607,7 +1602,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & .atPos(lambda.pos) .withTrace(pos, "from call site") .withSuggestions(suggestions) - .withFrame(*fun.lambda.env, lambda) + .withFrame(*fun.payload.lambda.env, lambda) .debugThrow(); } abort(); // can't happen @@ -1649,7 +1644,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & else if (vCur.isPrimOp()) { - size_t argsLeft = vCur.primOp->arity; + size_t argsLeft = vCur.primOp()->arity; if (nrArgs < argsLeft) { /* We don't have enough arguments, so create a tPrimOpApp chain. */ @@ -1657,7 +1652,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & return; } else { /* We have all the arguments, so call the primop. */ - auto * fn = vCur.primOp; + auto * fn = vCur.primOp(); nrPrimOpCalls++; if (countCalls) primOpCalls[fn->name]++; @@ -1680,10 +1675,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & Value * primOp = &vCur; while (primOp->isPrimOpApp()) { argsDone++; - primOp = primOp->primOpApp.left; + primOp = primOp->payload.primOpApp.left; } assert(primOp->isPrimOp()); - auto arity = primOp->primOp->arity; + auto arity = primOp->primOp()->arity; auto argsLeft = arity - argsDone; if (nrArgs < argsLeft) { @@ -1696,13 +1691,13 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & Value * vArgs[maxPrimOpArity]; auto n = argsDone; - for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp.left) - vArgs[--n] = arg->primOpApp.right; + for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->payload.primOpApp.left) + vArgs[--n] = arg->payload.primOpApp.right; for (size_t i = 0; i < argsLeft; ++i) vArgs[argsDone + i] = args[i]; - auto fn = primOp->primOp; + auto fn = primOp->primOp(); nrPrimOpCalls++; if (countCalls) primOpCalls[fn->name]++; @@ -1722,7 +1717,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } } - else if (vCur.type() == nAttrs && (functor = vCur.attrs->get(sFunctor))) { + else if (vCur.type() == nAttrs && (functor = vCur.attrs()->get(sFunctor))) { /* 'vCur' may be allocated on the stack of the calling function, but for functors we may keep a reference, so heap-allocate a copy and use that instead. */ @@ -1797,8 +1792,8 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) forceValue(fun, pos); if (fun.type() == nAttrs) { - auto found = fun.attrs->find(sFunctor); - if (found != fun.attrs->end()) { + auto found = fun.attrs()->find(sFunctor); + if (found != fun.attrs()->end()) { Value * v = allocValue(); callFunction(*found->value, fun, *v, pos); forceValue(*v, pos); @@ -1806,14 +1801,14 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) } } - if (!fun.isLambda() || !fun.lambda.fun->hasFormals()) { + if (!fun.isLambda() || !fun.payload.lambda.fun->hasFormals()) { res = fun; return; } - auto attrs = buildBindings(std::max(static_cast(fun.lambda.fun->formals->formals.size()), args.size())); + auto attrs = buildBindings(std::max(static_cast(fun.payload.lambda.fun->formals->formals.size()), args.size())); - if (fun.lambda.fun->formals->ellipsis) { + if (fun.payload.lambda.fun->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) @@ -1821,9 +1816,9 @@ void EvalState::autoCallFunction(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) { - Bindings::iterator j = args.find(i.name); - if (j != args.end()) { + for (auto & i : fun.payload.lambda.fun->formals->formals) { + auto j = args.get(i.name); + if (j) { attrs.insert(*j); } else if (!i.def) { error(R"(cannot evaluate a function that has an argument without a value ('%1%') @@ -1831,7 +1826,7 @@ Nix attempted to evaluate a function as a top level expression; in this case it must have its arguments supplied either by default values, or passed explicitly with '--arg' or '--argstr'. See https://nixos.org/manual/nix/stable/language/constructs.html#functions.)", symbols[i.name]) - .atPos(i.pos).withFrame(*fun.lambda.env, *fun.lambda.fun).debugThrow(); + .atPos(i.pos).withFrame(*fun.payload.lambda.env, *fun.payload.lambda.fun).debugThrow(); } } } @@ -1916,17 +1911,17 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) state.nrOpUpdates++; - if (v1.attrs->size() == 0) { v = v2; return; } - if (v2.attrs->size() == 0) { v = v1; return; } + if (v1.attrs()->size() == 0) { v = v2; return; } + if (v2.attrs()->size() == 0) { v = v1; return; } - auto attrs = state.buildBindings(v1.attrs->size() + v2.attrs->size()); + auto attrs = state.buildBindings(v1.attrs()->size() + v2.attrs()->size()); /* Merge the sets, preferring values from the second set. Make sure to keep the resulting vector in sorted order. */ - Bindings::iterator i = v1.attrs->begin(); - Bindings::iterator j = v2.attrs->begin(); + auto i = v1.attrs()->begin(); + auto j = v2.attrs()->begin(); - while (i != v1.attrs->end() && j != v2.attrs->end()) { + while (i != v1.attrs()->end() && j != v2.attrs()->end()) { if (i->name == j->name) { attrs.insert(*j); ++i; ++j; @@ -1937,12 +1932,12 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) attrs.insert(*j++); } - while (i != v1.attrs->end()) attrs.insert(*i++); - while (j != v2.attrs->end()) attrs.insert(*j++); + while (i != v1.attrs()->end()) attrs.insert(*i++); + while (j != v2.attrs()->end()) attrs.insert(*j++); v.mkAttrs(attrs.alreadySorted()); - state.nrOpUpdateValuesCopied += v.attrs->size(); + state.nrOpUpdateValuesCopied += v.attrs()->size(); } @@ -2034,19 +2029,19 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) if (firstType == nInt) { if (vTmp.type() == nInt) { - n += vTmp.integer; + n += vTmp.integer(); } else if (vTmp.type() == nFloat) { // Upgrade the type from int to float; firstType = nFloat; nf = n; - nf += vTmp.fpoint; + nf += vTmp.fpoint(); } else state.error("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); } else if (firstType == nFloat) { if (vTmp.type() == nInt) { - nf += vTmp.integer; + nf += vTmp.integer(); } else if (vTmp.type() == nFloat) { - nf += vTmp.fpoint; + nf += vTmp.fpoint(); } else state.error("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); } else { @@ -2119,11 +2114,11 @@ void EvalState::forceValueDeep(Value & v) forceValue(v, v.determinePos(noPos)); if (v.type() == nAttrs) { - for (auto & i : *v.attrs) + for (auto & i : *v.attrs()) try { // If the value is a thunk, we're evaling. Otherwise no trace necessary. auto dts = debugRepl && i.value->isThunk() - ? makeDebugTraceStacker(*this, *i.value->thunk.expr, *i.value->thunk.env, positions[i.pos], + ? makeDebugTraceStacker(*this, *i.value->payload.thunk.expr, *i.value->payload.thunk.env, positions[i.pos], "while evaluating the attribute '%1%'", symbols[i.name]) : nullptr; @@ -2154,13 +2149,13 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCt showType(v), ValuePrinter(*this, v, errorPrintOptions) ).atPos(pos).debugThrow(); - return v.integer; + return v.integer(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; } - return v.integer; + return v.integer(); } @@ -2169,14 +2164,14 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view err try { forceValue(v, pos); if (v.type() == nInt) - return v.integer; + return v.integer(); else if (v.type() != nFloat) error( "expected a float but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) ).atPos(pos).debugThrow(); - return v.fpoint; + return v.fpoint(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -2194,19 +2189,19 @@ bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx showType(v), ValuePrinter(*this, v, errorPrintOptions) ).atPos(pos).debugThrow(); - return v.boolean; + return v.boolean(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; } - return v.boolean; + return v.boolean(); } bool EvalState::isFunctor(Value & fun) { - return fun.type() == nAttrs && fun.attrs->find(sFunctor) != fun.attrs->end(); + return fun.type() == nAttrs && fun.attrs()->find(sFunctor) != fun.attrs()->end(); } @@ -2247,8 +2242,8 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string void copyContext(const Value & v, NixStringContext & context) { - if (v.string.context) - for (const char * * p = v.string.context; *p; ++p) + if (v.payload.string.context) + for (const char * * p = v.payload.string.context; *p; ++p) context.insert(NixStringContextElem::parse(*p)); } @@ -2274,8 +2269,8 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s bool EvalState::isDerivation(Value & v) { if (v.type() != nAttrs) return false; - Bindings::iterator i = v.attrs->find(sType); - if (i == v.attrs->end()) return false; + auto i = v.attrs()->get(sType); + if (!i) return false; forceValue(*i->value, i->pos); if (i->value->type() != nString) return false; return i->value->string_view().compare("derivation") == 0; @@ -2285,8 +2280,8 @@ bool EvalState::isDerivation(Value & v) std::optional EvalState::tryAttrsToString(const PosIdx pos, Value & v, NixStringContext & context, bool coerceMore, bool copyToStore) { - auto i = v.attrs->find(sToString); - if (i != v.attrs->end()) { + auto i = v.attrs()->find(sToString); + if (i != v.attrs()->end()) { Value v1; callFunction(*i->value, v, v1, pos); return coerceToString(pos, v1, context, @@ -2318,7 +2313,7 @@ BackedStringView EvalState::coerceToString( !canonicalizePath && !copyToStore ? // FIXME: hack to preserve path literals that end in a // slash, as in /foo/${x}. - v._path.path + v.payload.path.path : copyToStore ? store->printStorePath(copyPathToStore(context, v.path())) : std::string(v.path().path.abs()); @@ -2328,8 +2323,8 @@ BackedStringView EvalState::coerceToString( auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore); if (maybeString) return std::move(*maybeString); - auto i = v.attrs->find(sOutPath); - if (i == v.attrs->end()) { + auto i = v.attrs()->find(sOutPath); + if (i == v.attrs()->end()) { error( "cannot coerce %1% to a string: %2%", showType(v), @@ -2344,7 +2339,7 @@ BackedStringView EvalState::coerceToString( if (v.type() == nExternal) { try { - return v.external->coerceToString(*this, pos, context, coerceMore, copyToStore); + return v.external()->coerceToString(*this, pos, context, coerceMore, copyToStore); } catch (Error & e) { e.addTrace(nullptr, errorCtx); throw; @@ -2354,10 +2349,10 @@ BackedStringView EvalState::coerceToString( if (coerceMore) { /* Note that `false' is represented as an empty string for shell scripting convenience, just like `null'. */ - if (v.type() == nBool && v.boolean) return "1"; - if (v.type() == nBool && !v.boolean) return ""; - if (v.type() == nInt) return std::to_string(v.integer); - if (v.type() == nFloat) return std::to_string(v.fpoint); + if (v.type() == nBool && v.boolean()) return "1"; + if (v.type() == nBool && !v.boolean()) return ""; + if (v.type() == nInt) return std::to_string(v.integer()); + if (v.type() == nFloat) return std::to_string(v.fpoint()); if (v.type() == nNull) return ""; if (v.isList()) { @@ -2436,8 +2431,8 @@ SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext /* Similarly, handle __toString where the result may be a path value. */ if (v.type() == nAttrs) { - auto i = v.attrs->find(sToString); - if (i != v.attrs->end()) { + auto i = v.attrs()->find(sToString); + if (i != v.attrs()->end()) { Value v1; callFunction(*i->value, v, v1, pos); return coerceToPath(pos, v1, context, errorCtx); @@ -2531,19 +2526,19 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v // Special case type-compatibility between float and int if (v1.type() == nInt && v2.type() == nFloat) - return v1.integer == v2.fpoint; + return v1.integer() == v2.fpoint(); if (v1.type() == nFloat && v2.type() == nInt) - return v1.fpoint == v2.integer; + return v1.fpoint() == v2.integer(); // All other types are not compatible with each other. if (v1.type() != v2.type()) return false; switch (v1.type()) { case nInt: - return v1.integer == v2.integer; + return v1.integer() == v2.integer(); case nBool: - return v1.boolean == v2.boolean; + return v1.boolean() == v2.boolean(); case nString: return strcmp(v1.c_str(), v2.c_str()) == 0; @@ -2551,8 +2546,8 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v case nPath: return // FIXME: compare accessors by their fingerprint. - v1._path.accessor == v2._path.accessor - && strcmp(v1._path.path, v2._path.path) == 0; + v1.payload.path.accessor == v2.payload.path.accessor + && strcmp(v1.payload.path.path, v2.payload.path.path) == 0; case nNull: return true; @@ -2567,17 +2562,17 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v /* If both sets denote a derivation (type = "derivation"), then compare their outPaths. */ if (isDerivation(v1) && isDerivation(v2)) { - Bindings::iterator i = v1.attrs->find(sOutPath); - Bindings::iterator j = v2.attrs->find(sOutPath); - if (i != v1.attrs->end() && j != v2.attrs->end()) + auto i = v1.attrs()->get(sOutPath); + auto j = v2.attrs()->get(sOutPath); + if (i && j) return eqValues(*i->value, *j->value, pos, errorCtx); } - if (v1.attrs->size() != v2.attrs->size()) return false; + if (v1.attrs()->size() != v2.attrs()->size()) return false; /* Otherwise, compare the attributes one by one. */ - Bindings::iterator i, j; - for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) + Bindings::const_iterator i, j; + for (i = v1.attrs()->begin(), j = v2.attrs()->begin(); i != v1.attrs()->end(); ++i, ++j) if (i->name != j->name || !eqValues(*i->value, *j->value, pos, errorCtx)) return false; @@ -2589,10 +2584,10 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v return false; case nExternal: - return *v1.external == *v2.external; + return *v1.external() == *v2.external(); case nFloat: - return v1.fpoint == v2.fpoint; + return v1.fpoint() == v2.fpoint(); case nThunk: // Must not be left by forceValue default: diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index f15d1965307..0e569de2693 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -86,7 +86,7 @@ struct PrimOp void check(); }; -std::ostream & operator<<(std::ostream & output, PrimOp & primOp); +std::ostream & operator<<(std::ostream & output, const PrimOp & primOp); /** * Info about a constant @@ -635,9 +635,6 @@ public: inline Value * allocValue(); inline Env & allocEnv(size_t size); - Value * allocAttr(Value & vAttrs, Symbol name); - Value * allocAttr(Value & vAttrs, std::string_view name); - Bindings * allocBindings(size_t capacity); BindingsBuilder buildBindings(size_t capacity) diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index bca473453f3..1f0326eb5bc 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -111,7 +111,7 @@ static FlakeInput parseFlakeInput(EvalState & state, fetchers::Attrs attrs; std::optional url; - for (nix::Attr attr : *(value->attrs)) { + for (auto & attr : *value->attrs()) { try { if (attr.name == sUrl) { expectType(state, nString, *attr.value, attr.pos); @@ -119,7 +119,7 @@ static FlakeInput parseFlakeInput(EvalState & state, attrs.emplace("url", *url); } else if (attr.name == sFlake) { expectType(state, nBool, *attr.value, attr.pos); - input.isFlake = attr.value->boolean; + input.isFlake = attr.value->boolean(); } else if (attr.name == sInputs) { input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath); } else if (attr.name == sFollows) { @@ -136,10 +136,10 @@ static FlakeInput parseFlakeInput(EvalState & state, attrs.emplace(state.symbols[attr.name], attr.value->c_str()); break; case nBool: - attrs.emplace(state.symbols[attr.name], Explicit { attr.value->boolean }); + attrs.emplace(state.symbols[attr.name], Explicit { attr.value->boolean() }); break; case nInt: - attrs.emplace(state.symbols[attr.name], (long unsigned int) attr.value->integer); + attrs.emplace(state.symbols[attr.name], (long unsigned int) attr.value->integer()); break; default: if (attr.name == state.symbols.create("publicKeys")) { @@ -189,7 +189,7 @@ static std::map parseFlakeInputs( expectType(state, nAttrs, *value, pos); - for (nix::Attr & inputAttr : *(*value).attrs) { + for (auto & inputAttr : *value->attrs()) { inputs.emplace(state.symbols[inputAttr.name], parseFlakeInput(state, state.symbols[inputAttr.name], @@ -223,23 +223,23 @@ static Flake readFlake( .path = flakePath, }; - if (auto description = vInfo.attrs->get(state.sDescription)) { + if (auto description = vInfo.attrs()->get(state.sDescription)) { expectType(state, nString, *description->value, description->pos); flake.description = description->value->c_str(); } auto sInputs = state.symbols.create("inputs"); - if (auto inputs = vInfo.attrs->get(sInputs)) + if (auto inputs = vInfo.attrs()->get(sInputs)) flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakePath.parent().path.abs(), lockRootPath); // FIXME auto sOutputs = state.symbols.create("outputs"); - if (auto outputs = vInfo.attrs->get(sOutputs)) { + 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->formals->formals) { + if (outputs->value->isLambda() && outputs->value->payload.lambda.fun->hasFormals()) { + for (auto & formal : outputs->value->payload.lambda.fun->formals->formals) { if (formal.name != state.sSelf) flake.inputs.emplace(state.symbols[formal.name], FlakeInput { .ref = parseFlakeRef(state.symbols[formal.name]) @@ -252,10 +252,10 @@ static Flake readFlake( auto sNixConfig = state.symbols.create("nixConfig"); - if (auto nixConfig = vInfo.attrs->get(sNixConfig)) { + if (auto nixConfig = vInfo.attrs()->get(sNixConfig)) { expectType(state, nAttrs, *nixConfig->value, nixConfig->pos); - for (auto & setting : *nixConfig->value->attrs) { + for (auto & setting : *nixConfig->value->attrs()) { forceTrivialValue(state, *setting.value, setting.pos); if (setting.value->type() == nString) flake.config.settings.emplace( @@ -291,7 +291,7 @@ static Flake readFlake( } } - for (auto & attr : *vInfo.attrs) { + for (auto & attr : *vInfo.attrs()) { if (attr.name != state.sDescription && attr.name != sInputs && attr.name != sOutputs && @@ -879,17 +879,17 @@ static void prim_flakeRefToString( state.forceAttrs(*args[0], noPos, "while evaluating the argument passed to builtins.flakeRefToString"); fetchers::Attrs attrs; - for (const auto & attr : *args[0]->attrs) { + for (const auto & attr : *args[0]->attrs()) { auto t = attr.value->type(); if (t == nInt) { attrs.emplace(state.symbols[attr.name], - (uint64_t) attr.value->integer); + (uint64_t) attr.value->integer()); } else if (t == nBool) { attrs.emplace(state.symbols[attr.name], - Explicit { attr.value->boolean }); + Explicit { attr.value->boolean() }); } else if (t == nString) { attrs.emplace(state.symbols[attr.name], - std::string(attr.value->string_view())); + std::string(attr.value->string_view())); } else { state.error( "flake reference attribute sets may only contain integers, Booleans, " diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index e9ed1ef0891..cf10ed84ac9 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -11,7 +11,7 @@ namespace nix { -PackageInfo::PackageInfo(EvalState & state, std::string attrPath, Bindings * attrs) +PackageInfo::PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs) : state(&state), attrs(attrs), attrPath(std::move(attrPath)) { } @@ -69,12 +69,11 @@ std::string PackageInfo::querySystem() const std::optional PackageInfo::queryDrvPath() const { if (!drvPath && attrs) { - Bindings::iterator i = attrs->find(state->sDrvPath); NixStringContext context; - if (i == attrs->end()) - drvPath = {std::nullopt}; - else + if (auto i = attrs->get(state->sDrvPath)) drvPath = {state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")}; + else + drvPath = {std::nullopt}; } return drvPath.value_or(std::nullopt); } @@ -91,7 +90,7 @@ StorePath PackageInfo::requireDrvPath() const StorePath PackageInfo::queryOutPath() const { if (!outPath && attrs) { - Bindings::iterator i = attrs->find(state->sOutPath); + auto i = attrs->find(state->sOutPath); NixStringContext context; if (i != attrs->end()) outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation"); @@ -106,8 +105,8 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT { if (outputs.empty()) { /* Get the ‘outputs’ list. */ - Bindings::iterator i; - if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) { + const Attr * i; + if (attrs && (i = attrs->get(state->sOutputs))) { state->forceList(*i->value, i->pos, "while evaluating the 'outputs' attribute of a derivation"); /* For each output... */ @@ -116,13 +115,13 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT if (withPaths) { /* Evaluate the corresponding set. */ - Bindings::iterator out = attrs->find(state->symbols.create(output)); - if (out == attrs->end()) continue; // FIXME: throw error? + auto out = attrs->get(state->symbols.create(output)); + if (!out) continue; // FIXME: throw error? state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation"); /* And evaluate its ‘outPath’ attribute. */ - Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); - if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? + auto outPath = out->value->attrs()->get(state->sOutPath); + if (!outPath) continue; // FIXME: throw error? NixStringContext context; outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation")); } else @@ -135,8 +134,8 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT if (!onlyOutputsToInstall || !attrs) return outputs; - Bindings::iterator i; - if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) { + const Attr * i; + if (attrs && (i = attrs->get(state->sOutputSpecified)) && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) { Outputs result; auto out = outputs.find(queryOutputName()); if (out == outputs.end()) @@ -167,21 +166,21 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT std::string PackageInfo::queryOutputName() const { if (outputName == "" && attrs) { - Bindings::iterator i = attrs->find(state->sOutputName); - outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : ""; + auto i = attrs->get(state->sOutputName); + outputName = i ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : ""; } return outputName; } -Bindings * PackageInfo::getMeta() +const Bindings * PackageInfo::getMeta() { if (meta) return meta; if (!attrs) return 0; - Bindings::iterator a = attrs->find(state->sMeta); - if (a == attrs->end()) return 0; + auto a = attrs->get(state->sMeta); + if (!a) return 0; state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation"); - meta = a->value->attrs; + meta = a->value->attrs(); return meta; } @@ -205,9 +204,8 @@ bool PackageInfo::checkMeta(Value & v) return true; } else if (v.type() == nAttrs) { - Bindings::iterator i = v.attrs->find(state->sOutPath); - if (i != v.attrs->end()) return false; - for (auto & i : *v.attrs) + if (v.attrs()->get(state->sOutPath)) return false; + for (auto & i : *v.attrs()) if (!checkMeta(*i.value)) return false; return true; } @@ -219,8 +217,8 @@ bool PackageInfo::checkMeta(Value & v) Value * PackageInfo::queryMeta(const std::string & name) { if (!getMeta()) return 0; - Bindings::iterator a = meta->find(state->symbols.create(name)); - if (a == meta->end() || !checkMeta(*a->value)) return 0; + auto a = meta->get(state->symbols.create(name)); + if (!a || !checkMeta(*a->value)) return 0; return a->value; } @@ -237,7 +235,7 @@ NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def) { Value * v = queryMeta(name); if (!v) return def; - if (v->type() == nInt) return v->integer; + if (v->type() == nInt) return v->integer(); if (v->type() == nString) { /* Backwards compatibility with before we had support for integer meta fields. */ @@ -251,7 +249,7 @@ NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def) { Value * v = queryMeta(name); if (!v) return def; - if (v->type() == nFloat) return v->fpoint; + if (v->type() == nFloat) return v->fpoint(); if (v->type() == nString) { /* Backwards compatibility with before we had support for float meta fields. */ @@ -266,7 +264,7 @@ bool PackageInfo::queryMetaBool(const std::string & name, bool def) { Value * v = queryMeta(name); if (!v) return def; - if (v->type() == nBool) return v->boolean; + if (v->type() == nBool) return v->boolean(); if (v->type() == nString) { /* Backwards compatibility with before we had support for Boolean meta fields. */ @@ -292,7 +290,7 @@ void PackageInfo::setMeta(const std::string & name, Value * v) /* Cache for already considered attrsets. */ -typedef std::set Done; +typedef std::set Done; /* Evaluate value `v'. If it evaluates to a set of type `derivation', @@ -309,9 +307,9 @@ static bool getDerivation(EvalState & state, Value & v, /* Remove spurious duplicates (e.g., a set like `rec { x = derivation {...}; y = x;}'. */ - if (!done.insert(v.attrs).second) return false; + if (!done.insert(v.attrs()).second) return false; - PackageInfo drv(state, attrPath, v.attrs); + PackageInfo drv(state, attrPath, v.attrs()); drv.queryName(); @@ -361,14 +359,14 @@ static void getDerivations(EvalState & state, Value & vIn, /* !!! undocumented hackery to support combining channels in nix-env.cc. */ - bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end(); + bool combineChannels = v.attrs()->get(state.symbols.create("_combineChannels")); /* Consider the attributes in sorted order to get more deterministic behaviour in nix-env operations (e.g. when there are names clashes between derivations, the derivation bound to the attribute with the "lower" name should take precedence). */ - for (auto & i : v.attrs->lexicographicOrder(state.symbols)) { + for (auto & i : v.attrs()->lexicographicOrder(state.symbols)) { debug("evaluating attribute '%1%'", state.symbols[i->name]); if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex)) continue; @@ -380,8 +378,8 @@ static void getDerivations(EvalState & state, Value & vIn, should we recurse into it? => Only if it has a `recurseForDerivations = true' attribute. */ if (i->value->type() == nAttrs) { - Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations); - if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`")) + auto j = i->value->attrs()->get(state.sRecurseForDerivations); + if (j && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`")) getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); } } diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index e8c1190f7dc..db3eedb05c7 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -33,9 +33,9 @@ private: */ bool failed = false; - Bindings * attrs = nullptr, * meta = nullptr; + const Bindings * attrs = nullptr, * meta = nullptr; - Bindings * getMeta(); + const Bindings * getMeta(); bool checkMeta(Value & v); @@ -46,7 +46,7 @@ public: std::string attrPath; PackageInfo(EvalState & state) : state(&state) { }; - PackageInfo(EvalState & state, std::string attrPath, Bindings * attrs); + PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs); PackageInfo(EvalState & state, ref store, const std::string & drvPathWithOutputs); std::string queryName() const; diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 5bdc466ebd2..c1e2b044882 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -28,12 +28,12 @@ void Expr::show(const SymbolTable & symbols, std::ostream & str) const void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const { - str << v.integer; + str << v.integer(); } void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const { - str << v.fpoint; + str << v.fpoint(); } void ExprString::show(const SymbolTable & symbols, std::ostream & str) const diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index d0fcfd1940a..630ee807160 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -216,13 +216,13 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v else { state.forceAttrs(*vScope, pos, "while evaluating the first argument passed to builtins.scopedImport"); - Env * env = &state.allocEnv(vScope->attrs->size()); + Env * env = &state.allocEnv(vScope->attrs()->size()); env->up = &state.baseEnv; - auto staticEnv = std::make_shared(nullptr, state.staticBaseEnv.get(), vScope->attrs->size()); + auto staticEnv = std::make_shared(nullptr, state.staticBaseEnv.get(), vScope->attrs()->size()); unsigned int displ = 0; - for (auto & attr : *vScope->attrs) { + for (auto & attr : *vScope->attrs()) { staticEnv->vars.emplace_back(attr.name, displ); env->values[displ++] = attr.value; } @@ -411,7 +411,7 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Val case nList: t = "list"; break; case nFunction: t = "lambda"; break; case nExternal: - t = args[0]->external->typeOf(); + t = args[0]->external()->typeOf(); break; case nFloat: t = "float"; break; case nThunk: abort(); @@ -575,9 +575,9 @@ struct CompareValues { try { if (v1->type() == nFloat && v2->type() == nInt) - return v1->fpoint < v2->integer; + return v1->fpoint() < v2->integer(); if (v1->type() == nInt && v2->type() == nFloat) - return v1->integer < v2->fpoint; + return v1->integer() < v2->fpoint(); if (v1->type() != v2->type()) state.error("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow(); // Allow selecting a subset of enum values @@ -585,16 +585,16 @@ struct CompareValues #pragma GCC diagnostic ignored "-Wswitch-enum" switch (v1->type()) { case nInt: - return v1->integer < v2->integer; + return v1->integer() < v2->integer(); case nFloat: - return v1->fpoint < v2->fpoint; + return v1->fpoint() < v2->fpoint(); case nString: return strcmp(v1->c_str(), v2->c_str()) < 0; case nPath: // Note: we don't take the accessor into account // since it's not obvious how to compare them in a // reproducible way. - return strcmp(v1->_path.path, v2->_path.path) < 0; + return strcmp(v1->payload.path.path, v2->payload.path.path) < 0; case nList: // Lexicographic comparison for (size_t i = 0;; i++) { @@ -626,13 +626,13 @@ typedef std::list ValueList; #endif -static Bindings::iterator getAttr( +static Bindings::const_iterator getAttr( EvalState & state, Symbol attrSym, - Bindings * attrSet, + const Bindings * attrSet, std::string_view errorCtx) { - Bindings::iterator value = attrSet->find(attrSym); + auto value = attrSet->find(attrSym); if (value == attrSet->end()) { state.error("attribute '%s' missing", state.symbols[attrSym]).withTrace(noPos, errorCtx).debugThrow(); } @@ -644,7 +644,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a state.forceAttrs(*args[0], noPos, "while evaluating the first argument passed to builtins.genericClosure"); /* Get the start set. */ - Bindings::iterator startSet = getAttr(state, state.sStartSet, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure"); + auto startSet = getAttr(state, state.sStartSet, args[0]->attrs(), "in the attrset passed as argument to builtins.genericClosure"); state.forceList(*startSet->value, noPos, "while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure"); @@ -658,7 +658,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a } /* Get the operator. */ - Bindings::iterator op = getAttr(state, state.sOperator, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure"); + auto op = getAttr(state, state.sOperator, args[0]->attrs(), "in the attrset passed as argument to builtins.genericClosure"); state.forceFunction(*op->value, noPos, "while evaluating the 'operator' attribute passed as argument to builtins.genericClosure"); /* Construct the closure by applying the operator to elements of @@ -675,7 +675,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a state.forceAttrs(*e, noPos, "while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure"); - Bindings::iterator key = getAttr(state, state.sKey, e->attrs, "in one of the attrsets generated by (or initially passed to) builtins.genericClosure"); + auto key = getAttr(state, state.sKey, e->attrs(), "in one of the attrsets generated by (or initially passed to) builtins.genericClosure"); state.forceValue(*key->value, noPos); if (!doneKeys.insert(key->value).second) continue; @@ -1041,7 +1041,11 @@ static void prim_second(EvalState & state, const PosIdx pos, Value * * args, Val * Derivations *************************************************************/ -static void derivationStrictInternal(EvalState & state, const std::string & name, Bindings * attrs, Value & v); +static void derivationStrictInternal( + EvalState & state, + const std::string & name, + const Bindings * attrs, + Value & v); /* Construct (as a unobservable side effect) a Nix derivation expression that performs the derivation described by the argument @@ -1054,10 +1058,10 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * { state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.derivationStrict"); - Bindings * attrs = args[0]->attrs; + auto attrs = args[0]->attrs(); /* Figure out the name first (for stack backtraces). */ - Bindings::iterator nameAttr = getAttr(state, state.sName, attrs, "in the attrset passed as argument to builtins.derivationStrict"); + auto nameAttr = getAttr(state, state.sName, attrs, "in the attrset passed as argument to builtins.derivationStrict"); std::string drvName; try { @@ -1096,8 +1100,11 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * } } -static void derivationStrictInternal(EvalState & state, const std::string & -drvName, Bindings * attrs, Value & v) +static void derivationStrictInternal( + EvalState & state, + const std::string & drvName, + const Bindings * attrs, + Value & v) { /* Check whether attributes should be passed as a JSON file. */ using nlohmann::json; @@ -1670,11 +1677,11 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.findFile"); std::string prefix; - Bindings::iterator i = v2->attrs->find(state.sPrefix); - if (i != v2->attrs->end()) + auto i = v2->attrs()->find(state.sPrefix); + if (i != v2->attrs()->end()) prefix = state.forceStringNoCtx(*i->value, pos, "while evaluating the `prefix` attribute of an element of the list passed to builtins.findFile"); - i = getAttr(state, state.sPath, v2->attrs, "in an element of the __nixPath"); + i = getAttr(state, state.sPath, v2->attrs(), "in an element of the __nixPath"); NixStringContext context; auto path = state.coerceToString(pos, *i->value, context, @@ -2346,7 +2353,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value state.forceAttrs(*args[0], pos, "while evaluating the argument passed to 'builtins.path'"); - for (auto & attr : *args[0]->attrs) { + for (auto & attr : *args[0]->attrs()) { auto n = state.symbols[attr.name]; if (n == "path") path.emplace(state.coerceToPath(attr.pos, *attr.value, context, "while evaluating the 'path' attribute passed to 'builtins.path'")); @@ -2421,9 +2428,9 @@ static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args, { state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrNames"); - auto list = state.buildList(args[0]->attrs->size()); + auto list = state.buildList(args[0]->attrs()->size()); - for (const auto & [n, i] : enumerate(*args[0]->attrs)) + for (const auto & [n, i] : enumerate(*args[0]->attrs())) (list[n] = state.allocValue())->mkString(state.symbols[i.name]); std::sort(list.begin(), list.end(), @@ -2449,9 +2456,9 @@ static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args, { state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrValues"); - auto list = state.buildList(args[0]->attrs->size()); + auto list = state.buildList(args[0]->attrs()->size()); - for (const auto & [n, i] : enumerate(*args[0]->attrs)) + for (const auto & [n, i] : enumerate(*args[0]->attrs())) list[n] = (Value *) &i; std::sort(list.begin(), list.end(), @@ -2482,10 +2489,10 @@ void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v { auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getAttr"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.getAttr"); - Bindings::iterator i = getAttr( + auto i = getAttr( state, state.symbols.create(attr), - args[1]->attrs, + args[1]->attrs(), "in the attribute set under consideration" ); // !!! add to stack trace? @@ -2511,8 +2518,8 @@ static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * * { auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.unsafeGetAttrPos"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.unsafeGetAttrPos"); - Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); - if (i == args[1]->attrs->end()) + auto i = args[1]->attrs()->find(state.symbols.create(attr)); + if (i == args[1]->attrs()->end()) v.mkNull(); else state.mkPos(v, i->pos); @@ -2540,13 +2547,13 @@ static struct LazyPosAcessors { PrimOp primop_lineOfPos{ .arity = 1, .fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) { - v.mkInt(state.positions[PosIdx(args[0]->integer)].line); + v.mkInt(state.positions[PosIdx(args[0]->integer())].line); } }; PrimOp primop_columnOfPos{ .arity = 1, .fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) { - v.mkInt(state.positions[PosIdx(args[0]->integer)].column); + v.mkInt(state.positions[PosIdx(args[0]->integer())].column); } }; @@ -2577,7 +2584,7 @@ static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Va { auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hasAttr"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.hasAttr"); - v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); + v.mkBool(args[1]->attrs()->find(state.symbols.create(attr)) != args[1]->attrs()->end()); } static RegisterPrimOp primop_hasAttr({ @@ -2627,9 +2634,9 @@ static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args /* Copy all attributes not in that set. Note that we don't need to sort v.attrs because it's a subset of an already sorted vector. */ - auto attrs = state.buildBindings(args[0]->attrs->size()); + auto attrs = state.buildBindings(args[0]->attrs()->size()); std::set_difference( - args[0]->attrs->begin(), args[0]->attrs->end(), + args[0]->attrs()->begin(), args[0]->attrs()->end(), names.begin(), names.end(), std::back_inserter(attrs)); v.mkAttrs(attrs.alreadySorted()); @@ -2667,13 +2674,13 @@ static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args for (auto v2 : args[0]->listItems()) { state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.listToAttrs"); - Bindings::iterator j = getAttr(state, state.sName, v2->attrs, "in a {name=...; value=...;} pair"); + auto j = getAttr(state, state.sName, v2->attrs(), "in a {name=...; value=...;} pair"); auto name = state.forceStringNoCtx(*j->value, j->pos, "while evaluating the `name` attribute of an element of the list passed to builtins.listToAttrs"); auto sym = state.symbols.create(name); if (seen.insert(sym).second) { - Bindings::iterator j2 = getAttr(state, state.sValue, v2->attrs, "in a {name=...; value=...;} pair"); + auto j2 = getAttr(state, state.sValue, v2->attrs(), "in a {name=...; value=...;} pair"); attrs.insert(sym, j2->value, j2->pos); } } @@ -2717,8 +2724,8 @@ static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * a state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.intersectAttrs"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.intersectAttrs"); - Bindings &left = *args[0]->attrs; - Bindings &right = *args[1]->attrs; + auto & left = *args[0]->attrs(); + auto & right = *args[1]->attrs(); auto attrs = state.buildBindings(std::min(left.size(), right.size())); @@ -2762,14 +2769,14 @@ static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * a if (left.size() < right.size()) { for (auto & l : left) { - Bindings::iterator r = right.find(l.name); + auto r = right.find(l.name); if (r != right.end()) attrs.insert(*r); } } else { for (auto & r : right) { - Bindings::iterator l = left.find(r.name); + auto l = left.find(r.name); if (l != left.end()) attrs.insert(r); } @@ -2800,8 +2807,7 @@ static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, V for (auto v2 : args[1]->listItems()) { state.forceAttrs(*v2, pos, "while evaluating an element in the list passed as second argument to builtins.catAttrs"); - Bindings::iterator i = v2->attrs->find(attrName); - if (i != v2->attrs->end()) + if (auto i = v2->attrs()->get(attrName)) res[found++] = i->value; } @@ -2838,13 +2844,13 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg if (!args[0]->isLambda()) state.error("'functionArgs' requires a function").atPos(pos).debugThrow(); - if (!args[0]->lambda.fun->hasFormals()) { + if (!args[0]->payload.lambda.fun->hasFormals()) { v.mkAttrs(&state.emptyBindings); return; } - auto attrs = state.buildBindings(args[0]->lambda.fun->formals->formals.size()); - for (auto & i : args[0]->lambda.fun->formals->formals) + auto attrs = state.buildBindings(args[0]->payload.lambda.fun->formals->formals.size()); + for (auto & i : args[0]->payload.lambda.fun->formals->formals) attrs.insert(i.name, state.getBool(i.def), i.pos); v.mkAttrs(attrs); } @@ -2871,9 +2877,9 @@ static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, V { state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.mapAttrs"); - auto attrs = state.buildBindings(args[1]->attrs->size()); + auto attrs = state.buildBindings(args[1]->attrs()->size()); - for (auto & i : *args[1]->attrs) { + for (auto & i : *args[1]->attrs()) { Value * vName = state.allocValue(); Value * vFun2 = state.allocValue(); vName->mkString(state.symbols[i.name]); @@ -2923,7 +2929,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg for (auto & vElem : listItems) { state.forceAttrs(*vElem, noPos, "while evaluating a value of the list passed as second argument to builtins.zipAttrsWith"); - for (auto & attr : *vElem->attrs) + for (auto & attr : *vElem->attrs()) attrsSeen.try_emplace(attr.name).first->second.size++; } @@ -2931,7 +2937,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg elem.list.emplace(state.buildList(elem.size)); for (auto & vElem : listItems) { - for (auto & attr : *vElem->attrs) { + for (auto & attr : *vElem->attrs()) { auto & item = attrsSeen.at(attr.name); (*item.list)[item.pos++] = attr.value; } @@ -3375,7 +3381,7 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value callFunction. */ /* TODO: (layus) this is absurd. An optimisation like this should be outside the lambda creation */ - if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan) + if (args[0]->isPrimOp() && args[0]->primOp()->fun == prim_lessThan) return CompareValues(state, noPos, "while evaluating the ordering function passed to builtins.sort")(a, b); Value * vs[] = {a, b}; @@ -3866,18 +3872,17 @@ static RegisterPrimOp primop_hashString({ static void prim_convertHash(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.convertHash"); - auto &inputAttrs = args[0]->attrs; + auto inputAttrs = args[0]->attrs(); - Bindings::iterator iteratorHash = getAttr(state, state.symbols.create("hash"), inputAttrs, "while locating the attribute 'hash'"); + auto iteratorHash = getAttr(state, state.symbols.create("hash"), inputAttrs, "while locating the attribute 'hash'"); auto hash = state.forceStringNoCtx(*iteratorHash->value, pos, "while evaluating the attribute 'hash'"); - Bindings::iterator iteratorHashAlgo = inputAttrs->find(state.symbols.create("hashAlgo")); + auto iteratorHashAlgo = inputAttrs->get(state.symbols.create("hashAlgo")); std::optional ha = std::nullopt; - if (iteratorHashAlgo != inputAttrs->end()) { + if (iteratorHashAlgo) ha = parseHashAlgo(state.forceStringNoCtx(*iteratorHashAlgo->value, pos, "while evaluating the attribute 'hashAlgo'")); - } - Bindings::iterator iteratorToHashFormat = getAttr(state, state.symbols.create("toHashFormat"), args[0]->attrs, "while locating the attribute 'toHashFormat'"); + auto iteratorToHashFormat = getAttr(state, state.symbols.create("toHashFormat"), args[0]->attrs(), "while locating the attribute 'toHashFormat'"); HashFormat hf = parseHashFormat(state.forceStringNoCtx(*iteratorToHashFormat->value, pos, "while evaluating the attribute 'toHashFormat'")); v.mkString(Hash::parseAny(hash, ha).to_string(hf, hf == HashFormat::SRI)); @@ -4623,7 +4628,7 @@ void EvalState::createBaseEnv() /* Now that we've added all primops, sort the `builtins' set, because attribute lookups expect it to be sorted. */ - baseEnv.values[0]->attrs->sort(); + baseEnv.values[0]->payload.attrs->sort(); staticBaseEnv->sort(); diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 75d9e147d78..2d3013132f7 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -258,7 +258,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar auto sPath = state.symbols.create("path"); auto sAllOutputs = state.symbols.create("allOutputs"); - for (auto & i : *args[1]->attrs) { + for (auto & i : *args[1]->attrs()) { const auto & name = state.symbols[i.name]; if (!state.store->isStorePath(name)) state.error( @@ -269,17 +269,16 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar if (!settings.readOnlyMode) state.store->ensurePath(namePath); state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context"); - auto iter = i.value->attrs->find(sPath); - if (iter != i.value->attrs->end()) { - if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context")) + + if (auto attr = i.value->attrs()->get(sPath)) { + if (state.forceBool(*attr->value, attr->pos, "while evaluating the `path` attribute of a string context")) context.emplace(NixStringContextElem::Opaque { .path = namePath, }); } - iter = i.value->attrs->find(sAllOutputs); - if (iter != i.value->attrs->end()) { - if (state.forceBool(*iter->value, iter->pos, "while evaluating the `allOutputs` attribute of a string context")) { + if (auto attr = i.value->attrs()->get(sAllOutputs)) { + if (state.forceBool(*attr->value, attr->pos, "while evaluating the `allOutputs` attribute of a string context")) { if (!isDerivation(name)) { state.error( "tried to add all-outputs context of %s, which is not a derivation, to a string", @@ -292,17 +291,16 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar } } - iter = i.value->attrs->find(state.sOutputs); - if (iter != i.value->attrs->end()) { - state.forceList(*iter->value, iter->pos, "while evaluating the `outputs` attribute of a string context"); - if (iter->value->listSize() && !isDerivation(name)) { + if (auto attr = i.value->attrs()->get(state.sOutputs)) { + state.forceList(*attr->value, attr->pos, "while evaluating the `outputs` attribute of a string context"); + if (attr->value->listSize() && !isDerivation(name)) { state.error( "tried to add derivation output context of %s, which is not a derivation, to a string", name ).atPos(i.pos).debugThrow(); } - for (auto elem : iter->value->listItems()) { - auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context"); + for (auto elem : attr->value->listItems()) { + auto outputName = state.forceStringNoCtx(*elem, attr->pos, "while evaluating an output name within a string context"); context.emplace(NixStringContextElem::Built { .drvPath = makeConstantStorePathRef(namePath), .output = std::string { outputName }, diff --git a/src/libexpr/primops/fetchClosure.cc b/src/libexpr/primops/fetchClosure.cc index f51a6465dc7..fc5bb31454f 100644 --- a/src/libexpr/primops/fetchClosure.cc +++ b/src/libexpr/primops/fetchClosure.cc @@ -121,7 +121,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg std::optional toPath; std::optional inputAddressedMaybe; - for (auto & attr : *args[0]->attrs) { + for (auto & attr : *args[0]->attrs()) { const auto & attrName = state.symbols[attr.name]; auto attrHint = [&]() -> std::string { return "while evaluating the '" + attrName + "' attribute passed to builtins.fetchClosure"; diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index bfc19115aa4..d9ba6aa97d8 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -20,7 +20,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a if (args[0]->type() == nAttrs) { - for (auto & attr : *args[0]->attrs) { + for (auto & attr : *args[0]->attrs()) { std::string_view n(state.symbols[attr.name]); if (n == "url") url = state.coerceToString(attr.pos, *attr.value, context, diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 5061e40fdf2..409b36d81af 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -97,7 +97,7 @@ static void fetchTree( fetchers::Attrs attrs; - if (auto aType = args[0]->attrs->get(state.sType)) { + if (auto aType = args[0]->attrs()->get(state.sType)) { if (type) state.error( "unexpected attribute 'type'" @@ -110,7 +110,7 @@ static void fetchTree( attrs.emplace("type", type.value()); - for (auto & attr : *args[0]->attrs) { + for (auto & attr : *args[0]->attrs()) { if (attr.name == state.sType) continue; state.forceValue(*attr.value, attr.pos); if (attr.value->type() == nPath || attr.value->type() == nString) { @@ -121,9 +121,9 @@ static void fetchTree( : s); } else if (attr.value->type() == nBool) - attrs.emplace(state.symbols[attr.name], Explicit{attr.value->boolean}); + attrs.emplace(state.symbols[attr.name], Explicit{attr.value->boolean()}); else if (attr.value->type() == nInt) - attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer)); + attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer())); else if (state.symbols[attr.name] == "publicKeys") { experimentalFeatureSettings.require(Xp::VerifiedFetches); attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump()); @@ -422,7 +422,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v if (args[0]->type() == nAttrs) { - for (auto & attr : *args[0]->attrs) { + for (auto & attr : *args[0]->attrs()) { std::string_view n(state.symbols[attr.name]); if (n == "url") url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch"); diff --git a/src/libexpr/print-ambiguous.cc b/src/libexpr/print-ambiguous.cc index 521250cecdb..5d55b45da3b 100644 --- a/src/libexpr/print-ambiguous.cc +++ b/src/libexpr/print-ambiguous.cc @@ -21,10 +21,10 @@ void printAmbiguous( } switch (v.type()) { case nInt: - str << v.integer; + str << v.integer(); break; case nBool: - printLiteralBool(str, v.boolean); + printLiteralBool(str, v.boolean()); break; case nString: printLiteralString(str, v.string_view()); @@ -36,11 +36,11 @@ void printAmbiguous( str << "null"; break; case nAttrs: { - if (seen && !v.attrs->empty() && !seen->insert(v.attrs).second) + if (seen && !v.attrs()->empty() && !seen->insert(v.attrs()).second) str << "«repeated»"; else { str << "{ "; - for (auto & i : v.attrs->lexicographicOrder(symbols)) { + for (auto & i : v.attrs()->lexicographicOrder(symbols)) { str << symbols[i->name] << " = "; printAmbiguous(*i->value, symbols, str, seen, depth - 1); str << "; "; @@ -87,10 +87,10 @@ void printAmbiguous( } break; case nExternal: - str << *v.external; + str << *v.external(); break; case nFloat: - str << v.fpoint; + str << v.fpoint(); break; default: printError("Nix evaluator internal error: printAmbiguous: invalid value type"); diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index f67e9475060..7799a0bbebf 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -223,7 +223,7 @@ class Printer { if (options.ansiColors) output << ANSI_CYAN; - output << v.integer; + output << v.integer(); if (options.ansiColors) output << ANSI_NORMAL; } @@ -232,7 +232,7 @@ class Printer { if (options.ansiColors) output << ANSI_CYAN; - output << v.fpoint; + output << v.fpoint(); if (options.ansiColors) output << ANSI_NORMAL; } @@ -241,7 +241,7 @@ class Printer { if (options.ansiColors) output << ANSI_CYAN; - printLiteralBool(output, v.boolean); + printLiteralBool(output, v.boolean()); if (options.ansiColors) output << ANSI_NORMAL; } @@ -271,10 +271,9 @@ class Printer void printDerivation(Value & v) { - Bindings::iterator i = v.attrs->find(state.sDrvPath); NixStringContext context; std::string storePath; - if (i != v.attrs->end()) + if (auto i = v.attrs()->get(state.sDrvPath)) storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation")); if (options.ansiColors) @@ -312,7 +311,7 @@ class Printer void printAttrs(Value & v, size_t depth) { - if (seen && !seen->insert(v.attrs).second) { + if (seen && !seen->insert(v.attrs()).second) { printRepeated(); return; } @@ -324,7 +323,7 @@ class Printer output << "{"; AttrVec sorted; - for (auto & i : *v.attrs) + for (auto & i : *v.attrs()) sorted.emplace_back(std::pair(state.symbols[i.name], i.value)); if (options.maxAttrs == std::numeric_limits::max()) @@ -423,18 +422,18 @@ class Printer if (v.isLambda()) { output << "lambda"; - if (v.lambda.fun) { - if (v.lambda.fun->name) { - output << " " << state.symbols[v.lambda.fun->name]; + if (v.payload.lambda.fun) { + if (v.payload.lambda.fun->name) { + output << " " << state.symbols[v.payload.lambda.fun->name]; } std::ostringstream s; - s << state.positions[v.lambda.fun->pos]; + s << state.positions[v.payload.lambda.fun->pos]; output << " @ " << filterANSIEscapes(s.str()); } } else if (v.isPrimOp()) { - if (v.primOp) - output << *v.primOp; + if (v.primOp()) + output << *v.primOp(); else output << "primop"; } else if (v.isPrimOpApp()) { @@ -480,7 +479,7 @@ class Printer void printExternal(Value & v) { - v.external->print(output); + v.external()->print(output); } void printUnknown() diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index 3f877a7fd4f..936ecf07826 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -22,11 +22,11 @@ json printValueAsJSON(EvalState & state, bool strict, switch (v.type()) { case nInt: - out = v.integer; + out = v.integer(); break; case nBool: - out = v.boolean; + out = v.boolean(); break; case nString: @@ -52,24 +52,20 @@ json printValueAsJSON(EvalState & state, bool strict, out = *maybeString; break; } - auto i = v.attrs->find(state.sOutPath); - if (i == v.attrs->end()) { + if (auto i = v.attrs()->get(state.sOutPath)) + return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore); + else { out = json::object(); - StringSet names; - for (auto & j : *v.attrs) - names.emplace(state.symbols[j.name]); - for (auto & j : names) { - Attr & a(*v.attrs->find(state.symbols.create(j))); + for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) { try { - out[j] = printValueAsJSON(state, strict, *a.value, a.pos, context, copyToStore); + out[state.symbols[a->name]] = printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore); } catch (Error & e) { - e.addTrace(state.positions[a.pos], - HintFmt("while evaluating attribute '%1%'", j)); + e.addTrace(state.positions[a->pos], + HintFmt("while evaluating attribute '%1%'", state.symbols[a->name])); throw; } } - } else - return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore); + } break; } @@ -90,11 +86,11 @@ json printValueAsJSON(EvalState & state, bool strict, } case nExternal: - return v.external->printValueAsJSON(state, strict, context, copyToStore); + return v.external()->printValueAsJSON(state, strict, context, copyToStore); break; case nFloat: - out = v.fpoint; + out = v.fpoint(); break; case nThunk: diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 5032115bbb5..1de8cdf848d 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -32,23 +32,18 @@ static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos) static void showAttrs(EvalState & state, bool strict, bool location, - Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen) + const Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen) { StringSet names; - for (auto & i : attrs) - names.emplace(state.symbols[i.name]); - - for (auto & i : names) { - Attr & a(*attrs.find(state.symbols.create(i))); - + for (auto & a : attrs.lexicographicOrder(state.symbols)) { XMLAttrs xmlAttrs; - xmlAttrs["name"] = i; - if (location && a.pos) posToXML(state, xmlAttrs, state.positions[a.pos]); + xmlAttrs["name"] = state.symbols[a->name]; + if (location && a->pos) posToXML(state, xmlAttrs, state.positions[a->pos]); XMLOpenElement _(doc, "attr", xmlAttrs); printValueAsXML(state, strict, location, - *a.value, doc, context, drvsSeen, a.pos); + *a->value, doc, context, drvsSeen, a->pos); } } @@ -64,11 +59,11 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, switch (v.type()) { case nInt: - doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer))); + doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer()))); break; case nBool: - doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false")); + doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean() ? "true" : "false")); break; case nString: @@ -89,18 +84,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, if (state.isDerivation(v)) { XMLAttrs xmlAttrs; - Bindings::iterator a = v.attrs->find(state.symbols.create("derivation")); - Path drvPath; - a = v.attrs->find(state.sDrvPath); - if (a != v.attrs->end()) { + if (auto a = v.attrs()->get(state.sDrvPath)) { if (strict) state.forceValue(*a->value, a->pos); if (a->value->type() == nString) xmlAttrs["drvPath"] = drvPath = a->value->c_str(); } - a = v.attrs->find(state.sOutPath); - if (a != v.attrs->end()) { + if (auto a = v.attrs()->get(state.sOutPath)) { if (strict) state.forceValue(*a->value, a->pos); if (a->value->type() == nString) xmlAttrs["outPath"] = a->value->c_str(); @@ -109,14 +100,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, XMLOpenElement _(doc, "derivation", xmlAttrs); if (drvPath != "" && drvsSeen.insert(drvPath).second) - showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen); + showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen); else doc.writeEmptyElement("repeated"); } else { XMLOpenElement _(doc, "attrs"); - showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen); + showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen); } break; @@ -135,28 +126,28 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, break; } XMLAttrs xmlAttrs; - if (location) posToXML(state, xmlAttrs, state.positions[v.lambda.fun->pos]); + if (location) posToXML(state, xmlAttrs, state.positions[v.payload.lambda.fun->pos]); XMLOpenElement _(doc, "function", xmlAttrs); - if (v.lambda.fun->hasFormals()) { + if (v.payload.lambda.fun->hasFormals()) { XMLAttrs attrs; - if (v.lambda.fun->arg) attrs["name"] = state.symbols[v.lambda.fun->arg]; - if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1"; + if (v.payload.lambda.fun->arg) attrs["name"] = state.symbols[v.payload.lambda.fun->arg]; + if (v.payload.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1"; XMLOpenElement _(doc, "attrspat", attrs); - for (auto & i : v.lambda.fun->formals->lexicographicOrder(state.symbols)) + for (auto & i : v.payload.lambda.fun->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])); + doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.payload.lambda.fun->arg])); break; } case nExternal: - v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos); + v.external()->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos); break; case nFloat: - doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint))); + doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint()))); break; case nThunk: diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 335801b345b..eb71765c5ef 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -234,14 +234,14 @@ public: ExprLambda * fun; }; - union + using Payload = union { NixInt integer; bool boolean; StringWithContext string; - Path _path; + Path path; Bindings * attrs; struct { @@ -258,6 +258,8 @@ public: NixFloat fpoint; }; + Payload payload; + /** * Returns the normal type of a Value. This only returns nThunk if * the Value hasn't been forceValue'd @@ -286,34 +288,31 @@ public: abort(); } - /** - * After overwriting an app node, be sure to clear pointers in the - * Value to ensure that the target isn't kept alive unnecessarily. - */ - inline void clearValue() + inline void finishValue(InternalType newType, Payload newPayload) { - app.left = app.right = 0; + /* After overwriting thunk/app values, be sure to clear + pointers in the Value to ensure that the target isn't kept + alive unnecessarily. */ + payload.app.left = payload.app.right = 0; + + payload = newPayload; + + internalType = newType; } inline void mkInt(NixInt n) { - clearValue(); - internalType = tInt; - integer = n; + finishValue(tInt, { .integer = n }); } inline void mkBool(bool b) { - clearValue(); - internalType = tBool; - boolean = b; + finishValue(tBool, { .boolean = b }); } inline void mkString(const char * s, const char * * context = 0) { - internalType = tString; - string.c_str = s; - string.context = context; + finishValue(tString, { .string = { .c_str = s, .context = context } }); } void mkString(std::string_view s); @@ -331,63 +330,44 @@ public: inline void mkPath(InputAccessor * accessor, const char * path) { - clearValue(); - internalType = tPath; - _path.accessor = accessor; - _path.path = path; + finishValue(tPath, { .path = { .accessor = accessor, .path = path } }); } inline void mkNull() { - clearValue(); - internalType = tNull; + finishValue(tNull, {}); } inline void mkAttrs(Bindings * a) { - clearValue(); - internalType = tAttrs; - attrs = a; + finishValue(tAttrs, { .attrs = a }); } Value & mkAttrs(BindingsBuilder & bindings); void mkList(const ListBuilder & builder) { - clearValue(); - if (builder.size == 1) { - smallList[0] = builder.inlineElems[0]; - internalType = tList1; - } else if (builder.size == 2) { - smallList[0] = builder.inlineElems[0]; - smallList[1] = builder.inlineElems[1]; - internalType = tList2; - } else { - bigList.size = builder.size; - bigList.elems = builder.elems; - internalType = tListN; - } + if (builder.size == 1) + finishValue(tList1, { .smallList = { builder.inlineElems[0] } }); + else if (builder.size == 2) + finishValue(tList2, { .smallList = { builder.inlineElems[0], builder.inlineElems[1] } }); + else + finishValue(tListN, { .bigList = { .size = builder.size, .elems = builder.elems } }); } inline void mkThunk(Env * e, Expr * ex) { - internalType = tThunk; - thunk.env = e; - thunk.expr = ex; + finishValue(tThunk, { .thunk = { .env = e, .expr = ex } }); } inline void mkApp(Value * l, Value * r) { - internalType = tApp; - app.left = l; - app.right = r; + finishValue(tApp, { .app = { .left = l, .right = r } }); } inline void mkLambda(Env * e, ExprLambda * f) { - internalType = tLambda; - lambda.env = e; - lambda.fun = f; + finishValue(tLambda, { .lambda = { .env = e, .fun = f } }); } inline void mkBlackhole(); @@ -396,28 +376,22 @@ public: inline void mkPrimOpApp(Value * l, Value * r) { - internalType = tPrimOpApp; - primOpApp.left = l; - primOpApp.right = r; + finishValue(tPrimOpApp, { .primOpApp = { .left = l, .right = r } }); } /** * For a `tPrimOpApp` value, get the original `PrimOp` value. */ - PrimOp * primOpAppPrimOp() const; + const PrimOp * primOpAppPrimOp() const; inline void mkExternal(ExternalValueBase * e) { - clearValue(); - internalType = tExternal; - external = e; + finishValue(tExternal, { .external = e }); } inline void mkFloat(NixFloat n) { - clearValue(); - internalType = tFloat; - fpoint = n; + finishValue(tFloat, { .fpoint = n }); } bool isList() const @@ -427,7 +401,7 @@ public: Value * const * listElems() { - return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems; + return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems; } std::span listItems() const @@ -438,12 +412,12 @@ public: Value * const * listElems() const { - return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems; + return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems; } size_t listSize() const { - return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size; + return internalType == tList1 ? 1 : internalType == tList2 ? 2 : payload.bigList.size; } PosIdx determinePos(const PosIdx pos) const; @@ -459,26 +433,44 @@ public: { assert(internalType == tPath); return SourcePath( - ref(_path.accessor->shared_from_this()), - CanonPath(CanonPath::unchecked_t(), _path.path)); + ref(payload.path.accessor->shared_from_this()), + CanonPath(CanonPath::unchecked_t(), payload.path.path)); } std::string_view string_view() const { assert(internalType == tString); - return std::string_view(string.c_str); + return std::string_view(payload.string.c_str); } const char * const c_str() const { assert(internalType == tString); - return string.c_str; + return payload.string.c_str; } const char * * context() const { - return string.context; + return payload.string.context; } + + ExternalValueBase * external() const + { return payload.external; } + + const Bindings * attrs() const + { return payload.attrs; } + + const PrimOp * primOp() const + { return payload.primOp; } + + bool boolean() const + { return payload.boolean; } + + NixInt integer() const + { return payload.integer; } + + NixFloat fpoint() const + { return payload.fpoint; } }; @@ -486,13 +478,12 @@ extern ExprBlackHole eBlackHole; bool Value::isBlackhole() const { - return internalType == tThunk && thunk.expr == (Expr*) &eBlackHole; + return internalType == tThunk && payload.thunk.expr == (Expr*) &eBlackHole; } void Value::mkBlackhole() { - internalType = tThunk; - thunk.expr = (Expr*) &eBlackHole; + mkThunk(nullptr, (Expr *) &eBlackHole); } diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 35eef5b8342..35bc039264f 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -332,8 +332,8 @@ 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->formals->formals) { + if (v.type() == nFunction && v.payload.lambda.fun->hasFormals()) { + for (auto & i : v.payload.lambda.fun->formals->formals) { if (state->symbols[i.name] == "inNixShell") { add = true; break; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index f7975537525..9aff83bb0e8 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1237,15 +1237,15 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nInt) { attrs2["type"] = "int"; - attrs2["value"] = fmt("%1%", v->integer); + attrs2["value"] = fmt("%1%", v->integer()); xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nFloat) { attrs2["type"] = "float"; - attrs2["value"] = fmt("%1%", v->fpoint); + attrs2["value"] = fmt("%1%", v->fpoint()); xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nBool) { attrs2["type"] = "bool"; - attrs2["value"] = v->boolean ? "true" : "false"; + attrs2["value"] = v->boolean() ? "true" : "false"; xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nList) { attrs2["type"] = "strings"; @@ -1259,13 +1259,11 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) } else if (v->type() == nAttrs) { attrs2["type"] = "strings"; XMLOpenElement m(xml, "meta", attrs2); - Bindings & attrs = *v->attrs; - for (auto &i : attrs) { - Attr & a(*attrs.find(i.name)); - if(a.value->type() != nString) continue; + for (auto & i : *v->attrs()) { + if (i.value->type() != nString) continue; XMLAttrs attrs3; attrs3["type"] = globals.state->symbols[i.name]; - attrs3["value"] = a.value->c_str(); + attrs3["value"] = i.value->c_str(); xml.writeEmptyElement("string", attrs3); } } diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index dd27344aa60..6cbbacb1522 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -138,9 +138,9 @@ bool createUserEnv(EvalState & state, PackageInfos & elems, debug("evaluating user environment builder"); state.forceValue(topLevel, topLevel.determinePos(noPos)); NixStringContext context; - Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); + auto & aDrvPath(*topLevel.attrs()->find(state.sDrvPath)); auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, ""); - Attr & aOutPath(*topLevel.attrs->find(state.sOutPath)); + auto & aOutPath(*topLevel.attrs()->find(state.sOutPath)); auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context, ""); /* Realise the resulting store expression. */ diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 54cc6a17f36..2e50392f7aa 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -93,14 +93,14 @@ struct CmdBundle : InstallableValueCommand if (!evalState->isDerivation(*vRes)) throw Error("the bundler '%s' does not produce a derivation", bundler.what()); - auto attr1 = vRes->attrs->get(evalState->sDrvPath); + auto attr1 = vRes->attrs()->get(evalState->sDrvPath); if (!attr1) throw Error("the bundler '%s' does not produce a derivation", bundler.what()); NixStringContext context2; auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2, ""); - auto attr2 = vRes->attrs->get(evalState->sOutPath); + auto attr2 = vRes->attrs()->get(evalState->sOutPath); if (!attr2) throw Error("the bundler '%s' does not produce a derivation", bundler.what()); @@ -116,7 +116,7 @@ struct CmdBundle : InstallableValueCommand auto outPathS = store->printStorePath(outPath); if (!outLink) { - auto * attr = vRes->attrs->get(evalState->sName); + auto * attr = vRes->attrs()->get(evalState->sName); if (!attr) throw Error("attribute 'name' missing"); outLink = evalState->forceStringNoCtx(*attr->value, attr->pos, ""); diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 088be3b1782..7b9bf7cb133 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -89,7 +89,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption else if (v.type() == nAttrs) { if (mkdir(path.c_str(), 0777) == -1) throw SysError("creating directory '%s'", path); - for (auto & attr : *v.attrs) { + for (auto & attr : *v.attrs()) { std::string_view name = state->symbols[attr.name]; try { if (name == "." || name == "..") diff --git a/src/nix/flake.cc b/src/nix/flake.cc index a846f637114..4825ab0e132 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -169,7 +169,7 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, auto pos = vFlake.determinePos(noPos); state.forceAttrs(vFlake, pos, "while evaluating a flake to get its outputs"); - auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs")); + auto aOutputs = vFlake.attrs()->get(state.symbols.create("outputs")); assert(aOutputs); state.forceAttrs(*aOutputs->value, pos, "while evaluating the outputs of a flake"); @@ -179,10 +179,10 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, /* Hack: ensure that hydraJobs is evaluated before anything else. This way we can disable IFD for hydraJobs and then enable it for other outputs. */ - if (auto attr = aOutputs->value->attrs->get(sHydraJobs)) + if (auto attr = aOutputs->value->attrs()->get(sHydraJobs)) callback(state.symbols[attr->name], *attr->value, attr->pos); - for (auto & attr : *aOutputs->value->attrs) { + for (auto & attr : *aOutputs->value->attrs()) { if (attr.name != sHydraJobs) callback(state.symbols[attr.name], *attr.value, attr.pos); } @@ -451,10 +451,10 @@ 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.payload.lambda.fun->hasFormals() + || !argHasName(v.payload.lambda.fun->arg, "final")) throw Error("overlay does not take an argument named 'final'"); - auto body = dynamic_cast(v.lambda.fun->body); + auto body = dynamic_cast(v.payload.lambda.fun->body); if (!body || body->hasFormals() || !argHasName(body->arg, "prev")) @@ -489,7 +489,7 @@ struct CmdFlakeCheck : FlakeCommand if (state->isDerivation(v)) throw Error("jobset should not be a derivation at top-level"); - for (auto & attr : *v.attrs) { + for (auto & attr : *v.attrs()) { state->forceAttrs(*attr.value, attr.pos, ""); auto attrPath2 = concatStrings(attrPath, ".", state->symbols[attr.name]); if (state->isDerivation(*attr.value)) { @@ -528,7 +528,7 @@ struct CmdFlakeCheck : FlakeCommand state->forceAttrs(v, pos, ""); - if (auto attr = v.attrs->get(state->symbols.create("path"))) { + if (auto attr = v.attrs()->get(state->symbols.create("path"))) { if (attr->name == state->symbols.create("path")) { NixStringContext context; auto path = state->coerceToPath(attr->pos, *attr->value, context, ""); @@ -539,12 +539,12 @@ struct CmdFlakeCheck : FlakeCommand } else throw Error("template '%s' lacks attribute 'path'", attrPath); - if (auto attr = v.attrs->get(state->symbols.create("description"))) + if (auto attr = v.attrs()->get(state->symbols.create("description"))) state->forceStringNoCtx(*attr->value, attr->pos, ""); else throw Error("template '%s' lacks attribute 'description'", attrPath); - for (auto & attr : *v.attrs) { + for (auto & attr : *v.attrs()) { std::string_view name(state->symbols[attr.name]); if (name != "path" && name != "description" && name != "welcomeText") throw Error("template '%s' has unsupported attribute '%s'", attrPath, name); @@ -600,12 +600,12 @@ struct CmdFlakeCheck : FlakeCommand if (name == "checks") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { state->forceAttrs(*attr.value, attr.pos, ""); - for (auto & attr2 : *attr.value->attrs) { + for (auto & attr2 : *attr.value->attrs()) { auto drvPath = checkDerivation( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), *attr2.value, attr2.pos); @@ -622,7 +622,7 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "formatter") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { @@ -635,12 +635,12 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "packages" || name == "devShells") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { state->forceAttrs(*attr.value, attr.pos, ""); - for (auto & attr2 : *attr.value->attrs) + for (auto & attr2 : *attr.value->attrs()) checkDerivation( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), *attr2.value, attr2.pos); @@ -650,12 +650,12 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "apps") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { state->forceAttrs(*attr.value, attr.pos, ""); - for (auto & attr2 : *attr.value->attrs) + for (auto & attr2 : *attr.value->attrs()) checkApp( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), *attr2.value, attr2.pos); @@ -665,7 +665,7 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "defaultPackage" || name == "devShell") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { @@ -678,7 +678,7 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "defaultApp") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos) ) { @@ -691,7 +691,7 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "legacyPackages") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { checkSystemName(state->symbols[attr.name], attr.pos); checkSystemType(state->symbols[attr.name], attr.pos); // FIXME: do getDerivations? @@ -703,7 +703,7 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "overlays") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) + for (auto & attr : *vOutput.attrs()) checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); } @@ -713,14 +713,14 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "nixosModules") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) + for (auto & attr : *vOutput.attrs()) checkModule(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); } else if (name == "nixosConfigurations") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) + for (auto & attr : *vOutput.attrs()) checkNixOSConfiguration(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); } @@ -733,14 +733,14 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "templates") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) + for (auto & attr : *vOutput.attrs()) checkTemplate(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); } else if (name == "defaultBundler") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { @@ -753,12 +753,12 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "bundlers") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { state->forceAttrs(*attr.value, attr.pos, ""); - for (auto & attr2 : *attr.value->attrs) { + for (auto & attr2 : *attr.value->attrs()) { checkBundler( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), *attr2.value, attr2.pos); diff --git a/src/nix/main.cc b/src/nix/main.cc index 5af5f2e4174..1ee9e419814 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -256,7 +256,7 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) state.callFunction(*vGenerateManpage, state.getBuiltin("false"), *vRes, noPos); state.callFunction(*vRes, *vDump, *vRes, noPos); - auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md")); + auto attr = vRes->attrs()->get(state.symbols.create(mdName + ".md")); if (!attr) throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); @@ -400,11 +400,10 @@ void mainWrapped(int argc, char * * argv) auto res = nlohmann::json::object(); res["builtins"] = ({ auto builtinsJson = nlohmann::json::object(); - auto builtins = state.baseEnv.values[0]->attrs; - for (auto & builtin : *builtins) { + for (auto & builtin : *state.baseEnv.values[0]->attrs()) { auto b = nlohmann::json::object(); if (!builtin.value->isPrimOp()) continue; - auto primOp = builtin.value->primOp; + auto primOp = builtin.value->primOp(); if (!primOp->doc) continue; b["arity"] = primOp->arity; b["args"] = primOp->args; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index fabec5d8823..17178c84f8d 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -35,8 +35,8 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url) vMirrors); state.forceAttrs(vMirrors, noPos, "while evaluating the set of all mirrors"); - auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); - if (mirrorList == vMirrors.attrs->end()) + auto mirrorList = vMirrors.attrs()->get(state.symbols.create(mirrorName)); + if (!mirrorList) throw Error("unknown mirror name '%s'", mirrorName); state.forceList(*mirrorList->value, noPos, "while evaluating one mirror configuration"); @@ -213,7 +213,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) state->forceAttrs(v, noPos, "while evaluating the source attribute to prefetch"); /* Extract the URL. */ - auto * attr = v.attrs->get(state->symbols.create("urls")); + auto * attr = v.attrs()->get(state->symbols.create("urls")); if (!attr) throw Error("attribute 'urls' missing"); state->forceList(*attr->value, noPos, "while evaluating the urls to prefetch"); @@ -222,7 +222,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) url = state->forceString(*attr->value->listElems()[0], noPos, "while evaluating the first url from the urls list"); /* Extract the hash mode. */ - auto attr2 = v.attrs->get(state->symbols.create("outputHashMode")); + auto attr2 = v.attrs()->get(state->symbols.create("outputHashMode")); if (!attr2) printInfo("warning: this does not look like a fetchurl call"); else @@ -230,7 +230,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) /* Extract the name. */ if (!name) { - auto attr3 = v.attrs->get(state->symbols.create("name")); + auto attr3 = v.attrs()->get(state->symbols.create("name")); if (!attr3) name = state->forceString(*attr3->value, noPos, "while evaluating the name of the source to prefetch"); } diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh index d720cedde10..1a431399082 100644 --- a/tests/unit/libexpr-support/tests/libexpr.hh +++ b/tests/unit/libexpr-support/tests/libexpr.hh @@ -80,28 +80,28 @@ namespace nix { if (arg.type() != nInt) { return false; } - return arg.integer == v; + return arg.integer() == v; } MATCHER_P(IsFloatEq, v, fmt("The float is equal to \"%1%\"", v)) { if (arg.type() != nFloat) { return false; } - return arg.fpoint == v; + return arg.fpoint() == v; } MATCHER(IsTrue, "") { if (arg.type() != nBool) { return false; } - return arg.boolean == true; + return arg.boolean() == true; } MATCHER(IsFalse, "") { if (arg.type() != nBool) { return false; } - return arg.boolean == false; + return arg.boolean() == false; } MATCHER_P(IsPathEq, p, fmt("Is a path equal to \"%1%\"", p)) { @@ -134,8 +134,8 @@ namespace nix { if (arg.type() != nAttrs) { *result_listener << "Expected set got " << arg.type(); return false; - } else if (arg.attrs->size() != (size_t)n) { - *result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs->size(); + } else if (arg.attrs()->size() != (size_t) n) { + *result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs()->size(); return false; } return true; diff --git a/tests/unit/libexpr/primops.cc b/tests/unit/libexpr/primops.cc index b1426edaeee..7988eac70f6 100644 --- a/tests/unit/libexpr/primops.cc +++ b/tests/unit/libexpr/primops.cc @@ -72,7 +72,7 @@ namespace nix { auto v = eval("builtins.tryEval (throw \"\")"); ASSERT_THAT(v, IsAttrsOfSize(2)); auto s = createSymbol("success"); - auto p = v.attrs->get(s); + auto p = v.attrs()->get(s); ASSERT_NE(p, nullptr); ASSERT_THAT(*p->value, IsFalse()); } @@ -81,11 +81,11 @@ namespace nix { auto v = eval("builtins.tryEval 123"); ASSERT_THAT(v, IsAttrs()); auto s = createSymbol("success"); - auto p = v.attrs->get(s); + auto p = v.attrs()->get(s); ASSERT_NE(p, nullptr); ASSERT_THAT(*p->value, IsTrue()); s = createSymbol("value"); - p = v.attrs->get(s); + p = v.attrs()->get(s); ASSERT_NE(p, nullptr); ASSERT_THAT(*p->value, IsIntEq(123)); } @@ -157,18 +157,18 @@ namespace nix { auto v = eval(expr); ASSERT_THAT(v, IsAttrsOfSize(3)); - auto file = v.attrs->find(createSymbol("file")); + auto file = v.attrs()->find(createSymbol("file")); ASSERT_NE(file, nullptr); ASSERT_THAT(*file->value, IsString()); auto s = baseNameOf(file->value->string_view()); ASSERT_EQ(s, "foo.nix"); - auto line = v.attrs->find(createSymbol("line")); + auto line = v.attrs()->find(createSymbol("line")); ASSERT_NE(line, nullptr); state.forceValue(*line->value, noPos); ASSERT_THAT(*line->value, IsIntEq(4)); - auto column = v.attrs->find(createSymbol("column")); + auto column = v.attrs()->find(createSymbol("column")); ASSERT_NE(column, nullptr); state.forceValue(*column->value, noPos); ASSERT_THAT(*column->value, IsIntEq(3)); @@ -202,14 +202,14 @@ namespace nix { TEST_F(PrimOpTest, removeAttrsRetains) { auto v = eval("builtins.removeAttrs { x = 1; y = 2; } [\"x\"]"); ASSERT_THAT(v, IsAttrsOfSize(1)); - ASSERT_NE(v.attrs->find(createSymbol("y")), nullptr); + ASSERT_NE(v.attrs()->find(createSymbol("y")), nullptr); } TEST_F(PrimOpTest, listToAttrsEmptyList) { auto v = eval("builtins.listToAttrs []"); ASSERT_THAT(v, IsAttrsOfSize(0)); ASSERT_EQ(v.type(), nAttrs); - ASSERT_EQ(v.attrs->size(), 0); + ASSERT_EQ(v.attrs()->size(), 0); } TEST_F(PrimOpTest, listToAttrsNotFieldName) { @@ -219,7 +219,7 @@ namespace nix { TEST_F(PrimOpTest, listToAttrs) { auto v = eval("builtins.listToAttrs [ { name = \"key\"; value = 123; } ]"); ASSERT_THAT(v, IsAttrsOfSize(1)); - auto key = v.attrs->find(createSymbol("key")); + auto key = v.attrs()->find(createSymbol("key")); ASSERT_NE(key, nullptr); ASSERT_THAT(*key->value, IsIntEq(123)); } @@ -227,7 +227,7 @@ namespace nix { TEST_F(PrimOpTest, intersectAttrs) { auto v = eval("builtins.intersectAttrs { a = 1; b = 2; } { b = 3; c = 4; }"); ASSERT_THAT(v, IsAttrsOfSize(1)); - auto b = v.attrs->find(createSymbol("b")); + auto b = v.attrs()->find(createSymbol("b")); ASSERT_NE(b, nullptr); ASSERT_THAT(*b->value, IsIntEq(3)); } @@ -243,11 +243,11 @@ namespace nix { auto v = eval("builtins.functionArgs ({ x, y ? 123}: 1)"); ASSERT_THAT(v, IsAttrsOfSize(2)); - auto x = v.attrs->find(createSymbol("x")); + auto x = v.attrs()->find(createSymbol("x")); ASSERT_NE(x, nullptr); ASSERT_THAT(*x->value, IsFalse()); - auto y = v.attrs->find(createSymbol("y")); + auto y = v.attrs()->find(createSymbol("y")); ASSERT_NE(y, nullptr); ASSERT_THAT(*y->value, IsTrue()); } @@ -256,13 +256,13 @@ namespace nix { auto v = eval("builtins.mapAttrs (name: value: value * 10) { a = 1; b = 2; }"); ASSERT_THAT(v, IsAttrsOfSize(2)); - auto a = v.attrs->find(createSymbol("a")); + auto a = v.attrs()->find(createSymbol("a")); ASSERT_NE(a, nullptr); ASSERT_THAT(*a->value, IsThunk()); state.forceValue(*a->value, noPos); ASSERT_THAT(*a->value, IsIntEq(10)); - auto b = v.attrs->find(createSymbol("b")); + auto b = v.attrs()->find(createSymbol("b")); ASSERT_NE(b, nullptr); ASSERT_THAT(*b->value, IsThunk()); state.forceValue(*b->value, noPos); @@ -410,13 +410,13 @@ namespace nix { auto v = eval("builtins.partition (x: x > 10) [1 23 9 3 42]"); ASSERT_THAT(v, IsAttrsOfSize(2)); - auto right = v.attrs->get(createSymbol("right")); + auto right = v.attrs()->get(createSymbol("right")); ASSERT_NE(right, nullptr); ASSERT_THAT(*right->value, IsListOfSize(2)); ASSERT_THAT(*right->value->listElems()[0], IsIntEq(23)); ASSERT_THAT(*right->value->listElems()[1], IsIntEq(42)); - auto wrong = v.attrs->get(createSymbol("wrong")); + auto wrong = v.attrs()->get(createSymbol("wrong")); ASSERT_NE(wrong, nullptr); ASSERT_EQ(wrong->value->type(), nList); ASSERT_EQ(wrong->value->listSize(), 3); @@ -641,14 +641,14 @@ namespace nix { auto v = eval("derivation"); ASSERT_EQ(v.type(), nFunction); ASSERT_TRUE(v.isLambda()); - ASSERT_NE(v.lambda.fun, nullptr); - ASSERT_TRUE(v.lambda.fun->hasFormals()); + ASSERT_NE(v.payload.lambda.fun, nullptr); + ASSERT_TRUE(v.payload.lambda.fun->hasFormals()); } TEST_F(PrimOpTest, currentTime) { auto v = eval("builtins.currentTime"); ASSERT_EQ(v.type(), nInt); - ASSERT_TRUE(v.integer > 0); + ASSERT_TRUE(v.integer() > 0); } TEST_F(PrimOpTest, splitVersion) { @@ -709,11 +709,11 @@ namespace nix { auto v = eval(expr); ASSERT_THAT(v, IsAttrsOfSize(2)); - auto name = v.attrs->find(createSymbol("name")); + auto name = v.attrs()->find(createSymbol("name")); ASSERT_TRUE(name); ASSERT_THAT(*name->value, IsStringEq(expectedName)); - auto version = v.attrs->find(createSymbol("version")); + auto version = v.attrs()->find(createSymbol("version")); ASSERT_TRUE(version); ASSERT_THAT(*version->value, IsStringEq(expectedVersion)); } diff --git a/tests/unit/libexpr/trivial.cc b/tests/unit/libexpr/trivial.cc index 171727ac71b..61ea71a0f3c 100644 --- a/tests/unit/libexpr/trivial.cc +++ b/tests/unit/libexpr/trivial.cc @@ -62,11 +62,11 @@ namespace nix { TEST_F(TrivialExpressionTest, updateAttrs) { auto v = eval("{ a = 1; } // { b = 2; a = 3; }"); ASSERT_THAT(v, IsAttrsOfSize(2)); - auto a = v.attrs->find(createSymbol("a")); + auto a = v.attrs()->find(createSymbol("a")); ASSERT_NE(a, nullptr); ASSERT_THAT(*a->value, IsIntEq(3)); - auto b = v.attrs->find(createSymbol("b")); + auto b = v.attrs()->find(createSymbol("b")); ASSERT_NE(b, nullptr); ASSERT_THAT(*b->value, IsIntEq(2)); } @@ -151,7 +151,7 @@ namespace nix { auto v = eval(expr); ASSERT_THAT(v, IsAttrsOfSize(1)); - auto a = v.attrs->find(createSymbol("a")); + auto a = v.attrs()->find(createSymbol("a")); ASSERT_NE(a, nullptr); ASSERT_THAT(*a->value, IsThunk()); @@ -159,11 +159,11 @@ namespace nix { ASSERT_THAT(*a->value, IsAttrsOfSize(2)); - auto b = a->value->attrs->find(createSymbol("b")); + auto b = a->value->attrs()->find(createSymbol("b")); ASSERT_NE(b, nullptr); ASSERT_THAT(*b->value, IsIntEq(1)); - auto c = a->value->attrs->find(createSymbol("c")); + auto c = a->value->attrs()->find(createSymbol("c")); ASSERT_NE(c, nullptr); ASSERT_THAT(*c->value, IsIntEq(2)); } @@ -185,7 +185,7 @@ namespace nix { TEST_F(TrivialExpressionTest, bindOr) { auto v = eval("{ or = 1; }"); ASSERT_THAT(v, IsAttrsOfSize(1)); - auto b = v.attrs->find(createSymbol("or")); + auto b = v.attrs()->find(createSymbol("or")); ASSERT_NE(b, nullptr); ASSERT_THAT(*b->value, IsIntEq(1)); }