From f21ab50b95d5d2dbb3aaa8a5c55beb48ea26baa3 Mon Sep 17 00:00:00 2001
From: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Date: Mon, 3 May 2021 15:23:37 -0700
Subject: [PATCH] Add one more RFC that's already implemented

---
 rfcs/behavior-eq-metamethod.md | 57 ++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 rfcs/behavior-eq-metamethod.md

diff --git a/rfcs/behavior-eq-metamethod.md b/rfcs/behavior-eq-metamethod.md
new file mode 100644
index 00000000..f1d01fbe
--- /dev/null
+++ b/rfcs/behavior-eq-metamethod.md
@@ -0,0 +1,57 @@
+# Always call `__eq` when comparing for equality
+
+> Note: this RFC was adapted from an internal proposal that predates RFC process
+
+## Summary
+
+`__eq` metamethod will always be called during `==`/`~=` comparison, even for objects that are rawequal.
+
+## Motivation
+
+Lua 5.x has the following algorithm it uses for comparing userdatas and tables:
+
+- If two objects are not of the same type (userdata vs number), they aren't equal
+- If two objects are referentially equal, they are equal (!)
+- If no object has a metatable with `__eq` metamethod, they are equal iff they are referentially equal
+- Otherwise, pick one of the `__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. equal
+- `table.find` *doesn't* call `__eq` when objects are ref. equal
+
+## Design
+
+Since 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.
+
+## Drawbacks
+
+This represents a difference in a rather core behavior from all upstream versions of Lua.
+
+## Alternatives
+
+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
+```
+
+## References
+
+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