diff options
Diffstat (limited to '')
-rw-r--r-- | mfbt/Assertions.h | 34 | ||||
-rw-r--r-- | mfbt/Attributes.h | 8 | ||||
-rw-r--r-- | mfbt/Maybe.h | 212 | ||||
-rw-r--r-- | mfbt/tests/TestMaybe.cpp | 266 | ||||
-rw-r--r-- | mfbt/tests/TestUtf8.cpp | 10 |
5 files changed, 485 insertions, 45 deletions
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 <process.h> +# define MOZ_GET_PID() _getpid() +#elif !defined(__wasi__) +# include <unistd.h> +# 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 <functional> #include <new> // for placement new #include <ostream> #include <type_traits> @@ -202,6 +203,8 @@ class Maybe_CopyMove_Enabler<T, false, false, true> { 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<T, false, true, false> { 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<T, true> : MaybeStorageBase<T> { mIsSome{true} {} }; +template <typename T> +struct IsMaybeImpl : std::false_type {}; + +template <typename T> +struct IsMaybeImpl<Maybe<T>> : std::true_type {}; + +template <typename T> +using IsMaybe = IsMaybeImpl<std::decay_t<T>>; + } // namespace detail template <typename T, typename U = typename std::remove_cv< @@ -382,28 +396,35 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe * a const U&. */ template <typename U, - typename = std::enable_if_t<std::is_constructible_v<T, const U&>>> + std::enable_if_t<std::is_constructible_v<T, const U&>, bool> = true> MOZ_IMPLICIT Maybe(const Maybe<U>& aOther) { if (aOther.isSome()) { emplace(*aOther); } } + template <typename U, std::enable_if_t<!std::is_constructible_v<T, const U&>, + bool> = true> + explicit Maybe(const Maybe<U>& aOther) = delete; + /** * Maybe<T> can be move-constructed from a Maybe<U> if T is constructible from * a U&&. */ template <typename U, - typename = std::enable_if_t<std::is_constructible_v<T, U&&>>> + std::enable_if_t<std::is_constructible_v<T, U&&>, bool> = true> MOZ_IMPLICIT Maybe(Maybe<U>&& aOther) { if (aOther.isSome()) { emplace(std::move(*aOther)); aOther.reset(); } } + template <typename U, + std::enable_if_t<!std::is_constructible_v<T, U&&>, bool> = true> + explicit Maybe(Maybe<U>&& aOther) = delete; template <typename U, - typename = std::enable_if_t<std::is_constructible_v<T, const U&>>> + std::enable_if_t<std::is_constructible_v<T, const U&>, bool> = true> Maybe& operator=(const Maybe<U>& aOther) { if (aOther.isSome()) { if (mIsSome) { @@ -417,8 +438,12 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe return *this; } + template <typename U, std::enable_if_t<!std::is_constructible_v<T, const U&>, + bool> = true> + Maybe& operator=(const Maybe<U>& aOther) = delete; + template <typename U, - typename = std::enable_if_t<std::is_constructible_v<T, U&&>>> + std::enable_if_t<std::is_constructible_v<T, U&&>, bool> = true> Maybe& operator=(Maybe<U>&& aOther) { if (aOther.isSome()) { if (mIsSome) { @@ -434,6 +459,10 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe return *this; } + template <typename U, + std::enable_if_t<!std::is_constructible_v<T, U&&>, bool> = true> + Maybe& operator=(Maybe<U>&& 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 <typename Func> - constexpr Maybe& apply(Func&& aFunc) { + constexpr Maybe& apply(Func&& aFunc) & { if (isSome()) { std::forward<Func>(aFunc)(ref()); } @@ -605,20 +634,36 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe } template <typename Func> - constexpr const Maybe& apply(Func&& aFunc) const { + constexpr const Maybe& apply(Func&& aFunc) const& { if (isSome()) { std::forward<Func>(aFunc)(ref()); } return *this; } + template <typename Func> + constexpr Maybe& apply(Func&& aFunc) && { + if (isSome()) { + std::forward<Func>(aFunc)(extract()); + } + return *this; + } + + template <typename Func> + constexpr Maybe& apply(Func&& aFunc) const&& { + if (isSome()) { + std::forward<Func>(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 <typename Func> - constexpr auto map(Func&& aFunc) { + constexpr auto map(Func&& aFunc) & { if (isSome()) { return Some(std::forward<Func>(aFunc)(ref())); } @@ -626,13 +671,132 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe } template <typename Func> - constexpr auto map(Func&& aFunc) const { + constexpr auto map(Func&& aFunc) const& { if (isSome()) { return Some(std::forward<Func>(aFunc)(ref())); } return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{}; } + template <typename Func> + constexpr auto map(Func&& aFunc) && { + if (isSome()) { + return Some(std::forward<Func>(aFunc)(extract())); + } + return Maybe<decltype(std::forward<Func>(aFunc)(extract()))>{}; + } + + template <typename Func> + constexpr auto map(Func&& aFunc) const&& { + if (isSome()) { + return Some(std::forward<Func>(aFunc)(extract())); + } + return Maybe<decltype(std::forward<Func>(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<U> of any type U. + * If |isNothing()|, returns an empty Maybe value with the same type as what + * the provided function would have returned. + */ + template <typename Func> + constexpr auto andThen(Func&& aFunc) & { + static_assert(std::is_invocable_v<Func, T&>); + using U = std::invoke_result_t<Func, T&>; + static_assert(detail::IsMaybe<U>::value); + if (isSome()) { + return std::invoke(std::forward<Func>(aFunc), ref()); + } + return std::remove_cv_t<std::remove_reference_t<U>>{}; + } + + template <typename Func> + constexpr auto andThen(Func&& aFunc) const& { + static_assert(std::is_invocable_v<Func, const T&>); + using U = std::invoke_result_t<Func, const T&>; + static_assert(detail::IsMaybe<U>::value); + if (isSome()) { + return std::invoke(std::forward<Func>(aFunc), ref()); + } + return std::remove_cv_t<std::remove_reference_t<U>>{}; + } + + template <typename Func> + constexpr auto andThen(Func&& aFunc) && { + static_assert(std::is_invocable_v<Func, T&&>); + using U = std::invoke_result_t<Func, T&&>; + static_assert(detail::IsMaybe<U>::value); + if (isSome()) { + return std::invoke(std::forward<Func>(aFunc), extract()); + } + return std::remove_cv_t<std::remove_reference_t<U>>{}; + } + + template <typename Func> + constexpr auto andThen(Func&& aFunc) const&& { + static_assert(std::is_invocable_v<Func, const T&&>); + using U = std::invoke_result_t<Func, const T&&>; + static_assert(detail::IsMaybe<U>::value); + if (isSome()) { + return std::invoke(std::forward<Func>(aFunc), extract()); + } + return std::remove_cv_t<std::remove_reference_t<U>>{}; + } + + /* + * If |isNothing()|, runs the provided function or functor and returns its + * result. If |isSome()|, returns the contained value wrapped in a Maybe. + */ + template <typename Func> + constexpr Maybe orElse(Func&& aFunc) & { + static_assert(std::is_invocable_v<Func>); + using U = std::invoke_result_t<Func>; + static_assert( + std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); + if (isSome()) { + return *this; + } + return std::invoke(std::forward<Func>(aFunc)); + } + + template <typename Func> + constexpr Maybe orElse(Func&& aFunc) const& { + static_assert(std::is_invocable_v<Func>); + using U = std::invoke_result_t<Func>; + static_assert( + std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); + if (isSome()) { + return *this; + } + return std::invoke(std::forward<Func>(aFunc)); + } + + template <typename Func> + constexpr Maybe orElse(Func&& aFunc) && { + static_assert(std::is_invocable_v<Func>); + using U = std::invoke_result_t<Func>; + static_assert( + std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); + if (isSome()) { + return std::move(*this); + } + return std::invoke(std::forward<Func>(aFunc)); + } + + template <typename Func> + constexpr Maybe orElse(Func&& aFunc) const&& { + static_assert(std::is_invocable_v<Func>); + using U = std::invoke_result_t<Func>; + static_assert( + std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); + if (isSome()) { + return std::move(*this); + } + return std::invoke(std::forward<Func>(aFunc)); + } + /* If |isSome()|, empties this Maybe and destroys its contents. */ constexpr void reset() { if (isSome()) { @@ -705,7 +869,7 @@ class Maybe<T&> { void reset() { mValue = nullptr; } template <typename Func> - Maybe& apply(Func&& aFunc) { + const Maybe& apply(Func&& aFunc) const { if (isSome()) { std::forward<Func>(aFunc)(ref()); } @@ -713,29 +877,35 @@ class Maybe<T&> { } template <typename Func> - const Maybe& apply(Func&& aFunc) const { + auto map(Func&& aFunc) const { + Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val; if (isSome()) { - std::forward<Func>(aFunc)(ref()); + val.emplace(std::forward<Func>(aFunc)(ref())); } - return *this; + return val; } template <typename Func> - auto map(Func&& aFunc) { - Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val; + constexpr auto andThen(Func&& aFunc) const { + static_assert(std::is_invocable_v<Func, T&>); + using U = std::invoke_result_t<Func, T&>; + static_assert(detail::IsMaybe<U>::value); if (isSome()) { - val.emplace(std::forward<Func>(aFunc)(ref())); + return std::invoke(std::forward<Func>(aFunc), ref()); } - return val; + return std::remove_cv_t<std::remove_reference_t<U>>{}; } template <typename Func> - auto map(Func&& aFunc) const { - Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val; + constexpr Maybe orElse(Func&& aFunc) const { + static_assert(std::is_invocable_v<Func>); + using U = std::invoke_result_t<Func>; + static_assert( + std::is_same_v<Maybe, std::remove_cv_t<std::remove_reference_t<U>>>); if (isSome()) { - val.emplace(std::forward<Func>(aFunc)(ref())); + return *this; } - return val; + return std::invoke(std::forward<Func>(aFunc)); } bool refEquals(const Maybe<T&>& 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<UnmovableValue> 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<Maybe<UnmovableValue>>); static_assert(std::is_copy_assignable_v<Maybe<UnmovableValue>>); - // XXX Why do these static_asserts not hold? - // static_assert(!std::is_move_constructible_v<Maybe<UnmovableValue>>); - // static_assert(!std::is_move_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); @@ -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<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; } @@ -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<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 {}; } @@ -1439,6 +1453,245 @@ static bool TestReference() { 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<int> operator()(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. @@ -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 |