Bug 1827566 - Isolate SSE2 requirements to SSE-compiled file r=glandium
[gecko.git] / mfbt / Maybe.h
blob3298370f49d4e067ef71ea31961f7703a284c0e6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* A class for optional values and in-place lazy construction. */
9 #ifndef mozilla_Maybe_h
10 #define mozilla_Maybe_h
12 #include <new> // for placement new
13 #include <ostream>
14 #include <type_traits>
15 #include <utility>
17 #include "mozilla/Alignment.h"
18 #include "mozilla/Assertions.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/MaybeStorageBase.h"
21 #include "mozilla/MemoryChecking.h"
22 #include "mozilla/OperatorNewExtensions.h"
23 #include "mozilla/Poison.h"
24 #include "mozilla/ThreadSafety.h"
26 class nsCycleCollectionTraversalCallback;
28 template <typename T>
29 inline void CycleCollectionNoteChild(
30 nsCycleCollectionTraversalCallback& aCallback, T* aChild, const char* aName,
31 uint32_t aFlags);
33 namespace mozilla {
35 struct Nothing {};
37 inline constexpr bool operator==(const Nothing&, const Nothing&) {
38 return true;
41 template <class T>
42 class Maybe;
44 namespace detail {
46 // You would think that poisoning Maybe instances could just be a call
47 // to mozWritePoison. Unfortunately, using a simple call to
48 // mozWritePoison generates poor code on MSVC for small structures. The
49 // generated code contains (always not-taken) branches and does a bunch
50 // of setup for `rep stos{l,q}`, even though we know at compile time
51 // exactly how many words we're poisoning. Instead, we're going to
52 // force MSVC to generate the code we want via recursive templates.
54 // Write the given poisonValue into p at offset*sizeof(uintptr_t).
55 template <size_t offset>
56 inline void WritePoisonAtOffset(void* p, const uintptr_t poisonValue) {
57 memcpy(static_cast<char*>(p) + offset * sizeof(poisonValue), &poisonValue,
58 sizeof(poisonValue));
61 template <size_t Offset, size_t NOffsets>
62 struct InlinePoisoner {
63 static void poison(void* p, const uintptr_t poisonValue) {
64 WritePoisonAtOffset<Offset>(p, poisonValue);
65 InlinePoisoner<Offset + 1, NOffsets>::poison(p, poisonValue);
69 template <size_t N>
70 struct InlinePoisoner<N, N> {
71 static void poison(void*, const uintptr_t) {
72 // All done!
76 // We can't generate inline code for large structures, though, because we'll
77 // blow out recursive template instantiation limits, and the code would be
78 // bloated to boot. So provide a fallback to the out-of-line poisoner.
79 template <size_t ObjectSize>
80 struct OutOfLinePoisoner {
81 static MOZ_NEVER_INLINE void poison(void* p, const uintptr_t) {
82 mozWritePoison(p, ObjectSize);
86 template <typename T>
87 inline void PoisonObject(T* p) {
88 const uintptr_t POISON = mozPoisonValue();
89 std::conditional_t<(sizeof(T) <= 8 * sizeof(POISON)),
90 InlinePoisoner<0, sizeof(T) / sizeof(POISON)>,
91 OutOfLinePoisoner<sizeof(T)>>::poison(p, POISON);
94 template <typename T>
95 struct MaybePoisoner {
96 static const size_t N = sizeof(T);
98 static void poison(void* aPtr) {
99 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
100 if (N >= sizeof(uintptr_t)) {
101 PoisonObject(static_cast<std::remove_cv_t<T>*>(aPtr));
103 #endif
104 MOZ_MAKE_MEM_UNDEFINED(aPtr, N);
108 template <typename T,
109 bool TriviallyDestructibleAndCopyable =
110 IsTriviallyDestructibleAndCopyable<T>,
111 bool Copyable = std::is_copy_constructible_v<T>,
112 bool Movable = std::is_move_constructible_v<T>>
113 class Maybe_CopyMove_Enabler;
115 #define MOZ_MAYBE_COPY_OPS() \
116 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler& aOther) { \
117 if (downcast(aOther).isSome()) { \
118 downcast(*this).emplace(*downcast(aOther)); \
122 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler& aOther) { \
123 return downcast(*this).template operator=<T>(downcast(aOther)); \
126 #define MOZ_MAYBE_MOVE_OPS() \
127 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) { \
128 if (downcast(aOther).isSome()) { \
129 downcast(*this).emplace(std::move(*downcast(aOther))); \
130 downcast(aOther).reset(); \
134 constexpr Maybe_CopyMove_Enabler& operator=( \
135 Maybe_CopyMove_Enabler&& aOther) { \
136 downcast(*this).template operator=<T>(std::move(downcast(aOther))); \
138 return *this; \
141 #define MOZ_MAYBE_DOWNCAST() \
142 static constexpr Maybe<T>& downcast(Maybe_CopyMove_Enabler& aObj) { \
143 return static_cast<Maybe<T>&>(aObj); \
145 static constexpr const Maybe<T>& downcast( \
146 const Maybe_CopyMove_Enabler& aObj) { \
147 return static_cast<const Maybe<T>&>(aObj); \
150 template <typename T>
151 class Maybe_CopyMove_Enabler<T, true, true, true> {
152 public:
153 Maybe_CopyMove_Enabler() = default;
155 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = default;
156 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = default;
157 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) {
158 downcast(aOther).reset();
160 constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) {
161 downcast(aOther).reset();
162 return *this;
165 private:
166 MOZ_MAYBE_DOWNCAST()
169 template <typename T>
170 class Maybe_CopyMove_Enabler<T, true, false, true> {
171 public:
172 Maybe_CopyMove_Enabler() = default;
174 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete;
175 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete;
176 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) {
177 downcast(aOther).reset();
179 constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) {
180 downcast(aOther).reset();
181 return *this;
184 private:
185 MOZ_MAYBE_DOWNCAST()
188 template <typename T>
189 class Maybe_CopyMove_Enabler<T, false, true, true> {
190 public:
191 Maybe_CopyMove_Enabler() = default;
193 MOZ_MAYBE_COPY_OPS()
194 MOZ_MAYBE_MOVE_OPS()
196 private:
197 MOZ_MAYBE_DOWNCAST()
200 template <typename T>
201 class Maybe_CopyMove_Enabler<T, false, false, true> {
202 public:
203 Maybe_CopyMove_Enabler() = default;
205 MOZ_MAYBE_MOVE_OPS()
207 private:
208 MOZ_MAYBE_DOWNCAST()
211 template <typename T>
212 class Maybe_CopyMove_Enabler<T, false, true, false> {
213 public:
214 Maybe_CopyMove_Enabler() = default;
216 MOZ_MAYBE_COPY_OPS()
218 private:
219 MOZ_MAYBE_DOWNCAST()
222 template <typename T, bool TriviallyDestructibleAndCopyable>
223 class Maybe_CopyMove_Enabler<T, TriviallyDestructibleAndCopyable, false,
224 false> {
225 public:
226 Maybe_CopyMove_Enabler() = default;
228 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete;
229 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete;
230 Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&&) = delete;
231 Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&&) = delete;
234 #undef MOZ_MAYBE_COPY_OPS
235 #undef MOZ_MAYBE_MOVE_OPS
236 #undef MOZ_MAYBE_DOWNCAST
238 template <typename T, bool TriviallyDestructibleAndCopyable =
239 IsTriviallyDestructibleAndCopyable<T>>
240 struct MaybeStorage;
242 template <typename T>
243 struct MaybeStorage<T, false> : MaybeStorageBase<T> {
244 protected:
245 char mIsSome = false; // not bool -- guarantees minimal space consumption
247 MaybeStorage() = default;
248 explicit MaybeStorage(const T& aVal)
249 : MaybeStorageBase<T>{aVal}, mIsSome{true} {}
250 explicit MaybeStorage(T&& aVal)
251 : MaybeStorageBase<T>{std::move(aVal)}, mIsSome{true} {}
253 template <typename... Args>
254 explicit MaybeStorage(std::in_place_t, Args&&... aArgs)
255 : MaybeStorageBase<T>{std::in_place, std::forward<Args>(aArgs)...},
256 mIsSome{true} {}
258 public:
259 // Copy and move operations are no-ops, since copying is moving is implemented
260 // by Maybe_CopyMove_Enabler.
262 MaybeStorage(const MaybeStorage&) : MaybeStorageBase<T>{} {}
263 MaybeStorage& operator=(const MaybeStorage&) { return *this; }
264 MaybeStorage(MaybeStorage&&) : MaybeStorageBase<T>{} {}
265 MaybeStorage& operator=(MaybeStorage&&) { return *this; }
267 ~MaybeStorage() {
268 if (mIsSome) {
269 this->addr()->T::~T();
274 template <typename T>
275 struct MaybeStorage<T, true> : MaybeStorageBase<T> {
276 protected:
277 char mIsSome = false; // not bool -- guarantees minimal space consumption
279 constexpr MaybeStorage() = default;
280 constexpr explicit MaybeStorage(const T& aVal)
281 : MaybeStorageBase<T>{aVal}, mIsSome{true} {}
282 constexpr explicit MaybeStorage(T&& aVal)
283 : MaybeStorageBase<T>{std::move(aVal)}, mIsSome{true} {}
285 template <typename... Args>
286 constexpr explicit MaybeStorage(std::in_place_t, Args&&... aArgs)
287 : MaybeStorageBase<T>{std::in_place, std::forward<Args>(aArgs)...},
288 mIsSome{true} {}
291 } // namespace detail
293 template <typename T, typename U = typename std::remove_cv<
294 typename std::remove_reference<T>::type>::type>
295 constexpr Maybe<U> Some(T&& aValue);
298 * Maybe is a container class which contains either zero or one elements. It
299 * serves two roles. It can represent values which are *semantically* optional,
300 * augmenting a type with an explicit 'Nothing' value. In this role, it provides
301 * methods that make it easy to work with values that may be missing, along with
302 * equality and comparison operators so that Maybe values can be stored in
303 * containers. Maybe values can be constructed conveniently in expressions using
304 * type inference, as follows:
306 * void doSomething(Maybe<Foo> aFoo) {
307 * if (aFoo) // Make sure that aFoo contains a value...
308 * aFoo->takeAction(); // and then use |aFoo->| to access it.
309 * } // |*aFoo| also works!
311 * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value.
312 * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
314 * You'll note that it's important to check whether a Maybe contains a value
315 * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You
316 * can avoid these checks, and sometimes write more readable code, using
317 * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value
318 * in the Maybe and provide a default for the 'Nothing' case. You can also use
319 * |apply()| to call a function only if the Maybe holds a value, and |map()| to
320 * transform the value in the Maybe, returning another Maybe with a possibly
321 * different type.
323 * Maybe's other role is to support lazily constructing objects without using
324 * dynamic storage. A Maybe directly contains storage for a value, but it's
325 * empty by default. |emplace()|, as mentioned above, can be used to construct a
326 * value in Maybe's storage. The value a Maybe contains can be destroyed by
327 * calling |reset()|; this will happen automatically if a Maybe is destroyed
328 * while holding a value.
330 * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null
331 * value meaning 'Nothing' and any other value meaning 'Some'. You can convert
332 * from such a pointer to a Maybe value using 'ToMaybe()'.
334 * Maybe is inspired by similar types in the standard library of many other
335 * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's
336 * very similar to std::optional, which was proposed for C++14 and originated in
337 * Boost. The most important differences between Maybe and std::optional are:
339 * - std::optional<T> may be compared with T. We deliberately forbid that.
340 * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but
341 * lacks corresponding methods for |refOr()| and |ptrOr()|.
342 * - std::optional lacks |map()| and |apply()|, making it less suitable for
343 * functional-style code.
344 * - std::optional lacks many convenience functions that Maybe has. Most
345 * unfortunately, it lacks equivalents of the type-inferred constructor
346 * functions |Some()| and |Nothing()|.
348 template <class T>
349 class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe
350 : private detail::MaybeStorage<T>,
351 public detail::Maybe_CopyMove_Enabler<T> {
352 template <typename, bool, bool, bool>
353 friend class detail::Maybe_CopyMove_Enabler;
355 template <typename U, typename V>
356 friend constexpr Maybe<V> Some(U&& aValue);
358 struct SomeGuard {};
360 template <typename U>
361 constexpr Maybe(U&& aValue, SomeGuard)
362 : detail::MaybeStorage<T>{std::forward<U>(aValue)} {}
364 using detail::MaybeStorage<T>::mIsSome;
365 using detail::MaybeStorage<T>::mStorage;
367 void poisonData() { detail::MaybePoisoner<T>::poison(&mStorage.val); }
369 public:
370 using ValueType = T;
372 MOZ_ALLOW_TEMPORARY constexpr Maybe() = default;
374 MOZ_ALLOW_TEMPORARY MOZ_IMPLICIT constexpr Maybe(Nothing) : Maybe{} {}
376 template <typename... Args>
377 constexpr explicit Maybe(std::in_place_t, Args&&... aArgs)
378 : detail::MaybeStorage<T>{std::in_place, std::forward<Args>(aArgs)...} {}
381 * Maybe<T> can be copy-constructed from a Maybe<U> if T is constructible from
382 * a const U&.
384 template <typename U,
385 typename = std::enable_if_t<std::is_constructible_v<T, const U&>>>
386 MOZ_IMPLICIT Maybe(const Maybe<U>& aOther) {
387 if (aOther.isSome()) {
388 emplace(*aOther);
393 * Maybe<T> can be move-constructed from a Maybe<U> if T is constructible from
394 * a U&&.
396 template <typename U,
397 typename = std::enable_if_t<std::is_constructible_v<T, U&&>>>
398 MOZ_IMPLICIT Maybe(Maybe<U>&& aOther) {
399 if (aOther.isSome()) {
400 emplace(std::move(*aOther));
401 aOther.reset();
405 template <typename U,
406 typename = std::enable_if_t<std::is_constructible_v<T, const U&>>>
407 Maybe& operator=(const Maybe<U>& aOther) {
408 if (aOther.isSome()) {
409 if (mIsSome) {
410 ref() = aOther.ref();
411 } else {
412 emplace(*aOther);
414 } else {
415 reset();
417 return *this;
420 template <typename U,
421 typename = std::enable_if_t<std::is_constructible_v<T, U&&>>>
422 Maybe& operator=(Maybe<U>&& aOther) {
423 if (aOther.isSome()) {
424 if (mIsSome) {
425 ref() = std::move(aOther.ref());
426 } else {
427 emplace(std::move(*aOther));
429 aOther.reset();
430 } else {
431 reset();
434 return *this;
437 constexpr Maybe& operator=(Nothing) {
438 reset();
439 return *this;
442 /* Methods that check whether this Maybe contains a value */
443 constexpr explicit operator bool() const { return isSome(); }
444 constexpr bool isSome() const { return mIsSome; }
445 constexpr bool isNothing() const { return !mIsSome; }
447 /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|.
449 constexpr T value() const&;
450 constexpr T value() &&;
451 constexpr T value() const&&;
454 * Move the contents of this Maybe<T> out of internal storage and return it
455 * without calling the destructor. The internal storage is also reset to
456 * avoid multiple calls. Unsafe unless |isSome()|.
458 T extract() {
459 MOZ_RELEASE_ASSERT(isSome());
460 T v = std::move(mStorage.val);
461 reset();
462 return v;
466 * Returns the value (possibly |Nothing()|) by moving it out of this Maybe<T>
467 * and leaving |Nothing()| in its place.
469 Maybe<T> take() { return std::exchange(*this, Nothing()); }
472 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
473 * the default value provided.
475 * Note: If the value passed to aDefault is not the result of a trivial
476 * expression, but expensive to evaluate, e.g. |valueOr(ExpensiveFunction())|,
477 * use |valueOrFrom| instead, e.g.
478 * |valueOrFrom([arg] { return ExpensiveFunction(arg); })|. This ensures
479 * that the expensive expression is only evaluated when its result will
480 * actually be used.
482 template <typename V>
483 constexpr T valueOr(V&& aDefault) const {
484 if (isSome()) {
485 return ref();
487 return std::forward<V>(aDefault);
491 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
492 * the value returned from the function or functor provided.
494 template <typename F>
495 constexpr T valueOrFrom(F&& aFunc) const {
496 if (isSome()) {
497 return ref();
499 return aFunc();
502 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|.
504 T* ptr();
505 constexpr const T* ptr() const;
508 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
509 * returns the default value provided.
511 T* ptrOr(T* aDefault) {
512 if (isSome()) {
513 return ptr();
515 return aDefault;
518 constexpr const T* ptrOr(const T* aDefault) const {
519 if (isSome()) {
520 return ptr();
522 return aDefault;
526 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
527 * returns the value returned from the function or functor provided.
529 template <typename F>
530 T* ptrOrFrom(F&& aFunc) {
531 if (isSome()) {
532 return ptr();
534 return aFunc();
537 template <typename F>
538 const T* ptrOrFrom(F&& aFunc) const {
539 if (isSome()) {
540 return ptr();
542 return aFunc();
545 constexpr T* operator->();
546 constexpr const T* operator->() const;
548 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
549 constexpr T& ref() &;
550 constexpr const T& ref() const&;
551 constexpr T&& ref() &&;
552 constexpr const T&& ref() const&&;
555 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
556 * the default value provided.
558 constexpr T& refOr(T& aDefault) {
559 if (isSome()) {
560 return ref();
562 return aDefault;
565 constexpr const T& refOr(const T& aDefault) const {
566 if (isSome()) {
567 return ref();
569 return aDefault;
573 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
574 * value returned from the function or functor provided.
576 template <typename F>
577 constexpr T& refOrFrom(F&& aFunc) {
578 if (isSome()) {
579 return ref();
581 return aFunc();
584 template <typename F>
585 constexpr const T& refOrFrom(F&& aFunc) const {
586 if (isSome()) {
587 return ref();
589 return aFunc();
592 constexpr T& operator*() &;
593 constexpr const T& operator*() const&;
594 constexpr T&& operator*() &&;
595 constexpr const T&& operator*() const&&;
597 /* If |isSome()|, runs the provided function or functor on the contents of
598 * this Maybe. */
599 template <typename Func>
600 constexpr Maybe& apply(Func&& aFunc) {
601 if (isSome()) {
602 std::forward<Func>(aFunc)(ref());
604 return *this;
607 template <typename Func>
608 constexpr const Maybe& apply(Func&& aFunc) const {
609 if (isSome()) {
610 std::forward<Func>(aFunc)(ref());
612 return *this;
616 * If |isSome()|, runs the provided function and returns the result wrapped
617 * in a Maybe. If |isNothing()|, returns an empty Maybe value with the same
618 * value type as what the provided function would have returned.
620 template <typename Func>
621 constexpr auto map(Func&& aFunc) {
622 if (isSome()) {
623 return Some(std::forward<Func>(aFunc)(ref()));
625 return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{};
628 template <typename Func>
629 constexpr auto map(Func&& aFunc) const {
630 if (isSome()) {
631 return Some(std::forward<Func>(aFunc)(ref()));
633 return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{};
636 /* If |isSome()|, empties this Maybe and destroys its contents. */
637 constexpr void reset() {
638 if (isSome()) {
639 if constexpr (!std::is_trivially_destructible_v<T>) {
641 * Static analyzer gets confused if we have Maybe<MutexAutoLock>,
642 * so we suppress thread-safety warnings here
644 MOZ_PUSH_IGNORE_THREAD_SAFETY
645 ref().T::~T();
646 MOZ_POP_THREAD_SAFETY
647 poisonData();
649 mIsSome = false;
654 * Constructs a T value in-place in this empty Maybe<T>'s storage. The
655 * arguments to |emplace()| are the parameters to T's constructor.
657 template <typename... Args>
658 constexpr void emplace(Args&&... aArgs);
660 template <typename U>
661 constexpr std::enable_if_t<std::is_same_v<T, U> &&
662 std::is_copy_constructible_v<U> &&
663 !std::is_move_constructible_v<U>>
664 emplace(U&& aArgs) {
665 emplace(aArgs);
668 friend std::ostream& operator<<(std::ostream& aStream,
669 const Maybe<T>& aMaybe) {
670 if (aMaybe) {
671 aStream << aMaybe.ref();
672 } else {
673 aStream << "<Nothing>";
675 return aStream;
679 template <typename T>
680 class Maybe<T&> {
681 public:
682 constexpr Maybe() = default;
683 constexpr MOZ_IMPLICIT Maybe(Nothing) {}
685 void emplace(T& aRef) { mValue = &aRef; }
687 /* Methods that check whether this Maybe contains a value */
688 constexpr explicit operator bool() const { return isSome(); }
689 constexpr bool isSome() const { return mValue; }
690 constexpr bool isNothing() const { return !mValue; }
692 T& ref() const {
693 MOZ_RELEASE_ASSERT(isSome());
694 return *mValue;
697 T* operator->() const { return &ref(); }
698 T& operator*() const { return ref(); }
700 // Deliberately not defining value and ptr accessors, as these may be
701 // confusing on a reference-typed Maybe.
703 // XXX Should we define refOr?
705 void reset() { mValue = nullptr; }
707 template <typename Func>
708 Maybe& apply(Func&& aFunc) {
709 if (isSome()) {
710 std::forward<Func>(aFunc)(ref());
712 return *this;
715 template <typename Func>
716 const Maybe& apply(Func&& aFunc) const {
717 if (isSome()) {
718 std::forward<Func>(aFunc)(ref());
720 return *this;
723 template <typename Func>
724 auto map(Func&& aFunc) {
725 Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
726 if (isSome()) {
727 val.emplace(std::forward<Func>(aFunc)(ref()));
729 return val;
732 template <typename Func>
733 auto map(Func&& aFunc) const {
734 Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
735 if (isSome()) {
736 val.emplace(std::forward<Func>(aFunc)(ref()));
738 return val;
741 bool refEquals(const Maybe<T&>& aOther) const {
742 return mValue == aOther.mValue;
745 bool refEquals(const T& aOther) const { return mValue == &aOther; }
747 private:
748 T* mValue = nullptr;
751 template <typename T>
752 constexpr T Maybe<T>::value() const& {
753 MOZ_RELEASE_ASSERT(isSome());
754 return ref();
757 template <typename T>
758 constexpr T Maybe<T>::value() && {
759 MOZ_RELEASE_ASSERT(isSome());
760 return std::move(ref());
763 template <typename T>
764 constexpr T Maybe<T>::value() const&& {
765 MOZ_RELEASE_ASSERT(isSome());
766 return std::move(ref());
769 template <typename T>
770 T* Maybe<T>::ptr() {
771 MOZ_RELEASE_ASSERT(isSome());
772 return &ref();
775 template <typename T>
776 constexpr const T* Maybe<T>::ptr() const {
777 MOZ_RELEASE_ASSERT(isSome());
778 return &ref();
781 template <typename T>
782 constexpr T* Maybe<T>::operator->() {
783 MOZ_RELEASE_ASSERT(isSome());
784 return ptr();
787 template <typename T>
788 constexpr const T* Maybe<T>::operator->() const {
789 MOZ_RELEASE_ASSERT(isSome());
790 return ptr();
793 template <typename T>
794 constexpr T& Maybe<T>::ref() & {
795 MOZ_RELEASE_ASSERT(isSome());
796 return mStorage.val;
799 template <typename T>
800 constexpr const T& Maybe<T>::ref() const& {
801 MOZ_RELEASE_ASSERT(isSome());
802 return mStorage.val;
805 template <typename T>
806 constexpr T&& Maybe<T>::ref() && {
807 MOZ_RELEASE_ASSERT(isSome());
808 return std::move(mStorage.val);
811 template <typename T>
812 constexpr const T&& Maybe<T>::ref() const&& {
813 MOZ_RELEASE_ASSERT(isSome());
814 return std::move(mStorage.val);
817 template <typename T>
818 constexpr T& Maybe<T>::operator*() & {
819 MOZ_RELEASE_ASSERT(isSome());
820 return ref();
823 template <typename T>
824 constexpr const T& Maybe<T>::operator*() const& {
825 MOZ_RELEASE_ASSERT(isSome());
826 return ref();
829 template <typename T>
830 constexpr T&& Maybe<T>::operator*() && {
831 MOZ_RELEASE_ASSERT(isSome());
832 return std::move(ref());
835 template <typename T>
836 constexpr const T&& Maybe<T>::operator*() const&& {
837 MOZ_RELEASE_ASSERT(isSome());
838 return std::move(ref());
841 template <typename T>
842 template <typename... Args>
843 constexpr void Maybe<T>::emplace(Args&&... aArgs) {
844 MOZ_RELEASE_ASSERT(!isSome());
845 ::new (KnownNotNull, &mStorage.val) T(std::forward<Args>(aArgs)...);
846 mIsSome = true;
850 * Some() creates a Maybe<T> value containing the provided T value. If T has a
851 * move constructor, it's used to make this as efficient as possible.
853 * Some() selects the type of Maybe it returns by removing any const, volatile,
854 * or reference qualifiers from the type of the value you pass to it. This gives
855 * it more intuitive behavior when used in expressions, but it also means that
856 * if you need to construct a Maybe value that holds a const, volatile, or
857 * reference value, you need to use emplace() instead.
859 template <typename T, typename U>
860 constexpr Maybe<U> Some(T&& aValue) {
861 return {std::forward<T>(aValue), typename Maybe<U>::SomeGuard{}};
864 template <typename T>
865 constexpr Maybe<T&> SomeRef(T& aValue) {
866 Maybe<T&> value;
867 value.emplace(aValue);
868 return value;
871 template <typename T>
872 constexpr Maybe<T&> ToMaybeRef(T* const aPtr) {
873 return aPtr ? SomeRef(*aPtr) : Nothing{};
876 template <typename T>
877 Maybe<std::remove_cv_t<std::remove_reference_t<T>>> ToMaybe(T* aPtr) {
878 if (aPtr) {
879 return Some(*aPtr);
881 return Nothing();
885 * Two Maybe<T> values are equal if
886 * - both are Nothing, or
887 * - both are Some, and the values they contain are equal.
889 template <typename T>
890 constexpr bool operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
891 static_assert(!std::is_reference_v<T>,
892 "operator== is not defined for Maybe<T&>, compare values or "
893 "addresses explicitly instead");
894 if (aLHS.isNothing() != aRHS.isNothing()) {
895 return false;
897 return aLHS.isNothing() || *aLHS == *aRHS;
900 template <typename T>
901 constexpr bool operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
902 return !(aLHS == aRHS);
906 * We support comparison to Nothing to allow reasonable expressions like:
907 * if (maybeValue == Nothing()) { ... }
909 template <typename T>
910 constexpr bool operator==(const Maybe<T>& aLHS, const Nothing& aRHS) {
911 return aLHS.isNothing();
914 template <typename T>
915 constexpr bool operator!=(const Maybe<T>& aLHS, const Nothing& aRHS) {
916 return !(aLHS == aRHS);
919 template <typename T>
920 constexpr bool operator==(const Nothing& aLHS, const Maybe<T>& aRHS) {
921 return aRHS.isNothing();
924 template <typename T>
925 constexpr bool operator!=(const Nothing& aLHS, const Maybe<T>& aRHS) {
926 return !(aLHS == aRHS);
930 * Maybe<T> values are ordered in the same way T values are ordered, except that
931 * Nothing comes before anything else.
933 template <typename T>
934 constexpr bool operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
935 if (aLHS.isNothing()) {
936 return aRHS.isSome();
938 if (aRHS.isNothing()) {
939 return false;
941 return *aLHS < *aRHS;
944 template <typename T>
945 constexpr bool operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
946 return !(aLHS < aRHS || aLHS == aRHS);
949 template <typename T>
950 constexpr bool operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
951 return aLHS < aRHS || aLHS == aRHS;
954 template <typename T>
955 constexpr bool operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
956 return !(aLHS < aRHS);
959 template <typename T>
960 inline void ImplCycleCollectionTraverse(
961 nsCycleCollectionTraversalCallback& aCallback, mozilla::Maybe<T>& aField,
962 const char* aName, uint32_t aFlags = 0) {
963 if (aField) {
964 ImplCycleCollectionTraverse(aCallback, aField.ref(), aName, aFlags);
968 template <typename T>
969 inline void ImplCycleCollectionUnlink(mozilla::Maybe<T>& aField) {
970 if (aField) {
971 ImplCycleCollectionUnlink(aField.ref());
975 } // namespace mozilla
977 #endif /* mozilla_Maybe_h */