Skip to main content

eq_operator

Function eq_operator 

Source
pub fn eq_operator(l: &Value, r: &Value) -> bool
Expand description

Nix == / != at the OPERATOR, which is not the same relation as the one used for nested comparisons.

CppNix’s eqValues opens with a pointer-identity hack (if (&v1 == &v2) return true;) sitting ABOVE the type switch, whose nFunction arm is unconditionally false. That produces an asymmetry which looks like an inconsistency and is not:

let f = x: x; in  f  ==  f    -> false   (two distinct stack Values)
let f = x: x; in [f] == [f]   -> TRUE    (one shared Value*, hack fires)

ExprOpEq::eval evaluates each operand into its own stack Value, so at the top level the two are never the same object and the hack is structurally unreachable. A nested element, by contrast, really is the same Value* on both sides.

sui has no cell identity — Value is a 16-byte enum — so Rc::ptr_eq on the closure is standing in for CppNix’s Value* identity, and in several places it is the ONLY thing carrying it (sui binds { f = x: x; } as an eager Value::Lambda, with no thunk to share). Measured: making the lambda arm unconditionally false is net +8/-4 against the oracle. It breaks let e = { f = x: x; }; in (e // {}) == (e // {}) — an attrset literal of functions, merged, which is everywhere in nixpkgs — and it is the guard that stops nixpkgs stdenv looping forever on crossSystem != localSystem (see the tests below titled “Lambda identity equality”).

So the fix is not to weaken the relation but to reproduce the asymmetry where CppNix gets it for free: the operator is the one place sui can PROVE the operands are distinct cells, because eval_binop just materialized each one through its own independent force_concrete.

Nested callers (list/attrs recursion, builtins.elem) must keep using PartialEqbuiltins.elem f [f] is true in nix.