tor-browser

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

TestMaybe.cpp (60740B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include <type_traits>
      7 #include <utility>
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "mozilla/Attributes.h"
     11 #include "mozilla/Maybe.h"
     12 
     13 using mozilla::Maybe;
     14 using mozilla::Nothing;
     15 using mozilla::Some;
     16 using mozilla::SomeRef;
     17 using mozilla::ToMaybe;
     18 using mozilla::ToMaybeRef;
     19 
     20 #define RUN_TEST(t)                                                           \
     21  do {                                                                        \
     22    bool cond = (t());                                                        \
     23    MOZ_RELEASE_ASSERT(cond, "Unexpectedly returned false during test: " #t); \
     24    cond = AllDestructorsWereCalled();                                        \
     25    MOZ_RELEASE_ASSERT(cond,                                                  \
     26                       "Failed to destroy all objects during test: " #t);     \
     27  } while (false)
     28 
     29 enum Status {
     30  eWasDefaultConstructed,
     31  eWasConstructed,
     32  eWasCopyConstructed,
     33  eWasMoveConstructed,
     34  eWasConstMoveConstructed,
     35  eWasAssigned,
     36  eWasCopyAssigned,
     37  eWasMoveAssigned,
     38  eWasCopiedFrom,
     39  eWasMovedFrom,
     40  eWasConstMovedFrom,
     41 };
     42 
     43 static size_t sUndestroyedObjects = 0;
     44 
     45 static bool AllDestructorsWereCalled() { return sUndestroyedObjects == 0; }
     46 
     47 struct BasicValue {
     48  BasicValue() : mStatus(eWasDefaultConstructed), mTag(0) {
     49    ++sUndestroyedObjects;
     50  }
     51 
     52  explicit BasicValue(int aTag) : mStatus(eWasConstructed), mTag(aTag) {
     53    ++sUndestroyedObjects;
     54  }
     55 
     56  BasicValue(const BasicValue& aOther)
     57      : mStatus(eWasCopyConstructed), mTag(aOther.mTag) {
     58    ++sUndestroyedObjects;
     59  }
     60 
     61  BasicValue(BasicValue&& aOther)
     62      : mStatus(eWasMoveConstructed), mTag(aOther.mTag) {
     63    ++sUndestroyedObjects;
     64    aOther.mStatus = eWasMovedFrom;
     65    aOther.mTag = 0;
     66  }
     67 
     68  BasicValue(const BasicValue&& aOther)
     69      : mStatus(eWasConstMoveConstructed), mTag(aOther.mTag) {
     70    ++sUndestroyedObjects;
     71    aOther.mStatus = eWasConstMovedFrom;
     72  }
     73 
     74  ~BasicValue() { --sUndestroyedObjects; }
     75 
     76  BasicValue& operator=(const BasicValue& aOther) {
     77    mStatus = eWasCopyAssigned;
     78    mTag = aOther.mTag;
     79    return *this;
     80  }
     81 
     82  BasicValue& operator=(BasicValue&& aOther) {
     83    mStatus = eWasMoveAssigned;
     84    mTag = aOther.mTag;
     85    aOther.mStatus = eWasMovedFrom;
     86    aOther.mTag = 0;
     87    return *this;
     88  }
     89 
     90  bool operator==(const BasicValue& aOther) const {
     91    return mTag == aOther.mTag;
     92  }
     93 
     94  bool operator<(const BasicValue& aOther) const { return mTag < aOther.mTag; }
     95 
     96  Status GetStatus() const { return mStatus; }
     97  void SetTag(int aValue) { mTag = aValue; }
     98  int GetTag() const { return mTag; }
     99 
    100 private:
    101  mutable Status mStatus;
    102  int mTag;
    103 };
    104 
    105 struct UncopyableValue {
    106  UncopyableValue() : mStatus(eWasDefaultConstructed) { ++sUndestroyedObjects; }
    107 
    108  UncopyableValue(UncopyableValue&& aOther) : mStatus(eWasMoveConstructed) {
    109    ++sUndestroyedObjects;
    110    aOther.mStatus = eWasMovedFrom;
    111  }
    112 
    113  ~UncopyableValue() { --sUndestroyedObjects; }
    114 
    115  UncopyableValue& operator=(UncopyableValue&& aOther) {
    116    mStatus = eWasMoveAssigned;
    117    aOther.mStatus = eWasMovedFrom;
    118    return *this;
    119  }
    120 
    121  Status GetStatus() { return mStatus; }
    122 
    123 private:
    124  UncopyableValue(const UncopyableValue& aOther) = delete;
    125  UncopyableValue& operator=(const UncopyableValue& aOther) = delete;
    126 
    127  Status mStatus;
    128 };
    129 
    130 struct UnmovableValue {
    131  UnmovableValue() : mStatus(eWasDefaultConstructed) { ++sUndestroyedObjects; }
    132 
    133  UnmovableValue(const UnmovableValue& aOther) : mStatus(eWasCopyConstructed) {
    134    ++sUndestroyedObjects;
    135  }
    136 
    137  ~UnmovableValue() { --sUndestroyedObjects; }
    138 
    139  UnmovableValue& operator=(const UnmovableValue& aOther) {
    140    mStatus = eWasCopyAssigned;
    141    return *this;
    142  }
    143 
    144  Status GetStatus() { return mStatus; }
    145 
    146  UnmovableValue(UnmovableValue&& aOther) = delete;
    147  UnmovableValue& operator=(UnmovableValue&& aOther) = delete;
    148 
    149 private:
    150  Status mStatus;
    151 };
    152 
    153 struct UncopyableUnmovableValue {
    154  UncopyableUnmovableValue() : mStatus(eWasDefaultConstructed) {
    155    ++sUndestroyedObjects;
    156  }
    157 
    158  explicit UncopyableUnmovableValue(int) : mStatus(eWasConstructed) {
    159    ++sUndestroyedObjects;
    160  }
    161 
    162  ~UncopyableUnmovableValue() { --sUndestroyedObjects; }
    163 
    164  Status GetStatus() const { return mStatus; }
    165 
    166 private:
    167  UncopyableUnmovableValue(const UncopyableUnmovableValue& aOther) = delete;
    168  UncopyableUnmovableValue& operator=(const UncopyableUnmovableValue& aOther) =
    169      delete;
    170  UncopyableUnmovableValue(UncopyableUnmovableValue&& aOther) = delete;
    171  UncopyableUnmovableValue& operator=(UncopyableUnmovableValue&& aOther) =
    172      delete;
    173 
    174  Status mStatus;
    175 };
    176 
    177 static_assert(std::is_trivially_destructible_v<Maybe<int>>);
    178 static_assert(std::is_trivially_copy_constructible_v<Maybe<int>>);
    179 static_assert(std::is_trivially_copy_assignable_v<Maybe<int>>);
    180 
    181 static_assert(42 == Some(42).value());
    182 static_assert(42 == Some(42).valueOr(43));
    183 static_assert(42 == Maybe<int>{}.valueOr(42));
    184 static_assert(42 == Some(42).valueOrFrom([] { return 43; }));
    185 static_assert(42 == Maybe<int>{}.valueOrFrom([] { return 42; }));
    186 static_assert(Some(43) == [] {
    187  auto val = Some(42);
    188  val.apply([](int& val) { val += 1; });
    189  return val;
    190 }());
    191 static_assert(Some(43) == Some(42).map([](int val) { return val + 1; }));
    192 static_assert(Maybe<int>(std::in_place, 43) ==
    193              Maybe<int>(std::in_place, 42).map([](int val) {
    194                return val + 1;
    195              }));
    196 
    197 struct TriviallyDestructible {
    198  TriviallyDestructible() {  // not trivially constructible
    199  }
    200 };
    201 
    202 static_assert(std::is_trivially_destructible_v<Maybe<TriviallyDestructible>>);
    203 
    204 struct UncopyableValueLiteralType {
    205  explicit constexpr UncopyableValueLiteralType(int aValue) : mValue{aValue} {}
    206 
    207  UncopyableValueLiteralType(UncopyableValueLiteralType&&) = default;
    208  UncopyableValueLiteralType& operator=(UncopyableValueLiteralType&&) = default;
    209 
    210  int mValue;
    211 };
    212 
    213 static_assert(
    214    std::is_trivially_destructible_v<Maybe<UncopyableValueLiteralType>>);
    215 static_assert(!std::is_copy_constructible_v<Maybe<UncopyableValueLiteralType>>);
    216 static_assert(!std::is_copy_assignable_v<Maybe<UncopyableValueLiteralType>>);
    217 static_assert(std::is_move_constructible_v<Maybe<UncopyableValueLiteralType>>);
    218 static_assert(std::is_move_assignable_v<Maybe<UncopyableValueLiteralType>>);
    219 
    220 constexpr Maybe<UncopyableValueLiteralType> someUncopyable =
    221    Some(UncopyableValueLiteralType{42});
    222 static_assert(someUncopyable.isSome());
    223 static_assert(42 == someUncopyable->mValue);
    224 
    225 constexpr Maybe<UncopyableValueLiteralType> someUncopyableAssigned = [] {
    226  auto res = Maybe<UncopyableValueLiteralType>{};
    227  res = Some(UncopyableValueLiteralType{42});
    228  return res;
    229 }();
    230 static_assert(someUncopyableAssigned.isSome());
    231 static_assert(42 == someUncopyableAssigned->mValue);
    232 
    233 static bool TestBasicFeatures() {
    234  // Check that a Maybe<T> is initialized to Nothing.
    235  Maybe<BasicValue> mayValue;
    236  static_assert(std::is_same_v<BasicValue, decltype(mayValue)::ValueType>,
    237                "Should have BasicValue ValueType");
    238  MOZ_RELEASE_ASSERT(!mayValue);
    239  MOZ_RELEASE_ASSERT(!mayValue.isSome());
    240  MOZ_RELEASE_ASSERT(mayValue.isNothing());
    241 
    242  // Check that emplace() default constructs and the accessors work.
    243  mayValue.emplace();
    244  MOZ_RELEASE_ASSERT(mayValue);
    245  MOZ_RELEASE_ASSERT(mayValue.isSome());
    246  MOZ_RELEASE_ASSERT(!mayValue.isNothing());
    247  MOZ_RELEASE_ASSERT(*mayValue == BasicValue());
    248  static_assert(std::is_same_v<BasicValue&, decltype(*mayValue)>,
    249                "operator*() should return a BasicValue&");
    250  MOZ_RELEASE_ASSERT(mayValue.value() == BasicValue());
    251  static_assert(std::is_same_v<BasicValue, decltype(mayValue.value())>,
    252                "value() should return a BasicValue");
    253  MOZ_RELEASE_ASSERT(mayValue.ref() == BasicValue());
    254  static_assert(std::is_same_v<BasicValue&, decltype(mayValue.ref())>,
    255                "ref() should return a BasicValue&");
    256  MOZ_RELEASE_ASSERT(mayValue.ptr() != nullptr);
    257  static_assert(std::is_same_v<BasicValue*, decltype(mayValue.ptr())>,
    258                "ptr() should return a BasicValue*");
    259  MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasDefaultConstructed);
    260 
    261  // Check that reset() works.
    262  mayValue.reset();
    263  MOZ_RELEASE_ASSERT(!mayValue);
    264  MOZ_RELEASE_ASSERT(!mayValue.isSome());
    265  MOZ_RELEASE_ASSERT(mayValue.isNothing());
    266 
    267  // Check that emplace(T1) calls the correct constructor.
    268  mayValue.emplace(1);
    269  MOZ_RELEASE_ASSERT(mayValue);
    270  MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasConstructed);
    271  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
    272  mayValue.reset();
    273  MOZ_RELEASE_ASSERT(!mayValue);
    274 
    275  {
    276    // Check that Maybe(std::in_place, T1) calls the correct constructor.
    277    const auto mayValueConstructed = Maybe<BasicValue>(std::in_place, 1);
    278    MOZ_RELEASE_ASSERT(mayValueConstructed);
    279    MOZ_RELEASE_ASSERT(mayValueConstructed->GetStatus() == eWasConstructed);
    280    MOZ_RELEASE_ASSERT(mayValueConstructed->GetTag() == 1);
    281  }
    282 
    283  // Check that Some() and Nothing() work.
    284  mayValue = Some(BasicValue(2));
    285  MOZ_RELEASE_ASSERT(mayValue);
    286  MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasMoveConstructed);
    287  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
    288  mayValue = Nothing();
    289  MOZ_RELEASE_ASSERT(!mayValue);
    290 
    291  // Check that the accessors work through a const ref.
    292  mayValue.emplace();
    293  const Maybe<BasicValue>& mayValueCRef = mayValue;
    294  MOZ_RELEASE_ASSERT(mayValueCRef);
    295  MOZ_RELEASE_ASSERT(mayValueCRef.isSome());
    296  MOZ_RELEASE_ASSERT(!mayValueCRef.isNothing());
    297  MOZ_RELEASE_ASSERT(*mayValueCRef == BasicValue());
    298  static_assert(std::is_same_v<const BasicValue&, decltype(*mayValueCRef)>,
    299                "operator*() should return a BasicValue");
    300  MOZ_RELEASE_ASSERT(mayValueCRef.value() == BasicValue());
    301  static_assert(std::is_same_v<BasicValue, decltype(mayValueCRef.value())>,
    302                "value() should return a BasicValue");
    303  MOZ_RELEASE_ASSERT(mayValueCRef.ref() == BasicValue());
    304  static_assert(std::is_same_v<const BasicValue&, decltype(mayValueCRef.ref())>,
    305                "ref() should return a const BasicValue&");
    306  MOZ_RELEASE_ASSERT(mayValueCRef.ptr() != nullptr);
    307  static_assert(std::is_same_v<const BasicValue*, decltype(mayValueCRef.ptr())>,
    308                "ptr() should return a const BasicValue*");
    309  MOZ_RELEASE_ASSERT(mayValueCRef->GetStatus() == eWasDefaultConstructed);
    310  mayValue.reset();
    311 
    312  // Check that we can create and reference Maybe<const Type>.
    313  Maybe<const BasicValue> mayCValue1 = Some(BasicValue(5));
    314  MOZ_RELEASE_ASSERT(mayCValue1);
    315  MOZ_RELEASE_ASSERT(mayCValue1.isSome());
    316  MOZ_RELEASE_ASSERT(*mayCValue1 == BasicValue(5));
    317  const Maybe<const BasicValue>& mayCValue1Ref = mayCValue1;
    318  MOZ_RELEASE_ASSERT(mayCValue1Ref == mayCValue1);
    319  MOZ_RELEASE_ASSERT(*mayCValue1Ref == BasicValue(5));
    320  Maybe<const BasicValue> mayCValue2;
    321  mayCValue2.emplace(6);
    322  MOZ_RELEASE_ASSERT(mayCValue2);
    323  MOZ_RELEASE_ASSERT(mayCValue2.isSome());
    324  MOZ_RELEASE_ASSERT(*mayCValue2 == BasicValue(6));
    325 
    326  // Check that accessors work through rvalue-references.
    327  MOZ_RELEASE_ASSERT(Some(BasicValue()));
    328  MOZ_RELEASE_ASSERT(Some(BasicValue()).isSome());
    329  MOZ_RELEASE_ASSERT(!Some(BasicValue()).isNothing());
    330  MOZ_RELEASE_ASSERT(*Some(BasicValue()) == BasicValue());
    331  static_assert(std::is_same_v<BasicValue&&, decltype(*Some(BasicValue()))>,
    332                "operator*() should return a BasicValue&&");
    333  MOZ_RELEASE_ASSERT(Some(BasicValue()).value() == BasicValue());
    334  static_assert(
    335      std::is_same_v<BasicValue, decltype(Some(BasicValue()).value())>,
    336      "value() should return a BasicValue");
    337  MOZ_RELEASE_ASSERT(Some(BasicValue()).ref() == BasicValue());
    338  static_assert(
    339      std::is_same_v<BasicValue&&, decltype(Some(BasicValue()).ref())>,
    340      "ref() should return a BasicValue&&");
    341  MOZ_RELEASE_ASSERT(Some(BasicValue()).ptr() != nullptr);
    342  static_assert(std::is_same_v<BasicValue*, decltype(Some(BasicValue()).ptr())>,
    343                "ptr() should return a BasicValue*");
    344  MOZ_RELEASE_ASSERT(Some(BasicValue())->GetStatus() == eWasMoveConstructed);
    345 
    346  // Check that accessors work through const-rvalue-references.
    347  auto MakeConstMaybe = []() -> const Maybe<BasicValue> {
    348    return Some(BasicValue());
    349  };
    350  MOZ_RELEASE_ASSERT(MakeConstMaybe());
    351  MOZ_RELEASE_ASSERT(MakeConstMaybe().isSome());
    352  MOZ_RELEASE_ASSERT(!MakeConstMaybe().isNothing());
    353  MOZ_RELEASE_ASSERT(*MakeConstMaybe() == BasicValue());
    354  static_assert(std::is_same_v<const BasicValue&&, decltype(*MakeConstMaybe())>,
    355                "operator*() should return a const BasicValue&&");
    356  MOZ_RELEASE_ASSERT(MakeConstMaybe().value() == BasicValue());
    357  static_assert(std::is_same_v<BasicValue, decltype(MakeConstMaybe().value())>,
    358                "value() should return a BasicValue");
    359  MOZ_RELEASE_ASSERT(MakeConstMaybe().ref() == BasicValue());
    360  static_assert(
    361      std::is_same_v<const BasicValue&&, decltype(MakeConstMaybe().ref())>,
    362      "ref() should return a const BasicValue&&");
    363  MOZ_RELEASE_ASSERT(MakeConstMaybe().ptr() != nullptr);
    364  static_assert(
    365      std::is_same_v<const BasicValue*, decltype(MakeConstMaybe().ptr())>,
    366      "ptr() should return a const BasicValue*");
    367  MOZ_RELEASE_ASSERT(MakeConstMaybe()->GetStatus() == eWasMoveConstructed);
    368  MOZ_RELEASE_ASSERT(BasicValue(*MakeConstMaybe()).GetStatus() ==
    369                     eWasConstMoveConstructed);
    370 
    371  // Check that take works
    372  mayValue = Some(BasicValue(6));
    373  Maybe taken = mayValue.take();
    374  MOZ_RELEASE_ASSERT(taken->GetStatus() == eWasMoveConstructed);
    375  MOZ_RELEASE_ASSERT(taken == Some(BasicValue(6)));
    376  MOZ_RELEASE_ASSERT(!mayValue.isSome());
    377  MOZ_RELEASE_ASSERT(mayValue.take() == Nothing());
    378 
    379  // Check that extract works
    380  mayValue = Some(BasicValue(7));
    381  BasicValue extracted = mayValue.extract();
    382  MOZ_RELEASE_ASSERT(extracted.GetStatus() == eWasMoveConstructed);
    383  MOZ_RELEASE_ASSERT(extracted == BasicValue(7));
    384  MOZ_RELEASE_ASSERT(!mayValue.isSome());
    385 
    386  return true;
    387 }
    388 
    389 template <typename T>
    390 static void TestCopyMaybe() {
    391  {
    392    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
    393 
    394    Maybe<T> src = Some(T());
    395    Maybe<T> dstCopyConstructed = src;
    396 
    397    MOZ_RELEASE_ASSERT(2 == sUndestroyedObjects);
    398    MOZ_RELEASE_ASSERT(dstCopyConstructed->GetStatus() == eWasCopyConstructed);
    399  }
    400 
    401  {
    402    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
    403 
    404    Maybe<T> src = Some(T());
    405    Maybe<T> dstCopyAssigned;
    406    dstCopyAssigned = src;
    407 
    408    MOZ_RELEASE_ASSERT(2 == sUndestroyedObjects);
    409    MOZ_RELEASE_ASSERT(dstCopyAssigned->GetStatus() == eWasCopyConstructed);
    410  }
    411 }
    412 
    413 template <typename T>
    414 static void TestMoveMaybe() {
    415  {
    416    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
    417 
    418    Maybe<T> src = Some(T());
    419    Maybe<T> dstMoveConstructed = std::move(src);
    420 
    421    MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
    422    MOZ_RELEASE_ASSERT(dstMoveConstructed->GetStatus() == eWasMoveConstructed);
    423  }
    424 
    425  {
    426    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
    427 
    428    Maybe<T> src = Some(T());
    429    Maybe<T> dstMoveAssigned;
    430    dstMoveAssigned = std::move(src);
    431 
    432    MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
    433    MOZ_RELEASE_ASSERT(dstMoveAssigned->GetStatus() == eWasMoveConstructed);
    434  }
    435 
    436  {
    437    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
    438 
    439    Maybe<T> src = Some(T());
    440    Maybe<T> dstMoveConstructed = src.take();
    441 
    442    MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
    443    MOZ_RELEASE_ASSERT(dstMoveConstructed->GetStatus() == eWasMoveConstructed);
    444  }
    445 
    446  {
    447    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
    448 
    449    Maybe<T> src = Some(T());
    450    T dstMoveConstructed = src.extract();
    451 
    452    MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
    453    MOZ_RELEASE_ASSERT(dstMoveConstructed.GetStatus() == eWasMoveConstructed);
    454  }
    455 }
    456 
    457 static bool TestCopyAndMove() {
    458  MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
    459 
    460  {
    461    // Check that we get moves when possible for types that can support both
    462    // moves and copies.
    463    {
    464      Maybe<BasicValue> mayBasicValue = Some(BasicValue(1));
    465      MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
    466      MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed);
    467      MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 1);
    468      mayBasicValue = Some(BasicValue(2));
    469      MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
    470      MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveAssigned);
    471      MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 2);
    472      mayBasicValue.reset();
    473      MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
    474      mayBasicValue.emplace(BasicValue(3));
    475      MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
    476      MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed);
    477      MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 3);
    478 
    479      // Check that we get copies when moves aren't possible.
    480      Maybe<BasicValue> mayBasicValue2 = Some(*mayBasicValue);
    481      MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed);
    482      MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 3);
    483      mayBasicValue->SetTag(4);
    484      mayBasicValue2 = mayBasicValue;
    485      // This test should work again when we fix bug 1052940.
    486      // MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyAssigned);
    487      MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 4);
    488      mayBasicValue->SetTag(5);
    489      mayBasicValue2.reset();
    490      mayBasicValue2.emplace(*mayBasicValue);
    491      MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed);
    492      MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 5);
    493 
    494      // Check that std::move() works. (Another sanity check for move support.)
    495      Maybe<BasicValue> mayBasicValue3 = Some(std::move(*mayBasicValue));
    496      MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveConstructed);
    497      MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 5);
    498      MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMovedFrom);
    499      mayBasicValue2->SetTag(6);
    500      mayBasicValue3 = Some(std::move(*mayBasicValue2));
    501      MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveAssigned);
    502      MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 6);
    503      MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasMovedFrom);
    504      Maybe<BasicValue> mayBasicValue4;
    505      mayBasicValue4.emplace(std::move(*mayBasicValue3));
    506      MOZ_RELEASE_ASSERT(mayBasicValue4->GetStatus() == eWasMoveConstructed);
    507      MOZ_RELEASE_ASSERT(mayBasicValue4->GetTag() == 6);
    508      MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMovedFrom);
    509    }
    510 
    511    TestCopyMaybe<BasicValue>();
    512    TestMoveMaybe<BasicValue>();
    513  }
    514 
    515  MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
    516 
    517  {
    518    // Check that we always get copies for types that don't support moves.
    519    {
    520      Maybe<UnmovableValue> mayUnmovableValue = Some(UnmovableValue());
    521      MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed);
    522      mayUnmovableValue.reset();
    523      mayUnmovableValue.emplace(UnmovableValue());
    524      MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed);
    525    }
    526 
    527    TestCopyMaybe<UnmovableValue>();
    528 
    529    static_assert(std::is_copy_constructible_v<Maybe<UnmovableValue>>);
    530    static_assert(std::is_copy_assignable_v<Maybe<UnmovableValue>>);
    531    static_assert(!std::is_move_constructible_v<Maybe<UnmovableValue>>);
    532    static_assert(!std::is_move_assignable_v<Maybe<UnmovableValue>>);
    533  }
    534 
    535  MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
    536 
    537  {
    538    // Check that types that only support moves, but not copies, work.
    539    {
    540      Maybe<UncopyableValue> mayUncopyableValue = Some(UncopyableValue());
    541      MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() ==
    542                         eWasMoveConstructed);
    543      mayUncopyableValue = Some(UncopyableValue());
    544      MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveAssigned);
    545      mayUncopyableValue.reset();
    546      mayUncopyableValue.emplace(UncopyableValue());
    547      MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() ==
    548                         eWasMoveConstructed);
    549      mayUncopyableValue = Nothing();
    550    }
    551 
    552    TestMoveMaybe<BasicValue>();
    553 
    554    static_assert(!std::is_copy_constructible_v<Maybe<UncopyableValue>>);
    555    static_assert(!std::is_copy_assignable_v<Maybe<UncopyableValue>>);
    556    static_assert(std::is_move_constructible_v<Maybe<UncopyableValue>>);
    557    static_assert(std::is_move_assignable_v<Maybe<UncopyableValue>>);
    558  }
    559 
    560  MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
    561 
    562  {  // Check that types that support neither moves or copies work.
    563    {
    564      const auto mayUncopyableUnmovableValueConstructed =
    565          Maybe<UncopyableUnmovableValue>{std::in_place};
    566      MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValueConstructed->GetStatus() ==
    567                         eWasDefaultConstructed);
    568    }
    569 
    570    Maybe<UncopyableUnmovableValue> mayUncopyableUnmovableValue;
    571    mayUncopyableUnmovableValue.emplace();
    572    MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() ==
    573                       eWasDefaultConstructed);
    574    mayUncopyableUnmovableValue.reset();
    575    mayUncopyableUnmovableValue.emplace(0);
    576    MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() ==
    577                       eWasConstructed);
    578    mayUncopyableUnmovableValue = Nothing();
    579 
    580    static_assert(
    581        !std::is_copy_constructible_v<Maybe<UncopyableUnmovableValue>>);
    582    static_assert(!std::is_copy_assignable_v<Maybe<UncopyableUnmovableValue>>);
    583    static_assert(
    584        !std::is_move_constructible_v<Maybe<UncopyableUnmovableValue>>);
    585    static_assert(!std::is_move_assignable_v<Maybe<UncopyableUnmovableValue>>);
    586  }
    587 
    588  {
    589    // Test copy and move with a trivially copyable and trivially destructible
    590    // type.
    591    {
    592      constexpr Maybe<int> src = Some(42);
    593      constexpr Maybe<int> dstCopyConstructed = src;
    594 
    595      static_assert(src.isSome());
    596      static_assert(dstCopyConstructed.isSome());
    597      static_assert(42 == *src);
    598      static_assert(42 == *dstCopyConstructed);
    599      static_assert(42 == dstCopyConstructed.value());
    600    }
    601 
    602    {
    603      const Maybe<int> src = Some(42);
    604      Maybe<int> dstCopyAssigned;
    605      dstCopyAssigned = src;
    606 
    607      MOZ_RELEASE_ASSERT(src.isSome());
    608      MOZ_RELEASE_ASSERT(dstCopyAssigned.isSome());
    609      MOZ_RELEASE_ASSERT(42 == *src);
    610      MOZ_RELEASE_ASSERT(42 == *dstCopyAssigned);
    611    }
    612 
    613    {
    614      Maybe<int> src = Some(42);
    615      const Maybe<int> dstMoveConstructed = std::move(src);
    616 
    617      MOZ_RELEASE_ASSERT(!src.isSome());
    618      MOZ_RELEASE_ASSERT(dstMoveConstructed.isSome());
    619      MOZ_RELEASE_ASSERT(42 == *dstMoveConstructed);
    620    }
    621 
    622    {
    623      Maybe<int> src = Some(42);
    624      Maybe<int> dstMoveAssigned;
    625      dstMoveAssigned = std::move(src);
    626 
    627      MOZ_RELEASE_ASSERT(!src.isSome());
    628      MOZ_RELEASE_ASSERT(dstMoveAssigned.isSome());
    629      MOZ_RELEASE_ASSERT(42 == *dstMoveAssigned);
    630    }
    631  }
    632 
    633  return true;
    634 }
    635 
    636 static BasicValue* sStaticBasicValue = nullptr;
    637 
    638 static BasicValue MakeBasicValue() { return BasicValue(9); }
    639 
    640 static BasicValue& MakeBasicValueRef() { return *sStaticBasicValue; }
    641 
    642 static BasicValue* MakeBasicValuePtr() { return sStaticBasicValue; }
    643 
    644 static bool TestFunctionalAccessors() {
    645  BasicValue value(9);
    646  sStaticBasicValue = new BasicValue(9);
    647 
    648  // Check that the 'some' case of functional accessors works.
    649  Maybe<BasicValue> someValue = Some(BasicValue(3));
    650  MOZ_RELEASE_ASSERT(someValue.valueOr(value) == BasicValue(3));
    651  static_assert(std::is_same_v<BasicValue, decltype(someValue.valueOr(value))>,
    652                "valueOr should return a BasicValue");
    653  MOZ_RELEASE_ASSERT(someValue.valueOrFrom(&MakeBasicValue) == BasicValue(3));
    654  static_assert(
    655      std::is_same_v<BasicValue,
    656                     decltype(someValue.valueOrFrom(&MakeBasicValue))>,
    657      "valueOrFrom should return a BasicValue");
    658  MOZ_RELEASE_ASSERT(someValue.ptrOr(&value) != &value);
    659  static_assert(std::is_same_v<BasicValue*, decltype(someValue.ptrOr(&value))>,
    660                "ptrOr should return a BasicValue*");
    661  MOZ_RELEASE_ASSERT(*someValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3));
    662  static_assert(
    663      std::is_same_v<BasicValue*,
    664                     decltype(someValue.ptrOrFrom(&MakeBasicValuePtr))>,
    665      "ptrOrFrom should return a BasicValue*");
    666  MOZ_RELEASE_ASSERT(someValue.refOr(value) == BasicValue(3));
    667  static_assert(std::is_same_v<BasicValue&, decltype(someValue.refOr(value))>,
    668                "refOr should return a BasicValue&");
    669  MOZ_RELEASE_ASSERT(someValue.refOrFrom(&MakeBasicValueRef) == BasicValue(3));
    670  static_assert(
    671      std::is_same_v<BasicValue&,
    672                     decltype(someValue.refOrFrom(&MakeBasicValueRef))>,
    673      "refOrFrom should return a BasicValue&");
    674 
    675  // Check that the 'some' case works through a const reference.
    676  const Maybe<BasicValue>& someValueCRef = someValue;
    677  MOZ_RELEASE_ASSERT(someValueCRef.valueOr(value) == BasicValue(3));
    678  static_assert(
    679      std::is_same_v<BasicValue, decltype(someValueCRef.valueOr(value))>,
    680      "valueOr should return a BasicValue");
    681  MOZ_RELEASE_ASSERT(someValueCRef.valueOrFrom(&MakeBasicValue) ==
    682                     BasicValue(3));
    683  static_assert(
    684      std::is_same_v<BasicValue,
    685                     decltype(someValueCRef.valueOrFrom(&MakeBasicValue))>,
    686      "valueOrFrom should return a BasicValue");
    687  MOZ_RELEASE_ASSERT(someValueCRef.ptrOr(&value) != &value);
    688  static_assert(
    689      std::is_same_v<const BasicValue*, decltype(someValueCRef.ptrOr(&value))>,
    690      "ptrOr should return a const BasicValue*");
    691  MOZ_RELEASE_ASSERT(*someValueCRef.ptrOrFrom(&MakeBasicValuePtr) ==
    692                     BasicValue(3));
    693  static_assert(
    694      std::is_same_v<const BasicValue*,
    695                     decltype(someValueCRef.ptrOrFrom(&MakeBasicValuePtr))>,
    696      "ptrOrFrom should return a const BasicValue*");
    697  MOZ_RELEASE_ASSERT(someValueCRef.refOr(value) == BasicValue(3));
    698  static_assert(
    699      std::is_same_v<const BasicValue&, decltype(someValueCRef.refOr(value))>,
    700      "refOr should return a const BasicValue&");
    701  MOZ_RELEASE_ASSERT(someValueCRef.refOrFrom(&MakeBasicValueRef) ==
    702                     BasicValue(3));
    703  static_assert(
    704      std::is_same_v<const BasicValue&,
    705                     decltype(someValueCRef.refOrFrom(&MakeBasicValueRef))>,
    706      "refOrFrom should return a const BasicValue&");
    707 
    708  // Check that the 'none' case of functional accessors works.
    709  Maybe<BasicValue> noneValue;
    710  MOZ_RELEASE_ASSERT(noneValue.valueOr(value) == BasicValue(9));
    711  static_assert(std::is_same_v<BasicValue, decltype(noneValue.valueOr(value))>,
    712                "valueOr should return a BasicValue");
    713  MOZ_RELEASE_ASSERT(noneValue.valueOrFrom(&MakeBasicValue) == BasicValue(9));
    714  static_assert(
    715      std::is_same_v<BasicValue,
    716                     decltype(noneValue.valueOrFrom(&MakeBasicValue))>,
    717      "valueOrFrom should return a BasicValue");
    718  MOZ_RELEASE_ASSERT(noneValue.ptrOr(&value) == &value);
    719  static_assert(std::is_same_v<BasicValue*, decltype(noneValue.ptrOr(&value))>,
    720                "ptrOr should return a BasicValue*");
    721  MOZ_RELEASE_ASSERT(*noneValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9));
    722  static_assert(
    723      std::is_same_v<BasicValue*,
    724                     decltype(noneValue.ptrOrFrom(&MakeBasicValuePtr))>,
    725      "ptrOrFrom should return a BasicValue*");
    726  MOZ_RELEASE_ASSERT(noneValue.refOr(value) == BasicValue(9));
    727  static_assert(std::is_same_v<BasicValue&, decltype(noneValue.refOr(value))>,
    728                "refOr should return a BasicValue&");
    729  MOZ_RELEASE_ASSERT(noneValue.refOrFrom(&MakeBasicValueRef) == BasicValue(9));
    730  static_assert(
    731      std::is_same_v<BasicValue&,
    732                     decltype(noneValue.refOrFrom(&MakeBasicValueRef))>,
    733      "refOrFrom should return a BasicValue&");
    734 
    735  // Check that the 'none' case works through a const reference.
    736  const Maybe<BasicValue>& noneValueCRef = noneValue;
    737  MOZ_RELEASE_ASSERT(noneValueCRef.valueOr(value) == BasicValue(9));
    738  static_assert(
    739      std::is_same_v<BasicValue, decltype(noneValueCRef.valueOr(value))>,
    740      "valueOr should return a BasicValue");
    741  MOZ_RELEASE_ASSERT(noneValueCRef.valueOrFrom(&MakeBasicValue) ==
    742                     BasicValue(9));
    743  static_assert(
    744      std::is_same_v<BasicValue,
    745                     decltype(noneValueCRef.valueOrFrom(&MakeBasicValue))>,
    746      "valueOrFrom should return a BasicValue");
    747  MOZ_RELEASE_ASSERT(noneValueCRef.ptrOr(&value) == &value);
    748  static_assert(
    749      std::is_same_v<const BasicValue*, decltype(noneValueCRef.ptrOr(&value))>,
    750      "ptrOr should return a const BasicValue*");
    751  MOZ_RELEASE_ASSERT(*noneValueCRef.ptrOrFrom(&MakeBasicValuePtr) ==
    752                     BasicValue(9));
    753  static_assert(
    754      std::is_same_v<const BasicValue*,
    755                     decltype(noneValueCRef.ptrOrFrom(&MakeBasicValuePtr))>,
    756      "ptrOrFrom should return a const BasicValue*");
    757  MOZ_RELEASE_ASSERT(noneValueCRef.refOr(value) == BasicValue(9));
    758  static_assert(
    759      std::is_same_v<const BasicValue&, decltype(noneValueCRef.refOr(value))>,
    760      "refOr should return a const BasicValue&");
    761  MOZ_RELEASE_ASSERT(noneValueCRef.refOrFrom(&MakeBasicValueRef) ==
    762                     BasicValue(9));
    763  static_assert(
    764      std::is_same_v<const BasicValue&,
    765                     decltype(noneValueCRef.refOrFrom(&MakeBasicValueRef))>,
    766      "refOrFrom should return a const BasicValue&");
    767 
    768  // Clean up so the undestroyed objects count stays accurate.
    769  delete sStaticBasicValue;
    770  sStaticBasicValue = nullptr;
    771 
    772  return true;
    773 }
    774 
    775 static bool gFunctionWasApplied = false;
    776 
    777 static void IncrementTag(BasicValue& aValue) {
    778  gFunctionWasApplied = true;
    779  aValue.SetTag(aValue.GetTag() + 1);
    780 }
    781 
    782 static void AccessValue(const BasicValue&) { gFunctionWasApplied = true; }
    783 
    784 struct IncrementTagFunctor {
    785  IncrementTagFunctor() : mBy(1) {}
    786 
    787  void operator()(BasicValue& aValue) {
    788    aValue.SetTag(aValue.GetTag() + mBy.GetTag());
    789  }
    790 
    791  BasicValue mBy;
    792 };
    793 
    794 static bool TestApply() {
    795  // Check that apply handles the 'Nothing' case.
    796  gFunctionWasApplied = false;
    797  Maybe<BasicValue> mayValue;
    798  mayValue.apply(&IncrementTag);
    799  mayValue.apply(&AccessValue);
    800  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
    801 
    802  // Check that apply handles the 'Some' case.
    803  mayValue = Some(BasicValue(1));
    804  mayValue.apply(&IncrementTag);
    805  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
    806  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
    807  gFunctionWasApplied = false;
    808  mayValue.apply(&AccessValue);
    809  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
    810 
    811  // Check that apply works with a const reference.
    812  const Maybe<BasicValue>& mayValueCRef = mayValue;
    813  gFunctionWasApplied = false;
    814  mayValueCRef.apply(&AccessValue);
    815  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
    816 
    817  // Check that apply works with functors.
    818  IncrementTagFunctor tagIncrementer;
    819  MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
    820  mayValue = Some(BasicValue(1));
    821  mayValue.apply(tagIncrementer);
    822  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
    823  MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
    824 
    825  // Check that apply works with lambda expressions.
    826  int32_t two = 2;
    827  gFunctionWasApplied = false;
    828  mayValue = Some(BasicValue(2));
    829  mayValue.apply([&](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); });
    830  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 4);
    831  mayValue.apply([=](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); });
    832  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 8);
    833  mayValueCRef.apply(
    834      [&](const BasicValue& aVal) { gFunctionWasApplied = true; });
    835  MOZ_RELEASE_ASSERT(gFunctionWasApplied == true);
    836 
    837  // Check that apply can move the contained value.
    838  mayValue = Some(BasicValue(1));
    839  Maybe<BasicValue> otherValue;
    840  std::move(mayValue).apply(
    841      [&](BasicValue&& aVal) { otherValue = Some(std::move(aVal)); });
    842  MOZ_RELEASE_ASSERT(mayValue.isNothing());
    843  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
    844  MOZ_RELEASE_ASSERT(otherValue->GetStatus() == eWasMoveConstructed);
    845 
    846  return true;
    847 }
    848 
    849 static int TimesTwo(const BasicValue& aValue) { return aValue.GetTag() * 2; }
    850 
    851 static int TimesTwoAndResetOriginal(BasicValue& aValue) {
    852  int tag = aValue.GetTag();
    853  aValue.SetTag(1);
    854  return tag * 2;
    855 }
    856 
    857 struct MultiplyTagFunctor {
    858  MultiplyTagFunctor() : mBy(2) {}
    859 
    860  int operator()(BasicValue& aValue) { return aValue.GetTag() * mBy.GetTag(); }
    861 
    862  BasicValue mBy;
    863 };
    864 
    865 static bool TestMap() {
    866  // Check that map handles the 'Nothing' case.
    867  Maybe<BasicValue> mayValue;
    868  MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Nothing());
    869  static_assert(std::is_same_v<Maybe<int>, decltype(mayValue.map(&TimesTwo))>,
    870                "map(TimesTwo) should return a Maybe<int>");
    871  MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Nothing());
    872 
    873  // Check that map handles the 'Some' case.
    874  mayValue = Some(BasicValue(2));
    875  MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Some(4));
    876  MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Some(4));
    877  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
    878  mayValue = Some(BasicValue(2));
    879 
    880  // Check that map works with a const reference.
    881  mayValue->SetTag(2);
    882  const Maybe<BasicValue>& mayValueCRef = mayValue;
    883  MOZ_RELEASE_ASSERT(mayValueCRef.map(&TimesTwo) == Some(4));
    884  static_assert(
    885      std::is_same_v<Maybe<int>, decltype(mayValueCRef.map(&TimesTwo))>,
    886      "map(TimesTwo) should return a Maybe<int>");
    887 
    888  // Check that map works with functors.
    889  MultiplyTagFunctor tagMultiplier;
    890  MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed);
    891  MOZ_RELEASE_ASSERT(mayValue.map(tagMultiplier) == Some(4));
    892  MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed);
    893 
    894  // Check that map works with lambda expressions.
    895  int two = 2;
    896  mayValue = Some(BasicValue(2));
    897  Maybe<int> mappedValue =
    898      mayValue.map([&](const BasicValue& aVal) { return aVal.GetTag() * two; });
    899  MOZ_RELEASE_ASSERT(mappedValue == Some(4));
    900  mappedValue =
    901      mayValue.map([=](const BasicValue& aVal) { return aVal.GetTag() * two; });
    902  MOZ_RELEASE_ASSERT(mappedValue == Some(4));
    903  mappedValue = mayValueCRef.map(
    904      [&](const BasicValue& aVal) { return aVal.GetTag() * two; });
    905  MOZ_RELEASE_ASSERT(mappedValue == Some(4));
    906 
    907  // Check that map can move the contained value.
    908  mayValue = Some(BasicValue(1));
    909  Maybe<BasicValue> otherValue = std::move(mayValue).map(
    910      [](BasicValue&& aValue) { return std::move(aValue); });
    911  MOZ_RELEASE_ASSERT(mayValue.isNothing());
    912  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
    913  MOZ_RELEASE_ASSERT(otherValue->GetStatus() == eWasMoveConstructed);
    914 
    915  // Check that function object qualifiers are preserved when invoked.
    916  struct F {
    917    std::integral_constant<int, 1> operator()(int) & { return {}; }
    918    std::integral_constant<int, 2> operator()(int) const& { return {}; }
    919    std::integral_constant<int, 3> operator()(int) && { return {}; }
    920    std::integral_constant<int, 4> operator()(int) const&& { return {}; }
    921  };
    922  Maybe<int> mi = Some(0);
    923  const Maybe<int> cmi = Some(0);
    924  F f;
    925  static_assert(std::is_same<decltype(mi.map(f)),
    926                             Maybe<std::integral_constant<int, 1>>>::value,
    927                "Maybe.map(&)");
    928  MOZ_RELEASE_ASSERT(mi.map(f).value()() == 1);
    929  static_assert(std::is_same<decltype(cmi.map(f)),
    930                             Maybe<std::integral_constant<int, 1>>>::value,
    931                "const Maybe.map(&)");
    932  MOZ_RELEASE_ASSERT(cmi.map(f).value()() == 1);
    933  const F cf;
    934  static_assert(std::is_same<decltype(mi.map(cf)),
    935                             Maybe<std::integral_constant<int, 2>>>::value,
    936                "Maybe.map(const &)");
    937  MOZ_RELEASE_ASSERT(mi.map(cf).value() == 2);
    938  static_assert(std::is_same<decltype(cmi.map(cf)),
    939                             Maybe<std::integral_constant<int, 2>>>::value,
    940                "const Maybe.map(const &)");
    941  MOZ_RELEASE_ASSERT(cmi.map(cf).value() == 2);
    942  static_assert(std::is_same<decltype(mi.map(F{})),
    943                             Maybe<std::integral_constant<int, 3>>>::value,
    944                "Maybe.map(&&)");
    945  MOZ_RELEASE_ASSERT(mi.map(F{}).value() == 3);
    946  static_assert(std::is_same<decltype(cmi.map(F{})),
    947                             Maybe<std::integral_constant<int, 3>>>::value,
    948                "const Maybe.map(&&)");
    949  MOZ_RELEASE_ASSERT(cmi.map(F{}).value() == 3);
    950  using CF = const F;
    951  static_assert(std::is_same<decltype(mi.map(CF{})),
    952                             Maybe<std::integral_constant<int, 4>>>::value,
    953                "Maybe.map(const &&)");
    954  MOZ_RELEASE_ASSERT(mi.map(CF{}).value() == 4);
    955  static_assert(std::is_same<decltype(cmi.map(CF{})),
    956                             Maybe<std::integral_constant<int, 4>>>::value,
    957                "const Maybe.map(const &&)");
    958  MOZ_RELEASE_ASSERT(cmi.map(CF{}).value() == 4);
    959 
    960  return true;
    961 }
    962 
    963 static bool TestToMaybe() {
    964  BasicValue value(1);
    965  BasicValue* nullPointer = nullptr;
    966 
    967  // Check that a non-null pointer translates into a Some value.
    968  Maybe<BasicValue> mayValue = ToMaybe(&value);
    969  static_assert(std::is_same_v<Maybe<BasicValue>, decltype(ToMaybe(&value))>,
    970                "ToMaybe should return a Maybe<BasicValue>");
    971  MOZ_RELEASE_ASSERT(mayValue.isSome());
    972  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
    973  MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasCopyConstructed);
    974  MOZ_RELEASE_ASSERT(value.GetStatus() != eWasMovedFrom);
    975 
    976  // Check that a null pointer translates into a Nothing value.
    977  mayValue = ToMaybe(nullPointer);
    978  static_assert(
    979      std::is_same_v<Maybe<BasicValue>, decltype(ToMaybe(nullPointer))>,
    980      "ToMaybe should return a Maybe<BasicValue>");
    981  MOZ_RELEASE_ASSERT(mayValue.isNothing());
    982 
    983  return true;
    984 }
    985 
    986 static bool TestComparisonOperators() {
    987  Maybe<BasicValue> nothingValue = Nothing();
    988  Maybe<BasicValue> anotherNothingValue = Nothing();
    989  Maybe<BasicValue> oneValue = Some(BasicValue(1));
    990  Maybe<BasicValue> anotherOneValue = Some(BasicValue(1));
    991  Maybe<BasicValue> twoValue = Some(BasicValue(2));
    992 
    993  // Check equality.
    994  MOZ_RELEASE_ASSERT(nothingValue == anotherNothingValue);
    995  MOZ_RELEASE_ASSERT(oneValue == anotherOneValue);
    996 
    997  // Check inequality.
    998  MOZ_RELEASE_ASSERT(nothingValue != oneValue);
    999  MOZ_RELEASE_ASSERT(oneValue != nothingValue);
   1000  MOZ_RELEASE_ASSERT(oneValue != twoValue);
   1001 
   1002  // Check '<'.
   1003  MOZ_RELEASE_ASSERT(nothingValue < oneValue);
   1004  MOZ_RELEASE_ASSERT(oneValue < twoValue);
   1005 
   1006  // Check '<='.
   1007  MOZ_RELEASE_ASSERT(nothingValue <= anotherNothingValue);
   1008  MOZ_RELEASE_ASSERT(nothingValue <= oneValue);
   1009  MOZ_RELEASE_ASSERT(oneValue <= oneValue);
   1010  MOZ_RELEASE_ASSERT(oneValue <= twoValue);
   1011 
   1012  // Check '>'.
   1013  MOZ_RELEASE_ASSERT(oneValue > nothingValue);
   1014  MOZ_RELEASE_ASSERT(twoValue > oneValue);
   1015 
   1016  // Check '>='.
   1017  MOZ_RELEASE_ASSERT(nothingValue >= anotherNothingValue);
   1018  MOZ_RELEASE_ASSERT(oneValue >= nothingValue);
   1019  MOZ_RELEASE_ASSERT(oneValue >= oneValue);
   1020  MOZ_RELEASE_ASSERT(twoValue >= oneValue);
   1021 
   1022  return true;
   1023 }
   1024 
   1025 // Check that Maybe<> can wrap a superclass that happens to also be a concrete
   1026 // class (i.e. that the compiler doesn't warn when we invoke the superclass's
   1027 // destructor explicitly in |reset()|.
   1028 class MySuperClass {
   1029  virtual void VirtualMethod() { /* do nothing */ }
   1030 };
   1031 
   1032 class MyDerivedClass : public MySuperClass {
   1033  void VirtualMethod() override { /* do nothing */ }
   1034 };
   1035 
   1036 static bool TestVirtualFunction() {
   1037  Maybe<MySuperClass> super;
   1038  super.emplace();
   1039  super.reset();
   1040 
   1041  Maybe<MyDerivedClass> derived;
   1042  derived.emplace();
   1043  derived.reset();
   1044 
   1045  // If this compiles successfully, we've passed.
   1046  return true;
   1047 }
   1048 
   1049 static Maybe<int*> ReturnSomeNullptr() { return Some(nullptr); }
   1050 
   1051 struct D {
   1052  explicit D(const Maybe<int*>&) {}
   1053 };
   1054 
   1055 static bool TestSomeNullptrConversion() {
   1056  Maybe<int*> m1 = Some(nullptr);
   1057  MOZ_RELEASE_ASSERT(m1.isSome());
   1058  MOZ_RELEASE_ASSERT(m1);
   1059  MOZ_RELEASE_ASSERT(!*m1);
   1060 
   1061  auto m2 = ReturnSomeNullptr();
   1062  MOZ_RELEASE_ASSERT(m2.isSome());
   1063  MOZ_RELEASE_ASSERT(m2);
   1064  MOZ_RELEASE_ASSERT(!*m2);
   1065 
   1066  Maybe<decltype(nullptr)> m3 = Some(nullptr);
   1067  MOZ_RELEASE_ASSERT(m3.isSome());
   1068  MOZ_RELEASE_ASSERT(m3);
   1069  MOZ_RELEASE_ASSERT(*m3 == nullptr);
   1070 
   1071  D d(Some(nullptr));
   1072 
   1073  return true;
   1074 }
   1075 
   1076 struct Base {};
   1077 struct Derived : Base {};
   1078 
   1079 static Maybe<Base*> ReturnDerivedPointer() {
   1080  Derived* d = nullptr;
   1081  return Some(d);
   1082 }
   1083 
   1084 struct ExplicitConstructorBasePointer {
   1085  explicit ExplicitConstructorBasePointer(const Maybe<Base*>&) {}
   1086 };
   1087 
   1088 static bool TestSomePointerConversion() {
   1089  Base base;
   1090  Derived derived;
   1091 
   1092  Maybe<Base*> m1 = Some(&derived);
   1093  MOZ_RELEASE_ASSERT(m1.isSome());
   1094  MOZ_RELEASE_ASSERT(m1);
   1095  MOZ_RELEASE_ASSERT(*m1 == &derived);
   1096 
   1097  auto m2 = ReturnDerivedPointer();
   1098  MOZ_RELEASE_ASSERT(m2.isSome());
   1099  MOZ_RELEASE_ASSERT(m2);
   1100  MOZ_RELEASE_ASSERT(*m2 == nullptr);
   1101 
   1102  Maybe<Base*> m3 = Some(&base);
   1103  MOZ_RELEASE_ASSERT(m3.isSome());
   1104  MOZ_RELEASE_ASSERT(m3);
   1105  MOZ_RELEASE_ASSERT(*m3 == &base);
   1106 
   1107  auto s1 = Some(&derived);
   1108  Maybe<Base*> c1(s1);
   1109  MOZ_RELEASE_ASSERT(c1.isSome());
   1110  MOZ_RELEASE_ASSERT(c1);
   1111  MOZ_RELEASE_ASSERT(*c1 == &derived);
   1112 
   1113  ExplicitConstructorBasePointer ecbp(Some(&derived));
   1114 
   1115  return true;
   1116 }
   1117 
   1118 struct SourceType1 {
   1119  int mTag;
   1120 
   1121  operator int() const { return mTag; }
   1122 };
   1123 struct DestType {
   1124  int mTag;
   1125  Status mStatus;
   1126 
   1127  DestType() : mTag(0), mStatus(eWasDefaultConstructed) {}
   1128 
   1129  MOZ_IMPLICIT DestType(int aTag) : mTag(aTag), mStatus(eWasConstructed) {}
   1130 
   1131  MOZ_IMPLICIT DestType(SourceType1&& aSrc)
   1132      : mTag(aSrc.mTag), mStatus(eWasMoveConstructed) {}
   1133 
   1134  MOZ_IMPLICIT DestType(const SourceType1& aSrc)
   1135      : mTag(aSrc.mTag), mStatus(eWasCopyConstructed) {}
   1136 
   1137  DestType& operator=(int aTag) {
   1138    mTag = aTag;
   1139    mStatus = eWasAssigned;
   1140    return *this;
   1141  }
   1142 
   1143  DestType& operator=(SourceType1&& aSrc) {
   1144    mTag = aSrc.mTag;
   1145    mStatus = eWasMoveAssigned;
   1146    return *this;
   1147  }
   1148 
   1149  DestType& operator=(const SourceType1& aSrc) {
   1150    mTag = aSrc.mTag;
   1151    mStatus = eWasCopyAssigned;
   1152    return *this;
   1153  }
   1154 };
   1155 struct SourceType2 {
   1156  int mTag;
   1157 
   1158  operator DestType() const& {
   1159    DestType result;
   1160    result.mTag = mTag;
   1161    result.mStatus = eWasCopiedFrom;
   1162    return result;
   1163  }
   1164 
   1165  operator DestType() && {
   1166    DestType result;
   1167    result.mTag = mTag;
   1168    result.mStatus = eWasMovedFrom;
   1169    return result;
   1170  }
   1171 };
   1172 
   1173 static bool TestTypeConversion() {
   1174  {
   1175    Maybe<SourceType1> src = Some(SourceType1{1});
   1176    Maybe<DestType> dest = src;
   1177    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
   1178    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
   1179    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopyConstructed);
   1180 
   1181    src = Some(SourceType1{2});
   1182    dest = src;
   1183    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
   1184    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
   1185    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopyAssigned);
   1186  }
   1187 
   1188  {
   1189    Maybe<SourceType1> src = Some(SourceType1{1});
   1190    Maybe<DestType> dest = std::move(src);
   1191    MOZ_RELEASE_ASSERT(src.isNothing());
   1192    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
   1193    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMoveConstructed);
   1194 
   1195    src = Some(SourceType1{2});
   1196    dest = std::move(src);
   1197    MOZ_RELEASE_ASSERT(src.isNothing());
   1198    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
   1199    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMoveAssigned);
   1200  }
   1201 
   1202  {
   1203    Maybe<SourceType2> src = Some(SourceType2{1});
   1204    Maybe<DestType> dest = src;
   1205    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
   1206    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
   1207    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopiedFrom);
   1208 
   1209    src = Some(SourceType2{2});
   1210    dest = src;
   1211    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
   1212    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
   1213    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopiedFrom);
   1214  }
   1215 
   1216  {
   1217    Maybe<SourceType2> src = Some(SourceType2{1});
   1218    Maybe<DestType> dest = std::move(src);
   1219    MOZ_RELEASE_ASSERT(src.isNothing());
   1220    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
   1221    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMovedFrom);
   1222 
   1223    src = Some(SourceType2{2});
   1224    dest = std::move(src);
   1225    MOZ_RELEASE_ASSERT(src.isNothing());
   1226    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
   1227    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMovedFrom);
   1228  }
   1229 
   1230  {
   1231    Maybe<int> src = Some(1);
   1232    Maybe<DestType> dest = src;
   1233    MOZ_RELEASE_ASSERT(src.isSome() && *src == 1);
   1234    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
   1235    MOZ_RELEASE_ASSERT(dest->mStatus == eWasConstructed);
   1236 
   1237    src = Some(2);
   1238    dest = src;
   1239    MOZ_RELEASE_ASSERT(src.isSome() && *src == 2);
   1240    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
   1241    MOZ_RELEASE_ASSERT(dest->mStatus == eWasAssigned);
   1242  }
   1243 
   1244  {
   1245    Maybe<int> src = Some(1);
   1246    Maybe<DestType> dest = std::move(src);
   1247    MOZ_RELEASE_ASSERT(src.isNothing());
   1248    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
   1249    MOZ_RELEASE_ASSERT(dest->mStatus == eWasConstructed);
   1250 
   1251    src = Some(2);
   1252    dest = std::move(src);
   1253    MOZ_RELEASE_ASSERT(src.isNothing());
   1254    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
   1255    MOZ_RELEASE_ASSERT(dest->mStatus == eWasAssigned);
   1256  }
   1257 
   1258  {
   1259    Maybe<SourceType1> src = Some(SourceType1{1});
   1260    Maybe<int> dest = src;
   1261    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
   1262    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
   1263 
   1264    src = Some(SourceType1{2});
   1265    dest = src;
   1266    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
   1267    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
   1268  }
   1269 
   1270  {
   1271    Maybe<SourceType1> src = Some(SourceType1{1});
   1272    Maybe<int> dest = std::move(src);
   1273    MOZ_RELEASE_ASSERT(src.isNothing());
   1274    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
   1275 
   1276    src = Some(SourceType1{2});
   1277    dest = std::move(src);
   1278    MOZ_RELEASE_ASSERT(src.isNothing());
   1279    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
   1280  }
   1281 
   1282  {
   1283    Maybe<size_t> src = Some(1);
   1284    Maybe<char16_t> dest = src;
   1285    MOZ_RELEASE_ASSERT(src.isSome() && *src == 1);
   1286    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
   1287 
   1288    src = Some(2);
   1289    dest = src;
   1290    MOZ_RELEASE_ASSERT(src.isSome() && *src == 2);
   1291    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
   1292  }
   1293 
   1294  {
   1295    Maybe<size_t> src = Some(1);
   1296    Maybe<char16_t> dest = std::move(src);
   1297    MOZ_RELEASE_ASSERT(src.isNothing());
   1298    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);
   1299 
   1300    src = Some(2);
   1301    dest = std::move(src);
   1302    MOZ_RELEASE_ASSERT(src.isNothing());
   1303    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
   1304  }
   1305 
   1306  return true;
   1307 }
   1308 
   1309 static bool TestReference() {
   1310  static_assert(std::is_trivially_destructible_v<Maybe<int&>>);
   1311  static_assert(std::is_trivially_copy_constructible_v<Maybe<int&>>);
   1312  static_assert(std::is_trivially_copy_assignable_v<Maybe<int&>>);
   1313 
   1314  static_assert(Maybe<int&>{}.isNothing());
   1315  static_assert(Maybe<int&>{Nothing{}}.isNothing());
   1316 
   1317  {
   1318    Maybe<int&> defaultConstructed;
   1319 
   1320    MOZ_RELEASE_ASSERT(defaultConstructed.isNothing());
   1321    MOZ_RELEASE_ASSERT(!defaultConstructed.isSome());
   1322    MOZ_RELEASE_ASSERT(!defaultConstructed);
   1323  }
   1324 
   1325  {
   1326    Maybe<int&> nothing = Nothing();
   1327 
   1328    MOZ_RELEASE_ASSERT(nothing.isNothing());
   1329    MOZ_RELEASE_ASSERT(!nothing.isSome());
   1330    MOZ_RELEASE_ASSERT(!nothing);
   1331  }
   1332 
   1333  {
   1334    int foo = 42, bar = 42;
   1335    Maybe<int&> some = SomeRef(foo);
   1336 
   1337    MOZ_RELEASE_ASSERT(!some.isNothing());
   1338    MOZ_RELEASE_ASSERT(some.isSome());
   1339    MOZ_RELEASE_ASSERT(some);
   1340    MOZ_RELEASE_ASSERT(&some.ref() == &foo);
   1341 
   1342    MOZ_RELEASE_ASSERT(some.refEquals(foo));
   1343    MOZ_RELEASE_ASSERT(some.refEquals(SomeRef(foo)));
   1344    MOZ_RELEASE_ASSERT(!some.refEquals(Nothing()));
   1345    MOZ_RELEASE_ASSERT(!some.refEquals(bar));
   1346    MOZ_RELEASE_ASSERT(!some.refEquals(SomeRef(bar)));
   1347 
   1348    some.ref()++;
   1349    MOZ_RELEASE_ASSERT(43 == foo);
   1350 
   1351    (*some)++;
   1352    MOZ_RELEASE_ASSERT(44 == foo);
   1353  }
   1354 
   1355  {
   1356    int foo = 42, bar = 42;
   1357    Maybe<int&> some;
   1358    some.emplace(foo);
   1359 
   1360    MOZ_RELEASE_ASSERT(!some.isNothing());
   1361    MOZ_RELEASE_ASSERT(some.isSome());
   1362    MOZ_RELEASE_ASSERT(some);
   1363    MOZ_RELEASE_ASSERT(&some.ref() == &foo);
   1364 
   1365    MOZ_RELEASE_ASSERT(some.refEquals(foo));
   1366    MOZ_RELEASE_ASSERT(some.refEquals(SomeRef(foo)));
   1367    MOZ_RELEASE_ASSERT(!some.refEquals(Nothing()));
   1368    MOZ_RELEASE_ASSERT(!some.refEquals(bar));
   1369    MOZ_RELEASE_ASSERT(!some.refEquals(SomeRef(bar)));
   1370 
   1371    some.ref()++;
   1372    MOZ_RELEASE_ASSERT(43 == foo);
   1373  }
   1374 
   1375  {
   1376    Maybe<int&> defaultConstructed;
   1377    defaultConstructed.reset();
   1378 
   1379    MOZ_RELEASE_ASSERT(defaultConstructed.isNothing());
   1380    MOZ_RELEASE_ASSERT(!defaultConstructed.isSome());
   1381    MOZ_RELEASE_ASSERT(!defaultConstructed);
   1382  }
   1383 
   1384  {
   1385    int foo = 42;
   1386    Maybe<int&> some = SomeRef(foo);
   1387    some.reset();
   1388 
   1389    MOZ_RELEASE_ASSERT(some.isNothing());
   1390    MOZ_RELEASE_ASSERT(!some.isSome());
   1391    MOZ_RELEASE_ASSERT(!some);
   1392  }
   1393 
   1394  {
   1395    int foo = 42;
   1396    Maybe<int&> some = SomeRef(foo);
   1397 
   1398    auto& applied = some.apply([](int& ref) { ref++; });
   1399 
   1400    MOZ_RELEASE_ASSERT(&some == &applied);
   1401    MOZ_RELEASE_ASSERT(43 == foo);
   1402  }
   1403 
   1404  {
   1405    Maybe<int&> nothing;
   1406 
   1407    auto& applied = nothing.apply([](int& ref) { ref++; });
   1408 
   1409    MOZ_RELEASE_ASSERT(&nothing == &applied);
   1410  }
   1411 
   1412  {
   1413    int foo = 42;
   1414    Maybe<int&> some = SomeRef(foo);
   1415 
   1416    auto mapped = some.map([](int& ref) { return &ref; });
   1417    static_assert(std::is_same_v<decltype(mapped), Maybe<int*>>);
   1418 
   1419    MOZ_RELEASE_ASSERT(&foo == *mapped);
   1420  }
   1421 
   1422  {
   1423    Maybe<int&> nothing;
   1424 
   1425    auto mapped = nothing.map([](int& ref) { return &ref; });
   1426 
   1427    MOZ_RELEASE_ASSERT(mapped.isNothing());
   1428    MOZ_RELEASE_ASSERT(!mapped.isSome());
   1429    MOZ_RELEASE_ASSERT(!mapped);
   1430  }
   1431 
   1432  {
   1433    int foo = 42;
   1434    auto someRef = ToMaybeRef(&foo);
   1435 
   1436    static_assert(std::is_same_v<decltype(someRef), Maybe<int&>>);
   1437 
   1438    MOZ_RELEASE_ASSERT(someRef.isSome());
   1439    MOZ_RELEASE_ASSERT(&foo == &someRef.ref());
   1440  }
   1441 
   1442  {
   1443    int* fooPtr = nullptr;
   1444    auto someRef = ToMaybeRef(fooPtr);
   1445 
   1446    static_assert(std::is_same_v<decltype(someRef), Maybe<int&>>);
   1447 
   1448    MOZ_RELEASE_ASSERT(someRef.isNothing());
   1449  }
   1450 
   1451  return true;
   1452 }
   1453 
   1454 static Maybe<int> IncrementAndReturnTag(BasicValue& aValue) {
   1455  gFunctionWasApplied = true;
   1456  aValue.SetTag(aValue.GetTag() + 1);
   1457  return Some(aValue.GetTag());
   1458 }
   1459 
   1460 static Maybe<int> AccessValueAndReturnNothing(const BasicValue&) {
   1461  gFunctionWasApplied = true;
   1462  return Nothing();
   1463 }
   1464 
   1465 static Maybe<int> AccessValueAndReturnOther(const BasicValue&) {
   1466  gFunctionWasApplied = true;
   1467  return Some(42);
   1468 }
   1469 
   1470 struct IncrementAndReturnTagFunctor {
   1471  IncrementAndReturnTagFunctor() : mBy(1) {}
   1472 
   1473  Maybe<int> operator()(BasicValue& aValue) {
   1474    aValue.SetTag(aValue.GetTag() + mBy.GetTag());
   1475    return Some(aValue.GetTag());
   1476  }
   1477 
   1478  BasicValue mBy;
   1479 };
   1480 
   1481 struct AccessValueAndReturnOtherFunctor {
   1482  explicit AccessValueAndReturnOtherFunctor(int aVal) : mBy(aVal) {}
   1483 
   1484  Maybe<BasicValue> operator()() {
   1485    gFunctionWasApplied = true;
   1486    return Some(mBy);
   1487  }
   1488 
   1489  BasicValue mBy;
   1490 };
   1491 
   1492 static bool TestAndThen() {
   1493  // Check that andThen handles the 'Nothing' case.
   1494  gFunctionWasApplied = false;
   1495  Maybe<BasicValue> mayValue;
   1496  Maybe<int> otherValue = mayValue.andThen(&AccessValueAndReturnOther);
   1497  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
   1498  MOZ_RELEASE_ASSERT(otherValue.isNothing());
   1499 
   1500  // Check that andThen handles the 'Some' case.
   1501  mayValue = Some(BasicValue(1));
   1502  otherValue = mayValue.andThen(&AccessValueAndReturnNothing);
   1503  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
   1504  MOZ_RELEASE_ASSERT(otherValue.isNothing());
   1505  gFunctionWasApplied = false;
   1506  otherValue = mayValue.andThen(&IncrementAndReturnTag);
   1507  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
   1508  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
   1509  MOZ_RELEASE_ASSERT(*otherValue == 2);
   1510  gFunctionWasApplied = false;
   1511  otherValue = mayValue.andThen(&AccessValueAndReturnOther);
   1512  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
   1513  MOZ_RELEASE_ASSERT(*otherValue == 42);
   1514 
   1515  // Check that andThen works with a const reference.
   1516  const Maybe<BasicValue>& mayValueCRef = mayValue;
   1517  gFunctionWasApplied = false;
   1518  otherValue = mayValueCRef.andThen(&AccessValueAndReturnOther);
   1519  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
   1520  MOZ_RELEASE_ASSERT(*otherValue == 42);
   1521 
   1522  // Check that andThen works with functors.
   1523  IncrementAndReturnTagFunctor tagIncrementer;
   1524  MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
   1525  mayValue = Some(BasicValue(1));
   1526  otherValue = mayValue.andThen(tagIncrementer);
   1527  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
   1528  MOZ_RELEASE_ASSERT(*otherValue == 2);
   1529  MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
   1530 
   1531  // Check that andThen works with lambda expressions.
   1532  gFunctionWasApplied = false;
   1533  mayValue = Some(BasicValue(2));
   1534  otherValue = mayValue.andThen(
   1535      [](BasicValue& aVal) { return Some(aVal.GetTag() * 2); });
   1536  MOZ_RELEASE_ASSERT(*otherValue == 4);
   1537  otherValue = otherValue.andThen([](int aVal) { return Some(aVal * 2); });
   1538  MOZ_RELEASE_ASSERT(*otherValue == 8);
   1539  otherValue = mayValueCRef.andThen([&](const BasicValue& aVal) {
   1540    gFunctionWasApplied = true;
   1541    return Some(42);
   1542  });
   1543  MOZ_RELEASE_ASSERT(gFunctionWasApplied == true);
   1544  MOZ_RELEASE_ASSERT(*otherValue == 42);
   1545 
   1546  // Check that andThen can move the contained value.
   1547  mayValue = Some(BasicValue(1));
   1548  otherValue = std::move(mayValue).andThen([&](BasicValue&& aVal) {
   1549    BasicValue tmp = std::move(aVal);
   1550    return Some(tmp.GetTag());
   1551  });
   1552  MOZ_RELEASE_ASSERT(mayValue.isNothing());
   1553  MOZ_RELEASE_ASSERT(*otherValue == 1);
   1554 
   1555  return true;
   1556 }
   1557 
   1558 static bool TestOrElse() {
   1559  const auto createValue = [&](int aTag) {
   1560    return [&, aTag]() -> Maybe<BasicValue> {
   1561      gFunctionWasApplied = true;
   1562      return Some(BasicValue(aTag));
   1563    };
   1564  };
   1565  // Check that orElse handles the 'Some' case.
   1566  gFunctionWasApplied = false;
   1567  Maybe<BasicValue> mayValue = Some(BasicValue(1));
   1568  Maybe<BasicValue> otherValue = mayValue.orElse(createValue(2));
   1569  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
   1570  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
   1571 
   1572  // Check that orElse handles the 'Nothing' case.
   1573  mayValue = Nothing();
   1574  otherValue = mayValue.orElse(createValue(1));
   1575  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
   1576  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
   1577  gFunctionWasApplied = false;
   1578  otherValue = otherValue.orElse(createValue(2));
   1579  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
   1580  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
   1581 
   1582  // Check that orElse works with a const reference.
   1583  mayValue = Nothing();
   1584  const Maybe<BasicValue>& mayValueCRef = mayValue;
   1585  gFunctionWasApplied = false;
   1586  otherValue = mayValueCRef.orElse(createValue(1));
   1587  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
   1588  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
   1589 
   1590  // Check that orElse works with functors.
   1591  gFunctionWasApplied = false;
   1592  AccessValueAndReturnOtherFunctor accesser(42);
   1593  mayValue = Some(BasicValue(1));
   1594  otherValue = mayValue.orElse(accesser);
   1595  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
   1596  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
   1597  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
   1598  mayValue = Nothing();
   1599  otherValue = mayValue.orElse(accesser);
   1600  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
   1601  MOZ_RELEASE_ASSERT(mayValue.isNothing());
   1602  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 42);
   1603 
   1604  // Check that orElse works with lambda expressions.
   1605  gFunctionWasApplied = false;
   1606  mayValue = Nothing();
   1607  otherValue = mayValue.orElse([] { return Some(BasicValue(1)); });
   1608  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
   1609  mayValue = otherValue.orElse([] { return Some(BasicValue(2)); });
   1610  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
   1611  otherValue = mayValueCRef.orElse([&] {
   1612    gFunctionWasApplied = true;
   1613    return Some(BasicValue(42));
   1614  });
   1615  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
   1616  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
   1617 
   1618  // Check that orElse can move the contained value.
   1619  mayValue = Some(BasicValue(1));
   1620  otherValue = std::move(mayValue).orElse([] { return Some(BasicValue(2)); });
   1621  MOZ_RELEASE_ASSERT(mayValue.isNothing());
   1622  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
   1623 
   1624  return true;
   1625 }
   1626 
   1627 static bool TestComposedMoves() {
   1628  const auto moveAlong = [](UncopyableValue&& aValue) {
   1629    return Some(std::move(aValue));
   1630  };
   1631  const auto justNothing = []() -> Maybe<UncopyableValue> { return Nothing(); };
   1632  const auto createValue = [] { return Some(UncopyableValue()); };
   1633 
   1634  // Check that andThen and orElse can propagate a non-copyable value created
   1635  // mid-chain.
   1636  Maybe<UncopyableValue> mayValue;
   1637  Maybe<UncopyableValue> movedValue = std::move(mayValue)
   1638                                          .andThen(moveAlong)
   1639                                          .orElse(createValue)
   1640                                          .andThen(moveAlong);
   1641  MOZ_RELEASE_ASSERT(mayValue.isNothing());
   1642  MOZ_RELEASE_ASSERT(movedValue->GetStatus() == eWasMoveConstructed);
   1643 
   1644  // Check that andThen and orElse can propagate a non-copyable value created
   1645  // pre-chain.
   1646  mayValue = Some(UncopyableValue{});
   1647  movedValue = std::move(mayValue)
   1648                   .andThen(moveAlong)
   1649                   .orElse(justNothing)
   1650                   .andThen(moveAlong);
   1651  MOZ_RELEASE_ASSERT(mayValue.isNothing());
   1652  MOZ_RELEASE_ASSERT(movedValue->GetStatus() == eWasMoveAssigned);
   1653 
   1654  // Check that andThen and orElse can propagate a reference.
   1655  {
   1656    UncopyableValue val{};
   1657    UncopyableValue otherVal{};
   1658    const auto passAlong = [](UncopyableValue& aValue) {
   1659      return SomeRef(aValue);
   1660    };
   1661    const auto fallbackToOther = [&]() { return SomeRef(otherVal); };
   1662 
   1663    Maybe<UncopyableValue&> mayRef = SomeRef(val);
   1664    Maybe<UncopyableValue&> chainedRef =
   1665        mayRef.andThen(passAlong).orElse(fallbackToOther).andThen(passAlong);
   1666    MOZ_RELEASE_ASSERT(&val != &otherVal,
   1667                       "Distinct values should not compare equal");
   1668    MOZ_RELEASE_ASSERT(&*mayRef == &*chainedRef,
   1669                       "Chain should pass along the same reference");
   1670  }
   1671 
   1672  // Check that andThen and orElse can propagate a const reference.
   1673  {
   1674    const UncopyableValue val{};
   1675    const UncopyableValue otherVal{};
   1676    const auto passAlong = [](const UncopyableValue& aValue) {
   1677      return SomeRef(aValue);
   1678    };
   1679    const auto fallbackToOther = [&]() { return SomeRef(otherVal); };
   1680 
   1681    Maybe<const UncopyableValue&> mayRef = SomeRef(val);
   1682    Maybe<const UncopyableValue&> chainedRef =
   1683        mayRef.andThen(passAlong).orElse(fallbackToOther).andThen(passAlong);
   1684    MOZ_RELEASE_ASSERT(&val != &otherVal,
   1685                       "Distinct values should not compare equal");
   1686    MOZ_RELEASE_ASSERT(&*mayRef == &*chainedRef,
   1687                       "Chain should pass along the same reference");
   1688  }
   1689 
   1690  return true;
   1691 }
   1692 
   1693 static bool TestBeginEnd() {
   1694  for ([[maybe_unused]] auto v : static_cast<Maybe<int>>(Nothing())) {
   1695    MOZ_CRASH("Nothing should not be iterated");
   1696  }
   1697 
   1698  for (auto v : Some(42)) {
   1699    MOZ_RELEASE_ASSERT(v == 42, "Some(42) should be iterated");
   1700  }
   1701 
   1702  Maybe<int> maybe;
   1703  for ([[maybe_unused]] auto v : maybe) {
   1704    MOZ_CRASH("Nothing should not be iterated");
   1705  }
   1706 
   1707  maybe = Some(42);
   1708 
   1709  for (auto v : maybe) {
   1710    MOZ_RELEASE_ASSERT(v == 42, "Some(42) should be iterated");
   1711  }
   1712 
   1713  maybe = Nothing();
   1714 
   1715  for ([[maybe_unused]] auto v : maybe) {
   1716    MOZ_CRASH("Nothing should not be iterated");
   1717  }
   1718 
   1719  int v = 42;
   1720  Maybe<int*> maybePtr;
   1721  for ([[maybe_unused]] auto* v : maybePtr) {
   1722    MOZ_CRASH("Nothing should not be iterated");
   1723  }
   1724 
   1725  maybePtr = Some(&v);
   1726 
   1727  for (auto* v : maybePtr) {
   1728    MOZ_RELEASE_ASSERT(*v == 42, "Some(address) should be iterated");
   1729  }
   1730 
   1731  maybePtr = Nothing();
   1732  for ([[maybe_unused]] auto* v : maybePtr) {
   1733    MOZ_CRASH("Nothing should not be iterated");
   1734  }
   1735 
   1736  return true;
   1737 }
   1738 
   1739 // These are quasi-implementation details, but we assert them here to prevent
   1740 // backsliding to earlier times when Maybe<T> for smaller T took up more space
   1741 // than T's alignment required.
   1742 
   1743 static_assert(sizeof(Maybe<char>) == 2 * sizeof(char),
   1744              "Maybe<char> shouldn't bloat at all ");
   1745 static_assert(sizeof(Maybe<bool>) <= 2 * sizeof(bool),
   1746              "Maybe<bool> shouldn't bloat");
   1747 static_assert(sizeof(Maybe<int>) <= 2 * sizeof(int),
   1748              "Maybe<int> shouldn't bloat");
   1749 static_assert(sizeof(Maybe<long>) <= 2 * sizeof(long),
   1750              "Maybe<long> shouldn't bloat");
   1751 static_assert(sizeof(Maybe<double>) <= 2 * sizeof(double),
   1752              "Maybe<double> shouldn't bloat");
   1753 static_assert(sizeof(Maybe<int&>) == sizeof(int*));
   1754 
   1755 int main() {
   1756  RUN_TEST(TestBasicFeatures);
   1757  RUN_TEST(TestCopyAndMove);
   1758  RUN_TEST(TestFunctionalAccessors);
   1759  RUN_TEST(TestApply);
   1760  RUN_TEST(TestMap);
   1761  RUN_TEST(TestToMaybe);
   1762  RUN_TEST(TestComparisonOperators);
   1763  RUN_TEST(TestVirtualFunction);
   1764  RUN_TEST(TestSomeNullptrConversion);
   1765  RUN_TEST(TestSomePointerConversion);
   1766  RUN_TEST(TestTypeConversion);
   1767  RUN_TEST(TestReference);
   1768  RUN_TEST(TestAndThen);
   1769  RUN_TEST(TestOrElse);
   1770  RUN_TEST(TestComposedMoves);
   1771  RUN_TEST(TestBeginEnd);
   1772 
   1773  return 0;
   1774 }