Bug 1296760 - Allow Some(nullptr) and Some(Derived*) to convert to Maybe<Base*>. r=froydnj
authorJeff Walden <jwalden@mit.edu>
Wed, 24 Aug 2016 11:12:48 -0700
changeset 311520 837df946deca47e977f0cb04888e42e36705d0fc
parent 311519 ff77c6e687bce904a7968a57b25d50293cd21b9a
child 311521 cbd3ac4f2793311dec26b512e3745836e89edadf
push id81149
push userjwalden@mit.edu
push dateSun, 28 Aug 2016 03:42:06 +0000
treeherdermozilla-inbound@bfd9274acb82 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1296760
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1296760 - Allow Some(nullptr) and Some(Derived*) to convert to Maybe<Base*>. r=froydnj
mfbt/Maybe.h
mfbt/tests/TestMaybe.cpp
--- a/mfbt/Maybe.h
+++ b/mfbt/Maybe.h
@@ -11,16 +11,17 @@
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
 #include "mozilla/TypeTraits.h"
 
 #include <new>  // for placement new
+#include <type_traits>
 
 namespace mozilla {
 
 struct Nothing { };
 
 /*
  * Maybe is a container class which contains either zero or one elements. It
  * serves two roles. It can represent values which are *semantically* optional,
@@ -96,25 +97,66 @@ public:
   Maybe(const Maybe& aOther)
     : mIsSome(false)
   {
     if (aOther.mIsSome) {
       emplace(*aOther);
     }
   }
 
+  /**
+   * Maybe<T*> can be copy-constructed from a Maybe<U*> if U* and T* are
+   * compatible, or from Maybe<decltype(nullptr)>.
+   */
+  template<typename U,
+           typename =
+             typename std::enable_if<std::is_pointer<T>::value &&
+                                     (std::is_same<U, decltype(nullptr)>::value ||
+                                      (std::is_pointer<U>::value &&
+                                       std::is_base_of<typename std::remove_pointer<T>::type,
+                                                       typename std::remove_pointer<U>::type>::value))>::type>
+  MOZ_IMPLICIT
+  Maybe(const Maybe<U>& aOther)
+    : mIsSome(false)
+  {
+    if (aOther.isSome()) {
+      emplace(*aOther);
+    }
+  }
+
   Maybe(Maybe&& aOther)
     : mIsSome(false)
   {
     if (aOther.mIsSome) {
       emplace(Move(*aOther));
       aOther.reset();
     }
   }
 
+  /**
+   * Maybe<T*> can be move-constructed from a Maybe<U*> if U* and T* are
+   * compatible, or from Maybe<decltype(nullptr)>.
+   */
+  template<typename U,
+           typename =
+             typename std::enable_if<std::is_pointer<T>::value &&
+                                     (std::is_same<U, decltype(nullptr)>::value ||
+                                      (std::is_pointer<U>::value &&
+                                       std::is_base_of<typename std::remove_pointer<T>::type,
+                                                       typename std::remove_pointer<U>::type>::value))>::type>
+  MOZ_IMPLICIT
+  Maybe(Maybe<U>&& aOther)
+    : mIsSome(false)
+  {
+    if (aOther.isSome()) {
+      emplace(Move(*aOther));
+      aOther.reset();
+    }
+  }
+
   Maybe& operator=(const Maybe& aOther)
   {
     if (&aOther != this) {
       if (aOther.mIsSome) {
         if (mIsSome) {
           // XXX(seth): The correct code for this branch, below, can't be used
           // due to a bug in Visual Studio 2010. See bug 1052940.
           /*
--- a/mfbt/tests/TestMaybe.cpp
+++ b/mfbt/tests/TestMaybe.cpp
@@ -756,22 +756,105 @@ TestVirtualFunction() {
   Maybe<MyDerivedClass> derived;
   derived.emplace();
   derived.reset();
 
   // If this compiles successfully, we've passed.
   return true;
 }
 
+static Maybe<int*>
+ReturnSomeNullptr()
+{
+  return Some(nullptr);
+}
+
+struct D
+{
+  explicit D(Maybe<int*>) {}
+};
+
+static bool
+TestSomeNullptrConversion()
+{
+  Maybe<int*> m1 = Some(nullptr);
+  MOZ_RELEASE_ASSERT(m1.isSome());
+  MOZ_RELEASE_ASSERT(m1);
+  MOZ_RELEASE_ASSERT(!*m1);
+
+  auto m2 = ReturnSomeNullptr();
+  MOZ_RELEASE_ASSERT(m2.isSome());
+  MOZ_RELEASE_ASSERT(m2);
+  MOZ_RELEASE_ASSERT(!*m2);
+
+  Maybe<decltype(nullptr)> m3 = Some(nullptr);
+  MOZ_RELEASE_ASSERT(m3.isSome());
+  MOZ_RELEASE_ASSERT(m3);
+  MOZ_RELEASE_ASSERT(*m3 == nullptr);
+
+  D d(Some(nullptr));
+
+  return true;
+}
+
+struct Base {};
+struct Derived : Base {};
+
+static Maybe<Base*>
+ReturnDerivedPointer()
+{
+  Derived* d = nullptr;
+  return Some(d);
+}
+
+struct ExplicitConstructorBasePointer
+{
+  explicit ExplicitConstructorBasePointer(Maybe<Base*>) {}
+};
+
+static bool
+TestSomePointerConversion()
+{
+  Base base;
+  Derived derived;
+
+  Maybe<Base*> m1 = Some(&derived);
+  MOZ_RELEASE_ASSERT(m1.isSome());
+  MOZ_RELEASE_ASSERT(m1);
+  MOZ_RELEASE_ASSERT(*m1 == &derived);
+
+  auto m2 = ReturnDerivedPointer();
+  MOZ_RELEASE_ASSERT(m2.isSome());
+  MOZ_RELEASE_ASSERT(m2);
+  MOZ_RELEASE_ASSERT(*m2 == nullptr);
+
+  Maybe<Base*> m3 = Some(&base);
+  MOZ_RELEASE_ASSERT(m3.isSome());
+  MOZ_RELEASE_ASSERT(m3);
+  MOZ_RELEASE_ASSERT(*m3 == &base);
+
+  auto s1 = Some(&derived);
+  Maybe<Base*> c1(s1);
+  MOZ_RELEASE_ASSERT(c1.isSome());
+  MOZ_RELEASE_ASSERT(c1);
+  MOZ_RELEASE_ASSERT(*c1 == &derived);
+
+  ExplicitConstructorBasePointer ecbp(Some(&derived));
+
+  return true;
+}
+
 int
 main()
 {
   RUN_TEST(TestBasicFeatures);
   RUN_TEST(TestCopyAndMove);
   RUN_TEST(TestFunctionalAccessors);
   RUN_TEST(TestApply);
   RUN_TEST(TestMap);
   RUN_TEST(TestToMaybe);
   RUN_TEST(TestComparisonOperators);
   RUN_TEST(TestVirtualFunction);
+  RUN_TEST(TestSomeNullptrConversion);
+  RUN_TEST(TestSomePointerConversion);
 
   return 0;
 }