__eq
when comparing for equalityNote: this RFC was adapted from an internal proposal that predates RFC process
Status: Implemented
__eq
metamethod will always be called during ==
/~=
comparison, even for objects that are rawequal.
Lua 5.x has the following algorithm it uses for comparing userdatas and tables:
__eq
metamethod, they are equal iff they are referentially equal__eq
metamethods, call it with both objects as arguments and return the result.In mid-2019, we’ve released Luau which implements a fast path for userdata comparison. This fast path accidentally omitted step 2 for userdatas with C __eq
implementations (!), and thus comparing a userdata object vs itself would actually run __eq
metamethod. This is significant as it allowed users to use v == v
as a NaN check for vectors, coordinate frames, and other objects that have floating point contents.
Since this was a bug, we’re in a rather inconsistent state:
==
and ~=
in the code always call __eq
for userdata with C __eq
==
and ~=
don’t call __eq
for tables and custom newproxy-like userdatas with Lua __eq
when objects are ref. equaltable.find
doesn’t call __eq
when objects are ref. equalSince developers started relying on ==
behavior for NaN checks in the last two years since Luau release, the bug has become a feature. Additionally, it’s sort of a good feature since it allows to implement NaN semantics for custom types - userdatas, tables, etc.
Thus the proposal suggests changing the rules so that when __eq
metamethod is present, __eq
is always called even when comparing the object to itself.
This would effectively make the current ruleset for userdata objects official, and change the behavior for table.find
(which is probably not significant) and, more significantly, start calling user-provided __eq
even when the object is the same. It’s expected that any reasonable __eq
implementation can handle comparing the object to itself so this is not expected to result in breakage.
This represents a difference in a rather core behavior from all upstream versions of Lua.
We could instead equalize (ha!) the behavior between Luau and Lua. In fact, this is what we tried to do initially as the userdata behavior was considered a bug, but encountered the issue with games already depending on the new behavior.
We could work with developers to change their games to stop relying on this. However, this is more complicated to deploy and - upon reflection - makes ==
less intuitive than the main proposal when comparing objects with NaN, since e.g. it means that these two functions have a different behavior:
function compare1(a: Vector3, b: Vector3)
return a == b
end
function compare2(a: Vector3, b: Vector3)
return a.X == b.X and a.Y == b.Y and a.Z == b.Z
end
https://devforum.roblox.com/t/call-eq-even-when-tables-are-rawequal/1088886 https://devforum.roblox.com/t/nan-vector3-comparison-broken-cframe-too/1130778