* 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.
This commit is contained in:
Eelco Dolstra 2009-05-11 15:50:14 +00:00
parent 9536ba19d4
commit c34e6d71bc

View file

@ -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 */ static char * deepestStack = (char *) -1; /* for measuring stack usage */
@ -707,11 +745,9 @@ Expr evalExpr2(EvalState & state, Expr e)
However, we don't want to make (==) strict, because that would However, we don't want to make (==) strict, because that would
make operations like `big_derivation == null' very slow (unless make operations like `big_derivation == null' very slow (unless
we were to evaluate them side-by-side). */ we were to evaluate them side-by-side). */
if (matchOpEq(e, e1, e2)) if (matchOpEq(e, e1, e2)) return makeBool(areEqual(state, e1, e2));
return makeBool(evalExpr(state, e1) == evalExpr(state, e2));
if (matchOpNEq(e, e1, e2)) if (matchOpNEq(e, e1, e2)) return makeBool(!areEqual(state, e1, e2));
return makeBool(evalExpr(state, e1) != evalExpr(state, e2));
/* Negation. */ /* Negation. */
if (matchOpNot(e, e1)) if (matchOpNot(e, e1))