Bug 1325351 part 3 - Make Maybe accept value from different Maybe type when the inner type is convertible. r=froydnj
☠☠ backed out by 330a93f6a4e8 ☠ ☠
authorXidorn Quan <me@upsuper.org>
Fri, 23 Dec 2016 11:49:33 +1100
changeset 372338 45f0755ff471c8204229c95627e60b55cd971748
parent 372337 79754b9b89b744325ea773c83185a4520bdbd632
child 372339 320d6acadc2ae2224fd74a9aef0b79ccb3a1c93b
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1325351
milestone53.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 1325351 part 3 - Make Maybe accept value from different Maybe type when the inner type is convertible. r=froydnj MozReview-Commit-ID: 2kYTncYh1Or
mfbt/Maybe.h
mfbt/tests/TestMaybe.cpp
--- a/mfbt/Maybe.h
+++ b/mfbt/Maybe.h
@@ -98,26 +98,21 @@ public:
     : 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)>.
+   * Maybe<T> can be copy-constructed from a Maybe<U> if U is convertible to T.
    */
   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>
+             typename std::enable_if<std::is_convertible<U, T>::value>::type>
   MOZ_IMPLICIT
   Maybe(const Maybe<U>& aOther)
     : mIsSome(false)
   {
     if (aOther.isSome()) {
       emplace(*aOther);
     }
   }
@@ -127,26 +122,21 @@ public:
   {
     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)>.
+   * Maybe<T> can be move-constructed from a Maybe<U> if U is convertible to T.
    */
   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>
+             typename std::enable_if<std::is_convertible<U, T>::value>::type>
   MOZ_IMPLICIT
   Maybe(Maybe<U>&& aOther)
     : mIsSome(false)
   {
     if (aOther.isSome()) {
       emplace(Move(*aOther));
       aOther.reset();
     }
@@ -163,16 +153,33 @@ public:
         }
       } else {
         reset();
       }
     }
     return *this;
   }
 
+  template<typename U,
+           typename =
+             typename std::enable_if<std::is_convertible<U, T>::value>::type>
+  Maybe& operator=(const Maybe<U>& aOther)
+  {
+    if (aOther.isSome()) {
+      if (mIsSome) {
+        ref() = aOther.ref();
+      } else {
+        emplace(*aOther);
+      }
+    } else {
+      reset();
+    }
+    return *this;
+  }
+
   Maybe& operator=(Maybe&& aOther)
   {
     MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
 
     if (aOther.mIsSome) {
       if (mIsSome) {
         ref() = Move(aOther.ref());
       } else {
@@ -181,16 +188,35 @@ public:
       aOther.reset();
     } else {
       reset();
     }
 
     return *this;
   }
 
+  template<typename U,
+           typename =
+             typename std::enable_if<std::is_convertible<U, T>::value>::type>
+  Maybe& operator=(Maybe<U>&& aOther)
+  {
+    if (aOther.isSome()) {
+      if (mIsSome) {
+        ref() = Move(aOther.ref());
+      } else {
+        emplace(Move(*aOther));
+      }
+      aOther.reset();
+    } else {
+      reset();
+    }
+
+    return *this;
+  }
+
   /* Methods that check whether this Maybe contains a value */
   explicit operator bool() const { return isSome(); }
   bool isSome() const { return mIsSome; }
   bool isNothing() const { return !mIsSome; }
 
   /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|. */
   T value() const
   {
--- a/mfbt/tests/TestMaybe.cpp
+++ b/mfbt/tests/TestMaybe.cpp
@@ -42,19 +42,21 @@ using mozilla::UniquePtr;
   } while (false)
 
 enum Status
 {
   eWasDefaultConstructed,
   eWasConstructed,
   eWasCopyConstructed,
   eWasMoveConstructed,
+  eWasAssigned,
   eWasCopyAssigned,
   eWasMoveAssigned,
-  eWasMovedFrom
+  eWasCopiedFrom,
+  eWasMovedFrom,
 };
 
 static size_t sUndestroyedObjects = 0;
 
 static bool AllDestructorsWereCalled()
 {
   return sUndestroyedObjects == 0;
 }
@@ -837,24 +839,239 @@ TestSomePointerConversion()
   MOZ_RELEASE_ASSERT(c1);
   MOZ_RELEASE_ASSERT(*c1 == &derived);
 
   ExplicitConstructorBasePointer ecbp(Some(&derived));
 
   return true;
 }
 
+struct SourceType1
+{
+  int mTag;
+
+  operator int() const
+  {
+    return mTag;
+  }
+};
+struct DestType
+{
+  int mTag;
+  Status mStatus;
+
+  DestType()
+    : mTag(0)
+    , mStatus(eWasDefaultConstructed)
+  {}
+
+  MOZ_IMPLICIT DestType(int aTag)
+    : mTag(aTag)
+    , mStatus(eWasConstructed)
+  {}
+
+  MOZ_IMPLICIT DestType(SourceType1&& aSrc)
+    : mTag(aSrc.mTag)
+    , mStatus(eWasMoveConstructed)
+  {}
+
+  MOZ_IMPLICIT DestType(const SourceType1& aSrc)
+    : mTag(aSrc.mTag)
+    , mStatus(eWasCopyConstructed)
+  {}
+
+  DestType& operator=(int aTag)
+  {
+    mTag = aTag;
+    mStatus = eWasAssigned;
+    return *this;
+  }
+
+  DestType& operator=(SourceType1&& aSrc)
+  {
+    mTag = aSrc.mTag;
+    mStatus = eWasMoveAssigned;
+    return *this;
+  }
+
+  DestType& operator=(const SourceType1& aSrc)
+  {
+    mTag = aSrc.mTag;
+    mStatus = eWasCopyAssigned;
+    return *this;
+  }
+};
+struct SourceType2
+{
+  int mTag;
+
+  operator DestType() const&
+  {
+    DestType result;
+    result.mTag = mTag;
+    result.mStatus = eWasCopiedFrom;
+    return result;
+  }
+
+  operator DestType() &&
+  {
+    DestType result;
+    result.mTag = mTag;
+    result.mStatus = eWasMovedFrom;
+    return result;
+  }
+};
+
+static bool
+TestTypeConversion()
+{
+  {
+    Maybe<SourceType1> src = Some(SourceType1 {1});
+    Maybe<DestType> dest = src;
+    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopyConstructed);
+
+    src = Some(SourceType1 {2});
+    dest = src;
+    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopyAssigned);
+  }
+
+  {
+    Maybe<SourceType1> src = Some(SourceType1 {1});
+    Maybe<DestType> dest = Move(src);
+    MOZ_RELEASE_ASSERT(src.isNothing());
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMoveConstructed);
+
+    src = Some(SourceType1 {2});
+    dest = Move(src);
+    MOZ_RELEASE_ASSERT(src.isNothing());
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMoveAssigned);
+  }
+
+  {
+    Maybe<SourceType2> src = Some(SourceType2 {1});
+    Maybe<DestType> dest = src;
+    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopiedFrom);
+
+    src = Some(SourceType2 {2});
+    dest = src;
+    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopiedFrom);
+  }
+
+  {
+    Maybe<SourceType2> src = Some(SourceType2 {1});
+    Maybe<DestType> dest = Move(src);
+    MOZ_RELEASE_ASSERT(src.isNothing());
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMovedFrom);
+
+    src = Some(SourceType2 {2});
+    dest = Move(src);
+    MOZ_RELEASE_ASSERT(src.isNothing());
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMovedFrom);
+  }
+
+  {
+    Maybe<int> src = Some(1);
+    Maybe<DestType> dest = src;
+    MOZ_RELEASE_ASSERT(src.isSome() && *src == 1);
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasConstructed);
+
+    src = Some(2);
+    dest = src;
+    MOZ_RELEASE_ASSERT(src.isSome() && *src == 2);
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasAssigned);
+  }
+
+  {
+    Maybe<int> src = Some(1);
+    Maybe<DestType> dest = Move(src);
+    MOZ_RELEASE_ASSERT(src.isNothing());
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasConstructed);
+
+    src = Some(2);
+    dest = Move(src);
+    MOZ_RELEASE_ASSERT(src.isNothing());
+    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
+    MOZ_RELEASE_ASSERT(dest->mStatus == eWasAssigned);
+  }
+
+  {
+    Maybe<SourceType1> src = Some(SourceType1 {1});
+    Maybe<int> dest = src;
+    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
+    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
+
+    src = Some(SourceType1 {2});
+    dest = src;
+    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
+    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
+  }
+
+  {
+    Maybe<SourceType1> src = Some(SourceType1 {1});
+    Maybe<int> dest = Move(src);
+    MOZ_RELEASE_ASSERT(src.isNothing());
+    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
+
+    src = Some(SourceType1 {2});
+    dest = Move(src);
+    MOZ_RELEASE_ASSERT(src.isNothing());
+    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
+  }
+
+  {
+    Maybe<size_t> src = Some(1);
+    Maybe<char16_t> dest = src;
+    MOZ_RELEASE_ASSERT(src.isSome() && *src == 1);
+    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
+
+    src = Some(2);
+    dest = src;
+    MOZ_RELEASE_ASSERT(src.isSome() && *src == 2);
+    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
+  }
+
+  {
+    Maybe<size_t> src = Some(1);
+    Maybe<char16_t> dest = Move(src);
+    MOZ_RELEASE_ASSERT(src.isNothing());
+    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
+
+    src = Some(2);
+    dest = Move(src);
+    MOZ_RELEASE_ASSERT(src.isNothing());
+    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
+  }
+
+  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);
+  RUN_TEST(TestTypeConversion);
 
   return 0;
 }