Fix interaction between RetainPtr<T> and transparent comparisons.

The key is providing an implicit operator T* so that we can make
comparisons in terms of raw pointers in all cases. Unfortunately,
this now allows passing RetainPtr<T> as T* without an explicit
Get(), which is cleaner but will not catch the use of unretained
references.

-- remove now-redundant comparison functions.
-- kludge around some ambiguity in CPDF_DictionaryLocker

Change-Id: Ib06f665361f4efa514c6f028eb24af114f7506dc
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/98394
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_dictionary_unittest.cpp b/core/fpdfapi/parser/cpdf_dictionary_unittest.cpp
index e0a641f..73bd8c4 100644
--- a/core/fpdfapi/parser/cpdf_dictionary_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_dictionary_unittest.cpp
@@ -18,7 +18,7 @@
   dict->SetNewFor<CPDF_Stream>("the-stream");
   dict->SetNewFor<CPDF_Number>("the-number", 42);
 
-  CPDF_DictionaryLocker locked_dict(std::move(dict));
+  CPDF_DictionaryLocker locked_dict(dict.Get());
   auto it = locked_dict.begin();
   EXPECT_NE(it, locked_dict.end());
   EXPECT_EQ(it->first, ByteString("the-array"));
diff --git a/core/fxcrt/retain_ptr.h b/core/fxcrt/retain_ptr.h
index bbd4bce..fb1c95b 100644
--- a/core/fxcrt/retain_ptr.h
+++ b/core/fxcrt/retain_ptr.h
@@ -71,8 +71,9 @@
     m_pObj.reset(obj);
   }
 
-  explicit operator T*() const { return Get(); }
-  T* Get() const { return m_pObj.get(); }
+  operator T*() const noexcept { return Get(); }
+  T* Get() const noexcept { return m_pObj.get(); }
+
   UnownedPtr<T> BackPointer() const { return UnownedPtr<T>(Get()); }
   void Swap(RetainPtr& that) { m_pObj.swap(that.m_pObj); }
 
@@ -150,16 +151,6 @@
   mutable intptr_t m_nRefCount = 0;
 };
 
-template <typename T, typename U>
-inline bool operator==(const U* lhs, const RetainPtr<T>& rhs) {
-  return rhs == lhs;
-}
-
-template <typename T, typename U>
-inline bool operator!=(const U* lhs, const RetainPtr<T>& rhs) {
-  return rhs != lhs;
-}
-
 }  // namespace fxcrt
 
 using fxcrt::ReleaseDeleter;
diff --git a/core/fxcrt/retain_ptr_unittest.cpp b/core/fxcrt/retain_ptr_unittest.cpp
index ffb98b1..e3ca7e4 100644
--- a/core/fxcrt/retain_ptr_unittest.cpp
+++ b/core/fxcrt/retain_ptr_unittest.cpp
@@ -4,6 +4,7 @@
 
 #include "core/fxcrt/retain_ptr.h"
 
+#include <functional>
 #include <set>
 #include <utility>
 #include <vector>
@@ -12,6 +13,17 @@
 #include "testing/pseudo_retainable.h"
 #include "third_party/base/containers/contains.h"
 
+namespace {
+
+template <typename T, typename C = std::less<T>>
+class NoLinearSearchSet : public std::set<T, C> {
+ public:
+  typename std::set<T, C>::iterator begin() noexcept = delete;
+  typename std::set<T, C>::const_iterator cbegin() const noexcept = delete;
+};
+
+}  // namespace
+
 namespace fxcrt {
 namespace {
 
@@ -421,21 +433,53 @@
   // RetainPtrs for containers that use find().
   PseudoRetainable obj1;
   PseudoRetainable obj2;
-  NoLinearSearchSet<const PseudoRetainable*, std::less<>> the_set;
-  the_set.insert(&obj1);
-  EXPECT_TRUE(pdfium::Contains(the_set, &obj1));
-  EXPECT_FALSE(pdfium::Contains(the_set, &obj2));
-
   RetainPtr<PseudoRetainable> ptr1(&obj1);
   RetainPtr<PseudoRetainable> ptr2(&obj2);
-  // TODO(tsepez): remove Get() after fixing transparent compare for RetainPtr.
-  EXPECT_TRUE(pdfium::Contains(the_set, ptr1.Get()));
-  EXPECT_FALSE(pdfium::Contains(the_set, ptr2.Get()));
-
   RetainPtr<const PseudoRetainable> const_ptr1(&obj1);
   RetainPtr<const PseudoRetainable> const_ptr2(&obj2);
-  EXPECT_TRUE(pdfium::Contains(the_set, const_ptr1.Get()));
-  EXPECT_FALSE(pdfium::Contains(the_set, const_ptr2.Get()));
+  NoLinearSearchSet<RetainPtr<const PseudoRetainable>, std::less<>> the_set;
+
+  // Intially, two smart pointers to each object.
+  EXPECT_EQ(2, obj1.retain_count());
+  EXPECT_EQ(0, obj1.release_count());
+  EXPECT_EQ(2, obj2.retain_count());
+  EXPECT_EQ(0, obj2.release_count());
+
+  // Passed by const-ref, increment on copy into set's data structure.
+  the_set.insert(const_ptr1);
+  EXPECT_EQ(3, obj1.retain_count());
+  EXPECT_EQ(0, obj1.release_count());
+  EXPECT_EQ(2, obj2.retain_count());
+  EXPECT_EQ(0, obj2.release_count());
+
+  // None of the following should cause any churn.
+  EXPECT_NE(the_set.end(), the_set.find(&obj1));
+  EXPECT_EQ(the_set.end(), the_set.find(&obj2));
+  EXPECT_TRUE(pdfium::Contains(the_set, &obj1));
+  EXPECT_FALSE(pdfium::Contains(the_set, &obj2));
+  EXPECT_EQ(3, obj1.retain_count());
+  EXPECT_EQ(0, obj1.release_count());
+  EXPECT_EQ(2, obj2.retain_count());
+  EXPECT_EQ(0, obj2.release_count());
+
+  EXPECT_NE(the_set.end(), the_set.find(const_ptr1));
+  EXPECT_EQ(the_set.end(), the_set.find(const_ptr2));
+  EXPECT_TRUE(pdfium::Contains(the_set, const_ptr1));
+  EXPECT_FALSE(pdfium::Contains(the_set, const_ptr2));
+  EXPECT_EQ(3, obj1.retain_count());
+  EXPECT_EQ(0, obj1.release_count());
+  EXPECT_EQ(2, obj2.retain_count());
+  EXPECT_EQ(0, obj2.release_count());
+
+  // These involve const-removing conversions which seem to churn.
+  EXPECT_NE(the_set.end(), the_set.find(ptr1));
+  EXPECT_EQ(the_set.end(), the_set.find(ptr2));
+  EXPECT_TRUE(pdfium::Contains(the_set, ptr1));
+  EXPECT_FALSE(pdfium::Contains(the_set, ptr2));
+  EXPECT_EQ(5, obj1.retain_count());
+  EXPECT_EQ(2, obj1.release_count());
+  EXPECT_EQ(4, obj2.retain_count());
+  EXPECT_EQ(2, obj2.release_count());
 }
 
 TEST(RetainPtr, VectorContains) {