tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Result.h (30774B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      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 <algorithm>
     13 #include <cstdint>
     14 #include <cstring>
     15 #include <type_traits>
     16 #include "mozilla/Assertions.h"
     17 #include "mozilla/Attributes.h"
     18 #include "mozilla/CompactPair.h"
     19 #include "mozilla/MaybeStorageBase.h"
     20 
     21 namespace mozilla {
     22 
     23 /**
     24 * Empty struct, indicating success for operations that have no return value.
     25 * For example, if you declare another empty struct `struct OutOfMemory {};`,
     26 * then `Result<Ok, OutOfMemory>` represents either success or OOM.
     27 */
     28 struct Ok {};
     29 
     30 /**
     31 * A tag used to differentiate between GenericErrorResult created by the Err
     32 * function (completely new error) and GenericErrorResult created by the
     33 * Result::propagateErr function (propagated error). This can be used to track
     34 * error propagation and eventually produce error stacks for logging/debugging
     35 * purposes.
     36 */
     37 struct ErrorPropagationTag {};
     38 
     39 template <typename E>
     40 class GenericErrorResult;
     41 template <typename V, typename E>
     42 class Result;
     43 
     44 namespace detail {
     45 
     46 enum class PackingStrategy {
     47  Variant,
     48  NullIsOk,
     49  LowBitTagIsError,
     50  PackedVariant,
     51  ZeroIsEmptyError,
     52 };
     53 
     54 template <typename T>
     55 struct UnusedZero;
     56 
     57 template <typename V, typename E, PackingStrategy Strategy>
     58 class ResultImplementation;
     59 
     60 template <typename V>
     61 struct EmptyWrapper : V {
     62  constexpr EmptyWrapper() = default;
     63  explicit constexpr EmptyWrapper(const V&) {}
     64  explicit constexpr EmptyWrapper(std::in_place_t) {}
     65 
     66  constexpr V* addr() { return this; }
     67  constexpr const V* addr() const { return this; }
     68 };
     69 
     70 // The purpose of AlignedStorageOrEmpty is to make an empty class look like
     71 // std::aligned_storage_t for the purposes of the PackingStrategy::NullIsOk
     72 // specializations of ResultImplementation below. We can't use
     73 // std::aligned_storage_t itself with an empty class, since it would no longer
     74 // be empty.
     75 template <typename V>
     76 using AlignedStorageOrEmpty =
     77    std::conditional_t<std::is_empty_v<V>, EmptyWrapper<V>,
     78                       MaybeStorageBase<V>>;
     79 
     80 template <typename V, typename E>
     81 class ResultImplementationNullIsOkBase {
     82 protected:
     83  using ErrorStorageType = typename UnusedZero<E>::StorageType;
     84 
     85  static constexpr auto kNullValue = UnusedZero<E>::nullValue;
     86 
     87  static_assert(std::is_trivially_copyable_v<ErrorStorageType>);
     88 
     89  // XXX This can't be statically asserted in general, if ErrorStorageType is
     90  // not a basic type. With C++20 bit_cast, we could probably re-add such as
     91  // assertion. static_assert(kNullValue == decltype(kNullValue)(0));
     92 
     93  CompactPair<AlignedStorageOrEmpty<V>, ErrorStorageType> mValue;
     94 
     95 public:
     96  explicit constexpr ResultImplementationNullIsOkBase(const V& aSuccessValue)
     97      : mValue(aSuccessValue, kNullValue) {}
     98  explicit constexpr ResultImplementationNullIsOkBase(V&& aSuccessValue)
     99      : mValue(std::move(aSuccessValue), kNullValue) {}
    100  template <typename... Args>
    101  explicit constexpr ResultImplementationNullIsOkBase(std::in_place_t,
    102                                                      Args&&... aArgs)
    103      : mValue(std::piecewise_construct,
    104               std::tuple(std::in_place, std::forward<Args>(aArgs)...),
    105               std::tuple(kNullValue)) {}
    106  explicit constexpr ResultImplementationNullIsOkBase(E aErrorValue)
    107      : mValue(std::piecewise_construct, std::tuple<>(),
    108               std::tuple(UnusedZero<E>::Store(std::move(aErrorValue)))) {
    109    MOZ_ASSERT(mValue.second() != kNullValue);
    110  }
    111 
    112  constexpr ResultImplementationNullIsOkBase(
    113      ResultImplementationNullIsOkBase&& aOther)
    114      : mValue(std::piecewise_construct, std::tuple<>(),
    115               std::tuple(aOther.mValue.second())) {
    116    if constexpr (!std::is_empty_v<V>) {
    117      if (isOk()) {
    118        new (mValue.first().addr()) V(std::move(*aOther.mValue.first().addr()));
    119      }
    120    }
    121  }
    122  ResultImplementationNullIsOkBase& operator=(
    123      ResultImplementationNullIsOkBase&& aOther) {
    124    if constexpr (!std::is_empty_v<V>) {
    125      if (isOk()) {
    126        mValue.first().addr()->~V();
    127      }
    128    }
    129    mValue.second() = std::move(aOther.mValue.second());
    130    if constexpr (!std::is_empty_v<V>) {
    131      if (isOk()) {
    132        new (mValue.first().addr()) V(std::move(*aOther.mValue.first().addr()));
    133      }
    134    }
    135    return *this;
    136  }
    137 
    138  constexpr bool isOk() const { return mValue.second() == kNullValue; }
    139 
    140  constexpr const V& inspect() const { return *mValue.first().addr(); }
    141  constexpr V unwrap() { return std::move(*mValue.first().addr()); }
    142  constexpr void updateAfterTracing(V&& aValue) {
    143    MOZ_ASSERT(isOk());
    144    if (!std::is_empty_v<V>) {
    145      mValue.first().addr()->~V();
    146      new (mValue.first().addr()) V(std::move(aValue));
    147    }
    148  }
    149 
    150  constexpr decltype(auto) inspectErr() const {
    151    return UnusedZero<E>::Inspect(mValue.second());
    152  }
    153  constexpr E unwrapErr() { return UnusedZero<E>::Unwrap(mValue.second()); }
    154  constexpr void updateErrorAfterTracing(E&& aErrorValue) {
    155    mValue.second() = UnusedZero<E>::Store(std::move(aErrorValue));
    156  }
    157 };
    158 
    159 template <typename V, typename E,
    160          bool IsVTriviallyDestructible = std::is_trivially_destructible_v<V>>
    161 class ResultImplementationNullIsOk;
    162 
    163 template <typename V, typename E>
    164 class ResultImplementationNullIsOk<V, E, true>
    165    : public ResultImplementationNullIsOkBase<V, E> {
    166 public:
    167  using ResultImplementationNullIsOkBase<V,
    168                                         E>::ResultImplementationNullIsOkBase;
    169 };
    170 
    171 template <typename V, typename E>
    172 class ResultImplementationNullIsOk<V, E, false>
    173    : public ResultImplementationNullIsOkBase<V, E> {
    174 public:
    175  using ResultImplementationNullIsOkBase<V,
    176                                         E>::ResultImplementationNullIsOkBase;
    177 
    178  ResultImplementationNullIsOk(ResultImplementationNullIsOk&&) = default;
    179  ResultImplementationNullIsOk& operator=(ResultImplementationNullIsOk&&) =
    180      default;
    181 
    182  ~ResultImplementationNullIsOk() {
    183    if (this->isOk()) {
    184      this->mValue.first().addr()->~V();
    185    }
    186  }
    187 };
    188 
    189 /**
    190 * Specialization for when the success type is one of integral, pointer, or
    191 * enum, where 0 is unused, and the error type is an empty struct.
    192 */
    193 template <typename V, typename E>
    194 class ResultImplementation<V, E, PackingStrategy::ZeroIsEmptyError> {
    195  static_assert(std::is_integral_v<V> || std::is_pointer_v<V> ||
    196                std::is_enum_v<V>);
    197  static_assert(std::is_empty_v<E>);
    198 
    199  V mValue;
    200 
    201 public:
    202  static constexpr PackingStrategy Strategy = PackingStrategy::ZeroIsEmptyError;
    203 
    204  explicit constexpr ResultImplementation(V aValue) : mValue(aValue) {}
    205  explicit constexpr ResultImplementation(E aErrorValue) : mValue(V(0)) {}
    206 
    207  constexpr bool isOk() const { return mValue != V(0); }
    208 
    209  constexpr V inspect() const { return mValue; }
    210  constexpr V unwrap() { return inspect(); }
    211 
    212  constexpr E inspectErr() const { return E(); }
    213  constexpr E unwrapErr() { return inspectErr(); }
    214 
    215  constexpr void updateAfterTracing(V&& aValue) {
    216    this->~ResultImplementation();
    217    new (this) ResultImplementation(std::move(aValue));
    218  }
    219  constexpr void updateErrorAfterTracing(E&& aErrorValue) {
    220    this->~ResultImplementation();
    221    new (this) ResultImplementation(std::move(aErrorValue));
    222  }
    223 };
    224 
    225 /**
    226 * Specialization for when the success type is default-constructible and the
    227 * error type is a value type which can never have the value 0 (as determined by
    228 * UnusedZero<>).
    229 */
    230 template <typename V, typename E>
    231 class ResultImplementation<V, E, PackingStrategy::NullIsOk>
    232    : public ResultImplementationNullIsOk<V, E> {
    233 public:
    234  static constexpr PackingStrategy Strategy = PackingStrategy::NullIsOk;
    235  using ResultImplementationNullIsOk<V, E>::ResultImplementationNullIsOk;
    236 };
    237 
    238 template <size_t S>
    239 using UnsignedIntType = std::conditional_t<
    240    S == 1, std::uint8_t,
    241    std::conditional_t<
    242        S == 2, std::uint16_t,
    243        std::conditional_t<S == 3 || S == 4, std::uint32_t,
    244                           std::conditional_t<S <= 8, std::uint64_t, void>>>>;
    245 
    246 /**
    247 * Specialization for when alignment permits using the least significant bit
    248 * as a tag bit.
    249 */
    250 template <typename V, typename E>
    251 class ResultImplementation<V, E, PackingStrategy::LowBitTagIsError> {
    252  static_assert(std::is_trivially_copyable_v<V> &&
    253                std::is_trivially_destructible_v<V>);
    254  static_assert(std::is_trivially_copyable_v<E> &&
    255                std::is_trivially_destructible_v<E>);
    256 
    257  static constexpr size_t kRequiredSize = std::max(sizeof(V), sizeof(E));
    258 
    259  using StorageType = UnsignedIntType<kRequiredSize>;
    260 
    261 #if defined(__clang__)
    262  alignas(std::max(alignof(V), alignof(E))) StorageType mBits;
    263 #else
    264  // Some gcc versions choke on using std::max with alignas, see
    265  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94929 (and this seems to have
    266  // regressed in some gcc 9.x version before being fixed again) Keeping the
    267  // code above since we would eventually drop this when we no longer support
    268  // gcc versions with the bug.
    269  alignas(alignof(V) > alignof(E) ? alignof(V) : alignof(E)) StorageType mBits;
    270 #endif
    271 
    272 public:
    273  static constexpr PackingStrategy Strategy = PackingStrategy::LowBitTagIsError;
    274 
    275  explicit constexpr ResultImplementation(V aValue) : mBits(0) {
    276    if constexpr (!std::is_empty_v<V>) {
    277      std::memcpy(&mBits, &aValue, sizeof(V));
    278      MOZ_ASSERT((mBits & 1) == 0);
    279    } else {
    280      (void)aValue;
    281    }
    282  }
    283  explicit constexpr ResultImplementation(E aErrorValue) : mBits(1) {
    284    if constexpr (!std::is_empty_v<E>) {
    285      std::memcpy(&mBits, &aErrorValue, sizeof(E));
    286      MOZ_ASSERT((mBits & 1) == 0);
    287      mBits |= 1;
    288    } else {
    289      (void)aErrorValue;
    290    }
    291  }
    292 
    293  constexpr bool isOk() const { return (mBits & 1) == 0; }
    294 
    295  constexpr V inspect() const {
    296    V res;
    297    std::memcpy(&res, &mBits, sizeof(V));
    298    return res;
    299  }
    300  constexpr V unwrap() { return inspect(); }
    301 
    302  constexpr E inspectErr() const {
    303    const auto bits = mBits ^ 1;
    304    E res;
    305    std::memcpy(&res, &bits, sizeof(E));
    306    return res;
    307  }
    308  constexpr E unwrapErr() { return inspectErr(); }
    309 
    310  constexpr void updateAfterTracing(V&& aValue) {
    311    this->~ResultImplementation();
    312    new (this) ResultImplementation(std::move(aValue));
    313  }
    314  constexpr void updateErrorAfterTracing(E&& aErrorValue) {
    315    this->~ResultImplementation();
    316    new (this) ResultImplementation(std::move(aErrorValue));
    317  }
    318 };
    319 
    320 // Return true if any of the struct can fit in a word.
    321 template <typename V, typename E>
    322 struct IsPackableVariant {
    323  struct VEbool {
    324    explicit constexpr VEbool(V&& aValue) : v(std::move(aValue)), ok(true) {}
    325    explicit constexpr VEbool(E&& aErrorValue)
    326        : e(std::move(aErrorValue)), ok(false) {}
    327    V v;
    328    E e;
    329    bool ok;
    330  };
    331  struct EVbool {
    332    explicit constexpr EVbool(V&& aValue) : v(std::move(aValue)), ok(true) {}
    333    explicit constexpr EVbool(E&& aErrorValue)
    334        : e(std::move(aErrorValue)), ok(false) {}
    335    E e;
    336    V v;
    337    bool ok;
    338  };
    339 
    340  using Impl =
    341      std::conditional_t<sizeof(VEbool) <= sizeof(EVbool), VEbool, EVbool>;
    342 
    343  static const bool value = sizeof(Impl) <= sizeof(uintptr_t);
    344 };
    345 
    346 /**
    347 * Specialization for when both type are not using all the bytes, in order to
    348 * use one byte as a tag.
    349 */
    350 template <typename V, typename E>
    351 class ResultImplementation<V, E, PackingStrategy::PackedVariant> {
    352  using Impl = typename IsPackableVariant<V, E>::Impl;
    353  Impl data;
    354 
    355 public:
    356  static constexpr PackingStrategy Strategy = PackingStrategy::PackedVariant;
    357 
    358  explicit constexpr ResultImplementation(V aValue) : data(std::move(aValue)) {}
    359  explicit constexpr ResultImplementation(E aErrorValue)
    360      : data(std::move(aErrorValue)) {}
    361 
    362  constexpr bool isOk() const { return data.ok; }
    363 
    364  constexpr const V& inspect() const { return data.v; }
    365  constexpr V unwrap() { return std::move(data.v); }
    366 
    367  constexpr const E& inspectErr() const { return data.e; }
    368  constexpr E unwrapErr() { return std::move(data.e); }
    369 
    370  constexpr void updateAfterTracing(V&& aValue) {
    371    MOZ_ASSERT(data.ok);
    372    this->~ResultImplementation();
    373    new (this) ResultImplementation(std::move(aValue));
    374  }
    375  constexpr void updateErrorAfterTracing(E&& aErrorValue) {
    376    MOZ_ASSERT(!data.ok);
    377    this->~ResultImplementation();
    378    new (this) ResultImplementation(std::move(aErrorValue));
    379  }
    380 };
    381 
    382 // To use nullptr as a special value, we need the counter part to exclude zero
    383 // from its range of valid representations.
    384 //
    385 // By default assume that zero can be represented.
    386 template <typename T>
    387 struct UnusedZero {
    388  static const bool value = false;
    389 };
    390 
    391 // This template can be used as a helper for specializing UnusedZero for scoped
    392 // enum types which never use 0 as an error value, e.g.
    393 //
    394 // namespace mozilla::detail {
    395 //
    396 // template <>
    397 // struct UnusedZero<MyEnumType> : UnusedZeroEnum<MyEnumType> {};
    398 //
    399 // }  // namespace mozilla::detail
    400 //
    401 template <typename T>
    402 struct UnusedZeroEnum {
    403  using StorageType = std::underlying_type_t<T>;
    404 
    405  static constexpr bool value = true;
    406  static constexpr StorageType nullValue = 0;
    407 
    408  static constexpr T Inspect(const StorageType& aValue) {
    409    return static_cast<T>(aValue);
    410  }
    411  static constexpr T Unwrap(StorageType aValue) {
    412    return static_cast<T>(aValue);
    413  }
    414  static constexpr StorageType Store(T aValue) {
    415    return static_cast<StorageType>(aValue);
    416  }
    417 };
    418 
    419 // A bit of help figuring out which of the above specializations to use.
    420 //
    421 // We begin by safely assuming types don't have a spare bit, unless they are
    422 // empty.
    423 template <typename T>
    424 struct HasFreeLSB {
    425  static const bool value = std::is_empty_v<T>;
    426 };
    427 
    428 // As an incomplete type, void* does not have a spare bit.
    429 template <>
    430 struct HasFreeLSB<void*> {
    431  static const bool value = false;
    432 };
    433 
    434 // The lowest bit of a properly-aligned pointer is always zero if the pointee
    435 // type is greater than byte-aligned. That bit is free to use if it's masked
    436 // out of such pointers before they're dereferenced.
    437 template <typename T>
    438 struct HasFreeLSB<T*> {
    439  static const bool value = (alignof(T) & 1) == 0;
    440 };
    441 
    442 // Select one of the previous result implementation based on the properties of
    443 // the V and E types.
    444 template <typename V, typename E>
    445 struct SelectResultImpl {
    446  static const PackingStrategy value =
    447      (UnusedZero<V>::value && std::is_empty_v<E>)
    448          ? PackingStrategy::ZeroIsEmptyError
    449      : (HasFreeLSB<V>::value && HasFreeLSB<E>::value)
    450          ? PackingStrategy::LowBitTagIsError
    451      : (UnusedZero<E>::value && sizeof(E) <= sizeof(uintptr_t))
    452          ? PackingStrategy::NullIsOk
    453      : (std::is_default_constructible_v<V> &&
    454         std::is_default_constructible_v<E> && IsPackableVariant<V, E>::value)
    455          ? PackingStrategy::PackedVariant
    456          : PackingStrategy::Variant;
    457 
    458  using Type = ResultImplementation<V, E, value>;
    459 };
    460 
    461 template <typename T>
    462 struct IsResult : std::false_type {};
    463 
    464 template <typename V, typename E>
    465 struct IsResult<Result<V, E>> : std::true_type {};
    466 
    467 }  // namespace detail
    468 
    469 template <typename V, typename E>
    470 constexpr auto ToResult(Result<V, E>&& aValue)
    471    -> decltype(std::forward<Result<V, E>>(aValue)) {
    472  return std::forward<Result<V, E>>(aValue);
    473 }
    474 
    475 /**
    476 * Result<V, E> represents the outcome of an operation that can either succeed
    477 * or fail. It contains either a success value of type V or an error value of
    478 * type E.
    479 *
    480 * All Result methods are const, so results are basically immutable.
    481 * This is just like Variant<V, E> but with a slightly different API, and the
    482 * following cases are optimized so Result can be stored more efficiently:
    483 *
    484 * - If both the success and error types do not use their least significant bit,
    485 * are trivially copyable and destructible, Result<V, E> is guaranteed to be as
    486 * large as the larger type. This is determined via the HasFreeLSB trait. By
    487 * default, empty classes (in particular Ok) and aligned pointer types are
    488 * assumed to have a free LSB, but you can specialize this trait for other
    489 * types. If the success type is empty, the representation is guaranteed to be
    490 * all zero bits on success. Do not change this representation! There is JIT
    491 * code that depends on it. (Implementation note: The lowest bit is used as a
    492 * tag bit: 0 to indicate the Result's bits are a success value, 1 to indicate
    493 * the Result's bits (with the 1 masked out) encode an error value)
    494 *
    495 * - Else, if the error type can't have a all-zero bits representation and is
    496 * not larger than a pointer, a CompactPair is used to represent this rather
    497 * than a Variant. This has shown to be better optimizable, and the template
    498 * code is much simpler than that of Variant, so it should also compile faster.
    499 * Whether an error type can't be all-zero bits, is determined via the
    500 * UnusedZero trait. MFBT doesn't declare any public type UnusedZero, but
    501 * nsresult is declared UnusedZero in XPCOM.
    502 *
    503 * The purpose of Result is to reduce the screwups caused by using `false` or
    504 * `nullptr` to indicate errors.
    505 * What screwups? See <https://bugzilla.mozilla.org/show_bug.cgi?id=912928> for
    506 * a partial list.
    507 *
    508 * Result<const V, E> or Result<V, const E> are not meaningful. The success or
    509 * error values in a Result instance are non-modifiable in-place anyway. This
    510 * guarantee must also be maintained when evolving Result. They can be
    511 * unwrap()ped, but this loses const qualification. However, Result<const V, E>
    512 * or Result<V, const E> may be misleading and prevent movability. Just use
    513 * Result<V, E>. (Result<const V*, E> may make sense though, just Result<const
    514 * V* const, E> is not possible.)
    515 */
    516 template <typename V, typename E>
    517 class [[nodiscard]] Result final {
    518  // See class comment on Result<const V, E> and Result<V, const E>.
    519  static_assert(!std::is_const_v<V>);
    520  static_assert(!std::is_const_v<E>);
    521  static_assert(!std::is_reference_v<V>);
    522  static_assert(!std::is_reference_v<E>);
    523 
    524  using Impl = typename detail::SelectResultImpl<V, E>::Type;
    525 
    526  Impl mImpl;
    527  // Are you getting this error?
    528  // > error: implicit instantiation of undefined template
    529  // > 'mozilla::detail::ResultImplementation<$V,$E,
    530  // >                      mozilla::detail::PackingStrategy::Variant>'
    531  // You need to include "ResultVariant.h"!
    532 
    533 public:
    534  static constexpr detail::PackingStrategy Strategy = Impl::Strategy;
    535  using ok_type = V;
    536  using err_type = E;
    537 
    538  /** Create a success result. */
    539  MOZ_IMPLICIT constexpr Result(V&& aValue) : mImpl(std::move(aValue)) {
    540    MOZ_ASSERT(isOk());
    541  }
    542 
    543  /** Create a success result. */
    544  MOZ_IMPLICIT constexpr Result(const V& aValue) : mImpl(aValue) {
    545    MOZ_ASSERT(isOk());
    546  }
    547 
    548  /** Create a success result in-place. */
    549  template <typename... Args>
    550  explicit constexpr Result(std::in_place_t, Args&&... aArgs)
    551      : mImpl(std::in_place, std::forward<Args>(aArgs)...) {
    552    MOZ_ASSERT(isOk());
    553  }
    554 
    555  /** Create an error result. */
    556  explicit constexpr Result(const E& aErrorValue) : mImpl(aErrorValue) {
    557    MOZ_ASSERT(isErr());
    558  }
    559  explicit constexpr Result(E&& aErrorValue) : mImpl(std::move(aErrorValue)) {
    560    MOZ_ASSERT(isErr());
    561  }
    562 
    563  /**
    564   * Create a (success/error) result from another (success/error) result with
    565   * different but convertible value and error types.
    566   */
    567  template <typename V2, typename E2,
    568            typename = std::enable_if_t<std::is_convertible_v<V2, V> &&
    569                                        std::is_convertible_v<E2, E>>>
    570  MOZ_IMPLICIT constexpr Result(Result<V2, E2>&& aOther)
    571      : mImpl(aOther.isOk() ? Impl{aOther.unwrap()}
    572                            : Impl{aOther.unwrapErr()}) {}
    573 
    574  /**
    575   * Implementation detail of MOZ_TRY().
    576   * Create an error result from another error result.
    577   */
    578  template <typename E2>
    579  MOZ_IMPLICIT constexpr Result(GenericErrorResult<E2>&& aErrorResult)
    580      : mImpl(std::move(aErrorResult.mErrorValue)) {
    581    static_assert(std::is_convertible_v<E2, E>, "E2 must be convertible to E");
    582    MOZ_ASSERT(isErr());
    583  }
    584 
    585  /**
    586   * Implementation detail of MOZ_TRY().
    587   * Create an error result from another error result.
    588   */
    589  template <typename E2>
    590  MOZ_IMPLICIT constexpr Result(const GenericErrorResult<E2>& aErrorResult)
    591      : mImpl(aErrorResult.mErrorValue) {
    592    static_assert(std::is_convertible_v<E2, E>, "E2 must be convertible to E");
    593    MOZ_ASSERT(isErr());
    594  }
    595 
    596  Result(const Result&) = delete;
    597  Result(Result&&) = default;
    598  Result& operator=(const Result&) = delete;
    599  Result& operator=(Result&&) = default;
    600 
    601  /** True if this Result is a success result. */
    602  constexpr bool isOk() const { return mImpl.isOk(); }
    603 
    604  /** True if this Result is an error result. */
    605  constexpr bool isErr() const { return !mImpl.isOk(); }
    606 
    607  /** Take the success value from this Result, which must be a success result.
    608   */
    609  constexpr V unwrap() {
    610    MOZ_ASSERT(isOk());
    611    return mImpl.unwrap();
    612  }
    613 
    614  /**
    615   * Take the success value from this Result, which must be a success result.
    616   * If it is an error result, then return the aValue.
    617   */
    618  constexpr V unwrapOr(V aValue) {
    619    return MOZ_LIKELY(isOk()) ? mImpl.unwrap() : std::move(aValue);
    620  }
    621 
    622  /** Take the error value from this Result, which must be an error result. */
    623  constexpr E unwrapErr() {
    624    MOZ_ASSERT(isErr());
    625    return mImpl.unwrapErr();
    626  }
    627 
    628  /** Used only for GC tracing. If used in Rooted<Result<...>>, V must have a
    629   * GCPolicy for tracing it. */
    630  constexpr void updateAfterTracing(V&& aValue) {
    631    mImpl.updateAfterTracing(std::move(aValue));
    632  }
    633 
    634  /** Used only for GC tracing. If used in Rooted<Result<...>>, E must have a
    635   * GCPolicy for tracing it. */
    636  constexpr void updateErrorAfterTracing(E&& aErrorValue) {
    637    mImpl.updateErrorAfterTracing(std::move(aErrorValue));
    638  }
    639 
    640  /** See the success value from this Result, which must be a success result. */
    641  constexpr decltype(auto) inspect() const {
    642    static_assert(!std::is_reference_v<
    643                      std::invoke_result_t<decltype(&Impl::inspect), Impl>> ||
    644                  std::is_const_v<std::remove_reference_t<
    645                      std::invoke_result_t<decltype(&Impl::inspect), Impl>>>);
    646    MOZ_ASSERT(isOk());
    647    return mImpl.inspect();
    648  }
    649 
    650  /** See the error value from this Result, which must be an error result. */
    651  constexpr decltype(auto) inspectErr() const {
    652    static_assert(
    653        !std::is_reference_v<
    654            std::invoke_result_t<decltype(&Impl::inspectErr), Impl>> ||
    655        std::is_const_v<std::remove_reference_t<
    656            std::invoke_result_t<decltype(&Impl::inspectErr), Impl>>>);
    657    MOZ_ASSERT(isErr());
    658    return mImpl.inspectErr();
    659  }
    660 
    661  /** Propagate the error value from this Result, which must be an error result.
    662   *
    663   * This can be used to propagate an error from a function call to the caller
    664   * with a different value type, but the same error type:
    665   *
    666   *    Result<T1, E> Func1() {
    667   *       Result<T2, E> res = Func2();
    668   *       if (res.isErr()) { return res.propagateErr(); }
    669   *    }
    670   */
    671  constexpr GenericErrorResult<E> propagateErr() {
    672    MOZ_ASSERT(isErr());
    673    return GenericErrorResult<E>{mImpl.unwrapErr(), ErrorPropagationTag{}};
    674  }
    675 
    676  /**
    677   * Map a function V -> V2 over this result's success variant. If this result
    678   * is an error, do not invoke the function and propagate the error.
    679   *
    680   * Mapping over success values invokes the function to produce a new success
    681   * value:
    682   *
    683   *     // Map Result<int, E> to another Result<int, E>
    684   *     Result<int, E> res(5);
    685   *     Result<int, E> res2 = res.map([](int x) { return x * x; });
    686   *     MOZ_ASSERT(res.isOk());
    687   *     MOZ_ASSERT(res2.unwrap() == 25);
    688   *
    689   *     // Map Result<const char*, E> to Result<size_t, E>
    690   *     Result<const char*, E> res("hello, map!");
    691   *     Result<size_t, E> res2 = res.map(strlen);
    692   *     MOZ_ASSERT(res.isOk());
    693   *     MOZ_ASSERT(res2.unwrap() == 11);
    694   *
    695   * Mapping over an error does not invoke the function and propagates the
    696   * error:
    697   *
    698   *     Result<V, int> res(5);
    699   *     MOZ_ASSERT(res.isErr());
    700   *     Result<V2, int> res2 = res.map([](V v) { ... });
    701   *     MOZ_ASSERT(res2.isErr());
    702   *     MOZ_ASSERT(res2.unwrapErr() == 5);
    703   */
    704  template <typename F>
    705  constexpr auto map(F f) -> Result<std::invoke_result_t<F, V>, E> {
    706    using RetResult = Result<std::invoke_result_t<F, V>, E>;
    707    return MOZ_LIKELY(isOk()) ? RetResult(f(unwrap())) : RetResult(unwrapErr());
    708  }
    709 
    710  /**
    711   * Map a function E -> E2 over this result's error variant. If this result is
    712   * a success, do not invoke the function and move the success over.
    713   *
    714   * Mapping over error values invokes the function to produce a new error
    715   * value:
    716   *
    717   *     // Map Result<V, int> to another Result<V, int>
    718   *     Result<V, int> res(5);
    719   *     Result<V, int> res2 = res.mapErr([](int x) { return x * x; });
    720   *     MOZ_ASSERT(res2.isErr());
    721   *     MOZ_ASSERT(res2.unwrapErr() == 25);
    722   *
    723   *     // Map Result<V, const char*> to Result<V, size_t>
    724   *     Result<V, const char*> res("hello, mapErr!");
    725   *     Result<V, size_t> res2 = res.mapErr(strlen);
    726   *     MOZ_ASSERT(res2.isErr());
    727   *     MOZ_ASSERT(res2.unwrapErr() == 14);
    728   *
    729   * Mapping over a success does not invoke the function and moves the success:
    730   *
    731   *     Result<int, E> res(5);
    732   *     MOZ_ASSERT(res.isOk());
    733   *     Result<int, E2> res2 = res.mapErr([](E e) { ... });
    734   *     MOZ_ASSERT(res2.isOk());
    735   *     MOZ_ASSERT(res2.unwrap() == 5);
    736   */
    737  template <typename F>
    738  constexpr auto mapErr(F f) {
    739    using RetResult = Result<V, std::invoke_result_t<F, E>>;
    740    return MOZ_UNLIKELY(isErr()) ? RetResult(f(unwrapErr()))
    741                                 : RetResult(unwrap());
    742  }
    743 
    744  /**
    745   * Map a function E -> Result<V, E2> over this result's error variant. If
    746   * this result is a success, do not invoke the function and move the success
    747   * over.
    748   *
    749   * `orElse`ing over error values invokes the function to produce a new
    750   * result:
    751   *
    752   *     // `orElse` Result<V, int> error variant to another Result<V, int>
    753   *     // error variant or Result<V, int> success variant
    754   *     auto orElse = [](int x) -> Result<V, int> {
    755   *       if (x != 6) {
    756   *         return Err(x * x);
    757   *       }
    758   *       return V(...);
    759   *     };
    760   *
    761   *     Result<V, int> res(5);
    762   *     auto res2 = res.orElse(orElse);
    763   *     MOZ_ASSERT(res2.isErr());
    764   *     MOZ_ASSERT(res2.unwrapErr() == 25);
    765   *
    766   *     Result<V, int> res3(6);
    767   *     auto res4 = res3.orElse(orElse);
    768   *     MOZ_ASSERT(res4.isOk());
    769   *     MOZ_ASSERT(res4.unwrap() == ...);
    770   *
    771   *     // `orElse` Result<V, const char*> error variant to Result<V, size_t>
    772   *     // error variant or Result<V, size_t> success variant
    773   *     auto orElse = [](const char* s) -> Result<V, size_t> {
    774   *       if (strcmp(s, "foo")) {
    775   *         return Err(strlen(s));
    776   *       }
    777   *       return V(...);
    778   *     };
    779   *
    780   *     Result<V, const char*> res("hello, orElse!");
    781   *     auto res2 = res.orElse(orElse);
    782   *     MOZ_ASSERT(res2.isErr());
    783   *     MOZ_ASSERT(res2.unwrapErr() == 14);
    784   *
    785   *     Result<V, const char*> res3("foo");
    786   *     auto res4 = ress.orElse(orElse);
    787   *     MOZ_ASSERT(res4.isOk());
    788   *     MOZ_ASSERT(res4.unwrap() == ...);
    789   *
    790   * `orElse`ing over a success does not invoke the function and moves the
    791   * success:
    792   *
    793   *     Result<int, E> res(5);
    794   *     MOZ_ASSERT(res.isOk());
    795   *     Result<int, E2> res2 = res.orElse([](E e) { ... });
    796   *     MOZ_ASSERT(res2.isOk());
    797   *     MOZ_ASSERT(res2.unwrap() == 5);
    798   */
    799  template <typename F>
    800  auto orElse(F f) -> Result<V, typename std::invoke_result_t<F, E>::err_type> {
    801    return MOZ_UNLIKELY(isErr()) ? f(unwrapErr()) : unwrap();
    802  }
    803 
    804  /**
    805   * Given a function V -> Result<V2, E>, apply it to this result's success
    806   * value and return its result. If this result is an error value, it is
    807   * propagated.
    808   *
    809   * This is sometimes called "flatMap" or ">>=" in other contexts.
    810   *
    811   * `andThen`ing over success values invokes the function to produce a new
    812   * result:
    813   *
    814   *     Result<const char*, Error> res("hello, andThen!");
    815   *     Result<HtmlFreeString, Error> res2 = res.andThen([](const char* s) {
    816   *       return containsHtmlTag(s)
    817   *         ? Result<HtmlFreeString, Error>(Error("Invalid: contains HTML"))
    818   *         : Result<HtmlFreeString, Error>(HtmlFreeString(s));
    819   *       }
    820   *     });
    821   *     MOZ_ASSERT(res2.isOk());
    822   *     MOZ_ASSERT(res2.unwrap() == HtmlFreeString("hello, andThen!");
    823   *
    824   * `andThen`ing over error results does not invoke the function, and just
    825   * propagates the error result:
    826   *
    827   *     Result<int, const char*> res("some error");
    828   *     auto res2 = res.andThen([](int x) { ... });
    829   *     MOZ_ASSERT(res2.isErr());
    830   *     MOZ_ASSERT(res.unwrapErr() == res2.unwrapErr());
    831   */
    832  template <typename F, typename = std::enable_if_t<detail::IsResult<
    833                            std::invoke_result_t<F, V&&>>::value>>
    834  constexpr auto andThen(F f) -> std::invoke_result_t<F, V&&> {
    835    return MOZ_LIKELY(isOk()) ? f(unwrap()) : propagateErr();
    836  }
    837 
    838  bool operator==(const Result<V, E>& aOther) const {
    839    return (isOk() && aOther.isOk() && inspect() == aOther.inspect()) ||
    840           (isErr() && aOther.isErr() && inspectErr() == aOther.inspectErr());
    841  }
    842 
    843  bool operator!=(const Result<V, E>& aOther) const {
    844    return !(*this == aOther);
    845  }
    846 };
    847 
    848 /**
    849 * A type that auto-converts to an error Result. This is like a Result without
    850 * a success type. It's the best return type for functions that always return
    851 * an error--functions designed to build and populate error objects. It's also
    852 * useful in error-handling macros; see MOZ_TRY for an example.
    853 */
    854 template <typename E>
    855 class [[nodiscard]] GenericErrorResult {
    856  E mErrorValue;
    857 
    858  template <typename V, typename E2>
    859  friend class Result;
    860 
    861 public:
    862  explicit constexpr GenericErrorResult(const E& aErrorValue)
    863      : mErrorValue(aErrorValue) {}
    864 
    865  explicit constexpr GenericErrorResult(E&& aErrorValue)
    866      : mErrorValue(std::move(aErrorValue)) {}
    867 
    868  constexpr GenericErrorResult(const E& aErrorValue, const ErrorPropagationTag&)
    869      : GenericErrorResult(aErrorValue) {}
    870 
    871  constexpr GenericErrorResult(E&& aErrorValue, const ErrorPropagationTag&)
    872      : GenericErrorResult(std::move(aErrorValue)) {}
    873 };
    874 
    875 template <typename E>
    876 inline constexpr auto Err(E&& aErrorValue) {
    877  return GenericErrorResult<std::decay_t<E>>(std::forward<E>(aErrorValue));
    878 }
    879 
    880 }  // namespace mozilla
    881 
    882 #endif  // mozilla_Result_h