diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libexpr/eval.cc | 40 | ||||
-rw-r--r-- | src/libexpr/get-drvs.cc | 28 | ||||
-rw-r--r-- | src/libexpr/nixexpr.cc | 113 | ||||
-rw-r--r-- | src/libexpr/nixexpr.hh | 52 | ||||
-rw-r--r-- | src/libexpr/parser-state.hh | 13 | ||||
-rw-r--r-- | src/libexpr/parser.y | 16 | ||||
-rw-r--r-- | src/nix/eval.cc | 13 |
7 files changed, 219 insertions, 56 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index bb3e6f3bd..1739a04fa 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1217,6 +1217,18 @@ void ExprPath::eval(EvalState & state, Env & env, Value & v) } +Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up) +{ + Env & inheritEnv = state.allocEnv(inheritFromExprs->size()); + inheritEnv.up = &up; + + Displacement displ = 0; + for (auto from : *inheritFromExprs) + inheritEnv.values[displ++] = from->maybeThunk(state, up); + + return &inheritEnv; +} + void ExprAttrs::eval(EvalState & state, Env & env, Value & v) { v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish()); @@ -1228,6 +1240,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) Env & env2(state.allocEnv(attrs.size())); env2.up = &env; dynamicEnv = &env2; + Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env2) : nullptr; AttrDefs::iterator overrides = attrs.find(state.sOverrides); bool hasOverrides = overrides != attrs.end(); @@ -1238,11 +1251,11 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) Displacement displ = 0; for (auto & i : attrs) { Value * vAttr; - if (hasOverrides && !i.second.inherited) { + if (hasOverrides && i.second.kind != AttrDef::Kind::Inherited) { vAttr = state.allocValue(); - mkThunk(*vAttr, env2, i.second.e); + mkThunk(*vAttr, *i.second.chooseByKind(&env2, &env, inheritEnv), i.second.e); } else - vAttr = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); + 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)); } @@ -1274,9 +1287,15 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) } } - else - for (auto & i : attrs) - v.attrs->push_back(Attr(i.first, i.second.e->maybeThunk(state, env), i.second.pos)); + 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)); + } + } /* Dynamic attrs apply *after* rec and __overrides. */ for (auto & i : dynamicAttrs) { @@ -1308,12 +1327,17 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) Env & env2(state.allocEnv(attrs->attrs.size())); env2.up = &env; + Env * inheritEnv = attrs->inheritFromExprs ? attrs->buildInheritFromEnv(state, env2) : nullptr; + /* The recursive attributes are evaluated in the new environment, while the inherited attributes are evaluated in the original environment. */ Displacement displ = 0; - for (auto & i : attrs->attrs) - env2.values[displ++] = i.second.e->maybeThunk(state, i.second.inherited ? env : env2); + for (auto & i : attrs->attrs) { + env2.values[displ++] = i.second.e->maybeThunk( + state, + *i.second.chooseByKind(&env2, &env, inheritEnv)); + } auto dts = state.debugRepl ? makeDebugTraceStacker( diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 495407c39..e686ffe8c 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -296,22 +296,16 @@ void DrvInfo::setMeta(const std::string & name, Value * v) typedef std::set<Bindings *> Done; -/* Evaluate value `v'. If it evaluates to a set of type `derivation', - then put information about it in `drvs' (unless it's already in `done'). - The result boolean indicates whether it makes sense +/* The result boolean indicates whether it makes sense for the caller to recursively search for derivations in `v'. */ static bool getDerivation(EvalState & state, Value & v, - const std::string & attrPath, DrvInfos & drvs, Done & done, + const std::string & attrPath, DrvInfos & drvs, bool ignoreAssertionFailures) { try { state.forceValue(v, v.determinePos(noPos)); if (!state.isDerivation(v)) return true; - /* Remove spurious duplicates (e.g., a set like `rec { x = - derivation {...}; y = x;}'. */ - if (!done.insert(v.attrs).second) return false; - DrvInfo drv(state, attrPath, v.attrs); drv.queryName(); @@ -330,9 +324,8 @@ static bool getDerivation(EvalState & state, Value & v, std::optional<DrvInfo> getDerivation(EvalState & state, Value & v, bool ignoreAssertionFailures) { - Done done; DrvInfos drvs; - getDerivation(state, v, "", drvs, done, ignoreAssertionFailures); + getDerivation(state, v, "", drvs, ignoreAssertionFailures); if (drvs.size() != 1) return {}; return std::move(drvs.front()); } @@ -347,6 +340,9 @@ static std::string addToPath(const std::string & s1, const std::string & s2) static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*"); +/* Evaluate value `v'. If it evaluates to a set of type `derivation', + then put information about it in `drvs'. If it evaluates to a different + kind of set recurse (unless it's already in `done'). */ static void getDerivations(EvalState & state, Value & vIn, const std::string & pathPrefix, Bindings & autoArgs, DrvInfos & drvs, Done & done, @@ -356,10 +352,14 @@ static void getDerivations(EvalState & state, Value & vIn, state.autoCallFunction(autoArgs, vIn, v); /* Process the expression. */ - if (!getDerivation(state, v, pathPrefix, drvs, done, ignoreAssertionFailures)) ; + if (!getDerivation(state, v, pathPrefix, drvs, ignoreAssertionFailures)) ; else if (v.type() == nAttrs) { + /* Dont consider sets we've already seen, e.g. y in + `rec { x.d = derivation {...}; y = x; }`. */ + if (!done.insert(v.attrs).second) return; + /* !!! undocumented hackery to support combining channels in nix-env.cc. */ bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end(); @@ -376,7 +376,7 @@ static void getDerivations(EvalState & state, Value & vIn, std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]); if (combineChannels) getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); - else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) { + else if (getDerivation(state, *i->value, pathPrefix2, drvs, ignoreAssertionFailures)) { /* If the value of this attribute is itself a set, should we recurse into it? => Only if it has a `recurseForDerivations = true' attribute. */ @@ -390,9 +390,11 @@ static void getDerivations(EvalState & state, Value & vIn, } else if (v.type() == nList) { + // NOTE we can't really deduplicate here because small lists don't have stable addresses + // and can cause spurious duplicate detections due to v being on the stack. for (auto [n, elem] : enumerate(v.listItems())) { std::string pathPrefix2 = addToPath(pathPrefix, fmt("%d", n)); - if (getDerivation(state, *elem, pathPrefix2, drvs, done, ignoreAssertionFailures)) + if (getDerivation(state, *elem, pathPrefix2, drvs, ignoreAssertionFailures)) getDerivations(state, *elem, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); } } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 3525ea1a3..3bcd35fef 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -68,10 +68,8 @@ void ExprOpHasAttr::show(const SymbolTable & symbols, std::ostream & str) const str << ") ? " << showAttrPath(symbols, attrPath) << ")"; } -void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const +void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) const { - if (recursive) str << "rec "; - str << "{ "; typedef const decltype(attrs)::value_type * Attr; std::vector<Attr> sorted; for (auto & i : attrs) sorted.push_back(&i); @@ -79,10 +77,37 @@ void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const std::string_view sa = symbols[a->first], sb = symbols[b->first]; return sa < sb; }); + std::vector<Symbol> inherits; + std::map<ExprInheritFrom *, std::vector<Symbol>> inheritsFrom; for (auto & i : sorted) { - if (i->second.inherited) - str << "inherit " << symbols[i->first] << " " << "; "; - else { + switch (i->second.kind) { + case AttrDef::Kind::Plain: + break; + case AttrDef::Kind::Inherited: + inherits.push_back(i->first); + break; + case AttrDef::Kind::InheritedFrom: { + auto & select = dynamic_cast<ExprSelect &>(*i->second.e); + auto & from = dynamic_cast<ExprInheritFrom &>(*select.e); + inheritsFrom[&from].push_back(i->first); + break; + } + } + } + if (!inherits.empty()) { + str << "inherit"; + for (auto sym : inherits) str << " " << symbols[sym]; + str << "; "; + } + for (const auto & [from, syms] : inheritsFrom) { + str << "inherit ("; + (*inheritFromExprs)[from->displ]->show(symbols, str); + str << ")"; + for (auto sym : syms) str << " " << symbols[sym]; + str << "; "; + } + for (auto & i : sorted) { + if (i->second.kind == AttrDef::Kind::Plain) { str << symbols[i->first] << " = "; i->second.e->show(symbols, str); str << "; "; @@ -95,6 +120,13 @@ void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const i.valueExpr->show(symbols, str); str << "; "; } +} + +void ExprAttrs::show(const SymbolTable & symbols, std::ostream & str) const +{ + if (recursive) str << "rec "; + str << "{ "; + showBindings(symbols, str); str << "}"; } @@ -150,15 +182,7 @@ void ExprCall::show(const SymbolTable & symbols, std::ostream & str) const void ExprLet::show(const SymbolTable & symbols, std::ostream & str) const { str << "(let "; - for (auto & i : attrs->attrs) - if (i.second.inherited) { - str << "inherit " << symbols[i.first] << "; "; - } - else { - str << symbols[i.first] << " = "; - i.second.e->show(symbols, str); - str << "; "; - } + attrs->showBindings(symbols, str); str << "in "; body->show(symbols, str); str << ")"; @@ -303,6 +327,12 @@ void ExprVar::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & this->level = withLevel; } +void ExprInheritFrom::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) +{ + if (es.debugRepl) + es.exprEnvs.insert(std::make_pair(this, env)); +} + void ExprSelect::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) { if (es.debugRepl) @@ -326,22 +356,47 @@ void ExprOpHasAttr::bindVars(EvalState & es, const std::shared_ptr<const StaticE i.expr->bindVars(es, env); } +std::shared_ptr<const StaticEnv> ExprAttrs::bindInheritSources( + EvalState & es, const std::shared_ptr<const StaticEnv> & env) +{ + if (!inheritFromExprs) + return nullptr; + + // the inherit (from) source values are inserted into an env of its own, which + // does not introduce any variable names. + // analysis must see an empty env, or an env that contains only entries with + // otherwise unused names to not interfere with regular names. the parser + // has already filled all exprs that access this env with appropriate level + // and displacement, and nothing else is allowed to access it. ideally we'd + // not even *have* an expr that grabs anything from this env since it's fully + // invisible, but the evaluator does not allow for this yet. + auto inner = std::make_shared<StaticEnv>(nullptr, env.get(), 0); + for (auto from : *inheritFromExprs) + from->bindVars(es, env); + + return inner; +} + void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) { if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, env)); if (recursive) { - auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), recursive ? attrs.size() : 0); + auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> { + auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs.size()); - Displacement displ = 0; - for (auto & i : attrs) - newEnv->vars.emplace_back(i.first, i.second.displ = displ++); + Displacement displ = 0; + for (auto & i : attrs) + newEnv->vars.emplace_back(i.first, i.second.displ = displ++); + return newEnv; + }(); // No need to sort newEnv since attrs is in sorted order. + auto inheritFromEnv = bindInheritSources(es, newEnv); for (auto & i : attrs) - i.second.e->bindVars(es, i.second.inherited ? env : newEnv); + i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv)); for (auto & i : dynamicAttrs) { i.nameExpr->bindVars(es, newEnv); @@ -349,8 +404,10 @@ void ExprAttrs::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> } } else { + auto inheritFromEnv = bindInheritSources(es, env); + for (auto & i : attrs) - i.second.e->bindVars(es, env); + i.second.e->bindVars(es, i.second.chooseByKind(env, env, inheritFromEnv)); for (auto & i : dynamicAttrs) { i.nameExpr->bindVars(es, env); @@ -407,16 +464,20 @@ void ExprCall::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & void ExprLet::bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env) { - auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size()); + auto newEnv = [&] () -> std::shared_ptr<const StaticEnv> { + auto newEnv = std::make_shared<StaticEnv>(nullptr, env.get(), attrs->attrs.size()); - Displacement displ = 0; - for (auto & i : attrs->attrs) - newEnv->vars.emplace_back(i.first, i.second.displ = displ++); + Displacement displ = 0; + for (auto & i : attrs->attrs) + newEnv->vars.emplace_back(i.first, i.second.displ = displ++); + return newEnv; + }(); // No need to sort newEnv since attrs->attrs is in sorted order. + auto inheritFromEnv = attrs->bindInheritSources(es, newEnv); for (auto & i : attrs->attrs) - i.second.e->bindVars(es, i.second.inherited ? env : newEnv); + i.second.e->bindVars(es, i.second.chooseByKind(newEnv, env, inheritFromEnv)); if (es.debugRepl) es.exprEnvs.insert(std::make_pair(this, newEnv)); diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 6f34afaa7..f0265a896 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -129,6 +129,23 @@ struct ExprVar : Expr COMMON_METHODS }; +/** + * A pseudo-expression for the purpose of evaluating the `from` expression in `inherit (from)` syntax. + * Unlike normal variable references, the displacement is set during parsing, and always refers to + * `ExprAttrs::inheritFromExprs` (by itself or in `ExprLet`), whose values are put into their own `Env`. + */ +struct ExprInheritFrom : ExprVar +{ + ExprInheritFrom(PosIdx pos, Displacement displ): ExprVar(pos, {}) + { + this->level = 0; + this->displ = displ; + this->fromWith = nullptr; + } + + void bindVars(EvalState & es, const std::shared_ptr<const StaticEnv> & env); +}; + struct ExprSelect : Expr { PosIdx pos; @@ -154,16 +171,40 @@ struct ExprAttrs : Expr bool recursive; PosIdx pos; struct AttrDef { - bool inherited; + enum class Kind { + /** `attr = expr;` */ + Plain, + /** `inherit attr1 attrn;` */ + Inherited, + /** `inherit (expr) attr1 attrn;` */ + InheritedFrom, + }; + + Kind kind; Expr * e; PosIdx pos; Displacement displ; // displacement - AttrDef(Expr * e, const PosIdx & pos, bool inherited=false) - : inherited(inherited), e(e), pos(pos) { }; + AttrDef(Expr * e, const PosIdx & pos, Kind kind = Kind::Plain) + : kind(kind), e(e), pos(pos) { }; AttrDef() { }; + + template<typename T> + const T & chooseByKind(const T & plain, const T & inherited, const T & inheritedFrom) const + { + switch (kind) { + case Kind::Plain: + return plain; + case Kind::Inherited: + return inherited; + default: + case Kind::InheritedFrom: + return inheritedFrom; + } + } }; typedef std::map<Symbol, AttrDef> AttrDefs; AttrDefs attrs; + std::unique_ptr<std::vector<Expr *>> inheritFromExprs; struct DynamicAttrDef { Expr * nameExpr, * valueExpr; PosIdx pos; @@ -176,6 +217,11 @@ struct ExprAttrs : Expr ExprAttrs() : recursive(false) { }; PosIdx getPos() const override { return pos; } COMMON_METHODS + + std::shared_ptr<const StaticEnv> bindInheritSources( + EvalState & es, const std::shared_ptr<const StaticEnv> & env); + Env * buildInheritFromEnv(EvalState & state, Env & up); + void showBindings(const SymbolTable & symbols, std::ostream & str) const; }; struct ExprList : Expr diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index acadb676d..a83d8c8b2 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -88,7 +88,7 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * if (i->symbol) { ExprAttrs::AttrDefs::iterator j = attrs->attrs.find(i->symbol); if (j != attrs->attrs.end()) { - if (!j->second.inherited) { + if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) { ExprAttrs * attrs2 = dynamic_cast<ExprAttrs *>(j->second.e); if (!attrs2) dupAttr(attrPath, pos, j->second.pos); attrs = attrs2; @@ -117,13 +117,24 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * auto ae = dynamic_cast<ExprAttrs *>(e); auto jAttrs = dynamic_cast<ExprAttrs *>(j->second.e); if (jAttrs && ae) { + if (ae->inheritFromExprs && !jAttrs->inheritFromExprs) + jAttrs->inheritFromExprs = std::make_unique<std::vector<Expr *>>(); for (auto & ad : ae->attrs) { auto j2 = jAttrs->attrs.find(ad.first); if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error. dupAttr(ad.first, j2->second.pos, ad.second.pos); jAttrs->attrs.emplace(ad.first, ad.second); + if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) { + auto & sel = dynamic_cast<ExprSelect &>(*ad.second.e); + auto & from = dynamic_cast<ExprInheritFrom &>(*sel.e); + from.displ += jAttrs->inheritFromExprs->size(); + } } jAttrs->dynamicAttrs.insert(jAttrs->dynamicAttrs.end(), ae->dynamicAttrs.begin(), ae->dynamicAttrs.end()); + if (ae->inheritFromExprs) { + jAttrs->inheritFromExprs->insert(jAttrs->inheritFromExprs->end(), + ae->inheritFromExprs->begin(), ae->inheritFromExprs->end()); + } } else { dupAttr(attrPath, pos, j->second.pos); } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 447e4d61a..ee44a6d7a 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -311,17 +311,27 @@ binds if ($$->attrs.find(i.symbol) != $$->attrs.end()) state->dupAttr(i.symbol, state->at(@3), $$->attrs[i.symbol].pos); auto pos = state->at(@3); - $$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, true)); + $$->attrs.emplace( + i.symbol, + ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, ExprAttrs::AttrDef::Kind::Inherited)); } delete $3; } | binds INHERIT '(' expr ')' attrs ';' { $$ = $1; - /* !!! Should ensure sharing of the expression in $4. */ + if (!$$->inheritFromExprs) + $$->inheritFromExprs = std::make_unique<std::vector<Expr *>>(); + $$->inheritFromExprs->push_back($4); + auto from = new nix::ExprInheritFrom(state->at(@4), $$->inheritFromExprs->size() - 1); for (auto & i : *$6) { if ($$->attrs.find(i.symbol) != $$->attrs.end()) state->dupAttr(i.symbol, state->at(@6), $$->attrs[i.symbol].pos); - $$->attrs.emplace(i.symbol, ExprAttrs::AttrDef(new ExprSelect(CUR_POS, $4, i.symbol), state->at(@6))); + $$->attrs.emplace( + i.symbol, + ExprAttrs::AttrDef( + new ExprSelect(CUR_POS, from, i.symbol), + state->at(@6), + ExprAttrs::AttrDef::Kind::InheritedFrom)); } delete $6; } diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 2698a6de8..a9e4c8968 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -120,8 +120,17 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption } else { - state->forceValueDeep(*v); - logger->cout("%s", ValuePrinter(*state, *v, PrintOptions { .force = true })); + logger->cout( + "%s", + ValuePrinter( + *state, + *v, + PrintOptions { + .force = true, + .derivationPaths = true + } + ) + ); } } }; |