diff options
author | Eelco Dolstra <e.dolstra@tudelft.nl> | 2009-05-11 15:50:14 +0000 |
---|---|---|
committer | Eelco Dolstra <e.dolstra@tudelft.nl> | 2009-05-11 15:50:14 +0000 |
commit | c34e6d71bc62bb83f3bfed69f781ded4d5a46d3a (patch) | |
tree | 48d5876d3cf5ae6c07dc036774a051c4409865f5 /src/libexpr/eval.cc | |
parent | 9536ba19d437a82d4b521709b49ef6977321b692 (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.cc | 48 |
1 files changed, 42 insertions, 6 deletions
diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 01d33ad00..2b9b96559 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)); |