aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libexpr/eval.cc40
-rw-r--r--src/libexpr/get-drvs.cc28
-rw-r--r--src/libexpr/nixexpr.cc113
-rw-r--r--src/libexpr/nixexpr.hh52
-rw-r--r--src/libexpr/parser-state.hh13
-rw-r--r--src/libexpr/parser.y16
-rw-r--r--src/nix/eval.cc13
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
+ }
+ )
+ );
}
}
};