/* -*- 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 #include #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()); \ if (!cond) return 1; \ cond = AllDestructorsWereCalled(); \ MOZ_ASSERT(cond, "Failed to destroy all objects during test: " #t); \ if (!cond) return 1; \ } 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>); static_assert(std::is_trivially_copy_constructible_v>); static_assert(std::is_trivially_copy_assignable_v>); static_assert(42 == Some(42).value()); static_assert(42 == Some(42).valueOr(43)); static_assert(42 == Maybe{}.valueOr(42)); static_assert(42 == Some(42).valueOrFrom([] { return 43; })); static_assert(42 == Maybe{}.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(std::in_place, 43) == Maybe(std::in_place, 42).map([](int val) { return val + 1; })); struct TriviallyDestructible { TriviallyDestructible() { // not trivially constructible } }; static_assert(std::is_trivially_destructible_v>); 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>); static_assert(!std::is_copy_constructible_v>); static_assert(!std::is_copy_assignable_v>); static_assert(std::is_move_constructible_v>); static_assert(std::is_move_assignable_v>); constexpr Maybe someUncopyable = Some(UncopyableValueLiteralType{42}); static_assert(someUncopyable.isSome()); static_assert(42 == someUncopyable->mValue); constexpr Maybe someUncopyableAssigned = [] { auto res = Maybe{}; res = Some(UncopyableValueLiteralType{42}); return res; }(); static_assert(someUncopyableAssigned.isSome()); static_assert(42 == someUncopyableAssigned->mValue); static bool TestBasicFeatures() { // Check that a Maybe is initialized to Nothing. Maybe mayValue; static_assert(std::is_same_v, "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, "operator*() should return a BasicValue&"); MOZ_RELEASE_ASSERT(mayValue.value() == BasicValue()); static_assert(std::is_same_v, "value() should return a BasicValue"); MOZ_RELEASE_ASSERT(mayValue.ref() == BasicValue()); static_assert(std::is_same_v, "ref() should return a BasicValue&"); MOZ_RELEASE_ASSERT(mayValue.ptr() != nullptr); static_assert(std::is_same_v, "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(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& 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, "operator*() should return a BasicValue"); MOZ_RELEASE_ASSERT(mayValueCRef.value() == BasicValue()); static_assert(std::is_same_v, "value() should return a BasicValue"); MOZ_RELEASE_ASSERT(mayValueCRef.ref() == BasicValue()); static_assert(std::is_same_v, "ref() should return a const BasicValue&"); MOZ_RELEASE_ASSERT(mayValueCRef.ptr() != nullptr); static_assert(std::is_same_v, "ptr() should return a const BasicValue*"); MOZ_RELEASE_ASSERT(mayValueCRef->GetStatus() == eWasDefaultConstructed); mayValue.reset(); // Check that we can create and reference Maybe. Maybe mayCValue1 = Some(BasicValue(5)); MOZ_RELEASE_ASSERT(mayCValue1); MOZ_RELEASE_ASSERT(mayCValue1.isSome()); MOZ_RELEASE_ASSERT(*mayCValue1 == BasicValue(5)); const Maybe& mayCValue1Ref = mayCValue1; MOZ_RELEASE_ASSERT(mayCValue1Ref == mayCValue1); MOZ_RELEASE_ASSERT(*mayCValue1Ref == BasicValue(5)); Maybe 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, "operator*() should return a BasicValue&&"); MOZ_RELEASE_ASSERT(Some(BasicValue()).value() == BasicValue()); static_assert( std::is_same_v, "value() should return a BasicValue"); MOZ_RELEASE_ASSERT(Some(BasicValue()).ref() == BasicValue()); static_assert( std::is_same_v, "ref() should return a BasicValue&&"); MOZ_RELEASE_ASSERT(Some(BasicValue()).ptr() != nullptr); static_assert(std::is_same_v, "ptr() should return a BasicValue*"); MOZ_RELEASE_ASSERT(Some(BasicValue())->GetStatus() == eWasMoveConstructed); // Check that accessors work through const-rvalue-references. auto MakeConstMaybe = []() -> const Maybe { 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, "operator*() should return a const BasicValue&&"); MOZ_RELEASE_ASSERT(MakeConstMaybe().value() == BasicValue()); static_assert(std::is_same_v, "value() should return a BasicValue"); MOZ_RELEASE_ASSERT(MakeConstMaybe().ref() == BasicValue()); static_assert( std::is_same_v, "ref() should return a const BasicValue&&"); MOZ_RELEASE_ASSERT(MakeConstMaybe().ptr() != nullptr); static_assert( std::is_same_v, "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 static void TestCopyMaybe() { { MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); Maybe src = Some(T()); Maybe dstCopyConstructed = src; MOZ_RELEASE_ASSERT(2 == sUndestroyedObjects); MOZ_RELEASE_ASSERT(dstCopyConstructed->GetStatus() == eWasCopyConstructed); } { MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); Maybe src = Some(T()); Maybe dstCopyAssigned; dstCopyAssigned = src; MOZ_RELEASE_ASSERT(2 == sUndestroyedObjects); MOZ_RELEASE_ASSERT(dstCopyAssigned->GetStatus() == eWasCopyConstructed); } } template static void TestMoveMaybe() { { MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); Maybe src = Some(T()); Maybe dstMoveConstructed = std::move(src); MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects); MOZ_RELEASE_ASSERT(dstMoveConstructed->GetStatus() == eWasMoveConstructed); } { MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); Maybe src = Some(T()); Maybe dstMoveAssigned; dstMoveAssigned = std::move(src); MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects); MOZ_RELEASE_ASSERT(dstMoveAssigned->GetStatus() == eWasMoveConstructed); } { MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); Maybe src = Some(T()); Maybe dstMoveConstructed = src.take(); MOZ_RELEASE_ASSERT(1 == sUndestroyedObjects); MOZ_RELEASE_ASSERT(dstMoveConstructed->GetStatus() == eWasMoveConstructed); } { MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); Maybe 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 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 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 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 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(); TestMoveMaybe(); } MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); { // Check that we always get copies for types that don't support moves. { Maybe mayUnmovableValue = Some(UnmovableValue()); MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed); mayUnmovableValue = Some(UnmovableValue()); MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyAssigned); mayUnmovableValue.reset(); mayUnmovableValue.emplace(UnmovableValue()); MOZ_RELEASE_ASSERT(mayUnmovableValue->GetStatus() == eWasCopyConstructed); } TestCopyMaybe(); static_assert(std::is_copy_constructible_v>); static_assert(std::is_copy_assignable_v>); // XXX Why do these static_asserts not hold? // static_assert(!std::is_move_constructible_v>); // static_assert(!std::is_move_assignable_v>); } MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); { // Check that types that only support moves, but not copies, work. { Maybe 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(); static_assert(!std::is_copy_constructible_v>); static_assert(!std::is_copy_assignable_v>); static_assert(std::is_move_constructible_v>); static_assert(std::is_move_assignable_v>); } MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); { // Check that types that support neither moves or copies work. { const auto mayUncopyableUnmovableValueConstructed = Maybe{std::in_place}; MOZ_RELEASE_ASSERT(mayUncopyableUnmovableValueConstructed->GetStatus() == eWasDefaultConstructed); } Maybe 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>); static_assert(!std::is_copy_assignable_v>); static_assert( !std::is_move_constructible_v>); static_assert(!std::is_move_assignable_v>); } { // Test copy and move with a trivially copyable and trivially destructible // type. { constexpr Maybe src = Some(42); constexpr Maybe 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 src = Some(42); Maybe dstCopyAssigned; dstCopyAssigned = src; MOZ_RELEASE_ASSERT(src.isSome()); MOZ_RELEASE_ASSERT(dstCopyAssigned.isSome()); MOZ_RELEASE_ASSERT(42 == *src); MOZ_RELEASE_ASSERT(42 == *dstCopyAssigned); } { Maybe src = Some(42); const Maybe dstMoveConstructed = std::move(src); MOZ_RELEASE_ASSERT(!src.isSome()); MOZ_RELEASE_ASSERT(dstMoveConstructed.isSome()); MOZ_RELEASE_ASSERT(42 == *dstMoveConstructed); } { Maybe src = Some(42); Maybe 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 someValue = Some(BasicValue(3)); MOZ_RELEASE_ASSERT(someValue.valueOr(value) == BasicValue(3)); static_assert(std::is_same_v, "valueOr should return a BasicValue"); MOZ_RELEASE_ASSERT(someValue.valueOrFrom(&MakeBasicValue) == BasicValue(3)); static_assert( std::is_same_v, "valueOrFrom should return a BasicValue"); MOZ_RELEASE_ASSERT(someValue.ptrOr(&value) != &value); static_assert(std::is_same_v, "ptrOr should return a BasicValue*"); MOZ_RELEASE_ASSERT(*someValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3)); static_assert( std::is_same_v, "ptrOrFrom should return a BasicValue*"); MOZ_RELEASE_ASSERT(someValue.refOr(value) == BasicValue(3)); static_assert(std::is_same_v, "refOr should return a BasicValue&"); MOZ_RELEASE_ASSERT(someValue.refOrFrom(&MakeBasicValueRef) == BasicValue(3)); static_assert( std::is_same_v, "refOrFrom should return a BasicValue&"); // Check that the 'some' case works through a const reference. const Maybe& someValueCRef = someValue; MOZ_RELEASE_ASSERT(someValueCRef.valueOr(value) == BasicValue(3)); static_assert( std::is_same_v, "valueOr should return a BasicValue"); MOZ_RELEASE_ASSERT(someValueCRef.valueOrFrom(&MakeBasicValue) == BasicValue(3)); static_assert( std::is_same_v, "valueOrFrom should return a BasicValue"); MOZ_RELEASE_ASSERT(someValueCRef.ptrOr(&value) != &value); static_assert( std::is_same_v, "ptrOr should return a const BasicValue*"); MOZ_RELEASE_ASSERT(*someValueCRef.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(3)); static_assert( std::is_same_v, "ptrOrFrom should return a const BasicValue*"); MOZ_RELEASE_ASSERT(someValueCRef.refOr(value) == BasicValue(3)); static_assert( std::is_same_v, "refOr should return a const BasicValue&"); MOZ_RELEASE_ASSERT(someValueCRef.refOrFrom(&MakeBasicValueRef) == BasicValue(3)); static_assert( std::is_same_v, "refOrFrom should return a const BasicValue&"); // Check that the 'none' case of functional accessors works. Maybe noneValue; MOZ_RELEASE_ASSERT(noneValue.valueOr(value) == BasicValue(9)); static_assert(std::is_same_v, "valueOr should return a BasicValue"); MOZ_RELEASE_ASSERT(noneValue.valueOrFrom(&MakeBasicValue) == BasicValue(9)); static_assert( std::is_same_v, "valueOrFrom should return a BasicValue"); MOZ_RELEASE_ASSERT(noneValue.ptrOr(&value) == &value); static_assert(std::is_same_v, "ptrOr should return a BasicValue*"); MOZ_RELEASE_ASSERT(*noneValue.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9)); static_assert( std::is_same_v, "ptrOrFrom should return a BasicValue*"); MOZ_RELEASE_ASSERT(noneValue.refOr(value) == BasicValue(9)); static_assert(std::is_same_v, "refOr should return a BasicValue&"); MOZ_RELEASE_ASSERT(noneValue.refOrFrom(&MakeBasicValueRef) == BasicValue(9)); static_assert( std::is_same_v, "refOrFrom should return a BasicValue&"); // Check that the 'none' case works through a const reference. const Maybe& noneValueCRef = noneValue; MOZ_RELEASE_ASSERT(noneValueCRef.valueOr(value) == BasicValue(9)); static_assert( std::is_same_v, "valueOr should return a BasicValue"); MOZ_RELEASE_ASSERT(noneValueCRef.valueOrFrom(&MakeBasicValue) == BasicValue(9)); static_assert( std::is_same_v, "valueOrFrom should return a BasicValue"); MOZ_RELEASE_ASSERT(noneValueCRef.ptrOr(&value) == &value); static_assert( std::is_same_v, "ptrOr should return a const BasicValue*"); MOZ_RELEASE_ASSERT(*noneValueCRef.ptrOrFrom(&MakeBasicValuePtr) == BasicValue(9)); static_assert( std::is_same_v, "ptrOrFrom should return a const BasicValue*"); MOZ_RELEASE_ASSERT(noneValueCRef.refOr(value) == BasicValue(9)); static_assert( std::is_same_v, "refOr should return a const BasicValue&"); MOZ_RELEASE_ASSERT(noneValueCRef.refOrFrom(&MakeBasicValueRef) == BasicValue(9)); static_assert( std::is_same_v, "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 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& 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); 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 mayValue; MOZ_RELEASE_ASSERT(mayValue.map(&TimesTwo) == Nothing()); static_assert(std::is_same_v, 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& mayValueCRef = mayValue; MOZ_RELEASE_ASSERT(mayValueCRef.map(&TimesTwo) == Some(4)); static_assert( std::is_same_v, 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 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 function object qualifiers are preserved when invoked. struct F { std::integral_constant operator()(int) & { return {}; } std::integral_constant operator()(int) const& { return {}; } std::integral_constant operator()(int) && { return {}; } std::integral_constant operator()(int) const&& { return {}; } }; Maybe mi = Some(0); const Maybe cmi = Some(0); F f; static_assert(std::is_same>>::value, "Maybe.map(&)"); MOZ_RELEASE_ASSERT(mi.map(f).value()() == 1); static_assert(std::is_same>>::value, "const Maybe.map(&)"); MOZ_RELEASE_ASSERT(cmi.map(f).value()() == 1); const F cf; static_assert(std::is_same>>::value, "Maybe.map(const &)"); MOZ_RELEASE_ASSERT(mi.map(cf).value() == 2); static_assert(std::is_same>>::value, "const Maybe.map(const &)"); MOZ_RELEASE_ASSERT(cmi.map(cf).value() == 2); static_assert(std::is_same>>::value, "Maybe.map(&&)"); MOZ_RELEASE_ASSERT(mi.map(F{}).value() == 3); static_assert(std::is_same>>::value, "const Maybe.map(&&)"); MOZ_RELEASE_ASSERT(cmi.map(F{}).value() == 3); using CF = const F; static_assert(std::is_same>>::value, "Maybe.map(const &&)"); MOZ_RELEASE_ASSERT(mi.map(CF{}).value() == 4); static_assert(std::is_same>>::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 mayValue = ToMaybe(&value); static_assert(std::is_same_v, 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, decltype(ToMaybe(nullPointer))>, "ToMaybe should return a Maybe"); MOZ_RELEASE_ASSERT(mayValue.isNothing()); return true; } static bool TestComparisonOperators() { Maybe nothingValue = Nothing(); Maybe anotherNothingValue = Nothing(); Maybe oneValue = Some(BasicValue(1)); Maybe anotherOneValue = Some(BasicValue(1)); Maybe 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 super; super.emplace(); super.reset(); Maybe derived; derived.emplace(); derived.reset(); // If this compiles successfully, we've passed. return true; } static Maybe ReturnSomeNullptr() { return Some(nullptr); } struct D { explicit D(const Maybe&) {} }; static bool TestSomeNullptrConversion() { Maybe 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 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 ReturnDerivedPointer() { Derived* d = nullptr; return Some(d); } struct ExplicitConstructorBasePointer { explicit ExplicitConstructorBasePointer(const Maybe&) {} }; static bool TestSomePointerConversion() { Base base; Derived derived; Maybe 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 m3 = Some(&base); MOZ_RELEASE_ASSERT(m3.isSome()); MOZ_RELEASE_ASSERT(m3); MOZ_RELEASE_ASSERT(*m3 == &base); auto s1 = Some(&derived); Maybe 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 src = Some(SourceType1{1}); Maybe 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 src = Some(SourceType1{1}); Maybe 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 src = Some(SourceType2{1}); Maybe 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 src = Some(SourceType2{1}); Maybe 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 src = Some(1); Maybe 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 src = Some(1); Maybe 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 src = Some(SourceType1{1}); Maybe 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 src = Some(SourceType1{1}); Maybe 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 src = Some(1); Maybe 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 src = Some(1); Maybe 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>); static_assert(std::is_trivially_copy_constructible_v>); static_assert(std::is_trivially_copy_assignable_v>); static_assert(Maybe{}.isNothing()); static_assert(Maybe{Nothing{}}.isNothing()); { Maybe defaultConstructed; MOZ_RELEASE_ASSERT(defaultConstructed.isNothing()); MOZ_RELEASE_ASSERT(!defaultConstructed.isSome()); MOZ_RELEASE_ASSERT(!defaultConstructed); } { Maybe nothing = Nothing(); MOZ_RELEASE_ASSERT(nothing.isNothing()); MOZ_RELEASE_ASSERT(!nothing.isSome()); MOZ_RELEASE_ASSERT(!nothing); } { int foo = 42, bar = 42; Maybe 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 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 defaultConstructed; defaultConstructed.reset(); MOZ_RELEASE_ASSERT(defaultConstructed.isNothing()); MOZ_RELEASE_ASSERT(!defaultConstructed.isSome()); MOZ_RELEASE_ASSERT(!defaultConstructed); } { int foo = 42; Maybe some = SomeRef(foo); some.reset(); MOZ_RELEASE_ASSERT(some.isNothing()); MOZ_RELEASE_ASSERT(!some.isSome()); MOZ_RELEASE_ASSERT(!some); } { int foo = 42; Maybe some = SomeRef(foo); auto& applied = some.apply([](int& ref) { ref++; }); MOZ_RELEASE_ASSERT(&some == &applied); MOZ_RELEASE_ASSERT(43 == foo); } { Maybe nothing; auto& applied = nothing.apply([](int& ref) { ref++; }); MOZ_RELEASE_ASSERT(¬hing == &applied); } { int foo = 42; Maybe some = SomeRef(foo); auto mapped = some.map([](int& ref) { return &ref; }); static_assert(std::is_same_v>); MOZ_RELEASE_ASSERT(&foo == *mapped); } { Maybe 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>); MOZ_RELEASE_ASSERT(someRef.isSome()); MOZ_RELEASE_ASSERT(&foo == &someRef.ref()); } { int* fooPtr = nullptr; auto someRef = ToMaybeRef(fooPtr); static_assert(std::is_same_v>); MOZ_RELEASE_ASSERT(someRef.isNothing()); } return true; } // These are quasi-implementation details, but we assert them here to prevent // backsliding to earlier times when Maybe for smaller T took up more space // than T's alignment required. static_assert(sizeof(Maybe) == 2 * sizeof(char), "Maybe shouldn't bloat at all "); static_assert(sizeof(Maybe) <= 2 * sizeof(bool), "Maybe shouldn't bloat"); static_assert(sizeof(Maybe) <= 2 * sizeof(int), "Maybe shouldn't bloat"); static_assert(sizeof(Maybe) <= 2 * sizeof(long), "Maybe shouldn't bloat"); static_assert(sizeof(Maybe) <= 2 * sizeof(double), "Maybe shouldn't bloat"); static_assert(sizeof(Maybe) == 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); return 0; }