Bug 1529726 [wpt PR 15414] - HTML: window.length and named access, a=testonly
[gecko.git] / mfbt / Maybe.h
blob86b57c29e6b8785f7d8184c273d2bdaa978147d2
1 /* -*- Mode: C++; tab-width: 8; 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 "mozilla/Alignment.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/MemoryChecking.h"
16 #include "mozilla/Move.h"
17 #include "mozilla/OperatorNewExtensions.h"
18 #include "mozilla/Poison.h"
19 #include "mozilla/TypeTraits.h"
21 #include <new> // for placement new
22 #include <ostream>
23 #include <type_traits>
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 namespace detail {
38 // You would think that poisoning Maybe instances could just be a call
39 // to mozWritePoison. Unfortunately, using a simple call to
40 // mozWritePoison generates poor code on MSVC for small structures. The
41 // generated code contains (always not-taken) branches and does a bunch
42 // of setup for `rep stos{l,q}`, even though we know at compile time
43 // exactly how many words we're poisoning. Instead, we're going to
44 // force MSVC to generate the code we want via recursive templates.
46 // Write the given poisonValue into p at offset*sizeof(uintptr_t).
47 template <size_t offset>
48 inline void WritePoisonAtOffset(void* p, const uintptr_t poisonValue) {
49 memcpy(static_cast<char*>(p) + offset * sizeof(poisonValue), &poisonValue,
50 sizeof(poisonValue));
53 template <size_t Offset, size_t NOffsets>
54 struct InlinePoisoner {
55 static void poison(void* p, const uintptr_t poisonValue) {
56 WritePoisonAtOffset<Offset>(p, poisonValue);
57 InlinePoisoner<Offset + 1, NOffsets>::poison(p, poisonValue);
61 template <size_t N>
62 struct InlinePoisoner<N, N> {
63 static void poison(void*, const uintptr_t) {
64 // All done!
68 // We can't generate inline code for large structures, though, because we'll
69 // blow out recursive template instantiation limits, and the code would be
70 // bloated to boot. So provide a fallback to the out-of-line poisoner.
71 template <size_t ObjectSize>
72 struct OutOfLinePoisoner {
73 static void poison(void* p, const uintptr_t) {
74 mozWritePoison(p, ObjectSize);
78 template <typename T>
79 inline void PoisonObject(T* p) {
80 const uintptr_t POISON = mozPoisonValue();
81 Conditional<(sizeof(T) <= 8 * sizeof(POISON)),
82 InlinePoisoner<0, sizeof(T) / sizeof(POISON)>,
83 OutOfLinePoisoner<sizeof(T)>>::Type::poison(p, POISON);
86 template <typename T>
87 struct MaybePoisoner {
88 static const size_t N = sizeof(T);
90 static void poison(void* aPtr) {
91 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
92 if (N >= sizeof(uintptr_t)) {
93 PoisonObject(static_cast<typename RemoveCV<T>::Type*>(aPtr));
95 #endif
96 MOZ_MAKE_MEM_UNDEFINED(aPtr, N);
100 } // namespace detail
103 * Maybe is a container class which contains either zero or one elements. It
104 * serves two roles. It can represent values which are *semantically* optional,
105 * augmenting a type with an explicit 'Nothing' value. In this role, it provides
106 * methods that make it easy to work with values that may be missing, along with
107 * equality and comparison operators so that Maybe values can be stored in
108 * containers. Maybe values can be constructed conveniently in expressions using
109 * type inference, as follows:
111 * void doSomething(Maybe<Foo> aFoo) {
112 * if (aFoo) // Make sure that aFoo contains a value...
113 * aFoo->takeAction(); // and then use |aFoo->| to access it.
114 * } // |*aFoo| also works!
116 * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value.
117 * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
119 * You'll note that it's important to check whether a Maybe contains a value
120 * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You
121 * can avoid these checks, and sometimes write more readable code, using
122 * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value
123 * in the Maybe and provide a default for the 'Nothing' case. You can also use
124 * |apply()| to call a function only if the Maybe holds a value, and |map()| to
125 * transform the value in the Maybe, returning another Maybe with a possibly
126 * different type.
128 * Maybe's other role is to support lazily constructing objects without using
129 * dynamic storage. A Maybe directly contains storage for a value, but it's
130 * empty by default. |emplace()|, as mentioned above, can be used to construct a
131 * value in Maybe's storage. The value a Maybe contains can be destroyed by
132 * calling |reset()|; this will happen automatically if a Maybe is destroyed
133 * while holding a value.
135 * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null
136 * value meaning 'Nothing' and any other value meaning 'Some'. You can convert
137 * from such a pointer to a Maybe value using 'ToMaybe()'.
139 * Maybe is inspired by similar types in the standard library of many other
140 * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's
141 * very similar to std::optional, which was proposed for C++14 and originated in
142 * Boost. The most important differences between Maybe and std::optional are:
144 * - std::optional<T> may be compared with T. We deliberately forbid that.
145 * - std::optional allows in-place construction without a separate call to
146 * |emplace()| by using a dummy |in_place_t| value to tag the appropriate
147 * constructor.
148 * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but
149 * lacks corresponding methods for |refOr()| and |ptrOr()|.
150 * - std::optional lacks |map()| and |apply()|, making it less suitable for
151 * functional-style code.
152 * - std::optional lacks many convenience functions that Maybe has. Most
153 * unfortunately, it lacks equivalents of the type-inferred constructor
154 * functions |Some()| and |Nothing()|.
156 template <class T>
157 class MOZ_NON_PARAM MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe {
158 MOZ_ALIGNAS_IN_STRUCT(T) unsigned char mStorage[sizeof(T)];
159 char mIsSome; // not bool -- guarantees minimal space consumption
161 // GCC fails due to -Werror=strict-aliasing if |mStorage| is directly cast to
162 // T*. Indirecting through these functions addresses the problem.
163 void* data() { return mStorage; }
164 const void* data() const { return mStorage; }
166 void poisonData() { detail::MaybePoisoner<T>::poison(data()); }
168 public:
169 using ValueType = T;
171 MOZ_ALLOW_TEMPORARY Maybe() : mIsSome(false) {}
172 ~Maybe() { reset(); }
174 MOZ_ALLOW_TEMPORARY MOZ_IMPLICIT Maybe(Nothing) : mIsSome(false) {}
176 Maybe(const Maybe& aOther) : mIsSome(false) {
177 if (aOther.mIsSome) {
178 emplace(*aOther);
183 * Maybe<T> can be copy-constructed from a Maybe<U> if U is convertible to T.
185 template <typename U, typename = typename std::enable_if<
186 std::is_convertible<U, T>::value>::type>
187 MOZ_IMPLICIT Maybe(const Maybe<U>& aOther) : mIsSome(false) {
188 if (aOther.isSome()) {
189 emplace(*aOther);
193 Maybe(Maybe&& aOther) : mIsSome(false) {
194 if (aOther.mIsSome) {
195 emplace(std::move(*aOther));
196 aOther.reset();
201 * Maybe<T> can be move-constructed from a Maybe<U> if U is convertible to T.
203 template <typename U, typename = typename std::enable_if<
204 std::is_convertible<U, T>::value>::type>
205 MOZ_IMPLICIT Maybe(Maybe<U>&& aOther) : mIsSome(false) {
206 if (aOther.isSome()) {
207 emplace(std::move(*aOther));
208 aOther.reset();
212 Maybe& operator=(const Maybe& aOther) {
213 if (&aOther != this) {
214 if (aOther.mIsSome) {
215 if (mIsSome) {
216 ref() = aOther.ref();
217 } else {
218 emplace(*aOther);
220 } else {
221 reset();
224 return *this;
227 template <typename U, typename = typename std::enable_if<
228 std::is_convertible<U, T>::value>::type>
229 Maybe& operator=(const Maybe<U>& aOther) {
230 if (aOther.isSome()) {
231 if (mIsSome) {
232 ref() = aOther.ref();
233 } else {
234 emplace(*aOther);
236 } else {
237 reset();
239 return *this;
242 Maybe& operator=(Maybe&& aOther) {
243 MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
245 if (aOther.mIsSome) {
246 if (mIsSome) {
247 ref() = std::move(aOther.ref());
248 } else {
249 emplace(std::move(*aOther));
251 aOther.reset();
252 } else {
253 reset();
256 return *this;
259 template <typename U, typename = typename std::enable_if<
260 std::is_convertible<U, T>::value>::type>
261 Maybe& operator=(Maybe<U>&& aOther) {
262 if (aOther.isSome()) {
263 if (mIsSome) {
264 ref() = std::move(aOther.ref());
265 } else {
266 emplace(std::move(*aOther));
268 aOther.reset();
269 } else {
270 reset();
273 return *this;
276 /* Methods that check whether this Maybe contains a value */
277 explicit operator bool() const { return isSome(); }
278 bool isSome() const { return mIsSome; }
279 bool isNothing() const { return !mIsSome; }
281 /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|.
283 T value() const;
286 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
287 * the default value provided.
289 template <typename V>
290 T valueOr(V&& aDefault) const {
291 if (isSome()) {
292 return ref();
294 return std::forward<V>(aDefault);
298 * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
299 * the value returned from the function or functor provided.
301 template <typename F>
302 T valueOrFrom(F&& aFunc) const {
303 if (isSome()) {
304 return ref();
306 return aFunc();
309 /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|.
311 T* ptr();
312 const T* ptr() const;
315 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
316 * returns the default value provided.
318 T* ptrOr(T* aDefault) {
319 if (isSome()) {
320 return ptr();
322 return aDefault;
325 const T* ptrOr(const T* aDefault) const {
326 if (isSome()) {
327 return ptr();
329 return aDefault;
333 * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
334 * returns the value returned from the function or functor provided.
336 template <typename F>
337 T* ptrOrFrom(F&& aFunc) {
338 if (isSome()) {
339 return ptr();
341 return aFunc();
344 template <typename F>
345 const T* ptrOrFrom(F&& aFunc) const {
346 if (isSome()) {
347 return ptr();
349 return aFunc();
352 T* operator->();
353 const T* operator->() const;
355 /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
356 T& ref();
357 const T& ref() const;
360 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
361 * the default value provided.
363 T& refOr(T& aDefault) {
364 if (isSome()) {
365 return ref();
367 return aDefault;
370 const T& refOr(const T& aDefault) const {
371 if (isSome()) {
372 return ref();
374 return aDefault;
378 * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
379 * value returned from the function or functor provided.
381 template <typename F>
382 T& refOrFrom(F&& aFunc) {
383 if (isSome()) {
384 return ref();
386 return aFunc();
389 template <typename F>
390 const T& refOrFrom(F&& aFunc) const {
391 if (isSome()) {
392 return ref();
394 return aFunc();
397 T& operator*();
398 const T& operator*() const;
400 /* If |isSome()|, runs the provided function or functor on the contents of
401 * this Maybe. */
402 template <typename Func>
403 Maybe& apply(Func&& aFunc) {
404 if (isSome()) {
405 std::forward<Func>(aFunc)(ref());
407 return *this;
410 template <typename Func>
411 const Maybe& apply(Func&& aFunc) const {
412 if (isSome()) {
413 std::forward<Func>(aFunc)(ref());
415 return *this;
419 * If |isSome()|, runs the provided function and returns the result wrapped
420 * in a Maybe. If |isNothing()|, returns an empty Maybe value with the same
421 * value type as what the provided function would have returned.
423 template <typename Func>
424 auto map(Func&& aFunc) {
425 Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
426 if (isSome()) {
427 val.emplace(std::forward<Func>(aFunc)(ref()));
429 return val;
432 template <typename Func>
433 auto map(Func&& aFunc) const {
434 Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
435 if (isSome()) {
436 val.emplace(std::forward<Func>(aFunc)(ref()));
438 return val;
441 /* If |isSome()|, empties this Maybe and destroys its contents. */
442 void reset() {
443 if (isSome()) {
444 ref().T::~T();
445 mIsSome = false;
446 poisonData();
451 * Constructs a T value in-place in this empty Maybe<T>'s storage. The
452 * arguments to |emplace()| are the parameters to T's constructor.
454 template <typename... Args>
455 void emplace(Args&&... aArgs);
457 friend std::ostream& operator<<(std::ostream& aStream,
458 const Maybe<T>& aMaybe) {
459 if (aMaybe) {
460 aStream << aMaybe.ref();
461 } else {
462 aStream << "<Nothing>";
464 return aStream;
468 template <typename T>
469 T Maybe<T>::value() const {
470 MOZ_DIAGNOSTIC_ASSERT(mIsSome);
471 return ref();
474 template <typename T>
475 T* Maybe<T>::ptr() {
476 MOZ_DIAGNOSTIC_ASSERT(mIsSome);
477 return &ref();
480 template <typename T>
481 const T* Maybe<T>::ptr() const {
482 MOZ_DIAGNOSTIC_ASSERT(mIsSome);
483 return &ref();
486 template <typename T>
487 T* Maybe<T>::operator->() {
488 MOZ_DIAGNOSTIC_ASSERT(mIsSome);
489 return ptr();
492 template <typename T>
493 const T* Maybe<T>::operator->() const {
494 MOZ_DIAGNOSTIC_ASSERT(mIsSome);
495 return ptr();
498 template <typename T>
499 T& Maybe<T>::ref() {
500 MOZ_DIAGNOSTIC_ASSERT(mIsSome);
501 return *static_cast<T*>(data());
504 template <typename T>
505 const T& Maybe<T>::ref() const {
506 MOZ_DIAGNOSTIC_ASSERT(mIsSome);
507 return *static_cast<const T*>(data());
510 template <typename T>
511 T& Maybe<T>::operator*() {
512 MOZ_DIAGNOSTIC_ASSERT(mIsSome);
513 return ref();
516 template <typename T>
517 const T& Maybe<T>::operator*() const {
518 MOZ_DIAGNOSTIC_ASSERT(mIsSome);
519 return ref();
522 template <typename T>
523 template <typename... Args>
524 void Maybe<T>::emplace(Args&&... aArgs) {
525 MOZ_DIAGNOSTIC_ASSERT(!mIsSome);
526 ::new (KnownNotNull, data()) T(std::forward<Args>(aArgs)...);
527 mIsSome = true;
531 * Some() creates a Maybe<T> value containing the provided T value. If T has a
532 * move constructor, it's used to make this as efficient as possible.
534 * Some() selects the type of Maybe it returns by removing any const, volatile,
535 * or reference qualifiers from the type of the value you pass to it. This gives
536 * it more intuitive behavior when used in expressions, but it also means that
537 * if you need to construct a Maybe value that holds a const, volatile, or
538 * reference value, you need to use emplace() instead.
540 template <typename T, typename U = typename std::remove_cv<
541 typename std::remove_reference<T>::type>::type>
542 Maybe<U> Some(T&& aValue) {
543 Maybe<U> value;
544 value.emplace(std::forward<T>(aValue));
545 return value;
548 template <typename T>
549 Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type> ToMaybe(
550 T* aPtr) {
551 if (aPtr) {
552 return Some(*aPtr);
554 return Nothing();
558 * Two Maybe<T> values are equal if
559 * - both are Nothing, or
560 * - both are Some, and the values they contain are equal.
562 template <typename T>
563 bool operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
564 if (aLHS.isNothing() != aRHS.isNothing()) {
565 return false;
567 return aLHS.isNothing() || *aLHS == *aRHS;
570 template <typename T>
571 bool operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
572 return !(aLHS == aRHS);
576 * We support comparison to Nothing to allow reasonable expressions like:
577 * if (maybeValue == Nothing()) { ... }
579 template <typename T>
580 bool operator==(const Maybe<T>& aLHS, const Nothing& aRHS) {
581 return aLHS.isNothing();
584 template <typename T>
585 bool operator!=(const Maybe<T>& aLHS, const Nothing& aRHS) {
586 return !(aLHS == aRHS);
589 template <typename T>
590 bool operator==(const Nothing& aLHS, const Maybe<T>& aRHS) {
591 return aRHS.isNothing();
594 template <typename T>
595 bool operator!=(const Nothing& aLHS, const Maybe<T>& aRHS) {
596 return !(aLHS == aRHS);
600 * Maybe<T> values are ordered in the same way T values are ordered, except that
601 * Nothing comes before anything else.
603 template <typename T>
604 bool operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
605 if (aLHS.isNothing()) {
606 return aRHS.isSome();
608 if (aRHS.isNothing()) {
609 return false;
611 return *aLHS < *aRHS;
614 template <typename T>
615 bool operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
616 return !(aLHS < aRHS || aLHS == aRHS);
619 template <typename T>
620 bool operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
621 return aLHS < aRHS || aLHS == aRHS;
624 template <typename T>
625 bool operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
626 return !(aLHS < aRHS);
629 template <typename T>
630 inline void ImplCycleCollectionTraverse(
631 nsCycleCollectionTraversalCallback& aCallback, mozilla::Maybe<T>& aField,
632 const char* aName, uint32_t aFlags = 0) {
633 if (aField) {
634 ImplCycleCollectionTraverse(aCallback, aField.ref(), aName, aFlags);
638 template <typename T>
639 inline void ImplCycleCollectionUnlink(mozilla::Maybe<T>& aField) {
640 if (aField) {
641 ImplCycleCollectionUnlink(aField.ref());
645 } // namespace mozilla
647 #endif /* mozilla_Maybe_h */