Avoid ambiguity in RetainPtr<> constructors

When two different RetainPtr<> types appear in an expression, and
one is convertible to the other but not vice-versa, SFINAE is not
of any help because the compiler first has to pick a direction in
which to convert (i.e. it won't try both and reject the one that
fails).

-- code taken from base/memory/scoped_refptr.h
-- add a test case that fails to compile without the patch.

Change-Id: I7fea264643bf4b9343336cf68253dcac7199a71b
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/97351
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/retain_ptr.h b/core/fxcrt/retain_ptr.h
index b8bc6a0..d227257 100644
--- a/core/fxcrt/retain_ptr.h
+++ b/core/fxcrt/retain_ptr.h
@@ -9,6 +9,7 @@
 
 #include <functional>
 #include <memory>
+#include <type_traits>
 #include <utility>
 
 #include "core/fxcrt/unowned_ptr.h"
@@ -46,11 +47,15 @@
   RetainPtr(std::nullptr_t ptr) {}
 
   // Copy conversion constructor.
-  template <class U>
+  template <class U,
+            typename = typename std::enable_if<
+                std::is_convertible<U*, T*>::value>::type>
   RetainPtr(const RetainPtr<U>& that) : RetainPtr(that.Get()) {}
 
   // Move-conversion constructor.
-  template <class U>
+  template <class U,
+            typename = typename std::enable_if<
+                std::is_convertible<U*, T*>::value>::type>
   RetainPtr(RetainPtr<U>&& that) noexcept {
     Unleak(that.Leak());
   }
diff --git a/core/fxcrt/retain_ptr_unittest.cpp b/core/fxcrt/retain_ptr_unittest.cpp
index aae889c..84063fb 100644
--- a/core/fxcrt/retain_ptr_unittest.cpp
+++ b/core/fxcrt/retain_ptr_unittest.cpp
@@ -97,6 +97,15 @@
   EXPECT_EQ(1, obj.release_count());
 }
 
+TEST(RetainPtr, AmbiguousExpression) {
+  class A : public Retainable {};
+  class B : public A {};
+
+  // Test passes if it compiles without error.
+  RetainPtr<A> var = (0) ? pdfium::MakeRetain<A>() : pdfium::MakeRetain<B>();
+  EXPECT_TRUE(var);
+}
+
 TEST(RetainPtr, ResetNull) {
   PseudoRetainable obj;
   {