about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libexpr/eval.cc26
-rw-r--r--src/libexpr/nixexpr-ast.def1
2 files changed, 25 insertions, 2 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index e79d42504d0a..3da9cbc4d57e 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -170,16 +170,18 @@ static Expr substArgs(EvalState & state,
    to attributes substituted with selection expressions on the
    original set.  E.g., e = `rec {x = f x y; y = x;}' becomes `{x = f
    (e.x) (e.y); y = e.x;}'. */
-LocalNoInline(ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds))
+LocalNoInline(ATerm expandRec(EvalState & state, ATerm e, ATermList rbnds, ATermList nrbnds))
 {
     ATerm name;
     Expr e2;
     Pos pos;
+    Expr eOverrides = 0;
 
     /* Create the substitution list. */
     ATermMap subs(ATgetLength(rbnds) + ATgetLength(nrbnds));
     for (ATermIterator i(rbnds); i; ++i) {
         if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
+        if (name == sOverrides) eOverrides = e2;
         subs.set(name, makeSelect(e, name));
     }
     for (ATermIterator i(nrbnds); i; ++i) {
@@ -187,6 +189,22 @@ LocalNoInline(ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds))
         subs.set(name, e2);
     }
 
+    /* If the rec contains an attribute called `__overrides', then
+       evaluate it, and add the attributes in that set to the rec.
+       This allows overriding of recursive attributes, which is
+       otherwise not possible.  (You can use the // operator to
+       replace an attribute, but other attributes in the rec will
+       still reference the original value, because that value has been
+       substituted into the bodies of the other attributes.  Hence we
+       need __overrides.) */
+    ATermMap overrides;
+    if (eOverrides) {
+        eOverrides = evalExpr(state, eOverrides);
+        queryAllAttrs(overrides, overrides, false);
+        foreach (ATermMap::const_iterator, i, overrides)
+            subs.set(i->key, i->value);
+    }
+
     Substitution subs_(0, &subs);
 
     /* Create the non-recursive set. */
@@ -196,6 +214,10 @@ LocalNoInline(ATerm expandRec(ATerm e, ATermList rbnds, ATermList nrbnds))
         as.set(name, makeAttrRHS(substitute(subs_, e2), pos));
     }
 
+    if (eOverrides)
+        foreach (ATermMap::const_iterator, i, overrides)
+            as.set(i->key, makeAttrRHS(i->value, makeNoPos()));
+
     /* Copy the non-recursive bindings.  !!! inefficient */
     for (ATermIterator i(nrbnds); i; ++i) {
         if (!matchBind(*i, name, e2, pos)) abort(); /* can't happen */
@@ -667,7 +689,7 @@ Expr evalExpr2(EvalState & state, Expr e)
     /* Mutually recursive sets. */
     ATermList rbnds, nrbnds;
     if (matchRec(e, rbnds, nrbnds))
-        return expandRec(e, rbnds, nrbnds);
+        return expandRec(state, e, rbnds, nrbnds);
 
     /* Conditionals. */
     if (matchIf(e, e1, e2, e3))
diff --git a/src/libexpr/nixexpr-ast.def b/src/libexpr/nixexpr-ast.def
index ab749b3dbe84..ba8162984156 100644
--- a/src/libexpr/nixexpr-ast.def
+++ b/src/libexpr/nixexpr-ast.def
@@ -93,3 +93,4 @@ AttrRHS | Expr Pos | ATerm |
 
 eTrue = makeBool(makeTrue())
 eFalse = makeBool(makeFalse())
+sOverrides = toATerm("__overrides")