From def92d1b8e9d373e2f6f27c366d578d97d8960c6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:34:50 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- mfbt/Assertions.h | 34 ++++-- mfbt/Attributes.h | 8 ++ mfbt/Maybe.h | 212 +++++++++++++++++++++++++++++++++---- mfbt/tests/TestMaybe.cpp | 266 ++++++++++++++++++++++++++++++++++++++++++++++- mfbt/tests/TestUtf8.cpp | 10 -- 5 files changed, 485 insertions(+), 45 deletions(-) (limited to 'mfbt') diff --git a/mfbt/Assertions.h b/mfbt/Assertions.h index 634d340579..53b2930bf7 100644 --- a/mfbt/Assertions.h +++ b/mfbt/Assertions.h @@ -17,6 +17,18 @@ # define MOZ_BUFFER_STDERR #endif +// It appears that this is sometimes compiled without XP_WIN +#if defined(_WIN32) +# include +# define MOZ_GET_PID() _getpid() +#elif !defined(__wasi__) +# include +# define MOZ_GET_PID() getpid() +#else +// Prevent compiler warning +# define MOZ_GET_PID() -1 +#endif + #include "mozilla/Attributes.h" #include "mozilla/Compiler.h" #include "mozilla/Fuzzing.h" @@ -95,14 +107,15 @@ MOZ_MAYBE_UNUSED static void MOZ_ReportAssertionFailurePrintFrame( * method is primarily for internal use in this header, and only secondarily * for use in implementing release-build assertions. */ + MOZ_MAYBE_UNUSED static MOZ_COLD MOZ_NEVER_INLINE void MOZ_ReportAssertionFailure(const char* aStr, const char* aFilename, int aLine) MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS { MOZ_FUZZING_HANDLE_CRASH_EVENT4("MOZ_ASSERT", aFilename, aLine, aStr); #ifdef ANDROID __android_log_print(ANDROID_LOG_FATAL, "MOZ_Assert", - "Assertion failure: %s, at %s:%d\n", aStr, aFilename, - aLine); + "[%d] Assertion failure: %s, at %s:%d\n", MOZ_GET_PID(), + aStr, aFilename, aLine); # if defined(MOZ_DUMP_ASSERTION_STACK) MozWalkTheStackWithWriter(MOZ_ReportAssertionFailurePrintFrame, CallerPC(), /* aMaxFrames */ 0); @@ -110,11 +123,12 @@ MOZ_ReportAssertionFailure(const char* aStr, const char* aFilename, #else # if defined(MOZ_BUFFER_STDERR) char msg[1024] = ""; - snprintf(msg, sizeof(msg) - 1, "Assertion failure: %s, at %s:%d\n", aStr, - aFilename, aLine); + snprintf(msg, sizeof(msg) - 1, "[%d] Assertion failure: %s, at %s:%d\n", + MOZ_GET_PID(), aStr, aFilename, aLine); fputs(msg, stderr); # else - fprintf(stderr, "Assertion failure: %s, at %s:%d\n", aStr, aFilename, aLine); + fprintf(stderr, "[%d] Assertion failure: %s, at %s:%d\n", MOZ_GET_PID(), aStr, + aFilename, aLine); # endif # if defined(MOZ_DUMP_ASSERTION_STACK) MozWalkTheStack(stderr, CallerPC(), /* aMaxFrames */ 0); @@ -128,15 +142,17 @@ MOZ_MAYBE_UNUSED static MOZ_COLD MOZ_NEVER_INLINE void MOZ_ReportCrash( int aLine) MOZ_PRETEND_NORETURN_FOR_STATIC_ANALYSIS { #ifdef ANDROID __android_log_print(ANDROID_LOG_FATAL, "MOZ_CRASH", - "Hit MOZ_CRASH(%s) at %s:%d\n", aStr, aFilename, aLine); + "[%d] Hit MOZ_CRASH(%s) at %s:%d\n", MOZ_GET_PID(), aStr, + aFilename, aLine); #else # if defined(MOZ_BUFFER_STDERR) char msg[1024] = ""; - snprintf(msg, sizeof(msg) - 1, "Hit MOZ_CRASH(%s) at %s:%d\n", aStr, - aFilename, aLine); + snprintf(msg, sizeof(msg) - 1, "[%d] Hit MOZ_CRASH(%s) at %s:%d\n", + MOZ_GET_PID(), aStr, aFilename, aLine); fputs(msg, stderr); # else - fprintf(stderr, "Hit MOZ_CRASH(%s) at %s:%d\n", aStr, aFilename, aLine); + fprintf(stderr, "[%d] Hit MOZ_CRASH(%s) at %s:%d\n", MOZ_GET_PID(), aStr, + aFilename, aLine); # endif # if defined(MOZ_DUMP_ASSERTION_STACK) MozWalkTheStack(stderr, CallerPC(), /* aMaxFrames */ 0); diff --git a/mfbt/Attributes.h b/mfbt/Attributes.h index b4b0316a3a..3be46cf5a8 100644 --- a/mfbt/Attributes.h +++ b/mfbt/Attributes.h @@ -1031,4 +1031,12 @@ # define MOZ_EMPTY_BASES #endif +// XXX: GCC somehow does not allow attributes before lambda return types, while +// clang requires so. See also bug 1627007. +#ifdef __clang__ +# define MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA MOZ_CAN_RUN_SCRIPT_BOUNDARY +#else +# define MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA +#endif + #endif /* mozilla_Attributes_h */ diff --git a/mfbt/Maybe.h b/mfbt/Maybe.h index 100b139c79..2f92f753c0 100644 --- a/mfbt/Maybe.h +++ b/mfbt/Maybe.h @@ -9,6 +9,7 @@ #ifndef mozilla_Maybe_h #define mozilla_Maybe_h +#include #include // for placement new #include #include @@ -202,6 +203,8 @@ class Maybe_CopyMove_Enabler { public: Maybe_CopyMove_Enabler() = default; + Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete; + Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete; MOZ_MAYBE_MOVE_OPS() private: @@ -214,6 +217,8 @@ class Maybe_CopyMove_Enabler { Maybe_CopyMove_Enabler() = default; MOZ_MAYBE_COPY_OPS() + Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&&) = delete; + Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&&) = delete; private: MOZ_MAYBE_DOWNCAST() @@ -288,6 +293,15 @@ struct MaybeStorage : MaybeStorageBase { mIsSome{true} {} }; +template +struct IsMaybeImpl : std::false_type {}; + +template +struct IsMaybeImpl> : std::true_type {}; + +template +using IsMaybe = IsMaybeImpl>; + } // namespace detail template >> + std::enable_if_t, bool> = true> MOZ_IMPLICIT Maybe(const Maybe& aOther) { if (aOther.isSome()) { emplace(*aOther); } } + template , + bool> = true> + explicit Maybe(const Maybe& aOther) = delete; + /** * Maybe can be move-constructed from a Maybe if T is constructible from * a U&&. */ template >> + std::enable_if_t, bool> = true> MOZ_IMPLICIT Maybe(Maybe&& aOther) { if (aOther.isSome()) { emplace(std::move(*aOther)); aOther.reset(); } } + template , bool> = true> + explicit Maybe(Maybe&& aOther) = delete; template >> + std::enable_if_t, bool> = true> Maybe& operator=(const Maybe& aOther) { if (aOther.isSome()) { if (mIsSome) { @@ -417,8 +438,12 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe return *this; } + template , + bool> = true> + Maybe& operator=(const Maybe& aOther) = delete; + template >> + std::enable_if_t, bool> = true> Maybe& operator=(Maybe&& aOther) { if (aOther.isSome()) { if (mIsSome) { @@ -434,6 +459,10 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe return *this; } + template , bool> = true> + Maybe& operator=(Maybe&& aOther) = delete; + constexpr Maybe& operator=(Nothing) { reset(); return *this; @@ -455,7 +484,7 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe * without calling the destructor. The internal storage is also reset to * avoid multiple calls. Unsafe unless |isSome()|. */ - T extract() { + constexpr T extract() { MOZ_RELEASE_ASSERT(isSome()); T v = std::move(mStorage.val); reset(); @@ -597,7 +626,7 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe /* If |isSome()|, runs the provided function or functor on the contents of * this Maybe. */ template - constexpr Maybe& apply(Func&& aFunc) { + constexpr Maybe& apply(Func&& aFunc) & { if (isSome()) { std::forward(aFunc)(ref()); } @@ -605,20 +634,36 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe } template - constexpr const Maybe& apply(Func&& aFunc) const { + constexpr const Maybe& apply(Func&& aFunc) const& { if (isSome()) { std::forward(aFunc)(ref()); } return *this; } + template + constexpr Maybe& apply(Func&& aFunc) && { + if (isSome()) { + std::forward(aFunc)(extract()); + } + return *this; + } + + template + constexpr Maybe& apply(Func&& aFunc) const&& { + if (isSome()) { + std::forward(aFunc)(extract()); + } + return *this; + } + /* * If |isSome()|, runs the provided function and returns the result wrapped * in a Maybe. If |isNothing()|, returns an empty Maybe value with the same * value type as what the provided function would have returned. */ template - constexpr auto map(Func&& aFunc) { + constexpr auto map(Func&& aFunc) & { if (isSome()) { return Some(std::forward(aFunc)(ref())); } @@ -626,13 +671,132 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe } template - constexpr auto map(Func&& aFunc) const { + constexpr auto map(Func&& aFunc) const& { if (isSome()) { return Some(std::forward(aFunc)(ref())); } return Maybe(aFunc)(ref()))>{}; } + template + constexpr auto map(Func&& aFunc) && { + if (isSome()) { + return Some(std::forward(aFunc)(extract())); + } + return Maybe(aFunc)(extract()))>{}; + } + + template + constexpr auto map(Func&& aFunc) const&& { + if (isSome()) { + return Some(std::forward(aFunc)(extract())); + } + return Maybe(aFunc)(extract()))>{}; + } + + /* + * If |isSome()|, runs the provided function or functor on the contents of + * this Maybe and returns the result. Note that the provided function or + * functor must return a Maybe of any type U. + * If |isNothing()|, returns an empty Maybe value with the same type as what + * the provided function would have returned. + */ + template + constexpr auto andThen(Func&& aFunc) & { + static_assert(std::is_invocable_v); + using U = std::invoke_result_t; + static_assert(detail::IsMaybe::value); + if (isSome()) { + return std::invoke(std::forward(aFunc), ref()); + } + return std::remove_cv_t>{}; + } + + template + constexpr auto andThen(Func&& aFunc) const& { + static_assert(std::is_invocable_v); + using U = std::invoke_result_t; + static_assert(detail::IsMaybe::value); + if (isSome()) { + return std::invoke(std::forward(aFunc), ref()); + } + return std::remove_cv_t>{}; + } + + template + constexpr auto andThen(Func&& aFunc) && { + static_assert(std::is_invocable_v); + using U = std::invoke_result_t; + static_assert(detail::IsMaybe::value); + if (isSome()) { + return std::invoke(std::forward(aFunc), extract()); + } + return std::remove_cv_t>{}; + } + + template + constexpr auto andThen(Func&& aFunc) const&& { + static_assert(std::is_invocable_v); + using U = std::invoke_result_t; + static_assert(detail::IsMaybe::value); + if (isSome()) { + return std::invoke(std::forward(aFunc), extract()); + } + return std::remove_cv_t>{}; + } + + /* + * If |isNothing()|, runs the provided function or functor and returns its + * result. If |isSome()|, returns the contained value wrapped in a Maybe. + */ + template + constexpr Maybe orElse(Func&& aFunc) & { + static_assert(std::is_invocable_v); + using U = std::invoke_result_t; + static_assert( + std::is_same_v>>); + if (isSome()) { + return *this; + } + return std::invoke(std::forward(aFunc)); + } + + template + constexpr Maybe orElse(Func&& aFunc) const& { + static_assert(std::is_invocable_v); + using U = std::invoke_result_t; + static_assert( + std::is_same_v>>); + if (isSome()) { + return *this; + } + return std::invoke(std::forward(aFunc)); + } + + template + constexpr Maybe orElse(Func&& aFunc) && { + static_assert(std::is_invocable_v); + using U = std::invoke_result_t; + static_assert( + std::is_same_v>>); + if (isSome()) { + return std::move(*this); + } + return std::invoke(std::forward(aFunc)); + } + + template + constexpr Maybe orElse(Func&& aFunc) const&& { + static_assert(std::is_invocable_v); + using U = std::invoke_result_t; + static_assert( + std::is_same_v>>); + if (isSome()) { + return std::move(*this); + } + return std::invoke(std::forward(aFunc)); + } + /* If |isSome()|, empties this Maybe and destroys its contents. */ constexpr void reset() { if (isSome()) { @@ -705,7 +869,7 @@ class Maybe { void reset() { mValue = nullptr; } template - Maybe& apply(Func&& aFunc) { + const Maybe& apply(Func&& aFunc) const { if (isSome()) { std::forward(aFunc)(ref()); } @@ -713,29 +877,35 @@ class Maybe { } template - const Maybe& apply(Func&& aFunc) const { + auto map(Func&& aFunc) const { + Maybe(aFunc)(ref()))> val; if (isSome()) { - std::forward(aFunc)(ref()); + val.emplace(std::forward(aFunc)(ref())); } - return *this; + return val; } template - auto map(Func&& aFunc) { - Maybe(aFunc)(ref()))> val; + constexpr auto andThen(Func&& aFunc) const { + static_assert(std::is_invocable_v); + using U = std::invoke_result_t; + static_assert(detail::IsMaybe::value); if (isSome()) { - val.emplace(std::forward(aFunc)(ref())); + return std::invoke(std::forward(aFunc), ref()); } - return val; + return std::remove_cv_t>{}; } template - auto map(Func&& aFunc) const { - Maybe(aFunc)(ref()))> val; + constexpr Maybe orElse(Func&& aFunc) const { + static_assert(std::is_invocable_v); + using U = std::invoke_result_t; + static_assert( + std::is_same_v>>); if (isSome()) { - val.emplace(std::forward(aFunc)(ref())); + return *this; } - return val; + return std::invoke(std::forward(aFunc)); } bool refEquals(const Maybe& aOther) const { diff --git a/mfbt/tests/TestMaybe.cpp b/mfbt/tests/TestMaybe.cpp index 2c56b85b6d..f9e5ae00dc 100644 --- a/mfbt/tests/TestMaybe.cpp +++ b/mfbt/tests/TestMaybe.cpp @@ -519,8 +519,6 @@ static bool TestCopyAndMove() { { 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); @@ -530,9 +528,8 @@ static bool TestCopyAndMove() { 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>); + static_assert(!std::is_move_constructible_v>); + static_assert(!std::is_move_assignable_v>); } MOZ_RELEASE_ASSERT(0 == sUndestroyedObjects); @@ -837,6 +834,15 @@ static bool TestApply() { [&](const BasicValue& aVal) { gFunctionWasApplied = true; }); MOZ_RELEASE_ASSERT(gFunctionWasApplied == true); + // Check that apply can move the contained value. + mayValue = Some(BasicValue(1)); + Maybe 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; } @@ -898,6 +904,14 @@ static bool TestMap() { [&](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 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 operator()(int) & { return {}; } @@ -1439,6 +1453,245 @@ static bool TestReference() { return true; } +static Maybe IncrementAndReturnTag(BasicValue& aValue) { + gFunctionWasApplied = true; + aValue.SetTag(aValue.GetTag() + 1); + return Some(aValue.GetTag()); +} + +static Maybe AccessValueAndReturnNothing(const BasicValue&) { + gFunctionWasApplied = true; + return Nothing(); +} + +static Maybe AccessValueAndReturnOther(const BasicValue&) { + gFunctionWasApplied = true; + return Some(42); +} + +struct IncrementAndReturnTagFunctor { + IncrementAndReturnTagFunctor() : mBy(1) {} + + Maybe operator()(BasicValue& aValue) { + aValue.SetTag(aValue.GetTag() + mBy.GetTag()); + return Some(aValue.GetTag()); + } + + BasicValue mBy; +}; + +struct AccessValueAndReturnOtherFunctor { + explicit AccessValueAndReturnOtherFunctor(int aVal) : mBy(aVal) {} + + Maybe operator()() { + gFunctionWasApplied = true; + return Some(mBy); + } + + BasicValue mBy; +}; + +static bool TestAndThen() { + // Check that andThen handles the 'Nothing' case. + gFunctionWasApplied = false; + Maybe mayValue; + Maybe 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& 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 { + gFunctionWasApplied = true; + return Some(BasicValue(aTag)); + }; + }; + // Check that orElse handles the 'Some' case. + gFunctionWasApplied = false; + Maybe mayValue = Some(BasicValue(1)); + Maybe 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& 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 { return Nothing(); }; + const auto createValue = [] { return Some(UncopyableValue()); }; + + // Check that andThen and orElse can propagate a non-copyable value created + // mid-chain. + Maybe mayValue; + Maybe 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 mayRef = SomeRef(val); + Maybe 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 mayRef = SomeRef(val); + Maybe 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 for smaller T took up more space // than T's alignment required. @@ -1468,6 +1721,9 @@ int main() { RUN_TEST(TestSomePointerConversion); RUN_TEST(TestTypeConversion); RUN_TEST(TestReference); + RUN_TEST(TestAndThen); + RUN_TEST(TestOrElse); + RUN_TEST(TestComposedMoves); return 0; } diff --git a/mfbt/tests/TestUtf8.cpp b/mfbt/tests/TestUtf8.cpp index b3ff9e9ee8..2c51d9abfb 100644 --- a/mfbt/tests/TestUtf8.cpp +++ b/mfbt/tests/TestUtf8.cpp @@ -24,12 +24,6 @@ using mozilla::IsUtf8; using mozilla::Span; using mozilla::Utf8Unit; -// Disable the C++ 2a warning. See bug #1509926 -#if defined(__clang__) && (__clang_major__ >= 6) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wc++2a-compat" -#endif - static void TestUtf8Unit() { Utf8Unit c('A'); MOZ_RELEASE_ASSERT(c.toChar() == 'A'); @@ -749,7 +743,3 @@ int main() { TestDecodeOneUtf8CodePoint(); return 0; } - -#if defined(__clang__) && (__clang_major__ >= 6) -# pragma clang diagnostic pop -#endif -- cgit v1.2.3