Bug 1318677 part 2 - mozilla::Result: Use a single enum to dispatch to the Result implementation. r=Waldo
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Tue, 07 Feb 2017 18:57:43 +0000
changeset 341281 9d73f44230343ca5a667f24e6d8b949635c46cd3
parent 341280 2d01a306f0f424783e36b79cbb83662243569af8
child 341282 f8643ee1df78bfe91b4413905ab1be4e9f172358
push id31329
push usercbook@mozilla.com
push dateWed, 08 Feb 2017 10:30:09 +0000
treeherdermozilla-central@3a95aa424665 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1318677
milestone54.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 1318677 part 2 - mozilla::Result: Use a single enum to dispatch to the Result implementation. r=Waldo
mfbt/Result.h
--- a/mfbt/Result.h
+++ b/mfbt/Result.h
@@ -25,21 +25,27 @@ namespace mozilla {
  */
 struct Ok {};
 
 template <typename E> class GenericErrorResult;
 template <typename V, typename E> class Result;
 
 namespace detail {
 
-enum class VEmptiness { IsEmpty, IsNotEmpty };
-enum class Alignedness { IsAligned, IsNotAligned };
+enum class PackingStrategy {
+  Variant,
+  NullIsOk,
+  LowBitTagIsError,
+};
 
-template <typename V, typename E, VEmptiness EmptinessOfV, Alignedness Aligned>
-class ResultImplementation
+template <typename V, typename E, PackingStrategy Strategy>
+class ResultImplementation;
+
+template <typename V, typename E>
+class ResultImplementation<V, E, PackingStrategy::Variant>
 {
   mozilla::Variant<V, E> mStorage;
 
 public:
   explicit ResultImplementation(V aValue) : mStorage(aValue) {}
   explicit ResultImplementation(E aErrorValue) : mStorage(aErrorValue) {}
 
   bool isOk() const { return mStorage.template is<V>(); }
@@ -50,36 +56,36 @@ public:
   V unwrap() const { return mStorage.template as<V>(); }
   E unwrapErr() const { return mStorage.template as<E>(); }
 };
 
 /**
  * mozilla::Variant doesn't like storing a reference. This is a specialization
  * to store E as pointer if it's a reference.
  */
-template <typename V, typename E, VEmptiness EmptinessOfV, Alignedness Aligned>
-class ResultImplementation<V, E&, EmptinessOfV, Aligned>
+template <typename V, typename E>
+class ResultImplementation<V, E&, PackingStrategy::Variant>
 {
   mozilla::Variant<V, E*> mStorage;
 
 public:
   explicit ResultImplementation(V aValue) : mStorage(aValue) {}
   explicit ResultImplementation(E& aErrorValue) : mStorage(&aErrorValue) {}
 
   bool isOk() const { return mStorage.template is<V>(); }
   V unwrap() const { return mStorage.template as<V>(); }
   E& unwrapErr() const { return *mStorage.template as<E*>(); }
 };
 
 /**
  * Specialization for when the success type is Ok (or another empty class) and
  * the error type is a reference.
  */
-template <typename V, typename E, Alignedness Aligned>
-class ResultImplementation<V, E&, VEmptiness::IsEmpty, Aligned>
+template <typename V, typename E>
+class ResultImplementation<V, E&, PackingStrategy::NullIsOk>
 {
   E* mErrorValue;
 
 public:
   explicit ResultImplementation(V) : mErrorValue(nullptr) {}
   explicit ResultImplementation(E& aErrorValue) : mErrorValue(&aErrorValue) {}
 
   bool isOk() const { return mErrorValue == nullptr; }
@@ -87,18 +93,18 @@ public:
   V unwrap() const { return V(); }
   E& unwrapErr() const { return *mErrorValue; }
 };
 
 /**
  * Specialization for when alignment permits using the least significant bit as
  * a tag bit.
  */
-template <typename V, typename E, VEmptiness EmptinessOfV>
-class ResultImplementation<V*, E&, EmptinessOfV, Alignedness::IsAligned>
+template <typename V, typename E>
+class ResultImplementation<V*, E&, PackingStrategy::LowBitTagIsError>
 {
   uintptr_t mBits;
 
 public:
   explicit ResultImplementation(V* aValue)
     : mBits(reinterpret_cast<uintptr_t>(aValue))
   {
     MOZ_ASSERT((uintptr_t(aValue) % MOZ_ALIGNOF(V)) == 0,
@@ -112,16 +118,33 @@ public:
   }
 
   bool isOk() const { return (mBits & 1) == 0; }
 
   V* unwrap() const { return reinterpret_cast<V*>(mBits); }
   E& unwrapErr() const { return *reinterpret_cast<E*>(mBits ^ 1); }
 };
 
+// To use nullptr as a special value, we need the counter part to exclude zero
+// from its range of valid representations.
+//
+// By default assume that zero can be represented.
+template<typename T>
+struct UnusedZero
+{
+  static const bool value = false;
+};
+
+// References can't be null.
+template<typename T>
+struct UnusedZero<T&>
+{
+  static const bool value = true;
+};
+
 // A bit of help figuring out which of the above specializations to use.
 //
 // We begin by safely assuming types don't have a spare bit.
 template <typename T> struct HasFreeLSB { static const bool value = false; };
 
 // The lowest bit of a properly-aligned pointer is always zero if the pointee
 // type is greater than byte-aligned. That bit is free to use if it's masked
 // out of such pointers before they're dereferenced.
@@ -130,16 +153,31 @@ template <typename T> struct HasFreeLSB<
 };
 
 // We store references as pointers, so they have a free bit if a pointer would
 // have one.
 template <typename T> struct HasFreeLSB<T&> {
   static const bool value = HasFreeLSB<T*>::value;
 };
 
+// Select one of the previous result implementation based on the properties of
+// the V and E types.
+template <typename V, typename E>
+struct SelectResultImpl
+{
+  static const PackingStrategy value =
+      (IsEmpty<V>::value && UnusedZero<E>::value)
+    ? PackingStrategy::NullIsOk
+    : (detail::HasFreeLSB<V>::value && detail::HasFreeLSB<E>::value)
+    ? PackingStrategy::LowBitTagIsError
+    : PackingStrategy::Variant;
+
+  using Type = detail::ResultImplementation<V, E, value>;
+};
+
 template <typename T>
 struct IsResult : FalseType { };
 
 template <typename V, typename E>
 struct IsResult<Result<V, E>> : TrueType { };
 
 } // namespace detail
 
@@ -167,25 +205,18 @@ struct IsResult<Result<V, E>> : TrueType
  * The purpose of Result is to reduce the screwups caused by using `false` or
  * `nullptr` to indicate errors.
  * What screwups? See <https://bugzilla.mozilla.org/show_bug.cgi?id=912928> for
  * a partial list.
  */
 template <typename V, typename E>
 class MOZ_MUST_USE_TYPE Result final
 {
-  using Impl =
-    detail::ResultImplementation<V, E,
-                                 IsEmpty<V>::value
-                                   ? detail::VEmptiness::IsEmpty
-                                   : detail::VEmptiness::IsNotEmpty,
-                                 (detail::HasFreeLSB<V>::value &&
-                                  detail::HasFreeLSB<E>::value)
-                                   ? detail::Alignedness::IsAligned
-                                   : detail::Alignedness::IsNotAligned>;
+  using Impl = typename detail::SelectResultImpl<V, E>::Type;
+
   Impl mImpl;
 
 public:
   /**
    * Create a success result.
    */
   MOZ_IMPLICIT Result(V aValue) : mImpl(aValue) { MOZ_ASSERT(isOk()); }