Bug 1708422: part 6) Change some pointer members of `mozInlineSpellChecker::SpellChec...
[gecko.git] / mfbt / Maybe.h
blob5ebb5bb976435615d49d7c77797ba513a47f05bc
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"
25 class nsCycleCollectionTraversalCallback;
27 template <typename T>
28 inline void CycleCollectionNoteChild(
29 nsCycleCollectionTraversalCallback& aCallback, T* aChild, const char* aName,
30 uint32_t aFlags);
32 namespace mozilla {
34 struct Nothing {};
36 inline constexpr bool operator==(const Nothing&, const Nothing&) {
37 return true;
40 template <class T>
41 class Maybe;
43 namespace detail {
45 // You would think that poisoning Maybe instances could just be a call
46 // to mozWritePoison. Unfortunately, using a simple call to
47 // mozWritePoison generates poor code on MSVC for small structures. The
48 // generated code contains (always not-taken) branches and does a bunch
49 // of setup for `rep stos{l,q}`, even though we know at compile time
50 // exactly how many words we're poisoning. Instead, we're going to
51 // force MSVC to generate the code we want via recursive templates.
53 // Write the given poisonValue into p at offset*sizeof(uintptr_t).
54 template <size_t offset>
55 inline void WritePoisonAtOffset(void* p, const uintptr_t poisonValue) {
56 memcpy(static_cast<char*>(p) + offset * sizeof(poisonValue), &poisonValue,
57 sizeof(poisonValue));
60 template <size_t Offset, size_t NOffsets>
61 struct InlinePoisoner {
62 static void poison(void* p, const uintptr_t poisonValue) {
63 WritePoisonAtOffset<Offset>(p, poisonValue);
64 InlinePoisoner<Offset + 1, NOffsets>::poison(p, poisonValue);
68 template <size_t N>
69 struct InlinePoisoner<N, N> {
70 static void poison(void*, const uintptr_t) {
71 // All done!
75 // We can't generate inline code for large structures, though, because we'll
76 // blow out recursive template instantiation limits, and the code would be
77 // bloated to boot. So provide a fallback to the out-of-line poisoner.
78 template <size_t ObjectSize>
79 struct OutOfLinePoisoner {
80 static MOZ_NEVER_INLINE void poison(void* p, const uintptr_t) {
81 mozWritePoison(p, ObjectSize);
85 template <typename T>
86 inline void PoisonObject(T* p) {
87 const uintptr_t POISON = mozPoisonValue();
88 std::conditional_t<(sizeof(T) <= 8 * sizeof(POISON)),
89 InlinePoisoner<0, sizeof(T) / sizeof(POISON)>,
90 OutOfLinePoisoner<sizeof(T)>>::poison(p, POISON);
93 template <typename T>
94 struct MaybePoisoner {
95 static const size_t N = sizeof(T);
97 static void poison(void* aPtr) {
98 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
99 if (N >= sizeof(uintptr_t)) {
100 PoisonObject(static_cast<std::remove_cv_t<T>*>(aPtr));
102 #endif
103 MOZ_MAKE_MEM_UNDEFINED(aPtr, N);
107 template <typename T,
108 bool TriviallyDestructibleAndCopyable =
109 IsTriviallyDestructibleAndCopyable<T>,
110 bool Copyable = std::is_copy_constructible_v<T>,
111 bool Movable = std::is_move_constructible_v<T>>
112 class Maybe_CopyMove_Enabler;
114 #define MOZ_MAYBE_COPY_OPS() \
115 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler& aOther) { \
116 if (downcast(aOther).isSome()) { \
117 downcast(*this).emplace(*downcast(aOther)); \
121 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler& aOther) { \
122 return downcast(*this).template operator=<T>(downcast(aOther)); \
125 #define MOZ_MAYBE_MOVE_OPS() \
126 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) { \
127 if (downcast(aOther).isSome()) { \
128 downcast(*this).emplace(std::move(*downcast(aOther))); \
129 downcast(aOther).reset(); \
133 constexpr Maybe_CopyMove_Enabler& operator=( \
134 Maybe_CopyMove_Enabler&& aOther) { \
135 downcast(*this).template operator=<T>(std::move(downcast(aOther))); \
137 return *this; \
140 #define MOZ_MAYBE_DOWNCAST() \
141 static constexpr Maybe<T>& downcast(Maybe_CopyMove_Enabler& aObj) { \
142 return static_cast<Maybe<T>&>(aObj); \
144 static constexpr const Maybe<T>& downcast( \
145 const Maybe_CopyMove_Enabler& aObj) { \
146 return static_cast<const Maybe<T>&>(aObj); \
149 template <typename T>
150 class Maybe_CopyMove_Enabler<T, true, true, true> {
151 public:
152 Maybe_CopyMove_Enabler() = default;
154 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = default;
155 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = default;
156 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) {
157 downcast(aOther).reset();
159 constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) {
160 downcast(aOther).reset();
161 return *this;
164 private:
165 MOZ_MAYBE_DOWNCAST()
168 template <typename T>
169 class Maybe_CopyMove_Enabler<T, true, false, true> {
170 public:
171 Maybe_CopyMove_Enabler() = default;
173 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete;
174 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete;
175 constexpr Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&& aOther) {
176 downcast(aOther).reset();
178 constexpr Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&& aOther) {
179 downcast(aOther).reset();
180 return *this;
183 private:
184 MOZ_MAYBE_DOWNCAST()
187 template <typename T>
188 class Maybe_CopyMove_Enabler<T, false, true, true> {
189 public:
190 Maybe_CopyMove_Enabler() = default;
192 MOZ_MAYBE_COPY_OPS()
193 MOZ_MAYBE_MOVE_OPS()
195 private:
196 MOZ_MAYBE_DOWNCAST()
199 template <typename T>
200 class Maybe_CopyMove_Enabler<T, false, false, true> {
201 public:
202 Maybe_CopyMove_Enabler() = default;
204 MOZ_MAYBE_MOVE_OPS()
206 private:
207 MOZ_MAYBE_DOWNCAST()
210 template <typename T>
211 class Maybe_CopyMove_Enabler<T, false, true, false> {
212 public:
213 Maybe_CopyMove_Enabler() = default;
215 MOZ_MAYBE_COPY_OPS()
217 private:
218 MOZ_MAYBE_DOWNCAST()
221 template <typename T, bool TriviallyDestructibleAndCopyable>
222 class Maybe_CopyMove_Enabler<T, TriviallyDestructibleAndCopyable, false,
223 false> {
224 public:
225 Maybe_CopyMove_Enabler() = default;
227 Maybe_CopyMove_Enabler(const Maybe_CopyMove_Enabler&) = delete;
228 Maybe_CopyMove_Enabler& operator=(const Maybe_CopyMove_Enabler&) = delete;
229 Maybe_CopyMove_Enabler(Maybe_CopyMove_Enabler&&) = delete;
230 Maybe_CopyMove_Enabler& operator=(Maybe_CopyMove_Enabler&&) = delete;
233 #undef MOZ_MAYBE_COPY_OPS
234 #undef MOZ_MAYBE_MOVE_OPS
235 #undef MOZ_MAYBE_DOWNCAST
237 template <typename T, bool TriviallyDestructibleAndCopyable =
238 IsTriviallyDestructibleAndCopyable<T>>
239 struct MaybeStorage;
241 template <typename T>
242 struct MaybeStorage<T, false> : MaybeStorageBase<T> {
243 protected:
244 char mIsSome = false; // not bool -- guarantees minimal space consumption
246 MaybeStorage() = default;
247 explicit MaybeStorage(const T& aVal)
248 : MaybeStorageBase<T>{aVal}, mIsSome{true} {}
249 explicit MaybeStorage(T&& aVal)
250 : MaybeStorageBase<T>{std::move(aVal)}, mIsSome{true} {}
252 template <typename... Args>
253 explicit MaybeStorage(std::in_place_t, Args&&... aArgs)
254 : MaybeStorageBase<T>{std::in_place, std::forward<Args>(aArgs)...},
255 mIsSome{true} {}
257 public:
258 // Copy and move operations are no-ops, since copying is moving is implemented
259 // by Maybe_CopyMove_Enabler.
261 MaybeStorage(const MaybeStorage&) : MaybeStorageBase<T>{} {}
262 MaybeStorage& operator=(const MaybeStorage&) { return *this; }
263 MaybeStorage(MaybeStorage&&) : MaybeStorageBase<T>{} {}
264 MaybeStorage& operator=(MaybeStorage&&) { return *this; }
266 ~MaybeStorage() {
267 if (mIsSome) {
268 this->addr()->T::~T();
273 template <typename T>
274 struct MaybeStorage<T, true> : MaybeStorageBase<T> {
275 protected:
276 char mIsSome = false; // not bool -- guarantees minimal space consumption
278 constexpr MaybeStorage() = default;
279 constexpr explicit MaybeStorage(const T& aVal)
280 : MaybeStorageBase<T>{aVal}, mIsSome{true} {}
281 constexpr explicit MaybeStorage(T&& aVal)
282 : MaybeStorageBase<T>{std::move(aVal)}, mIsSome{true} {}
284 template <typename... Args>
285 constexpr explicit MaybeStorage(std::in_place_t, Args&&... aArgs)
286 : MaybeStorageBase<T>{std::in_place, std::forward<Args>(aArgs)...},
287 mIsSome{true} {}
290 } // namespace detail
292 template <typename T, typename U = typename std::remove_cv<
293 typename std::remove_reference<T>::type>::type>
294 constexpr Maybe<U> Some(T&& aValue);
297 * Maybe is a container class which contains either zero or one elements. It
298 * serves two roles. It can represent values which are *semantically* optional,
299 * augmenting a type with an explicit 'Nothing' value. In this role, it provides
300 * methods that make it easy to work with values that may be missing, along with
301 * equality and comparison operators so that Maybe values can be stored in
302 * containers. Maybe values can be constructed conveniently in expressions using
303 * type inference, as follows:
305 * void doSomething(Maybe<Foo> aFoo) {
306 * if (aFoo) // Make sure that aFoo contains a value...
307 * aFoo->takeAction(); // and then use |aFoo->| to access it.
308 * } // |*aFoo| also works!
310 * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value.
311 * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
313 * You'll note that it's important to check whether a Maybe contains a value
314 * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You
315 * can avoid these checks, and sometimes write more readable code, using
316 * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value
317 * in the Maybe and provide a default for the 'Nothing' case. You can also use
318 * |apply()| to call a function only if the Maybe holds a value, and |map()| to
319 * transform the value in the Maybe, returning another Maybe with a possibly
320 * different type.
322 * Maybe's other role is to support lazily constructing objects without using
323 * dynamic storage. A Maybe directly contains storage for a value, but it's
324 * empty by default. |emplace()|, as mentioned above, can be used to construct a
325 * value in Maybe's storage. The value a Maybe contains can be destroyed by
326 * calling |reset()|; this will happen automatically if a Maybe is destroyed
327 * while holding a value.
329 * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null
330 * value meaning 'Nothing' and any other value meaning 'Some'. You can convert
331 * from such a pointer to a Maybe value using 'ToMaybe()'.
333 * Maybe is inspired by similar types in the standard library of many other
334 * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's
335 * very similar to std::optional, which was proposed for C++14 and originated in
336 * Boost. The most important differences between Maybe and std::optional are:
338 * - std::optional<T> may be compared with T. We deliberately forbid that.
339 * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but
340 * lacks corresponding methods for |refOr()| and |ptrOr()|.
341 * - std::optional lacks |map()| and |apply()|, making it less suitable for
342 * functional-style code.
343 * - std::optional lacks many convenience functions that Maybe has. Most
344 * unfortunately, it lacks equivalents of the type-inferred constructor
345 * functions |Some()| and |Nothing()|.
347 template <class T>
348 class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe
349 : private detail::MaybeStorage<T>,
350 public detail::Maybe_CopyMove_Enabler<T> {
351 template <typename, bool, bool, bool>
352 friend class detail::Maybe_CopyMove_Enabler;
354 template <typename U, typename V>
355 friend constexpr Maybe<V> Some(U&& aValue);
357 struct SomeGuard {};
359 template <typename U>
360 constexpr Maybe(U&& aValue, SomeGuard)
361 : detail::MaybeStorage<T>{std::forward<U>(aValue)} {}
363 using detail::MaybeStorage<T>::mIsSome;
364 using detail::MaybeStorage<T>::mStorage;
366 void poisonData() { detail::MaybePoisoner<T>::poison(&mStorage.val); }
368 public:
369 using ValueType = T;
371 MOZ_ALLOW_TEMPORARY constexpr Maybe() = default;
373 MOZ_ALLOW_TEMPORARY MOZ_IMPLICIT constexpr Maybe(Nothing) : Maybe{} {}
375 template <typename... Args>
376 constexpr explicit Maybe(std::in_place_t, Args&&... aArgs)
377 : detail::MaybeStorage<T>{std::in_place, std::forward<Args>(aArgs)...} {}
380 * Maybe<T> can be copy-constructed from a Maybe<U> if T is constructible from
381 * a const U&.
383 template <typename U,
384 typename = std::enable_if_t<std::is_constructible_v<T, const U&>>>
385 MOZ_IMPLICIT Maybe(const Maybe<U>& aOther) {
386 if (aOther.isSome()) {
387 emplace(*aOther);
392 * Maybe<T> can be move-constructed from a Maybe<U> if T is constructible from
393 * a U&&.
395 template <typename U,
396 typename = std::enable_if_t<std::is_constructible_v<T, U&&>>>
397 MOZ_IMPLICIT Maybe(Maybe<U>&& aOther) {
398 if (aOther.isSome()) {
399 emplace(std::move(*aOther));
400 aOther.reset();
404 template <typename U,
405 typename = std::enable_if_t<std::is_constructible_v<T, const U&>>>
406 Maybe& operator=(const Maybe<U>& aOther) {
407 if (aOther.isSome()) {
408 if (mIsSome) {
409 ref() = aOther.ref();
410 } else {
411 emplace(*aOther);
413 } else {
414 reset();
416 return *this;
419 template <typename U,
420 typename = std::enable_if_t<std::is_constructible_v<T, U&&>>>
421 Maybe& operator=(Maybe<U>&& aOther) {
422 if (aOther.isSome()) {
423 if (mIsSome) {
424 ref() = std::move(aOther.ref());
425 } else {
426 emplace(std::move(*aOther));
428 aOther.reset();
429 } else {
430 reset();
433 return *this;
436 constexpr Maybe& operator=(Nothing) {
437 reset();
438 return *this;
441 /* Methods that check whether this Maybe contains a value */
442 constexpr explicit operator bool() const { return isSome(); }
443 constexpr bool isSome() const { return mIsSome; }
444 constexpr bool isNothing() const { return !mIsSome; }
446 /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|.
448 constexpr T value() const&;
449 constexpr T value() &&;
450 constexpr T value() const&&;
453 * Move the contents of this Maybe<T> out of internal storage and return it
454 * without calling the destructor. The internal storage is also reset to
455 * avoid multiple calls. Unsafe unless |isSome()|.
457 T extract() {
458 MOZ_DIAGNOSTIC_ASSERT(isSome());
459 T v = std::move(mStorage.val);
460 reset();
461 return v;
465 * Returns the value (possibly |Nothing()|) by moving it out of this Maybe<T>
466 * and leaving |Nothing()| in its place.
468 Maybe<T> take() { return std::exchange(*this, Nothing()); }
471 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
472 * the default value provided.
474 * Note: If the value passed to aDefault is not the result of a trivial
475 * expression, but expensive to evaluate, e.g. |valueOr(ExpensiveFunction())|,
476 * use |valueOrFrom| instead, e.g.
477 * |valueOrFrom([arg] { return ExpensiveFunction(arg); })|. This ensures
478 * that the expensive expression is only evaluated when its result will
479 * actually be used.
481 template <typename V>
482 constexpr T valueOr(V&& aDefault) const {
483 if (isSome()) {
484 return ref();
486 return std::forward<V>(aDefault);
490 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
491 * the value returned from the function or functor provided.
493 template <typename F>
494 constexpr T valueOrFrom(F&& aFunc) const {
495 if (isSome()) {
496 return ref();
498 return aFunc();
501 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|.
503 T* ptr();
504 constexpr const T* ptr() const;
507 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
508 * returns the default value provided.
510 T* ptrOr(T* aDefault) {
511 if (isSome()) {
512 return ptr();
514 return aDefault;
517 constexpr const T* ptrOr(const T* aDefault) const {
518 if (isSome()) {
519 return ptr();
521 return aDefault;
525 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
526 * returns the value returned from the function or functor provided.
528 template <typename F>
529 T* ptrOrFrom(F&& aFunc) {
530 if (isSome()) {
531 return ptr();
533 return aFunc();
536 template <typename F>
537 const T* ptrOrFrom(F&& aFunc) const {
538 if (isSome()) {
539 return ptr();
541 return aFunc();
544 constexpr T* operator->();
545 constexpr const T* operator->() const;
547 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
548 constexpr T& ref() &;
549 constexpr const T& ref() const&;
550 constexpr T&& ref() &&;
551 constexpr const T&& ref() const&&;
554 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
555 * the default value provided.
557 constexpr T& refOr(T& aDefault) {
558 if (isSome()) {
559 return ref();
561 return aDefault;
564 constexpr const T& refOr(const T& aDefault) const {
565 if (isSome()) {
566 return ref();
568 return aDefault;
572 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
573 * value returned from the function or functor provided.
575 template <typename F>
576 constexpr T& refOrFrom(F&& aFunc) {
577 if (isSome()) {
578 return ref();
580 return aFunc();
583 template <typename F>
584 constexpr const T& refOrFrom(F&& aFunc) const {
585 if (isSome()) {
586 return ref();
588 return aFunc();
591 constexpr T& operator*() &;
592 constexpr const T& operator*() const&;
593 constexpr T&& operator*() &&;
594 constexpr const T&& operator*() const&&;
596 /* If |isSome()|, runs the provided function or functor on the contents of
597 * this Maybe. */
598 template <typename Func>
599 constexpr Maybe& apply(Func&& aFunc) {
600 if (isSome()) {
601 std::forward<Func>(aFunc)(ref());
603 return *this;
606 template <typename Func>
607 constexpr const Maybe& apply(Func&& aFunc) const {
608 if (isSome()) {
609 std::forward<Func>(aFunc)(ref());
611 return *this;
615 * If |isSome()|, runs the provided function and returns the result wrapped
616 * in a Maybe. If |isNothing()|, returns an empty Maybe value with the same
617 * value type as what the provided function would have returned.
619 template <typename Func>
620 constexpr auto map(Func&& aFunc) {
621 if (isSome()) {
622 return Some(std::forward<Func>(aFunc)(ref()));
624 return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{};
627 template <typename Func>
628 constexpr auto map(Func&& aFunc) const {
629 if (isSome()) {
630 return Some(std::forward<Func>(aFunc)(ref()));
632 return Maybe<decltype(std::forward<Func>(aFunc)(ref()))>{};
635 /* If |isSome()|, empties this Maybe and destroys its contents. */
636 constexpr void reset() {
637 if (isSome()) {
638 if constexpr (!std::is_trivially_destructible_v<T>) {
639 ref().T::~T();
640 poisonData();
642 mIsSome = false;
647 * Constructs a T value in-place in this empty Maybe<T>'s storage. The
648 * arguments to |emplace()| are the parameters to T's constructor.
650 template <typename... Args>
651 constexpr void emplace(Args&&... aArgs);
653 template <typename U>
654 constexpr std::enable_if_t<std::is_same_v<T, U> &&
655 std::is_copy_constructible_v<U> &&
656 !std::is_move_constructible_v<U>>
657 emplace(U&& aArgs) {
658 emplace(aArgs);
661 friend std::ostream& operator<<(std::ostream& aStream,
662 const Maybe<T>& aMaybe) {
663 if (aMaybe) {
664 aStream << aMaybe.ref();
665 } else {
666 aStream << "<Nothing>";
668 return aStream;
672 template <typename T>
673 class Maybe<T&> {
674 public:
675 constexpr Maybe() = default;
676 constexpr MOZ_IMPLICIT Maybe(Nothing) {}
678 void emplace(T& aRef) { mValue = &aRef; }
680 /* Methods that check whether this Maybe contains a value */
681 constexpr explicit operator bool() const { return isSome(); }
682 constexpr bool isSome() const { return mValue; }
683 constexpr bool isNothing() const { return !mValue; }
685 T& ref() const {
686 MOZ_DIAGNOSTIC_ASSERT(isSome());
687 return *mValue;
690 T* operator->() const { return &ref(); }
691 T& operator*() const { return ref(); }
693 // Deliberately not defining value and ptr accessors, as these may be
694 // confusing on a reference-typed Maybe.
696 // XXX Should we define refOr?
698 void reset() { mValue = nullptr; }
700 template <typename Func>
701 Maybe& apply(Func&& aFunc) {
702 if (isSome()) {
703 std::forward<Func>(aFunc)(ref());
705 return *this;
708 template <typename Func>
709 const Maybe& apply(Func&& aFunc) const {
710 if (isSome()) {
711 std::forward<Func>(aFunc)(ref());
713 return *this;
716 template <typename Func>
717 auto map(Func&& aFunc) {
718 Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
719 if (isSome()) {
720 val.emplace(std::forward<Func>(aFunc)(ref()));
722 return val;
725 template <typename Func>
726 auto map(Func&& aFunc) const {
727 Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
728 if (isSome()) {
729 val.emplace(std::forward<Func>(aFunc)(ref()));
731 return val;
734 bool refEquals(const Maybe<T&>& aOther) const {
735 return mValue == aOther.mValue;
738 bool refEquals(const T& aOther) const { return mValue == &aOther; }
740 private:
741 T* mValue = nullptr;
744 template <typename T>
745 constexpr T Maybe<T>::value() const& {
746 MOZ_DIAGNOSTIC_ASSERT(isSome());
747 return ref();
750 template <typename T>
751 constexpr T Maybe<T>::value() && {
752 MOZ_DIAGNOSTIC_ASSERT(isSome());
753 return std::move(ref());
756 template <typename T>
757 constexpr T Maybe<T>::value() const&& {
758 MOZ_DIAGNOSTIC_ASSERT(isSome());
759 return std::move(ref());
762 template <typename T>
763 T* Maybe<T>::ptr() {
764 MOZ_DIAGNOSTIC_ASSERT(isSome());
765 return &ref();
768 template <typename T>
769 constexpr const T* Maybe<T>::ptr() const {
770 MOZ_DIAGNOSTIC_ASSERT(isSome());
771 return &ref();
774 template <typename T>
775 constexpr T* Maybe<T>::operator->() {
776 MOZ_DIAGNOSTIC_ASSERT(isSome());
777 return ptr();
780 template <typename T>
781 constexpr const T* Maybe<T>::operator->() const {
782 MOZ_DIAGNOSTIC_ASSERT(isSome());
783 return ptr();
786 template <typename T>
787 constexpr T& Maybe<T>::ref() & {
788 MOZ_DIAGNOSTIC_ASSERT(isSome());
789 return mStorage.val;
792 template <typename T>
793 constexpr const T& Maybe<T>::ref() const& {
794 MOZ_DIAGNOSTIC_ASSERT(isSome());
795 return mStorage.val;
798 template <typename T>
799 constexpr T&& Maybe<T>::ref() && {
800 MOZ_DIAGNOSTIC_ASSERT(isSome());
801 return std::move(mStorage.val);
804 template <typename T>
805 constexpr const T&& Maybe<T>::ref() const&& {
806 MOZ_DIAGNOSTIC_ASSERT(isSome());
807 return std::move(mStorage.val);
810 template <typename T>
811 constexpr T& Maybe<T>::operator*() & {
812 MOZ_DIAGNOSTIC_ASSERT(isSome());
813 return ref();
816 template <typename T>
817 constexpr const T& Maybe<T>::operator*() const& {
818 MOZ_DIAGNOSTIC_ASSERT(isSome());
819 return ref();
822 template <typename T>
823 constexpr T&& Maybe<T>::operator*() && {
824 MOZ_DIAGNOSTIC_ASSERT(isSome());
825 return std::move(ref());
828 template <typename T>
829 constexpr const T&& Maybe<T>::operator*() const&& {
830 MOZ_DIAGNOSTIC_ASSERT(isSome());
831 return std::move(ref());
834 template <typename T>
835 template <typename... Args>
836 constexpr void Maybe<T>::emplace(Args&&... aArgs) {
837 MOZ_DIAGNOSTIC_ASSERT(!isSome());
838 ::new (KnownNotNull, &mStorage.val) T(std::forward<Args>(aArgs)...);
839 mIsSome = true;
843 * Some() creates a Maybe<T> value containing the provided T value. If T has a
844 * move constructor, it's used to make this as efficient as possible.
846 * Some() selects the type of Maybe it returns by removing any const, volatile,
847 * or reference qualifiers from the type of the value you pass to it. This gives
848 * it more intuitive behavior when used in expressions, but it also means that
849 * if you need to construct a Maybe value that holds a const, volatile, or
850 * reference value, you need to use emplace() instead.
852 template <typename T, typename U>
853 constexpr Maybe<U> Some(T&& aValue) {
854 return {std::forward<T>(aValue), typename Maybe<U>::SomeGuard{}};
857 template <typename T>
858 constexpr Maybe<T&> SomeRef(T& aValue) {
859 Maybe<T&> value;
860 value.emplace(aValue);
861 return value;
864 template <typename T>
865 constexpr Maybe<T&> ToMaybeRef(T* const aPtr) {
866 return aPtr ? SomeRef(*aPtr) : Nothing{};
869 template <typename T>
870 Maybe<std::remove_cv_t<std::remove_reference_t<T>>> ToMaybe(T* aPtr) {
871 if (aPtr) {
872 return Some(*aPtr);
874 return Nothing();
878 * Two Maybe<T> values are equal if
879 * - both are Nothing, or
880 * - both are Some, and the values they contain are equal.
882 template <typename T>
883 constexpr bool operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
884 static_assert(!std::is_reference_v<T>,
885 "operator== is not defined for Maybe<T&>, compare values or "
886 "addresses explicitly instead");
887 if (aLHS.isNothing() != aRHS.isNothing()) {
888 return false;
890 return aLHS.isNothing() || *aLHS == *aRHS;
893 template <typename T>
894 constexpr bool operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
895 return !(aLHS == aRHS);
899 * We support comparison to Nothing to allow reasonable expressions like:
900 * if (maybeValue == Nothing()) { ... }
902 template <typename T>
903 constexpr bool operator==(const Maybe<T>& aLHS, const Nothing& aRHS) {
904 return aLHS.isNothing();
907 template <typename T>
908 constexpr bool operator!=(const Maybe<T>& aLHS, const Nothing& aRHS) {
909 return !(aLHS == aRHS);
912 template <typename T>
913 constexpr bool operator==(const Nothing& aLHS, const Maybe<T>& aRHS) {
914 return aRHS.isNothing();
917 template <typename T>
918 constexpr bool operator!=(const Nothing& aLHS, const Maybe<T>& aRHS) {
919 return !(aLHS == aRHS);
923 * Maybe<T> values are ordered in the same way T values are ordered, except that
924 * Nothing comes before anything else.
926 template <typename T>
927 constexpr bool operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
928 if (aLHS.isNothing()) {
929 return aRHS.isSome();
931 if (aRHS.isNothing()) {
932 return false;
934 return *aLHS < *aRHS;
937 template <typename T>
938 constexpr bool operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
939 return !(aLHS < aRHS || aLHS == aRHS);
942 template <typename T>
943 constexpr bool operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
944 return aLHS < aRHS || aLHS == aRHS;
947 template <typename T>
948 constexpr bool operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
949 return !(aLHS < aRHS);
952 template <typename T>
953 inline void ImplCycleCollectionTraverse(
954 nsCycleCollectionTraversalCallback& aCallback, mozilla::Maybe<T>& aField,
955 const char* aName, uint32_t aFlags = 0) {
956 if (aField) {
957 ImplCycleCollectionTraverse(aCallback, aField.ref(), aName, aFlags);
961 template <typename T>
962 inline void ImplCycleCollectionUnlink(mozilla::Maybe<T>& aField) {
963 if (aField) {
964 ImplCycleCollectionUnlink(aField.ref());
968 } // namespace mozilla
970 #endif /* mozilla_Maybe_h */