From c34e6d71bc62bb83f3bfed69f781ded4d5a46d3a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 11 May 2009 15:50:14 +0000 Subject: [PATCH] * 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. --- src/libexpr/eval.cc | 48 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 01d33ad00c..2b9b96559d 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));