about summary refs log tree commit diff
path: root/src/libexpr/eval.cc
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2009-05-11T15·50+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2009-05-11T15·50+0000
commitc34e6d71bc62bb83f3bfed69f781ded4d5a46d3a (patch)
tree48d5876d3cf5ae6c07dc036774a051c4409865f5 /src/libexpr/eval.cc
parent9536ba19d437a82d4b521709b49ef6977321b692 (diff)
* Disallow equality tests between attribute sets. This was always
  broken, but now the evaluator checks for it to prevent Nix
  expressions from relying on undefined behaviour.  Equality tests are
  implemented using a shallow pointer equality test between ATerms.
  However, because attribute sets are lazy and contain position
  information, this can give false positives.  For instance,
  previously

    let y = {x = 1;}; in y == y

  evaluated to true, while the equivalent expression

    {x = 1;} == {x = 1;}

  evaluated to false.  So disallow these tests for now.  (Eventually
  we may want to implement deep equality tests for attribute sets,
  like lib.eqStrict.)
  
* Idem: disallow comparisons between functions.

* Implemented deep comparisons of lists.  This had the same problem as
  attribute sets - the elements in the list weren't evaluated.  For
  instance,

    ["xy"] == [("x" + "y")]

  evaluated to false.  Now it works properly.

Diffstat (limited to 'src/libexpr/eval.cc')
-rw-r--r--src/libexpr/eval.cc48
1 files changed, 42 insertions, 6 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc
index 01d33ad00c6b..2b9b96559d7d 100644
--- a/src/libexpr/eval.cc
+++ b/src/libexpr/eval.cc
@@ -644,6 +644,44 @@ LocalNoInline(Expr evalOpConcat(EvalState & state, Expr e1, Expr e2))
 }
 
 
+/* Implementation of the `==' and `!=' operators. */
+LocalNoInline(bool areEqual(EvalState & state, Expr e1, Expr e2))
+{
+    e1 = evalExpr(state, e1);
+    e2 = evalExpr(state, e2);
+
+    /* We cannot test functions/primops for equality, and we currently
+       don't support testing equality between attribute sets or lists
+       - that would have to be a deep equality test to be sound. */
+    AFun sym1 = ATgetAFun(e1);
+    AFun sym2 = ATgetAFun(e2);
+
+    if (sym1 != sym2) return false;
+
+    /* Functions are incomparable. */
+    if (sym1 == symFunction || sym1 == symPrimOp) return false;
+
+    if (sym1 == symAttrs)
+        throw EvalError("comparison of attribute sets is not implemented");
+
+    if (e1 == e2) return true;
+    
+    if (sym1 == symList) {
+        ATermList es1; matchList(e1, es1);
+        ATermList es2; matchList(e2, es2);
+        if (ATgetLength(es1) != ATgetLength(es2)) return false;
+        ATermIterator i(es1), j(es2);
+        while (*i) {
+            if (!areEqual(state, *i, *j)) return false;
+            ++i; ++j;
+        }
+        return true;
+    }
+    
+    return false;
+}
+
+
 static char * deepestStack = (char *) -1; /* for measuring stack usage */
 
 
@@ -707,12 +745,10 @@ Expr evalExpr2(EvalState & state, Expr e)
        However, we don't want to make (==) strict, because that would
        make operations like `big_derivation == null' very slow (unless
        we were to evaluate them side-by-side). */
-    if (matchOpEq(e, e1, e2))
-        return makeBool(evalExpr(state, e1) == evalExpr(state, e2));
-
-    if (matchOpNEq(e, e1, e2))
-        return makeBool(evalExpr(state, e1) != evalExpr(state, e2));
-
+    if (matchOpEq(e, e1, e2)) return makeBool(areEqual(state, e1, e2));
+        
+    if (matchOpNEq(e, e1, e2)) return makeBool(!areEqual(state, e1, e2));
+        
     /* Negation. */
     if (matchOpNot(e, e1))
         return makeBool(!evalBool(state, e1));