Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/mfbt/tests/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 58 kB image not shown  

Quelle  TestMaybe.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


#include <type_traits>
#include <utility>

#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"

using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::Some;
using mozilla::SomeRef;
using mozilla::ToMaybe;
using mozilla::ToMaybeRef;

#define RUN_TEST(t)                                                           \
  do {                                                                        \
    bool cond = (t());                                                        \
    MOZ_RELEASE_ASSERT(cond, "Unexpectedly returned false during test: " #t); \
    cond = AllDestructorsWereCalled();                                        \
    MOZ_RELEASE_ASSERT(cond,                                                  \
                       "Failed to destroy all objects during test: " #t);     \
  } while (false)

enum Status {
  eWasDefaultConstructed,
  eWasConstructed,
  eWasCopyConstructed,
  eWasMoveConstructed,
  eWasConstMoveConstructed,
  eWasAssigned,
  eWasCopyAssigned,
  eWasMoveAssigned,
  eWasCopiedFrom,
  eWasMovedFrom,
  eWasConstMovedFrom,
};

static size_t sUndestroyedObjects = 0;

static bool AllDestructorsWereCalled() { return sUndestroyedObjects == 0; }

struct BasicValue {
  BasicValue() : mStatus(eWasDefaultConstructed), mTag(0) {
    ++sUndestroyedObjects;
  }

  explicit BasicValue(int aTag) : mStatus(eWasConstructed), mTag(aTag) {
    ++sUndestroyedObjects;
  }

  BasicValue(const BasicValue& aOther)
      : mStatus(eWasCopyConstructed), mTag(aOther.mTag) {
    ++sUndestroyedObjects;
  }

  BasicValue(BasicValue&& aOther)
      : mStatus(eWasMoveConstructed), mTag(aOther.mTag) {
    ++sUndestroyedObjects;
    aOther.mStatus = eWasMovedFrom;
    aOther.mTag = 0;
  }

  BasicValue(const BasicValue&& aOther)
      : mStatus(eWasConstMoveConstructed), mTag(aOther.mTag) {
    ++sUndestroyedObjects;
    aOther.mStatus = eWasConstMovedFrom;
  }

  ~BasicValue() { --sUndestroyedObjects; }

  BasicValue& operator=(const BasicValue& aOther) {
    mStatus = eWasCopyAssigned;
    mTag = aOther.mTag;
    return *this;
  }

  BasicValue& operator=(BasicValue&& aOther) {
    mStatus = eWasMoveAssigned;
    mTag = aOther.mTag;
    aOther.mStatus = eWasMovedFrom;
    aOther.mTag = 0;
    return *this;
  }

  bool operator==(const BasicValue& aOther) const {
    return mTag == aOther.mTag;
  }

  bool operator<(const BasicValue& aOther) const { return mTag < aOther.mTag; }

  Status GetStatus() const { return mStatus; }
  void SetTag(int aValue) { mTag = aValue; }
  int GetTag() const { return mTag; }

 private:
  mutable Status mStatus;
  int mTag;
};

struct UncopyableValue {
  UncopyableValue() : mStatus(eWasDefaultConstructed) { ++sUndestroyedObjects; }

  UncopyableValue(UncopyableValue&& aOther) : mStatus(eWasMoveConstructed) {
    ++sUndestroyedObjects;
    aOther.mStatus = eWasMovedFrom;
  }

  ~UncopyableValue() { --sUndestroyedObjects; }

  UncopyableValue& operator=(UncopyableValue&& aOther) {
    mStatus = eWasMoveAssigned;
    aOther.mStatus = eWasMovedFrom;
    return *this;
  }

  Status GetStatus() { return mStatus; }

 private:
  UncopyableValue(const UncopyableValue& aOther) = delete;
  UncopyableValue& operator=(const UncopyableValue& aOther) = delete;

  Status mStatus;
};

struct UnmovableValue {
  UnmovableValue() : mStatus(eWasDefaultConstructed) { ++sUndestroyedObjects; }

  UnmovableValue(const UnmovableValue& aOther) : mStatus(eWasCopyConstructed) {
    ++sUndestroyedObjects;
  }

  ~UnmovableValue() { --sUndestroyedObjects; }

  UnmovableValue& operator=(const UnmovableValue& aOther) {
    mStatus = eWasCopyAssigned;
    return *this;
  }

  Status GetStatus() { return mStatus; }

  UnmovableValue(UnmovableValue&& aOther) = delete;
  UnmovableValue& operator=(UnmovableValue&& aOther) = delete;

 private:
  Status mStatus;
};

struct UncopyableUnmovableValue {
  UncopyableUnmovableValue() : mStatus(eWasDefaultConstructed) {
    ++sUndestroyedObjects;
  }

  explicit UncopyableUnmovableValue(int) : mStatus(eWasConstructed) {
    ++sUndestroyedObjects;
  }

  ~UncopyableUnmovableValue() { --sUndestroyedObjects; }

  Status GetStatus() const { return mStatus; }

 private:
  UncopyableUnmovableValue(const UncopyableUnmovableValue& aOther) = delete;
  UncopyableUnmovableValue& operator=(const UncopyableUnmovableValue& aOther) =
      delete;
  UncopyableUnmovableValue(UncopyableUnmovableValue&& aOther) = delete;
  UncopyableUnmovableValue& operator=(UncopyableUnmovableValue&& aOther) =
      delete;

  Status mStatus;
};

static_assert(std::is_trivially_destructible_v<Maybe<int>>);
static_assert(std::is_trivially_copy_constructible_v<Maybe<int>>);
static_assert(std::is_trivially_copy_assignable_v<Maybe<int>>);

static_assert(42 == Some(42).value());
static_assert(42 == Some(42).valueOr(43));
static_assert(42 == Maybe<int>{}.valueOr(42));
static_assert(42 == Some(42).valueOrFrom([] { return 43; }));
static_assert(42 == Maybe<int>{}.valueOrFrom([] { return 42; }));
static_assert(Some(43) == [] {
  auto val = Some(42);
  val.apply([](int& val) { val += 1; });
  return val;
}());
static_assert(Some(43) == Some(42).map([](int val) { return val + 1; }));
static_assert(Maybe<int>(std::in_place, 43) ==
              Maybe<int>(std::in_place, 42).map([](int val) {
                return val + 1;
              }));

struct TriviallyDestructible {
  TriviallyDestructible() {  // not trivially constructible
  }
};

static_assert(std::is_trivially_destructible_v<Maybe<TriviallyDestructible>>);

struct UncopyableValueLiteralType {
  explicit constexpr UncopyableValueLiteralType(int aValue) : mValue{aValue} {}

  UncopyableValueLiteralType(UncopyableValueLiteralType&&) = default;
  UncopyableValueLiteralType& operator=(UncopyableValueLiteralType&&) = default;

  int mValue;
};

static_assert(
    std::is_trivially_destructible_v<Maybe<UncopyableValueLiteralType>>);
static_assert(!std::is_copy_constructible_v<Maybe<UncopyableValueLiteralType>>);
static_assert(!std::is_copy_assignable_v<Maybe<UncopyableValueLiteralType>>);
static_assert(std::is_move_constructible_v<Maybe<UncopyableValueLiteralType>>);
static_assert(std::is_move_assignable_v<Maybe<UncopyableValueLiteralType>>);

constexpr Maybe<UncopyableValueLiteralType> someUncopyable =
    Some(UncopyableValueLiteralType{42});
static_assert(someUncopyable.isSome());
static_assert(42 == someUncopyable->mValue);

constexpr Maybe<UncopyableValueLiteralType> someUncopyableAssigned = [] {
  auto res = Maybe<UncopyableValueLiteralType>{};
  res = Some(UncopyableValueLiteralType{42});
  return res;
}();
static_assert(someUncopyableAssigned.isSome());
static_assert(42 == someUncopyableAssigned->mValue);

static bool TestBasicFeatures() {
  // Check that a Maybe<T> is initialized to Nothing.
  Maybe<BasicValue> mayValue;
  static_assert(std::is_same_v<BasicValue, decltype(mayValue)::ValueType>,
                "Should have BasicValue ValueType");
  MOZ_RELEASE_ASSERT(!mayValue);
  MOZ_RELEASE_ASSERT(!mayValue.isSome());
  MOZ_RELEASE_ASSERT(mayValue.isNothing());

  // Check that emplace() default constructs and the accessors work.
  mayValue.emplace();
  MOZ_RELEASE_ASSERT(mayValue);
  MOZ_RELEASE_ASSERT(mayValue.isSome());
  MOZ_RELEASE_ASSERT(!mayValue.isNothing());
  MOZ_RELEASE_ASSERT(*mayValue == BasicValue());
  static_assert(std::is_same_v<BasicValue&, decltype(*mayValue)>,
                "operator*() should return a BasicValue&");
  MOZ_RELEASE_ASSERT(mayValue.value() == BasicValue());
  static_assert(std::is_same_v<BasicValue, decltype(mayValue.value())>,
                "value() should return a BasicValue");
  MOZ_RELEASE_ASSERT(mayValue.ref() == BasicValue());
  static_assert(std::is_same_v<BasicValue&, decltype(mayValue.ref())>,
                "ref() should return a BasicValue&");
  MOZ_RELEASE_ASSERT(mayValue.ptr() != nullptr);
  static_assert(std::is_same_v<BasicValue*, decltype(mayValue.ptr())>,
                "ptr() should return a BasicValue*");
  MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasDefaultConstructed);

  // Check that reset() works.
  mayValue.reset();
  MOZ_RELEASE_ASSERT(!mayValue);
  MOZ_RELEASE_ASSERT(!mayValue.isSome());
  MOZ_RELEASE_ASSERT(mayValue.isNothing());

  // Check that emplace(T1) calls the correct constructor.
  mayValue.emplace(1);
  MOZ_RELEASE_ASSERT(mayValue);
  MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasConstructed);
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
  mayValue.reset();
  MOZ_RELEASE_ASSERT(!mayValue);

  {
    // Check that Maybe(std::in_place, T1) calls the correct constructor.
    const auto mayValueConstructed = Maybe<BasicValue>(std::in_place, 1);
    MOZ_RELEASE_ASSERT(mayValueConstructed);
    MOZ_RELEASE_ASSERT(mayValueConstructed->GetStatus() == eWasConstructed);
    MOZ_RELEASE_ASSERT(mayValueConstructed->GetTag() == 1);
  }

  // Check that Some() and Nothing() work.
  mayValue = Some(BasicValue(2));
  MOZ_RELEASE_ASSERT(mayValue);
  MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasMoveConstructed);
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
  mayValue = Nothing();
  MOZ_RELEASE_ASSERT(!mayValue);

  // Check that the accessors work through a const ref.
  mayValue.emplace();
  const Maybe<BasicValue>& mayValueCRef = mayValue;
  MOZ_RELEASE_ASSERT(mayValueCRef);
  MOZ_RELEASE_ASSERT(mayValueCRef.isSome());
  MOZ_RELEASE_ASSERT(!mayValueCRef.isNothing());
  MOZ_RELEASE_ASSERT(*mayValueCRef == BasicValue());
  static_assert(std::is_same_v<const BasicValue&, decltype(*mayValueCRef)>,
                "operator*() should return a BasicValue");
  MOZ_RELEASE_ASSERT(mayValueCRef.value() == BasicValue());
  static_assert(std::is_same_v<BasicValue, decltype(mayValueCRef.value())>,
                "value() should return a BasicValue");
  MOZ_RELEASE_ASSERT(mayValueCRef.ref() == BasicValue());
  static_assert(std::is_same_v<const BasicValue&, decltype(mayValueCRef.ref())>,
                "ref() should return a const BasicValue&");
  MOZ_RELEASE_ASSERT(mayValueCRef.ptr() != nullptr);
  static_assert(std::is_same_v<const BasicValue*, decltype(mayValueCRef.ptr())>,
                "ptr() should return a const BasicValue*");
  MOZ_RELEASE_ASSERT(mayValueCRef->GetStatus() == eWasDefaultConstructed);
  mayValue.reset();

  // Check that we can create and reference Maybe<const Type>.
  Maybe<const BasicValue> mayCValue1 = Some(BasicValue(5));
  MOZ_RELEASE_ASSERT(mayCValue1);
  MOZ_RELEASE_ASSERT(mayCValue1.isSome());
  MOZ_RELEASE_ASSERT(*mayCValue1 == BasicValue(5));
  const Maybe<const BasicValue>& mayCValue1Ref = mayCValue1;
  MOZ_RELEASE_ASSERT(mayCValue1Ref == mayCValue1);
  MOZ_RELEASE_ASSERT(*mayCValue1Ref == BasicValue(5));
  Maybe<const BasicValue> mayCValue2;
  mayCValue2.emplace(6);
  MOZ_RELEASE_ASSERT(mayCValue2);
  MOZ_RELEASE_ASSERT(mayCValue2.isSome());
  MOZ_RELEASE_ASSERT(*mayCValue2 == BasicValue(6));

  // Check that accessors work through rvalue-references.
  MOZ_RELEASE_ASSERT(Some(BasicValue()));
  MOZ_RELEASE_ASSERT(Some(BasicValue()).isSome());
  MOZ_RELEASE_ASSERT(!Some(BasicValue()).isNothing());
  MOZ_RELEASE_ASSERT(*Some(BasicValue()) == BasicValue());
  static_assert(std::is_same_v<BasicValue&&, decltype(*Some(BasicValue()))>,
                "operator*() should return a BasicValue&&");
  MOZ_RELEASE_ASSERT(Some(BasicValue()).value() == BasicValue());
  static_assert(
      std::is_same_v<BasicValue, decltype(Some(BasicValue()).value())>,
      "value() should return a BasicValue");
  MOZ_RELEASE_ASSERT(Some(BasicValue()).ref() == BasicValue());
  static_assert(
      std::is_same_v<BasicValue&&, decltype(Some(BasicValue()).ref())>,
      "ref() should return a BasicValue&&");
  MOZ_RELEASE_ASSERT(Some(BasicValue()).ptr() != nullptr);
  static_assert(std::is_same_v<BasicValue*, decltype(Some(BasicValue()).ptr())>,
                "ptr() should return a BasicValue*");
  MOZ_RELEASE_ASSERT(Some(BasicValue())->GetStatus() == eWasMoveConstructed);

  // Check that accessors work through const-rvalue-references.
  auto MakeConstMaybe = []() -> const Maybe<BasicValue> {
    return Some(BasicValue());
  };
  MOZ_RELEASE_ASSERT(MakeConstMaybe());
  MOZ_RELEASE_ASSERT(MakeConstMaybe().isSome());
  MOZ_RELEASE_ASSERT(!MakeConstMaybe().isNothing());
  MOZ_RELEASE_ASSERT(*MakeConstMaybe() == BasicValue());
  static_assert(std::is_same_v<const BasicValue&&, decltype(*MakeConstMaybe())>,
                "operator*() should return a const BasicValue&&");
  MOZ_RELEASE_ASSERT(MakeConstMaybe().value() == BasicValue());
  static_assert(std::is_same_v<BasicValue, decltype(MakeConstMaybe().value())>,
                "value() should return a BasicValue");
  MOZ_RELEASE_ASSERT(MakeConstMaybe().ref() == BasicValue());
  static_assert(
      std::is_same_v<const BasicValue&&, decltype(MakeConstMaybe().ref())>,
      "ref() should return a const BasicValue&&");
  MOZ_RELEASE_ASSERT(MakeConstMaybe().ptr() != nullptr);
  static_assert(
      std::is_same_v<const BasicValue*, decltype(MakeConstMaybe().ptr())>,
      "ptr() should return a const BasicValue*");
  MOZ_RELEASE_ASSERT(MakeConstMaybe()->GetStatus() == eWasMoveConstructed);
  MOZ_RELEASE_ASSERT(BasicValue(*MakeConstMaybe()).GetStatus() ==
                     eWasConstMoveConstructed);

  // Check that take works
  mayValue = Some(BasicValue(6));
  Maybe taken = mayValue.take();
  MOZ_RELEASE_ASSERT(taken->GetStatus() == eWasMoveConstructed);
  MOZ_RELEASE_ASSERT(taken == Some(BasicValue(6)));
  MOZ_RELEASE_ASSERT(!mayValue.isSome());
  MOZ_RELEASE_ASSERT(mayValue.take() == Nothing());

  // Check that extract works
  mayValue = Some(BasicValue(7));
  BasicValue extracted = mayValue.extract();
  MOZ_RELEASE_ASSERT(extracted.GetStatus() == eWasMoveConstructed);
  MOZ_RELEASE_ASSERT(extracted == BasicValue(7));
  MOZ_RELEASE_ASSERT(!mayValue.isSome());

  return true;
}

template <typename T>
static void TestCopyMaybe() {
  {
    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);

    Maybe<T> src = Some(T());
    Maybe<T> dstCopyConstructed = src;

    MOZ_RELEASE_ASSERT(2 == sUndestroyedObjects);
    MOZ_RELEASE_ASSERT(dstCopyConstructed->GetStatus() == eWasCopyConstructed);
  }

  {
    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);

    Maybe<T> src = Some(T());
    Maybe<T> dstCopyAssigned;
    dstCopyAssigned = src;

    MOZ_RELEASE_ASSERT(2 == sUndestroyedObjects);
    MOZ_RELEASE_ASSERT(dstCopyAssigned->GetStatus() == eWasCopyConstructed);
  }
}

template <typename T>
static void TestMoveMaybe() {
  {
    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);

    Maybe<T> src = Some(T());
    Maybe<T> dstMoveConstructed = std::move(src);

    MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
    MOZ_RELEASE_ASSERT(dstMoveConstructed->GetStatus() == eWasMoveConstructed);
  }

  {
    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);

    Maybe<T> src = Some(T());
    Maybe<T> dstMoveAssigned;
    dstMoveAssigned = std::move(src);

    MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
    MOZ_RELEASE_ASSERT(dstMoveAssigned->GetStatus() == eWasMoveConstructed);
  }

  {
    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);

    Maybe<T> src = Some(T());
    Maybe<T> dstMoveConstructed = src.take();

    MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
    MOZ_RELEASE_ASSERT(dstMoveConstructed->GetStatus() == eWasMoveConstructed);
  }

  {
    MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);

    Maybe<T> src = Some(T());
    T dstMoveConstructed = src.extract();

    MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
    MOZ_RELEASE_ASSERT(dstMoveConstructed.GetStatus() == eWasMoveConstructed);
  }
}

static bool TestCopyAndMove() {
  MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);

  {
    // Check that we get moves when possible for types that can support both
    // moves and copies.
    {
      Maybe<BasicValue> mayBasicValue = Some(BasicValue(1));
      MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
      MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed);
      MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 1);
      mayBasicValue = Some(BasicValue(2));
      MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
      MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveAssigned);
      MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 2);
      mayBasicValue.reset();
      MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);
      mayBasicValue.emplace(BasicValue(3));
      MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects);
      MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMoveConstructed);
      MOZ_RELEASE_ASSERT(mayBasicValue->GetTag() == 3);

      // Check that we get copies when moves aren't possible.
      Maybe<BasicValue> mayBasicValue2 = Some(*mayBasicValue);
      MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed);
      MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 3);
      mayBasicValue->SetTag(4);
      mayBasicValue2 = mayBasicValue;
      // This test should work again when we fix bug 1052940.
      // MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyAssigned);
      MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 4);
      mayBasicValue->SetTag(5);
      mayBasicValue2.reset();
      mayBasicValue2.emplace(*mayBasicValue);
      MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasCopyConstructed);
      MOZ_RELEASE_ASSERT(mayBasicValue2->GetTag() == 5);

      // Check that std::move() works. (Another sanity check for move support.)
      Maybe<BasicValue> mayBasicValue3 = Some(std::move(*mayBasicValue));
      MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveConstructed);
      MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 5);
      MOZ_RELEASE_ASSERT(mayBasicValue->GetStatus() == eWasMovedFrom);
      mayBasicValue2->SetTag(6);
      mayBasicValue3 = Some(std::move(*mayBasicValue2));
      MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMoveAssigned);
      MOZ_RELEASE_ASSERT(mayBasicValue3->GetTag() == 6);
      MOZ_RELEASE_ASSERT(mayBasicValue2->GetStatus() == eWasMovedFrom);
      Maybe<BasicValue> mayBasicValue4;
      mayBasicValue4.emplace(std::move(*mayBasicValue3));
      MOZ_RELEASE_ASSERT(mayBasicValue4->GetStatus() == eWasMoveConstructed);
      MOZ_RELEASE_ASSERT(mayBasicValue4->GetTag() == 6);
      MOZ_RELEASE_ASSERT(mayBasicValue3->GetStatus() == eWasMovedFrom);
    }

    TestCopyMaybe<BasicValue>();
    TestMoveMaybe<BasicValue>();
  }

  MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);

  {
    // Check that we always get copies for types that don't support moves.
    {
      Maybe<UnmovableValue> mayUnmovableValue = Some(UnmovableValue());
      MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed);
      mayUnmovableValue.reset();
      mayUnmovableValue.emplace(UnmovableValue());
      MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed);
    }

    TestCopyMaybe<UnmovableValue>();

    static_assert(std::is_copy_constructible_v<Maybe<UnmovableValue>>);
    static_assert(std::is_copy_assignable_v<Maybe<UnmovableValue>>);
    static_assert(!std::is_move_constructible_v<Maybe<UnmovableValue>>);
    static_assert(!std::is_move_assignable_v<Maybe<UnmovableValue>>);
  }

  MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);

  {
    // Check that types that only support moves, but not copies, work.
    {
      Maybe<UncopyableValue> mayUncopyableValue = Some(UncopyableValue());
      MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() ==
                         eWasMoveConstructed);
      mayUncopyableValue = Some(UncopyableValue());
      MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() == eWasMoveAssigned);
      mayUncopyableValue.reset();
      mayUncopyableValue.emplace(UncopyableValue());
      MOZ_RELEASE_ASSERT(mayUncopyableValue->GetStatus() ==
                         eWasMoveConstructed);
      mayUncopyableValue = Nothing();
    }

    TestMoveMaybe<BasicValue>();

    static_assert(!std::is_copy_constructible_v<Maybe<UncopyableValue>>);
    static_assert(!std::is_copy_assignable_v<Maybe<UncopyableValue>>);
    static_assert(std::is_move_constructible_v<Maybe<UncopyableValue>>);
    static_assert(std::is_move_assignable_v<Maybe<UncopyableValue>>);
  }

  MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects);

  {  // Check that types that support neither moves or copies work.
    {
      const auto mayUncopyableUnmovableValueConstructed =
          Maybe<UncopyableUnmovableValue>{std::in_place};
      MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValueConstructed->GetStatus() ==
                         eWasDefaultConstructed);
    }

    Maybe<UncopyableUnmovableValue> mayUncopyableUnmovableValue;
    mayUncopyableUnmovableValue.emplace();
    MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() ==
                       eWasDefaultConstructed);
    mayUncopyableUnmovableValue.reset();
    mayUncopyableUnmovableValue.emplace(0);
    MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValue->GetStatus() ==
                       eWasConstructed);
    mayUncopyableUnmovableValue = Nothing();

    static_assert(
        !std::is_copy_constructible_v<Maybe<UncopyableUnmovableValue>>);
    static_assert(!std::is_copy_assignable_v<Maybe<UncopyableUnmovableValue>>);
    static_assert(
        !std::is_move_constructible_v<Maybe<UncopyableUnmovableValue>>);
    static_assert(!std::is_move_assignable_v<Maybe<UncopyableUnmovableValue>>);
  }

  {
    // Test copy and move with a trivially copyable and trivially destructible
    // type.
    {
      constexpr Maybe<int> src = Some(42);
      constexpr Maybe<int> dstCopyConstructed = src;

      static_assert(src.isSome());
      static_assert(dstCopyConstructed.isSome());
      static_assert(42 == *src);
      static_assert(42 == *dstCopyConstructed);
      static_assert(42 == dstCopyConstructed.value());
    }

    {
      const Maybe<int> src = Some(42);
      Maybe<int> dstCopyAssigned;
      dstCopyAssigned = src;

      MOZ_RELEASE_ASSERT(src.isSome());
      MOZ_RELEASE_ASSERT(dstCopyAssigned.isSome());
      MOZ_RELEASE_ASSERT(42 == *src);
      MOZ_RELEASE_ASSERT(42 == *dstCopyAssigned);
    }

    {
      Maybe<int> src = Some(42);
      const Maybe<int> dstMoveConstructed = std::move(src);

      MOZ_RELEASE_ASSERT(!src.isSome());
      MOZ_RELEASE_ASSERT(dstMoveConstructed.isSome());
      MOZ_RELEASE_ASSERT(42 == *dstMoveConstructed);
    }

    {
      Maybe<int> src = Some(42);
      Maybe<int> dstMoveAssigned;
      dstMoveAssigned = std::move(src);

      MOZ_RELEASE_ASSERT(!src.isSome());
      MOZ_RELEASE_ASSERT(dstMoveAssigned.isSome());
      MOZ_RELEASE_ASSERT(42 == *dstMoveAssigned);
    }
  }

  return true;
}

static BasicValue* sStaticBasicValue = nullptr;

static BasicValue MakeBasicValue() { return BasicValue(9); }

static BasicValue& MakeBasicValueRef() { return *sStaticBasicValue; }

static BasicValue* MakeBasicValuePtr() { return sStaticBasicValue; }

static bool TestFunctionalAccessors() {
  BasicValue value(9);
  sStaticBasicValue = new BasicValue(9);

  // Check that the 'some' case of functional accessors works.
  Maybe<BasicValue> someValue = Some(BasicValue(3));
  MOZ_RELEASE_ASSERT(someValue.valueOr(value) == BasicValue(3));
  static_assert(std::is_same_v<BasicValue, decltype(someValue.valueOr(value))>,
                "valueOr should return a BasicValue");
  MOZ_RELEASE_ASSERT(someValue.valueOrFrom(&MakeBasicValue) == BasicValue(3));
  static_assert(
      std::is_same_v<BasicValue,
                     decltype(someValue.valueOrFrom(&MakeBasicValue))>,
      "valueOrFrom should return a BasicValue");
  MOZ_RELEASE_ASSERT(someValue.ptrOr(&value) != &value);
  static_assert(std::is_same_v<BasicValue*, decltype(someValue.ptrOr(&value))>,
                "ptrOr should return a BasicValue*");
  MOZ_RELEASE_ASSERT(*someValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3));
  static_assert(
      std::is_same_v<BasicValue*,
                     decltype(someValue.ptrOrFrom(&MakeBasicValuePtr))>,
      "ptrOrFrom should return a BasicValue*");
  MOZ_RELEASE_ASSERT(someValue.refOr(value) == BasicValue(3));
  static_assert(std::is_same_v<BasicValue&, decltype(someValue.refOr(value))>,
                "refOr should return a BasicValue&");
  MOZ_RELEASE_ASSERT(someValue.refOrFrom(&MakeBasicValueRef) == BasicValue(3));
  static_assert(
      std::is_same_v<BasicValue&,
                     decltype(someValue.refOrFrom(&MakeBasicValueRef))>,
      "refOrFrom should return a BasicValue&");

  // Check that the 'some' case works through a const reference.
  const Maybe<BasicValue>& someValueCRef = someValue;
  MOZ_RELEASE_ASSERT(someValueCRef.valueOr(value) == BasicValue(3));
  static_assert(
      std::is_same_v<BasicValue, decltype(someValueCRef.valueOr(value))>,
      "valueOr should return a BasicValue");
  MOZ_RELEASE_ASSERT(someValueCRef.valueOrFrom(&MakeBasicValue) ==
                     BasicValue(3));
  static_assert(
      std::is_same_v<BasicValue,
                     decltype(someValueCRef.valueOrFrom(&MakeBasicValue))>,
      "valueOrFrom should return a BasicValue");
  MOZ_RELEASE_ASSERT(someValueCRef.ptrOr(&value) != &value);
  static_assert(
      std::is_same_v<const BasicValue*, decltype(someValueCRef.ptrOr(&value))>,
      "ptrOr should return a const BasicValue*");
  MOZ_RELEASE_ASSERT(*someValueCRef.ptrOrFrom(&MakeBasicValuePtr) ==
                     BasicValue(3));
  static_assert(
      std::is_same_v<const BasicValue*,
                     decltype(someValueCRef.ptrOrFrom(&MakeBasicValuePtr))>,
      "ptrOrFrom should return a const BasicValue*");
  MOZ_RELEASE_ASSERT(someValueCRef.refOr(value) == BasicValue(3));
  static_assert(
      std::is_same_v<const BasicValue&, decltype(someValueCRef.refOr(value))>,
      "refOr should return a const BasicValue&");
  MOZ_RELEASE_ASSERT(someValueCRef.refOrFrom(&MakeBasicValueRef) ==
                     BasicValue(3));
  static_assert(
      std::is_same_v<const BasicValue&,
                     decltype(someValueCRef.refOrFrom(&MakeBasicValueRef))>,
      "refOrFrom should return a const BasicValue&");

  // Check that the 'none' case of functional accessors works.
  Maybe<BasicValue> noneValue;
  MOZ_RELEASE_ASSERT(noneValue.valueOr(value) == BasicValue(9));
  static_assert(std::is_same_v<BasicValue, decltype(noneValue.valueOr(value))>,
                "valueOr should return a BasicValue");
  MOZ_RELEASE_ASSERT(noneValue.valueOrFrom(&MakeBasicValue) == BasicValue(9));
  static_assert(
      std::is_same_v<BasicValue,
                     decltype(noneValue.valueOrFrom(&MakeBasicValue))>,
      "valueOrFrom should return a BasicValue");
  MOZ_RELEASE_ASSERT(noneValue.ptrOr(&value) == &value);
  static_assert(std::is_same_v<BasicValue*, decltype(noneValue.ptrOr(&value))>,
                "ptrOr should return a BasicValue*");
  MOZ_RELEASE_ASSERT(*noneValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9));
  static_assert(
      std::is_same_v<BasicValue*,
                     decltype(noneValue.ptrOrFrom(&MakeBasicValuePtr))>,
      "ptrOrFrom should return a BasicValue*");
  MOZ_RELEASE_ASSERT(noneValue.refOr(value) == BasicValue(9));
  static_assert(std::is_same_v<BasicValue&, decltype(noneValue.refOr(value))>,
                "refOr should return a BasicValue&");
  MOZ_RELEASE_ASSERT(noneValue.refOrFrom(&MakeBasicValueRef) == BasicValue(9));
  static_assert(
      std::is_same_v<BasicValue&,
                     decltype(noneValue.refOrFrom(&MakeBasicValueRef))>,
      "refOrFrom should return a BasicValue&");

  // Check that the 'none' case works through a const reference.
  const Maybe<BasicValue>& noneValueCRef = noneValue;
  MOZ_RELEASE_ASSERT(noneValueCRef.valueOr(value) == BasicValue(9));
  static_assert(
      std::is_same_v<BasicValue, decltype(noneValueCRef.valueOr(value))>,
      "valueOr should return a BasicValue");
  MOZ_RELEASE_ASSERT(noneValueCRef.valueOrFrom(&MakeBasicValue) ==
                     BasicValue(9));
  static_assert(
      std::is_same_v<BasicValue,
                     decltype(noneValueCRef.valueOrFrom(&MakeBasicValue))>,
      "valueOrFrom should return a BasicValue");
  MOZ_RELEASE_ASSERT(noneValueCRef.ptrOr(&value) == &value);
  static_assert(
      std::is_same_v<const BasicValue*, decltype(noneValueCRef.ptrOr(&value))>,
      "ptrOr should return a const BasicValue*");
  MOZ_RELEASE_ASSERT(*noneValueCRef.ptrOrFrom(&MakeBasicValuePtr) ==
                     BasicValue(9));
  static_assert(
      std::is_same_v<const BasicValue*,
                     decltype(noneValueCRef.ptrOrFrom(&MakeBasicValuePtr))>,
      "ptrOrFrom should return a const BasicValue*");
  MOZ_RELEASE_ASSERT(noneValueCRef.refOr(value) == BasicValue(9));
  static_assert(
      std::is_same_v<const BasicValue&, decltype(noneValueCRef.refOr(value))>,
      "refOr should return a const BasicValue&");
  MOZ_RELEASE_ASSERT(noneValueCRef.refOrFrom(&MakeBasicValueRef) ==
                     BasicValue(9));
  static_assert(
      std::is_same_v<const BasicValue&,
                     decltype(noneValueCRef.refOrFrom(&MakeBasicValueRef))>,
      "refOrFrom should return a const BasicValue&");

  // Clean up so the undestroyed objects count stays accurate.
  delete sStaticBasicValue;
  sStaticBasicValue = nullptr;

  return true;
}

static bool gFunctionWasApplied = false;

static void IncrementTag(BasicValue& aValue) {
  gFunctionWasApplied = true;
  aValue.SetTag(aValue.GetTag() + 1);
}

static void AccessValue(const BasicValue&) { gFunctionWasApplied = true; }

struct IncrementTagFunctor {
  IncrementTagFunctor() : mBy(1) {}

  void operator()(BasicValue& aValue) {
    aValue.SetTag(aValue.GetTag() + mBy.GetTag());
  }

  BasicValue mBy;
};

static bool TestApply() {
  // Check that apply handles the 'Nothing' case.
  gFunctionWasApplied = false;
  Maybe<BasicValue> mayValue;
  mayValue.apply(&IncrementTag);
  mayValue.apply(&AccessValue);
  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);

  // Check that apply handles the 'Some' case.
  mayValue = Some(BasicValue(1));
  mayValue.apply(&IncrementTag);
  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
  gFunctionWasApplied = false;
  mayValue.apply(&AccessValue);
  MOZ_RELEASE_ASSERT(gFunctionWasApplied);

  // Check that apply works with a const reference.
  const Maybe<BasicValue>& mayValueCRef = mayValue;
  gFunctionWasApplied = false;
  mayValueCRef.apply(&AccessValue);
  MOZ_RELEASE_ASSERT(gFunctionWasApplied);

  // Check that apply works with functors.
  IncrementTagFunctor tagIncrementer;
  MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
  mayValue = Some(BasicValue(1));
  mayValue.apply(tagIncrementer);
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
  MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);

  // Check that apply works with lambda expressions.
  int32_t two = 2;
  gFunctionWasApplied = false;
  mayValue = Some(BasicValue(2));
  mayValue.apply([&](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); });
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 4);
  mayValue.apply([=](BasicValue& aVal) { aVal.SetTag(aVal.GetTag() * two); });
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 8);
  mayValueCRef.apply(
      [&](const BasicValue& aVal) { gFunctionWasApplied = true; });
  MOZ_RELEASE_ASSERT(gFunctionWasApplied == true);

  // Check that apply can move the contained value.
  mayValue = Some(BasicValue(1));
  Maybe<BasicValue> otherValue;
  std::move(mayValue).apply(
      [&](BasicValue&& aVal) { otherValue = Some(std::move(aVal)); });
  MOZ_RELEASE_ASSERT(mayValue.isNothing());
  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
  MOZ_RELEASE_ASSERT(otherValue->GetStatus() == eWasMoveConstructed);

  return true;
}

static int TimesTwo(const BasicValue& aValue) { return aValue.GetTag() * 2; }

static int TimesTwoAndResetOriginal(BasicValue& aValue) {
  int tag = aValue.GetTag();
  aValue.SetTag(1);
  return tag * 2;
}

struct MultiplyTagFunctor {
  MultiplyTagFunctor() : mBy(2) {}

  int operator()(BasicValue& aValue) { return aValue.GetTag() * mBy.GetTag(); }

  BasicValue mBy;
};

static bool TestMap() {
  // Check that map handles the 'Nothing' case.
  Maybe<BasicValue> mayValue;
  MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Nothing());
  static_assert(std::is_same_v<Maybe<int>, decltype(mayValue.map(&TimesTwo))>,
                "map(TimesTwo) should return a Maybe");
  MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Nothing());

  // Check that map handles the 'Some' case.
  mayValue = Some(BasicValue(2));
  MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Some(4));
  MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwoAndResetOriginal) == Some(4));
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
  mayValue = Some(BasicValue(2));

  // Check that map works with a const reference.
  mayValue->SetTag(2);
  const Maybe<BasicValue>& mayValueCRef = mayValue;
  MOZ_RELEASE_ASSERT(mayValueCRef.map(&TimesTwo) == Some(4));
  static_assert(
      std::is_same_v<Maybe<int>, decltype(mayValueCRef.map(&TimesTwo))>,
      "map(TimesTwo) should return a Maybe");

  // Check that map works with functors.
  MultiplyTagFunctor tagMultiplier;
  MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed);
  MOZ_RELEASE_ASSERT(mayValue.map(tagMultiplier) == Some(4));
  MOZ_RELEASE_ASSERT(tagMultiplier.mBy.GetStatus() == eWasConstructed);

  // Check that map works with lambda expressions.
  int two = 2;
  mayValue = Some(BasicValue(2));
  Maybe<int> mappedValue =
      mayValue.map([&](const BasicValue& aVal) { return aVal.GetTag() * two; });
  MOZ_RELEASE_ASSERT(mappedValue == Some(4));
  mappedValue =
      mayValue.map([=](const BasicValue& aVal) { return aVal.GetTag() * two; });
  MOZ_RELEASE_ASSERT(mappedValue == Some(4));
  mappedValue = mayValueCRef.map(
      [&](const BasicValue& aVal) { return aVal.GetTag() * two; });
  MOZ_RELEASE_ASSERT(mappedValue == Some(4));

  // Check that map can move the contained value.
  mayValue = Some(BasicValue(1));
  Maybe<BasicValue> otherValue = std::move(mayValue).map(
      [](BasicValue&& aValue) { return std::move(aValue); });
  MOZ_RELEASE_ASSERT(mayValue.isNothing());
  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
  MOZ_RELEASE_ASSERT(otherValue->GetStatus() == eWasMoveConstructed);

  // Check that function object qualifiers are preserved when invoked.
  struct F {
    std::integral_constant<int, 1> operator()(int) & { return {}; }
    std::integral_constant<int, 2> operator()(intconst& { return {}; }
    std::integral_constant<int, 3> operator()(int) && { return {}; }
    std::integral_constant<int, 4> operator()(intconst&& { return {}; }
  };
  Maybe<int> mi = Some(0);
  const Maybe<int> cmi = Some(0);
  F f;
  static_assert(std::is_same<decltype(mi.map(f)),
                             Maybe<std::integral_constant<int, 1>>>::value,
                "Maybe.map(&)");
  MOZ_RELEASE_ASSERT(mi.map(f).value()() == 1);
  static_assert(std::is_same<decltype(cmi.map(f)),
                             Maybe<std::integral_constant<int, 1>>>::value,
                "const Maybe.map(&)");
  MOZ_RELEASE_ASSERT(cmi.map(f).value()() == 1);
  const F cf;
  static_assert(std::is_same<decltype(mi.map(cf)),
                             Maybe<std::integral_constant<int, 2>>>::value,
                "Maybe.map(const &)");
  MOZ_RELEASE_ASSERT(mi.map(cf).value() == 2);
  static_assert(std::is_same<decltype(cmi.map(cf)),
                             Maybe<std::integral_constant<int, 2>>>::value,
                "const Maybe.map(const &)");
  MOZ_RELEASE_ASSERT(cmi.map(cf).value() == 2);
  static_assert(std::is_same<decltype(mi.map(F{})),
                             Maybe<std::integral_constant<int, 3>>>::value,
                "Maybe.map(&&)");
  MOZ_RELEASE_ASSERT(mi.map(F{}).value() == 3);
  static_assert(std::is_same<decltype(cmi.map(F{})),
                             Maybe<std::integral_constant<int, 3>>>::value,
                "const Maybe.map(&&)");
  MOZ_RELEASE_ASSERT(cmi.map(F{}).value() == 3);
  using CF = const F;
  static_assert(std::is_same<decltype(mi.map(CF{})),
                             Maybe<std::integral_constant<int, 4>>>::value,
                "Maybe.map(const &&)");
  MOZ_RELEASE_ASSERT(mi.map(CF{}).value() == 4);
  static_assert(std::is_same<decltype(cmi.map(CF{})),
                             Maybe<std::integral_constant<int, 4>>>::value,
                "const Maybe.map(const &&)");
  MOZ_RELEASE_ASSERT(cmi.map(CF{}).value() == 4);

  return true;
}

static bool TestToMaybe() {
  BasicValue value(1);
  BasicValue* nullPointer = nullptr;

  // Check that a non-null pointer translates into a Some value.
  Maybe<BasicValue> mayValue = ToMaybe(&value);
  static_assert(std::is_same_v<Maybe<BasicValue>, decltype(ToMaybe(&value))>,
                "ToMaybe should return a Maybe");
  MOZ_RELEASE_ASSERT(mayValue.isSome());
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
  MOZ_RELEASE_ASSERT(mayValue->GetStatus() == eWasCopyConstructed);
  MOZ_RELEASE_ASSERT(value.GetStatus() != eWasMovedFrom);

  // Check that a null pointer translates into a Nothing value.
  mayValue = ToMaybe(nullPointer);
  static_assert(
      std::is_same_v<Maybe<BasicValue>, decltype(ToMaybe(nullPointer))>,
      "ToMaybe should return a Maybe");
  MOZ_RELEASE_ASSERT(mayValue.isNothing());

  return true;
}

static bool TestComparisonOperators() {
  Maybe<BasicValue> nothingValue = Nothing();
  Maybe<BasicValue> anotherNothingValue = Nothing();
  Maybe<BasicValue> oneValue = Some(BasicValue(1));
  Maybe<BasicValue> anotherOneValue = Some(BasicValue(1));
  Maybe<BasicValue> twoValue = Some(BasicValue(2));

  // Check equality.
  MOZ_RELEASE_ASSERT(nothingValue == anotherNothingValue);
  MOZ_RELEASE_ASSERT(oneValue == anotherOneValue);

  // Check inequality.
  MOZ_RELEASE_ASSERT(nothingValue != oneValue);
  MOZ_RELEASE_ASSERT(oneValue != nothingValue);
  MOZ_RELEASE_ASSERT(oneValue != twoValue);

  // Check '<'.
  MOZ_RELEASE_ASSERT(nothingValue < oneValue);
  MOZ_RELEASE_ASSERT(oneValue < twoValue);

  // Check '<='.
  MOZ_RELEASE_ASSERT(nothingValue <= anotherNothingValue);
  MOZ_RELEASE_ASSERT(nothingValue <= oneValue);
  MOZ_RELEASE_ASSERT(oneValue <= oneValue);
  MOZ_RELEASE_ASSERT(oneValue <= twoValue);

  // Check '>'.
  MOZ_RELEASE_ASSERT(oneValue > nothingValue);
  MOZ_RELEASE_ASSERT(twoValue > oneValue);

  // Check '>='.
  MOZ_RELEASE_ASSERT(nothingValue >= anotherNothingValue);
  MOZ_RELEASE_ASSERT(oneValue >= nothingValue);
  MOZ_RELEASE_ASSERT(oneValue >= oneValue);
  MOZ_RELEASE_ASSERT(twoValue >= oneValue);

  return true;
}

// Check that Maybe<> can wrap a superclass that happens to also be a concrete
// class (i.e. that the compiler doesn't warn when we invoke the superclass's
// destructor explicitly in |reset()|.
class MySuperClass {
  virtual void VirtualMethod() { /* do nothing */ }
};

class MyDerivedClass : public MySuperClass {
  void VirtualMethod() override { /* do nothing */ }
};

static bool TestVirtualFunction() {
  Maybe<MySuperClass> super;
  super.emplace();
  super.reset();

  Maybe<MyDerivedClass> derived;
  derived.emplace();
  derived.reset();

  // If this compiles successfully, we've passed.
  return true;
}

static Maybe<int*> ReturnSomeNullptr() { return Some(nullptr); }

struct D {
  explicit D(const Maybe<int*>&) {}
};

static bool TestSomeNullptrConversion() {
  Maybe<int*> m1 = Some(nullptr);
  MOZ_RELEASE_ASSERT(m1.isSome());
  MOZ_RELEASE_ASSERT(m1);
  MOZ_RELEASE_ASSERT(!*m1);

  auto m2 = ReturnSomeNullptr();
  MOZ_RELEASE_ASSERT(m2.isSome());
  MOZ_RELEASE_ASSERT(m2);
  MOZ_RELEASE_ASSERT(!*m2);

  Maybe<decltype(nullptr)> m3 = Some(nullptr);
  MOZ_RELEASE_ASSERT(m3.isSome());
  MOZ_RELEASE_ASSERT(m3);
  MOZ_RELEASE_ASSERT(*m3 == nullptr);

  D d(Some(nullptr));

  return true;
}

struct Base {};
struct Derived : Base {};

static Maybe<Base*> ReturnDerivedPointer() {
  Derived* d = nullptr;
  return Some(d);
}

struct ExplicitConstructorBasePointer {
  explicit ExplicitConstructorBasePointer(const Maybe<Base*>&) {}
};

static bool TestSomePointerConversion() {
  Base base;
  Derived derived;

  Maybe<Base*> m1 = Some(&derived);
  MOZ_RELEASE_ASSERT(m1.isSome());
  MOZ_RELEASE_ASSERT(m1);
  MOZ_RELEASE_ASSERT(*m1 == &derived);

  auto m2 = ReturnDerivedPointer();
  MOZ_RELEASE_ASSERT(m2.isSome());
  MOZ_RELEASE_ASSERT(m2);
  MOZ_RELEASE_ASSERT(*m2 == nullptr);

  Maybe<Base*> m3 = Some(&base);
  MOZ_RELEASE_ASSERT(m3.isSome());
  MOZ_RELEASE_ASSERT(m3);
  MOZ_RELEASE_ASSERT(*m3 == &base);

  auto s1 = Some(&derived);
  Maybe<Base*> c1(s1);
  MOZ_RELEASE_ASSERT(c1.isSome());
  MOZ_RELEASE_ASSERT(c1);
  MOZ_RELEASE_ASSERT(*c1 == &derived);

  ExplicitConstructorBasePointer ecbp(Some(&derived));

  return true;
}

struct SourceType1 {
  int mTag;

  operator int() const { return mTag; }
};
struct DestType {
  int mTag;
  Status mStatus;

  DestType() : mTag(0), mStatus(eWasDefaultConstructed) {}

  MOZ_IMPLICIT DestType(int aTag) : mTag(aTag), mStatus(eWasConstructed) {}

  MOZ_IMPLICIT DestType(SourceType1&& aSrc)
      : mTag(aSrc.mTag), mStatus(eWasMoveConstructed) {}

  MOZ_IMPLICIT DestType(const SourceType1& aSrc)
      : mTag(aSrc.mTag), mStatus(eWasCopyConstructed) {}

  DestType& operator=(int aTag) {
    mTag = aTag;
    mStatus = eWasAssigned;
    return *this;
  }

  DestType& operator=(SourceType1&& aSrc) {
    mTag = aSrc.mTag;
    mStatus = eWasMoveAssigned;
    return *this;
  }

  DestType& operator=(const SourceType1& aSrc) {
    mTag = aSrc.mTag;
    mStatus = eWasCopyAssigned;
    return *this;
  }
};
struct SourceType2 {
  int mTag;

  operator DestType() const& {
    DestType result;
    result.mTag = mTag;
    result.mStatus = eWasCopiedFrom;
    return result;
  }

  operator DestType() && {
    DestType result;
    result.mTag = mTag;
    result.mStatus = eWasMovedFrom;
    return result;
  }
};

static bool TestTypeConversion() {
  {
    Maybe<SourceType1> src = Some(SourceType1{1});
    Maybe<DestType> dest = src;
    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopyConstructed);

    src = Some(SourceType1{2});
    dest = src;
    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopyAssigned);
  }

  {
    Maybe<SourceType1> src = Some(SourceType1{1});
    Maybe<DestType> dest = std::move(src);
    MOZ_RELEASE_ASSERT(src.isNothing());
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMoveConstructed);

    src = Some(SourceType1{2});
    dest = std::move(src);
    MOZ_RELEASE_ASSERT(src.isNothing());
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMoveAssigned);
  }

  {
    Maybe<SourceType2> src = Some(SourceType2{1});
    Maybe<DestType> dest = src;
    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopiedFrom);

    src = Some(SourceType2{2});
    dest = src;
    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasCopiedFrom);
  }

  {
    Maybe<SourceType2> src = Some(SourceType2{1});
    Maybe<DestType> dest = std::move(src);
    MOZ_RELEASE_ASSERT(src.isNothing());
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMovedFrom);

    src = Some(SourceType2{2});
    dest = std::move(src);
    MOZ_RELEASE_ASSERT(src.isNothing());
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasMovedFrom);
  }

  {
    Maybe<int> src = Some(1);
    Maybe<DestType> dest = src;
    MOZ_RELEASE_ASSERT(src.isSome() && *src == 1);
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasConstructed);

    src = Some(2);
    dest = src;
    MOZ_RELEASE_ASSERT(src.isSome() && *src == 2);
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasAssigned);
  }

  {
    Maybe<int> src = Some(1);
    Maybe<DestType> dest = std::move(src);
    MOZ_RELEASE_ASSERT(src.isNothing());
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 1);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasConstructed);

    src = Some(2);
    dest = std::move(src);
    MOZ_RELEASE_ASSERT(src.isNothing());
    MOZ_RELEASE_ASSERT(dest.isSome() && dest->mTag == 2);
    MOZ_RELEASE_ASSERT(dest->mStatus == eWasAssigned);
  }

  {
    Maybe<SourceType1> src = Some(SourceType1{1});
    Maybe<int> dest = src;
    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 1);
    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);

    src = Some(SourceType1{2});
    dest = src;
    MOZ_RELEASE_ASSERT(src.isSome() && src->mTag == 2);
    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
  }

  {
    Maybe<SourceType1> src = Some(SourceType1{1});
    Maybe<int> dest = std::move(src);
    MOZ_RELEASE_ASSERT(src.isNothing());
    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);

    src = Some(SourceType1{2});
    dest = std::move(src);
    MOZ_RELEASE_ASSERT(src.isNothing());
    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
  }

  {
    Maybe<size_t> src = Some(1);
    Maybe<char16_t> dest = src;
    MOZ_RELEASE_ASSERT(src.isSome() && *src == 1);
    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);

    src = Some(2);
    dest = src;
    MOZ_RELEASE_ASSERT(src.isSome() && *src == 2);
    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
  }

  {
    Maybe<size_t> src = Some(1);
    Maybe<char16_t> dest = std::move(src);
    MOZ_RELEASE_ASSERT(src.isNothing());
    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 1);

    src = Some(2);
    dest = std::move(src);
    MOZ_RELEASE_ASSERT(src.isNothing());
    MOZ_RELEASE_ASSERT(dest.isSome() && *dest == 2);
  }

  return true;
}

static bool TestReference() {
  static_assert(std::is_trivially_destructible_v<Maybe<int&>>);
  static_assert(std::is_trivially_copy_constructible_v<Maybe<int&>>);
  static_assert(std::is_trivially_copy_assignable_v<Maybe<int&>>);

  static_assert(Maybe<int&>{}.isNothing());
  static_assert(Maybe<int&>{Nothing{}}.isNothing());

  {
    Maybe<int&> defaultConstructed;

    MOZ_RELEASE_ASSERT(defaultConstructed.isNothing());
    MOZ_RELEASE_ASSERT(!defaultConstructed.isSome());
    MOZ_RELEASE_ASSERT(!defaultConstructed);
  }

  {
    Maybe<int&> nothing = Nothing();

    MOZ_RELEASE_ASSERT(nothing.isNothing());
    MOZ_RELEASE_ASSERT(!nothing.isSome());
    MOZ_RELEASE_ASSERT(!nothing);
  }

  {
    int foo = 42, bar = 42;
    Maybe<int&> some = SomeRef(foo);

    MOZ_RELEASE_ASSERT(!some.isNothing());
    MOZ_RELEASE_ASSERT(some.isSome());
    MOZ_RELEASE_ASSERT(some);
    MOZ_RELEASE_ASSERT(&some.ref() == &foo);

    MOZ_RELEASE_ASSERT(some.refEquals(foo));
    MOZ_RELEASE_ASSERT(some.refEquals(SomeRef(foo)));
    MOZ_RELEASE_ASSERT(!some.refEquals(Nothing()));
    MOZ_RELEASE_ASSERT(!some.refEquals(bar));
    MOZ_RELEASE_ASSERT(!some.refEquals(SomeRef(bar)));

    some.ref()++;
    MOZ_RELEASE_ASSERT(43 == foo);

    (*some)++;
    MOZ_RELEASE_ASSERT(44 == foo);
  }

  {
    int foo = 42, bar = 42;
    Maybe<int&> some;
    some.emplace(foo);

    MOZ_RELEASE_ASSERT(!some.isNothing());
    MOZ_RELEASE_ASSERT(some.isSome());
    MOZ_RELEASE_ASSERT(some);
    MOZ_RELEASE_ASSERT(&some.ref() == &foo);

    MOZ_RELEASE_ASSERT(some.refEquals(foo));
    MOZ_RELEASE_ASSERT(some.refEquals(SomeRef(foo)));
    MOZ_RELEASE_ASSERT(!some.refEquals(Nothing()));
    MOZ_RELEASE_ASSERT(!some.refEquals(bar));
    MOZ_RELEASE_ASSERT(!some.refEquals(SomeRef(bar)));

    some.ref()++;
    MOZ_RELEASE_ASSERT(43 == foo);
  }

  {
    Maybe<int&> defaultConstructed;
    defaultConstructed.reset();

    MOZ_RELEASE_ASSERT(defaultConstructed.isNothing());
    MOZ_RELEASE_ASSERT(!defaultConstructed.isSome());
    MOZ_RELEASE_ASSERT(!defaultConstructed);
  }

  {
    int foo = 42;
    Maybe<int&> some = SomeRef(foo);
    some.reset();

    MOZ_RELEASE_ASSERT(some.isNothing());
    MOZ_RELEASE_ASSERT(!some.isSome());
    MOZ_RELEASE_ASSERT(!some);
  }

  {
    int foo = 42;
    Maybe<int&> some = SomeRef(foo);

    auto& applied = some.apply([](int& ref) { ref++; });

    MOZ_RELEASE_ASSERT(&some == &applied);
    MOZ_RELEASE_ASSERT(43 == foo);
  }

  {
    Maybe<int&> nothing;

    auto& applied = nothing.apply([](int& ref) { ref++; });

    MOZ_RELEASE_ASSERT(¬hing == &applied);
  }

  {
    int foo = 42;
    Maybe<int&> some = SomeRef(foo);

    auto mapped = some.map([](int& ref) { return &ref; });
    static_assert(std::is_same_v<decltype(mapped), Maybe<int*>>);

    MOZ_RELEASE_ASSERT(&foo == *mapped);
  }

  {
    Maybe<int&> nothing;

    auto mapped = nothing.map([](int& ref) { return &ref; });

    MOZ_RELEASE_ASSERT(mapped.isNothing());
    MOZ_RELEASE_ASSERT(!mapped.isSome());
    MOZ_RELEASE_ASSERT(!mapped);
  }

  {
    int foo = 42;
    auto someRef = ToMaybeRef(&foo);

    static_assert(std::is_same_v<decltype(someRef), Maybe<int&>>);

    MOZ_RELEASE_ASSERT(someRef.isSome());
    MOZ_RELEASE_ASSERT(&foo == &someRef.ref());
  }

  {
    int* fooPtr = nullptr;
    auto someRef = ToMaybeRef(fooPtr);

    static_assert(std::is_same_v<decltype(someRef), Maybe<int&>>);

    MOZ_RELEASE_ASSERT(someRef.isNothing());
  }

  return true;
}

static Maybe<int> IncrementAndReturnTag(BasicValue& aValue) {
  gFunctionWasApplied = true;
  aValue.SetTag(aValue.GetTag() + 1);
  return Some(aValue.GetTag());
}

static Maybe<int> AccessValueAndReturnNothing(const BasicValue&) {
  gFunctionWasApplied = true;
  return Nothing();
}

static Maybe<int> AccessValueAndReturnOther(const BasicValue&) {
  gFunctionWasApplied = true;
  return Some(42);
}

struct IncrementAndReturnTagFunctor {
  IncrementAndReturnTagFunctor() : mBy(1) {}

  Maybe<intoperator()(BasicValue& aValue) {
    aValue.SetTag(aValue.GetTag() + mBy.GetTag());
    return Some(aValue.GetTag());
  }

  BasicValue mBy;
};

struct AccessValueAndReturnOtherFunctor {
  explicit AccessValueAndReturnOtherFunctor(int aVal) : mBy(aVal) {}

  Maybe<BasicValue> operator()() {
    gFunctionWasApplied = true;
    return Some(mBy);
  }

  BasicValue mBy;
};

static bool TestAndThen() {
  // Check that andThen handles the 'Nothing' case.
  gFunctionWasApplied = false;
  Maybe<BasicValue> mayValue;
  Maybe<int> otherValue = mayValue.andThen(&AccessValueAndReturnOther);
  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(otherValue.isNothing());

  // Check that andThen handles the 'Some' case.
  mayValue = Some(BasicValue(1));
  otherValue = mayValue.andThen(&AccessValueAndReturnNothing);
  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(otherValue.isNothing());
  gFunctionWasApplied = false;
  otherValue = mayValue.andThen(&IncrementAndReturnTag);
  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
  MOZ_RELEASE_ASSERT(*otherValue == 2);
  gFunctionWasApplied = false;
  otherValue = mayValue.andThen(&AccessValueAndReturnOther);
  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(*otherValue == 42);

  // Check that andThen works with a const reference.
  const Maybe<BasicValue>& mayValueCRef = mayValue;
  gFunctionWasApplied = false;
  otherValue = mayValueCRef.andThen(&AccessValueAndReturnOther);
  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(*otherValue == 42);

  // Check that andThen works with functors.
  IncrementAndReturnTagFunctor tagIncrementer;
  MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);
  mayValue = Some(BasicValue(1));
  otherValue = mayValue.andThen(tagIncrementer);
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 2);
  MOZ_RELEASE_ASSERT(*otherValue == 2);
  MOZ_RELEASE_ASSERT(tagIncrementer.mBy.GetStatus() == eWasConstructed);

  // Check that andThen works with lambda expressions.
  gFunctionWasApplied = false;
  mayValue = Some(BasicValue(2));
  otherValue = mayValue.andThen(
      [](BasicValue& aVal) { return Some(aVal.GetTag() * 2); });
  MOZ_RELEASE_ASSERT(*otherValue == 4);
  otherValue = otherValue.andThen([](int aVal) { return Some(aVal * 2); });
  MOZ_RELEASE_ASSERT(*otherValue == 8);
  otherValue = mayValueCRef.andThen([&](const BasicValue& aVal) {
    gFunctionWasApplied = true;
    return Some(42);
  });
  MOZ_RELEASE_ASSERT(gFunctionWasApplied == true);
  MOZ_RELEASE_ASSERT(*otherValue == 42);

  // Check that andThen can move the contained value.
  mayValue = Some(BasicValue(1));
  otherValue = std::move(mayValue).andThen([&](BasicValue&& aVal) {
    BasicValue tmp = std::move(aVal);
    return Some(tmp.GetTag());
  });
  MOZ_RELEASE_ASSERT(mayValue.isNothing());
  MOZ_RELEASE_ASSERT(*otherValue == 1);

  return true;
}

static bool TestOrElse() {
  const auto createValue = [&](int aTag) {
    return [&, aTag]() -> Maybe<BasicValue> {
      gFunctionWasApplied = true;
      return Some(BasicValue(aTag));
    };
  };
  // Check that orElse handles the 'Some' case.
  gFunctionWasApplied = false;
  Maybe<BasicValue> mayValue = Some(BasicValue(1));
  Maybe<BasicValue> otherValue = mayValue.orElse(createValue(2));
  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);

  // Check that orElse handles the 'Nothing' case.
  mayValue = Nothing();
  otherValue = mayValue.orElse(createValue(1));
  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
  gFunctionWasApplied = false;
  otherValue = otherValue.orElse(createValue(2));
  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);

  // Check that orElse works with a const reference.
  mayValue = Nothing();
  const Maybe<BasicValue>& mayValueCRef = mayValue;
  gFunctionWasApplied = false;
  otherValue = mayValueCRef.orElse(createValue(1));
  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);

  // Check that orElse works with functors.
  gFunctionWasApplied = false;
  AccessValueAndReturnOtherFunctor accesser(42);
  mayValue = Some(BasicValue(1));
  otherValue = mayValue.orElse(accesser);
  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
  mayValue = Nothing();
  otherValue = mayValue.orElse(accesser);
  MOZ_RELEASE_ASSERT(gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(mayValue.isNothing());
  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 42);

  // Check that orElse works with lambda expressions.
  gFunctionWasApplied = false;
  mayValue = Nothing();
  otherValue = mayValue.orElse([] { return Some(BasicValue(1)); });
  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);
  mayValue = otherValue.orElse([] { return Some(BasicValue(2)); });
  MOZ_RELEASE_ASSERT(mayValue->GetTag() == 1);
  otherValue = mayValueCRef.orElse([&] {
    gFunctionWasApplied = true;
    return Some(BasicValue(42));
  });
  MOZ_RELEASE_ASSERT(!gFunctionWasApplied);
  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);

  // Check that orElse can move the contained value.
  mayValue = Some(BasicValue(1));
  otherValue = std::move(mayValue).orElse([] { return Some(BasicValue(2)); });
  MOZ_RELEASE_ASSERT(mayValue.isNothing());
  MOZ_RELEASE_ASSERT(otherValue->GetTag() == 1);

  return true;
}

static bool TestComposedMoves() {
  const auto moveAlong = [](UncopyableValue&& aValue) {
    return Some(std::move(aValue));
  };
  const auto justNothing = []() -> Maybe<UncopyableValue> { return Nothing(); };
  const auto createValue = [] { return Some(UncopyableValue()); };

  // Check that andThen and orElse can propagate a non-copyable value created
  // mid-chain.
  Maybe<UncopyableValue> mayValue;
  Maybe<UncopyableValue> movedValue = std::move(mayValue)
                                          .andThen(moveAlong)
                                          .orElse(createValue)
                                          .andThen(moveAlong);
  MOZ_RELEASE_ASSERT(mayValue.isNothing());
  MOZ_RELEASE_ASSERT(movedValue->GetStatus() == eWasMoveConstructed);

  // Check that andThen and orElse can propagate a non-copyable value created
  // pre-chain.
  mayValue = Some(UncopyableValue{});
  movedValue = std::move(mayValue)
                   .andThen(moveAlong)
                   .orElse(justNothing)
                   .andThen(moveAlong);
  MOZ_RELEASE_ASSERT(mayValue.isNothing());
  MOZ_RELEASE_ASSERT(movedValue->GetStatus() == eWasMoveAssigned);

  // Check that andThen and orElse can propagate a reference.
  {
    UncopyableValue val{};
    UncopyableValue otherVal{};
    const auto passAlong = [](UncopyableValue& aValue) {
      return SomeRef(aValue);
    };
    const auto fallbackToOther = [&]() { return SomeRef(otherVal); };

    Maybe<UncopyableValue&> mayRef = SomeRef(val);
    Maybe<UncopyableValue&> chainedRef =
        mayRef.andThen(passAlong).orElse(fallbackToOther).andThen(passAlong);
    MOZ_RELEASE_ASSERT(&val != &otherVal,
                       "Distinct values should not compare equal");
    MOZ_RELEASE_ASSERT(&*mayRef == &*chainedRef,
                       "Chain should pass along the same reference");
  }

  // Check that andThen and orElse can propagate a const reference.
  {
    const UncopyableValue val{};
    const UncopyableValue otherVal{};
    const auto passAlong = [](const UncopyableValue& aValue) {
      return SomeRef(aValue);
    };
    const auto fallbackToOther = [&]() { return SomeRef(otherVal); };

    Maybe<const UncopyableValue&> mayRef = SomeRef(val);
    Maybe<const UncopyableValue&> chainedRef =
        mayRef.andThen(passAlong).orElse(fallbackToOther).andThen(passAlong);
    MOZ_RELEASE_ASSERT(&val != &otherVal,
                       "Distinct values should not compare equal");
    MOZ_RELEASE_ASSERT(&*mayRef == &*chainedRef,
                       "Chain should pass along the same reference");
  }

  return true;
}

// These are quasi-implementation details, but we assert them here to prevent
// backsliding to earlier times when Maybe<T> for smaller T took up more space
// than T's alignment required.

static_assert(sizeof(Maybe<char>) == 2 * sizeof(char),
              "Maybe shouldn't bloat at all ");
static_assert(sizeof(Maybe<bool>) <= 2 * sizeof(bool),
              "Maybe shouldn't bloat");
static_assert(sizeof(Maybe<int>) <= 2 * sizeof(int),
              "Maybe shouldn't bloat");
static_assert(sizeof(Maybe<long>) <= 2 * sizeof(long),
              "Maybe shouldn't bloat");
static_assert(sizeof(Maybe<double>) <= 2 * sizeof(double),
              "Maybe shouldn't bloat");
static_assert(sizeof(Maybe<int&>) == sizeof(int*));

int main() {
  RUN_TEST(TestBasicFeatures);
  RUN_TEST(TestCopyAndMove);
  RUN_TEST(TestFunctionalAccessors);
  RUN_TEST(TestApply);
  RUN_TEST(TestMap);
  RUN_TEST(TestToMaybe);
  RUN_TEST(TestComparisonOperators);
  RUN_TEST(TestVirtualFunction);
  RUN_TEST(TestSomeNullptrConversion);
  RUN_TEST(TestSomePointerConversion);
  RUN_TEST(TestTypeConversion);
  RUN_TEST(TestReference);
  RUN_TEST(TestAndThen);
  RUN_TEST(TestOrElse);
  RUN_TEST(TestComposedMoves);

  return 0;
}

100%


¤ Dauer der Verarbeitung: 0.40 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.