LCOV - code coverage report
Current view: top level - mfbt - Result.h (source / functions) Hit Total Coverage
Test: output.info Lines: 40 48 83.3 %
Date: 2017-07-14 16:53:18 Functions: 137 253 54.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /* A type suitable for returning either a value or an error from a function. */
       8             : 
       9             : #ifndef mozilla_Result_h
      10             : #define mozilla_Result_h
      11             : 
      12             : #include "mozilla/Alignment.h"
      13             : #include "mozilla/Assertions.h"
      14             : #include "mozilla/Attributes.h"
      15             : #include "mozilla/Types.h"
      16             : #include "mozilla/TypeTraits.h"
      17             : #include "mozilla/Variant.h"
      18             : 
      19             : namespace mozilla {
      20             : 
      21             : /**
      22             :  * Empty struct, indicating success for operations that have no return value.
      23             :  * For example, if you declare another empty struct `struct OutOfMemory {};`,
      24             :  * then `Result<Ok, OutOfMemory>` represents either success or OOM.
      25             :  */
      26             : struct Ok {};
      27             : 
      28             : template <typename E> class GenericErrorResult;
      29             : template <typename V, typename E> class Result;
      30             : 
      31             : namespace detail {
      32             : 
      33             : enum class PackingStrategy {
      34             :   Variant,
      35             :   NullIsOk,
      36             :   LowBitTagIsError,
      37             :   PackedVariant,
      38             : };
      39             : 
      40             : template <typename V, typename E, PackingStrategy Strategy>
      41             : class ResultImplementation;
      42             : 
      43             : template <typename V, typename E>
      44        5134 : class ResultImplementation<V, E, PackingStrategy::Variant>
      45             : {
      46             :   mozilla::Variant<V, E> mStorage;
      47             : 
      48             : public:
      49        5134 :   explicit ResultImplementation(V aValue) : mStorage(aValue) {}
      50           0 :   explicit ResultImplementation(E aErrorValue) : mStorage(aErrorValue) {}
      51             : 
      52       15402 :   bool isOk() const { return mStorage.template is<V>(); }
      53             : 
      54             :   // The callers of these functions will assert isOk() has the proper value, so
      55             :   // these functions (in all ResultImplementation specializations) don't need
      56             :   // to do so.
      57        5134 :   V unwrap() const { return mStorage.template as<V>(); }
      58           0 :   E unwrapErr() const { return mStorage.template as<E>(); }
      59             : };
      60             : 
      61             : /**
      62             :  * mozilla::Variant doesn't like storing a reference. This is a specialization
      63             :  * to store E as pointer if it's a reference.
      64             :  */
      65             : template <typename V, typename E>
      66             : class ResultImplementation<V, E&, PackingStrategy::Variant>
      67             : {
      68             :   mozilla::Variant<V, E*> mStorage;
      69             : 
      70             : public:
      71             :   explicit ResultImplementation(V aValue) : mStorage(aValue) {}
      72             :   explicit ResultImplementation(E& aErrorValue) : mStorage(&aErrorValue) {}
      73             : 
      74             :   bool isOk() const { return mStorage.template is<V>(); }
      75             :   V unwrap() const { return mStorage.template as<V>(); }
      76             :   E& unwrapErr() const { return *mStorage.template as<E*>(); }
      77             : };
      78             : 
      79             : /**
      80             :  * Specialization for when the success type is Ok (or another empty class) and
      81             :  * the error type is a reference.
      82             :  */
      83             : template <typename V, typename E>
      84             : class ResultImplementation<V, E&, PackingStrategy::NullIsOk>
      85             : {
      86             :   E* mErrorValue;
      87             : 
      88             : public:
      89        1564 :   explicit ResultImplementation(V) : mErrorValue(nullptr) {}
      90           0 :   explicit ResultImplementation(E& aErrorValue) : mErrorValue(&aErrorValue) {}
      91             : 
      92        3128 :   bool isOk() const { return mErrorValue == nullptr; }
      93             : 
      94             :   V unwrap() const { return V(); }
      95           0 :   E& unwrapErr() const { return *mErrorValue; }
      96             : };
      97             : 
      98             : /**
      99             :  * Specialization for when alignment permits using the least significant bit as
     100             :  * a tag bit.
     101             :  */
     102             : template <typename V, typename E>
     103             : class ResultImplementation<V*, E&, PackingStrategy::LowBitTagIsError>
     104             : {
     105             :   uintptr_t mBits;
     106             : 
     107             : public:
     108      140739 :   explicit ResultImplementation(V* aValue)
     109      140739 :     : mBits(reinterpret_cast<uintptr_t>(aValue))
     110             :   {
     111      140739 :     MOZ_ASSERT((uintptr_t(aValue) % MOZ_ALIGNOF(V)) == 0,
     112             :                "Result value pointers must not be misaligned");
     113      140739 :   }
     114           0 :   explicit ResultImplementation(E& aErrorValue)
     115           0 :     : mBits(reinterpret_cast<uintptr_t>(&aErrorValue) | 1)
     116             :   {
     117           0 :     MOZ_ASSERT((uintptr_t(&aErrorValue) % MOZ_ALIGNOF(E)) == 0,
     118             :                "Result errors must not be misaligned");
     119           0 :   }
     120             : 
     121      422217 :   bool isOk() const { return (mBits & 1) == 0; }
     122             : 
     123      140739 :   V* unwrap() const { return reinterpret_cast<V*>(mBits); }
     124             :   E& unwrapErr() const { return *reinterpret_cast<E*>(mBits ^ 1); }
     125             : };
     126             : 
     127             : // Return true if any of the struct can fit in a word.
     128             : template<typename V, typename E>
     129             : struct IsPackableVariant
     130             : {
     131             :   struct VEbool {
     132             :       V v;
     133             :       E e;
     134             :       bool ok;
     135             :   };
     136             :   struct EVbool {
     137             :       E e;
     138             :       V v;
     139             :       bool ok;
     140             :   };
     141             : 
     142             :   using Impl = typename Conditional<sizeof(VEbool) <= sizeof(EVbool),
     143             :                                     VEbool, EVbool>::Type;
     144             : 
     145             :   static const bool value = sizeof(Impl) <= sizeof(uintptr_t);
     146             : };
     147             : 
     148             : /**
     149             :  * Specialization for when both type are not using all the bytes, in order to
     150             :  * use one byte as a tag.
     151             :  */
     152             : template <typename V, typename E>
     153             : class ResultImplementation<V, E, PackingStrategy::PackedVariant>
     154             : {
     155             :   using Impl = typename IsPackableVariant<V, E>::Impl;
     156             :   Impl data;
     157             : 
     158             : public:
     159       45459 :   explicit ResultImplementation(V aValue)
     160             :   {
     161        1370 :     data.v = aValue;
     162       45459 :     data.ok = true;
     163       45459 :   }
     164          29 :   explicit ResultImplementation(E aErrorValue)
     165             :   {
     166          29 :     data.e = aErrorValue;
     167          29 :     data.ok = false;
     168          29 :   }
     169             : 
     170       93593 :   bool isOk() const { return data.ok; }
     171             : 
     172        1370 :   V unwrap() const { return data.v; }
     173          32 :   E unwrapErr() const { return data.e; }
     174             : };
     175             : 
     176             : // To use nullptr as a special value, we need the counter part to exclude zero
     177             : // from its range of valid representations.
     178             : //
     179             : // By default assume that zero can be represented.
     180             : template<typename T>
     181             : struct UnusedZero
     182             : {
     183             :   static const bool value = false;
     184             : };
     185             : 
     186             : // References can't be null.
     187             : template<typename T>
     188             : struct UnusedZero<T&>
     189             : {
     190             :   static const bool value = true;
     191             : };
     192             : 
     193             : // A bit of help figuring out which of the above specializations to use.
     194             : //
     195             : // We begin by safely assuming types don't have a spare bit.
     196             : template <typename T> struct HasFreeLSB { static const bool value = false; };
     197             : 
     198             : // The lowest bit of a properly-aligned pointer is always zero if the pointee
     199             : // type is greater than byte-aligned. That bit is free to use if it's masked
     200             : // out of such pointers before they're dereferenced.
     201             : template <typename T> struct HasFreeLSB<T*> {
     202             :   static const bool value = (MOZ_ALIGNOF(T) & 1) == 0;
     203             : };
     204             : 
     205             : // We store references as pointers, so they have a free bit if a pointer would
     206             : // have one.
     207             : template <typename T> struct HasFreeLSB<T&> {
     208             :   static const bool value = HasFreeLSB<T*>::value;
     209             : };
     210             : 
     211             : // Select one of the previous result implementation based on the properties of
     212             : // the V and E types.
     213             : template <typename V, typename E>
     214             : struct SelectResultImpl
     215             : {
     216             :   static const PackingStrategy value =
     217             :       (IsEmpty<V>::value && UnusedZero<E>::value)
     218             :     ? PackingStrategy::NullIsOk
     219             :     : (detail::HasFreeLSB<V>::value && detail::HasFreeLSB<E>::value)
     220             :     ? PackingStrategy::LowBitTagIsError
     221             :     : (IsDefaultConstructible<V>::value && IsDefaultConstructible<E>::value &&
     222             :        IsPackableVariant<V, E>::value)
     223             :     ? PackingStrategy::PackedVariant
     224             :     : PackingStrategy::Variant;
     225             : 
     226             :   using Type = detail::ResultImplementation<V, E, value>;
     227             : };
     228             : 
     229             : template <typename T>
     230             : struct IsResult : FalseType { };
     231             : 
     232             : template <typename V, typename E>
     233             : struct IsResult<Result<V, E>> : TrueType { };
     234             : 
     235             : } // namespace detail
     236             : 
     237             : /**
     238             :  * Result<V, E> represents the outcome of an operation that can either succeed
     239             :  * or fail. It contains either a success value of type V or an error value of
     240             :  * type E.
     241             :  *
     242             :  * All Result methods are const, so results are basically immutable.
     243             :  * This is just like Variant<V, E> but with a slightly different API, and the
     244             :  * following cases are optimized so Result can be stored more efficiently:
     245             :  *
     246             :  * - If the success type is Ok (or another empty class) and the error type is a
     247             :  *   reference, Result<V, E&> is guaranteed to be pointer-sized and all zero
     248             :  *   bits on success. Do not change this representation! There is JIT code that
     249             :  *   depends on it.
     250             :  *
     251             :  * - If the success type is a pointer type and the error type is a reference
     252             :  *   type, and the least significant bit is unused for both types when stored
     253             :  *   as a pointer (due to alignment rules), Result<V*, E&> is guaranteed to be
     254             :  *   pointer-sized. In this case, we use the lowest bit as tag bit: 0 to
     255             :  *   indicate the Result's bits are a V, 1 to indicate the Result's bits (with
     256             :  *   the 1 masked out) encode an E*.
     257             :  *
     258             :  * The purpose of Result is to reduce the screwups caused by using `false` or
     259             :  * `nullptr` to indicate errors.
     260             :  * What screwups? See <https://bugzilla.mozilla.org/show_bug.cgi?id=912928> for
     261             :  * a partial list.
     262             :  */
     263             : template <typename V, typename E>
     264        5134 : class MOZ_MUST_USE_TYPE Result final
     265             : {
     266             :   using Impl = typename detail::SelectResultImpl<V, E>::Type;
     267             : 
     268             :   Impl mImpl;
     269             : 
     270             : public:
     271             :   /**
     272             :    * Create a success result.
     273             :    */
     274      192895 :   MOZ_IMPLICIT Result(V aValue) : mImpl(aValue) { MOZ_ASSERT(isOk()); }
     275             : 
     276             :   /**
     277             :    * Create an error result.
     278             :    */
     279             :   explicit Result(E aErrorValue) : mImpl(aErrorValue) { MOZ_ASSERT(isErr()); }
     280             : 
     281             :   /**
     282             :    * Implementation detail of MOZ_TRY().
     283             :    * Create an error result from another error result.
     284             :    */
     285             :   template <typename E2>
     286          29 :   MOZ_IMPLICIT Result(const GenericErrorResult<E2>& aErrorResult)
     287          29 :     : mImpl(aErrorResult.mErrorValue)
     288             :   {
     289             :     static_assert(mozilla::IsConvertible<E2, E>::value,
     290             :                   "E2 must be convertible to E");
     291          29 :     MOZ_ASSERT(isErr());
     292          29 :   }
     293             : 
     294             :   Result(const Result&) = default;
     295             :   Result& operator=(const Result&) = default;
     296             : 
     297             :   /** True if this Result is a success result. */
     298      340139 :   bool isOk() const { return mImpl.isOk(); }
     299             : 
     300             :   /** True if this Result is an error result. */
     301      194200 :   bool isErr() const { return !mImpl.isOk(); }
     302             : 
     303             :   /** Get the success value from this Result, which must be a success result. */
     304      147242 :   V unwrap() const {
     305      147242 :     MOZ_ASSERT(isOk());
     306      147242 :     return mImpl.unwrap();
     307             :   }
     308             : 
     309             :   /** Get the error value from this Result, which must be an error result. */
     310          32 :   E unwrapErr() const {
     311          32 :     MOZ_ASSERT(isErr());
     312          32 :     return mImpl.unwrapErr();
     313             :   }
     314             : 
     315             :   /**
     316             :    * Map a function V -> W over this result's success variant. If this result is
     317             :    * an error, do not invoke the function and return a copy of the error.
     318             :    *
     319             :    * Mapping over success values invokes the function to produce a new success
     320             :    * value:
     321             :    *
     322             :    *     // Map Result<int, E> to another Result<int, E>
     323             :    *     Result<int, E> res(5);
     324             :    *     Result<int, E> res2 = res.map([](int x) { return x * x; });
     325             :    *     MOZ_ASSERT(res2.unwrap() == 25);
     326             :    *
     327             :    *     // Map Result<const char*, E> to Result<size_t, E>
     328             :    *     Result<const char*, E> res("hello, map!");
     329             :    *     Result<size_t, E> res2 = res.map(strlen);
     330             :    *     MOZ_ASSERT(res2.unwrap() == 11);
     331             :    *
     332             :    * Mapping over an error does not invoke the function and copies the error:
     333             :    *
     334             :    *     Result<V, int> res(5);
     335             :    *     MOZ_ASSERT(res.isErr());
     336             :    *     Result<W, int> res2 = res.map([](V v) { ... });
     337             :    *     MOZ_ASSERT(res2.isErr());
     338             :    *     MOZ_ASSERT(res2.unwrapErr() == 5);
     339             :    */
     340             :   template<typename F>
     341             :   auto map(F f) const -> Result<decltype(f(*((V*) nullptr))), E> {
     342             :       using RetResult = Result<decltype(f(*((V*) nullptr))), E>;
     343             :       return isOk() ? RetResult(f(unwrap())) : RetResult(unwrapErr());
     344             :   }
     345             : 
     346             :   /**
     347             :    * Given a function V -> Result<W, E>, apply it to this result's success value
     348             :    * and return its result. If this result is an error value, then return a
     349             :    * copy.
     350             :    *
     351             :    * This is sometimes called "flatMap" or ">>=" in other contexts.
     352             :    *
     353             :    * `andThen`ing over success values invokes the function to produce a new
     354             :    * result:
     355             :    *
     356             :    *     Result<const char*, Error> res("hello, andThen!");
     357             :    *     Result<HtmlFreeString, Error> res2 = res.andThen([](const char* s) {
     358             :    *       return containsHtmlTag(s)
     359             :    *         ? Result<HtmlFreeString, Error>(Error("Invalid: contains HTML"))
     360             :    *         : Result<HtmlFreeString, Error>(HtmlFreeString(s));
     361             :    *       }
     362             :    *     });
     363             :    *     MOZ_ASSERT(res2.isOk());
     364             :    *     MOZ_ASSERT(res2.unwrap() == HtmlFreeString("hello, andThen!");
     365             :    *
     366             :    * `andThen`ing over error results does not invoke the function, and just
     367             :    * produces a new copy of the error result:
     368             :    *
     369             :    *     Result<int, const char*> res("some error");
     370             :    *     auto res2 = res.andThen([](int x) { ... });
     371             :    *     MOZ_ASSERT(res2.isErr());
     372             :    *     MOZ_ASSERT(res.unwrapErr() == res2.unwrapErr());
     373             :    */
     374             :   template<
     375             :       typename F,
     376             :       typename = typename EnableIf<
     377             :           detail::IsResult<decltype((*((F*) nullptr))(*((V*) nullptr)))>::value
     378             :       >::Type
     379             :   >
     380             :   auto andThen(F f) const -> decltype(f(*((V*) nullptr))) {
     381             :       return isOk() ? f(unwrap()) : GenericErrorResult<E>(unwrapErr());
     382             :   }
     383             : };
     384             : 
     385             : /**
     386             :  * A type that auto-converts to an error Result. This is like a Result without
     387             :  * a success type. It's the best return type for functions that always return
     388             :  * an error--functions designed to build and populate error objects. It's also
     389             :  * useful in error-handling macros; see MOZ_TRY for an example.
     390             :  */
     391             : template <typename E>
     392             : class MOZ_MUST_USE_TYPE GenericErrorResult
     393             : {
     394             :   E mErrorValue;
     395             : 
     396             :   template<typename V, typename E2> friend class Result;
     397             : 
     398             : public:
     399          29 :   explicit GenericErrorResult(E aErrorValue) : mErrorValue(aErrorValue) {}
     400             : };
     401             : 
     402             : template <typename E>
     403             : inline GenericErrorResult<E>
     404          29 : Err(E&& aErrorValue)
     405             : {
     406          29 :   return GenericErrorResult<E>(aErrorValue);
     407             : }
     408             : 
     409             : } // namespace mozilla
     410             : 
     411             : /**
     412             :  * MOZ_TRY(expr) is the C++ equivalent of Rust's `try!(expr);`. First, it
     413             :  * evaluates expr, which must produce a Result value. On success, it
     414             :  * discards the result altogether. On error, it immediately returns an error
     415             :  * Result from the enclosing function.
     416             :  */
     417             : #define MOZ_TRY(expr) \
     418             :   do { \
     419             :     auto mozTryTempResult_ = (expr); \
     420             :     if (mozTryTempResult_.isErr()) { \
     421             :       return ::mozilla::Err(mozTryTempResult_.unwrapErr()); \
     422             :     } \
     423             :   } while (0)
     424             : 
     425             : /**
     426             :  * MOZ_TRY_VAR(target, expr) is the C++ equivalent of Rust's `target = try!(expr);`.
     427             :  * First, it evaluates expr, which must produce a Result value.
     428             :  * On success, the result's success value is assigned to target.
     429             :  * On error, immediately returns the error result.
     430             :  * |target| must evaluate to a reference without any side effects.
     431             :  */
     432             : #define MOZ_TRY_VAR(target, expr) \
     433             :   do { \
     434             :     auto mozTryVarTempResult_ = (expr); \
     435             :     if (mozTryVarTempResult_.isErr()) { \
     436             :       return ::mozilla::Err( \
     437             :           mozTryVarTempResult_.unwrapErr()); \
     438             :     } \
     439             :     (target) = mozTryVarTempResult_.unwrap(); \
     440             :   } while (0)
     441             : 
     442             : #endif // mozilla_Result_h

Generated by: LCOV version 1.13