summaryrefslogtreecommitdiffstats
path: root/mfbt
diff options
context:
space:
mode:
Diffstat (limited to 'mfbt')
-rw-r--r--mfbt/Assertions.h34
-rw-r--r--mfbt/Attributes.h8
-rw-r--r--mfbt/Maybe.h212
-rw-r--r--mfbt/tests/TestMaybe.cpp266
-rw-r--r--mfbt/tests/TestUtf8.cpp10
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