Bug 1660051 [wpt PR 25111] - Origin isolation: expand getter test coverage, a=testonly
[gecko.git] / mfbt / Variant.h
blob9f4d3d970efa598638799af9f415dc10b74a0367
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 template class for tagged unions. */
9 #include <new>
10 #include <stdint.h>
12 #include "mozilla/Assertions.h"
13 #include "mozilla/FunctionTypeTraits.h"
14 #include "mozilla/HashFunctions.h"
15 #include "mozilla/OperatorNewExtensions.h"
16 #include "mozilla/TemplateLib.h"
17 #include <type_traits>
18 #include <utility>
20 #ifndef mozilla_Variant_h
21 # define mozilla_Variant_h
23 namespace IPC {
24 template <typename T>
25 struct ParamTraits;
26 } // namespace IPC
28 namespace mozilla {
30 namespace ipc {
31 template <typename T>
32 struct IPDLParamTraits;
33 } // namespace ipc
35 template <typename... Ts>
36 class Variant;
38 namespace detail {
40 // Nth<N, types...>::Type is the Nth type (0-based) in the list of types Ts.
41 template <size_t N, typename... Ts>
42 struct Nth;
44 template <typename T, typename... Ts>
45 struct Nth<0, T, Ts...> {
46 using Type = T;
49 template <size_t N, typename T, typename... Ts>
50 struct Nth<N, T, Ts...> {
51 using Type = typename Nth<N - 1, Ts...>::Type;
54 /// SelectVariantTypeHelper is used in the implementation of SelectVariantType.
55 template <typename T, typename... Variants>
56 struct SelectVariantTypeHelper;
58 template <typename T>
59 struct SelectVariantTypeHelper<T> {
60 static constexpr size_t count = 0;
63 template <typename T, typename... Variants>
64 struct SelectVariantTypeHelper<T, T, Variants...> {
65 typedef T Type;
66 static constexpr size_t count =
67 1 + SelectVariantTypeHelper<T, Variants...>::count;
70 template <typename T, typename... Variants>
71 struct SelectVariantTypeHelper<T, const T, Variants...> {
72 typedef const T Type;
73 static constexpr size_t count =
74 1 + SelectVariantTypeHelper<T, Variants...>::count;
77 template <typename T, typename... Variants>
78 struct SelectVariantTypeHelper<T, const T&, Variants...> {
79 typedef const T& Type;
80 static constexpr size_t count =
81 1 + SelectVariantTypeHelper<T, Variants...>::count;
84 template <typename T, typename... Variants>
85 struct SelectVariantTypeHelper<T, T&&, Variants...> {
86 typedef T&& Type;
87 static constexpr size_t count =
88 1 + SelectVariantTypeHelper<T, Variants...>::count;
91 template <typename T, typename Head, typename... Variants>
92 struct SelectVariantTypeHelper<T, Head, Variants...>
93 : public SelectVariantTypeHelper<T, Variants...> {};
95 /**
96 * SelectVariantType takes a type T and a list of variant types Variants and
97 * yields a type Type, selected from Variants, that can store a value of type T
98 * or a reference to type T. If no such type was found, Type is not defined.
99 * SelectVariantType also has a `count` member that contains the total number of
100 * selectable types (which will be used to check that a requested type is not
101 * ambiguously present twice.)
103 template <typename T, typename... Variants>
104 struct SelectVariantType
105 : public SelectVariantTypeHelper<
106 std::remove_const_t<std::remove_reference_t<T>>, Variants...> {};
108 // Compute a fast, compact type that can be used to hold integral values that
109 // distinctly map to every type in Ts.
110 template <typename... Ts>
111 struct VariantTag {
112 private:
113 static const size_t TypeCount = sizeof...(Ts);
115 public:
116 using Type = std::conditional_t < TypeCount < 3, bool,
117 std::conditional_t<TypeCount<(1 << 8), uint_fast8_t,
118 size_t // stop caring past a certain
119 // point :-)
123 // TagHelper gets the given sentinel tag value for the given type T. This has to
124 // be split out from VariantImplementation because you can't nest a partial
125 // template specialization within a template class.
127 template <typename Tag, size_t N, typename T, typename U, typename Next,
128 bool isMatch>
129 struct TagHelper;
131 // In the case where T != U, we continue recursion.
132 template <typename Tag, size_t N, typename T, typename U, typename Next>
133 struct TagHelper<Tag, N, T, U, Next, false> {
134 static Tag tag() { return Next::template tag<U>(); }
137 // In the case where T == U, return the tag number.
138 template <typename Tag, size_t N, typename T, typename U, typename Next>
139 struct TagHelper<Tag, N, T, U, Next, true> {
140 static Tag tag() { return Tag(N); }
143 // The VariantImplementation template provides the guts of mozilla::Variant. We
144 // create a VariantImplementation for each T in Ts... which handles
145 // construction, destruction, etc for when the Variant's type is T. If the
146 // Variant's type isn't T, it punts the request on to the next
147 // VariantImplementation.
149 template <typename Tag, size_t N, typename... Ts>
150 struct VariantImplementation;
152 // The singly typed Variant / recursion base case.
153 template <typename Tag, size_t N, typename T>
154 struct VariantImplementation<Tag, N, T> {
155 template <typename U>
156 static Tag tag() {
157 static_assert(std::is_same_v<T, U>, "mozilla::Variant: tag: bad type!");
158 return Tag(N);
161 template <typename Variant>
162 static void copyConstruct(void* aLhs, const Variant& aRhs) {
163 ::new (KnownNotNull, aLhs) T(aRhs.template as<N>());
166 template <typename Variant>
167 static void moveConstruct(void* aLhs, Variant&& aRhs) {
168 ::new (KnownNotNull, aLhs) T(aRhs.template extract<N>());
171 template <typename Variant>
172 static void destroy(Variant& aV) {
173 aV.template as<N>().~T();
176 template <typename Variant>
177 static bool equal(const Variant& aLhs, const Variant& aRhs) {
178 return aLhs.template as<N>() == aRhs.template as<N>();
181 template <typename Matcher, typename ConcreteVariant>
182 static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant& aV) {
183 if constexpr (std::is_invocable_v<Matcher, Tag,
184 decltype(aV.template as<N>())>) {
185 return std::forward<Matcher>(aMatcher)(Tag(N), aV.template as<N>());
186 } else {
187 return std::forward<Matcher>(aMatcher)(aV.template as<N>());
191 template <typename ConcreteVariant, typename Matcher>
192 static decltype(auto) matchN(ConcreteVariant& aV, Matcher&& aMatcher) {
193 if constexpr (std::is_invocable_v<Matcher, Tag,
194 decltype(aV.template as<N>())>) {
195 return std::forward<Matcher>(aMatcher)(Tag(N), aV.template as<N>());
196 } else {
197 return std::forward<Matcher>(aMatcher)(aV.template as<N>());
202 // VariantImplementation for some variant type T.
203 template <typename Tag, size_t N, typename T, typename... Ts>
204 struct VariantImplementation<Tag, N, T, Ts...> {
205 // The next recursive VariantImplementation.
206 using Next = VariantImplementation<Tag, N + 1, Ts...>;
208 template <typename U>
209 static Tag tag() {
210 return TagHelper<Tag, N, T, U, Next, std::is_same_v<T, U>>::tag();
213 template <typename Variant>
214 static void copyConstruct(void* aLhs, const Variant& aRhs) {
215 if (aRhs.template is<N>()) {
216 ::new (KnownNotNull, aLhs) T(aRhs.template as<N>());
217 } else {
218 Next::copyConstruct(aLhs, aRhs);
222 template <typename Variant>
223 static void moveConstruct(void* aLhs, Variant&& aRhs) {
224 if (aRhs.template is<N>()) {
225 ::new (KnownNotNull, aLhs) T(aRhs.template extract<N>());
226 } else {
227 Next::moveConstruct(aLhs, std::move(aRhs));
231 template <typename Variant>
232 static void destroy(Variant& aV) {
233 if (aV.template is<N>()) {
234 aV.template as<N>().~T();
235 } else {
236 Next::destroy(aV);
240 template <typename Variant>
241 static bool equal(const Variant& aLhs, const Variant& aRhs) {
242 if (aLhs.template is<N>()) {
243 MOZ_ASSERT(aRhs.template is<N>());
244 return aLhs.template as<N>() == aRhs.template as<N>();
245 } else {
246 return Next::equal(aLhs, aRhs);
250 template <typename Matcher, typename ConcreteVariant>
251 static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant& aV) {
252 if (aV.template is<N>()) {
253 if constexpr (std::is_invocable_v<Matcher, Tag,
254 decltype(aV.template as<N>())>) {
255 return std::forward<Matcher>(aMatcher)(Tag(N), aV.template as<N>());
256 } else {
257 return std::forward<Matcher>(aMatcher)(aV.template as<N>());
259 } else {
260 // If you're seeing compilation errors here like "no matching
261 // function for call to 'match'" then that means that the
262 // Matcher doesn't exhaust all variant types. There must exist a
263 // Matcher::operator()(T&) for every variant type T.
265 // If you're seeing compilation errors here like "cannot initialize
266 // return object of type <...> with an rvalue of type <...>" then that
267 // means that the Matcher::operator()(T&) overloads are returning
268 // different types. They must all return the same type.
269 return Next::match(std::forward<Matcher>(aMatcher), aV);
273 template <typename ConcreteVariant, typename Mi, typename... Ms>
274 static decltype(auto) matchN(ConcreteVariant& aV, Mi&& aMi, Ms&&... aMs) {
275 if (aV.template is<N>()) {
276 if constexpr (std::is_invocable_v<Mi, Tag,
277 decltype(aV.template as<N>())>) {
278 return std::forward<Mi>(aMi)(Tag(N), aV.template as<N>());
279 } else {
280 return std::forward<Mi>(aMi)(aV.template as<N>());
282 } else {
283 // If you're seeing compilation errors here like "no matching
284 // function for call to 'match'" then that means that the
285 // Matchers don't exhaust all variant types. There must exist a
286 // Matcher (with its operator()(T&)) for every variant type T, in the
287 // exact same order.
288 return Next::matchN(aV, std::forward<Ms>(aMs)...);
294 * AsVariantTemporary stores a value of type T to allow construction of a
295 * Variant value via type inference. Because T is copied and there's no
296 * guarantee that the copy can be elided, AsVariantTemporary is best used with
297 * primitive or very small types.
299 template <typename T>
300 struct AsVariantTemporary {
301 explicit AsVariantTemporary(const T& aValue) : mValue(aValue) {}
303 template <typename U>
304 explicit AsVariantTemporary(U&& aValue) : mValue(std::forward<U>(aValue)) {}
306 AsVariantTemporary(const AsVariantTemporary& aOther)
307 : mValue(aOther.mValue) {}
309 AsVariantTemporary(AsVariantTemporary&& aOther)
310 : mValue(std::move(aOther.mValue)) {}
312 AsVariantTemporary() = delete;
313 void operator=(const AsVariantTemporary&) = delete;
314 void operator=(AsVariantTemporary&&) = delete;
316 std::remove_const_t<std::remove_reference_t<T>> mValue;
319 } // namespace detail
321 // Used to unambiguously specify one of the Variant's type.
322 template <typename T>
323 struct VariantType {
324 using Type = T;
327 // Used to specify one of the Variant's type by index.
328 template <size_t N>
329 struct VariantIndex {
330 static constexpr size_t index = N;
334 * # mozilla::Variant
336 * A variant / tagged union / heterogenous disjoint union / sum-type template
337 * class. Similar in concept to (but not derived from) `boost::variant`.
339 * Sometimes, you may wish to use a C union with non-POD types. However, this is
340 * forbidden in C++ because it is not clear which type in the union should have
341 * its constructor and destructor run on creation and deletion
342 * respectively. This is the problem that `mozilla::Variant` solves.
344 * ## Usage
346 * A `mozilla::Variant` instance is constructed (via move or copy) from one of
347 * its variant types (ignoring const and references). It does *not* support
348 * construction from subclasses of variant types or types that coerce to one of
349 * the variant types.
351 * Variant<char, uint32_t> v1('a');
352 * Variant<UniquePtr<A>, B, C> v2(MakeUnique<A>());
353 * Variant<bool, char> v3(VariantType<char>, 0); // disambiguation needed
354 * Variant<int, int> v4(VariantIndex<1>, 0); // 2nd int
356 * Because specifying the full type of a Variant value is often verbose,
357 * there are two easier ways to construct values:
359 * A. AsVariant() can be used to construct a Variant value using type inference
360 * in contexts such as expressions or when returning values from functions.
361 * Because AsVariant() must copy or move the value into a temporary and this
362 * cannot necessarily be elided by the compiler, it's mostly appropriate only
363 * for use with primitive or very small types.
365 * Variant<char, uint32_t> Foo() { return AsVariant('x'); }
366 * // ...
367 * Variant<char, uint32_t> v1 = Foo(); // v1 holds char('x').
369 * B. Brace-construction with VariantType or VariantIndex; this also allows
370 * in-place construction with any number of arguments.
372 * struct AB { AB(int, int){...} };
373 * static Variant<AB, bool> foo()
375 * return {VariantIndex<0>{}, 1, 2};
377 * // ...
378 * Variant<AB, bool> v0 = Foo(); // v0 holds AB(1,2).
380 * All access to the contained value goes through type-safe accessors.
381 * Either the stored type, or the type index may be provided.
383 * void
384 * Foo(Variant<A, B, C> v)
386 * if (v.is<A>()) {
387 * A& ref = v.as<A>();
388 * ...
389 * } else (v.is<1>()) { // Instead of v.is<B>.
390 * ...
391 * } else {
392 * ...
396 * In some situation, a Variant may be constructed from templated types, in
397 * which case it is possible that the same type could be given multiple times by
398 * an external developer. Or seemingly-different types could be aliases.
399 * In this case, repeated types can only be accessed through their index, to
400 * prevent ambiguous access by type.
402 * // Bad!
403 * template <typename T>
404 * struct ResultOrError
406 * Variant<T, int> m;
407 * ResultOrError() : m(int(0)) {} // Error '0' by default
408 * ResultOrError(const T& r) : m(r) {}
409 * bool IsResult() const { return m.is<T>(); }
410 * bool IsError() const { return m.is<int>(); }
411 * };
412 * // Now instantiante with the result being an int too:
413 * ResultOrError<int> myResult(123); // Fail!
414 * // In Variant<int, int>, which 'int' are we refering to, from inside
415 * // ResultOrError functions?
417 * // Good!
418 * template <typename T>
419 * struct ResultOrError
421 * Variant<T, int> m;
422 * ResultOrError() : m(VariantIndex<1>{}, 0) {} // Error '0' by default
423 * ResultOrError(const T& r) : m(VariantIndex<0>{}, r) {}
424 * bool IsResult() const { return m.is<0>(); } // 0 -> T
425 * bool IsError() const { return m.is<1>(); } // 1 -> int
426 * };
427 * // Now instantiante with the result being an int too:
428 * ResultOrError<int> myResult(123); // It now works!
430 * Attempting to use the contained value as type `T1` when the `Variant`
431 * instance contains a value of type `T2` causes an assertion failure.
433 * A a;
434 * Variant<A, B, C> v(a);
435 * v.as<B>(); // <--- Assertion failure!
437 * Trying to use a `Variant<Ts...>` instance as some type `U` that is not a
438 * member of the set of `Ts...` is a compiler error.
440 * A a;
441 * Variant<A, B, C> v(a);
442 * v.as<SomeRandomType>(); // <--- Compiler error!
444 * Additionally, you can turn a `Variant` that `is<T>` into a `T` by moving it
445 * out of the containing `Variant` instance with the `extract<T>` method:
447 * Variant<UniquePtr<A>, B, C> v(MakeUnique<A>());
448 * auto ptr = v.extract<UniquePtr<A>>();
450 * Finally, you can exhaustively match on the contained variant and branch into
451 * different code paths depending on which type is contained. This is preferred
452 * to manually checking every variant type T with is<T>() because it provides
453 * compile-time checking that you handled every type, rather than runtime
454 * assertion failures.
456 * // Bad!
457 * char* foo(Variant<A, B, C, D>& v) {
458 * if (v.is<A>()) {
459 * return ...;
460 * } else if (v.is<B>()) {
461 * return ...;
462 * } else {
463 * return doSomething(v.as<C>()); // Forgot about case D!
467 * // Instead, a single function object (that can deal with all possible
468 * // options) may be provided:
469 * struct FooMatcher
471 * // The return type of all matchers must be identical.
472 * char* operator()(A& a) { ... }
473 * char* operator()(B& b) { ... }
474 * char* operator()(C& c) { ... }
475 * char* operator()(D& d) { ... } // Compile-time error to forget D!
477 * char* foo(Variant<A, B, C, D>& v) {
478 * return v.match(FooMatcher());
481 * // In some situations, a single generic lambda may also be appropriate:
482 * char* foo(Variant<A, B, C, D>& v) {
483 * return v.match([](auto&) {...});
486 * // Alternatively, multiple function objects may be provided, each one
487 * // corresponding to an option, in the same order:
488 * char* foo(Variant<A, B, C, D>& v) {
489 * return v.match([](A&) { ... },
490 * [](B&) { ... },
491 * [](C&) { ... },
492 * [](D&) { ... });
495 * // In rare cases, the index of the currently-active alternative is
496 * // needed, it may be obtained by adding a first parameter in the matcner
497 * // callback, which will receive the index in its most compact type (just
498 * // use `size_t` if the exact type is not important), e.g.:
499 * char* foo(Variant<A, B, C, D>& v) {
500 * return v.match([](auto aIndex, auto& aAlternative) {...});
501 * // --OR--
502 * return v.match([](size_t aIndex, auto& aAlternative) {...});
505 * ## Examples
507 * A tree is either an empty leaf, or a node with a value and two children:
509 * struct Leaf { };
511 * template<typename T>
512 * struct Node
514 * T value;
515 * Tree<T>* left;
516 * Tree<T>* right;
517 * };
519 * template<typename T>
520 * using Tree = Variant<Leaf, Node<T>>;
522 * A copy-on-write string is either a non-owning reference to some existing
523 * string, or an owning reference to our copy:
525 * class CopyOnWriteString
527 * Variant<const char*, UniquePtr<char[]>> string;
529 * ...
530 * };
532 * Because Variant must be aligned suitable to hold any value stored within it,
533 * and because |alignas| requirements don't affect platform ABI with respect to
534 * how parameters are laid out in memory, Variant can't be used as the type of a
535 * function parameter. Pass Variant to functions by pointer or reference
536 * instead.
538 template <typename... Ts>
539 class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant {
540 friend struct IPC::ParamTraits<mozilla::Variant<Ts...>>;
541 friend struct mozilla::ipc::IPDLParamTraits<mozilla::Variant<Ts...>>;
543 using Tag = typename detail::VariantTag<Ts...>::Type;
544 using Impl = detail::VariantImplementation<Tag, 0, Ts...>;
546 static constexpr size_t RawDataAlignment = tl::Max<alignof(Ts)...>::value;
547 static constexpr size_t RawDataSize = tl::Max<sizeof(Ts)...>::value;
549 // Raw storage for the contained variant value.
550 alignas(RawDataAlignment) unsigned char rawData[RawDataSize];
552 // Each type is given a unique tag value that lets us keep track of the
553 // contained variant value's type.
554 Tag tag;
556 // Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a
557 // -Werror compile error) to reinterpret_cast<> |rawData| to |T*|, even
558 // through |void*|. Placing the latter cast in these separate functions
559 // breaks the chain such that affected GCC versions no longer warn/error.
560 void* ptr() { return rawData; }
562 const void* ptr() const { return rawData; }
564 public:
565 /** Perfect forwarding construction for some variant type T. */
566 template <typename RefT,
567 // RefT captures both const& as well as && (as intended, to support
568 // perfect forwarding), so we have to remove those qualifiers here
569 // when ensuring that T is a variant of this type, and getting T's
570 // tag, etc.
571 typename T = typename detail::SelectVariantType<RefT, Ts...>::Type>
572 explicit Variant(RefT&& aT) : tag(Impl::template tag<T>()) {
573 static_assert(
574 detail::SelectVariantType<RefT, Ts...>::count == 1,
575 "Variant can only be selected by type if that type is unique");
576 ::new (KnownNotNull, ptr()) T(std::forward<RefT>(aT));
580 * Perfect forwarding construction for some variant type T, by
581 * explicitly giving the type.
582 * This is necessary to construct from any number of arguments,
583 * or to convert from a type that is not in the Variant's type list.
585 template <typename T, typename... Args>
586 MOZ_IMPLICIT Variant(const VariantType<T>&, Args&&... aTs)
587 : tag(Impl::template tag<T>()) {
588 ::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
592 * Perfect forwarding construction for some variant type T, by
593 * explicitly giving the type index.
594 * This is necessary to construct from any number of arguments,
595 * or to convert from a type that is not in the Variant's type list,
596 * or to construct a type that is present more than once in the Variant.
598 template <size_t N, typename... Args>
599 MOZ_IMPLICIT Variant(const VariantIndex<N>&, Args&&... aTs) : tag(N) {
600 using T = typename detail::Nth<N, Ts...>::Type;
601 ::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
605 * Constructs this Variant from an AsVariantTemporary<T> such that T can be
606 * stored in one of the types allowable in this Variant. This is used in the
607 * implementation of AsVariant().
609 template <typename RefT>
610 MOZ_IMPLICIT Variant(detail::AsVariantTemporary<RefT>&& aValue)
611 : tag(Impl::template tag<
612 typename detail::SelectVariantType<RefT, Ts...>::Type>()) {
613 using T = typename detail::SelectVariantType<RefT, Ts...>::Type;
614 static_assert(
615 detail::SelectVariantType<RefT, Ts...>::count == 1,
616 "Variant can only be selected by type if that type is unique");
617 ::new (KnownNotNull, ptr()) T(std::move(aValue.mValue));
620 /** Copy construction. */
621 Variant(const Variant& aRhs) : tag(aRhs.tag) {
622 Impl::copyConstruct(ptr(), aRhs);
625 /** Move construction. */
626 Variant(Variant&& aRhs) : tag(aRhs.tag) {
627 Impl::moveConstruct(ptr(), std::move(aRhs));
630 /** Copy assignment. */
631 Variant& operator=(const Variant& aRhs) {
632 MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
633 this->~Variant();
634 ::new (KnownNotNull, this) Variant(aRhs);
635 return *this;
638 /** Move assignment. */
639 Variant& operator=(Variant&& aRhs) {
640 MOZ_ASSERT(&aRhs != this, "self-assign disallowed");
641 this->~Variant();
642 ::new (KnownNotNull, this) Variant(std::move(aRhs));
643 return *this;
646 /** Move assignment from AsVariant(). */
647 template <typename T>
648 Variant& operator=(detail::AsVariantTemporary<T>&& aValue) {
649 static_assert(
650 detail::SelectVariantType<T, Ts...>::count == 1,
651 "Variant can only be selected by type if that type is unique");
652 this->~Variant();
653 ::new (KnownNotNull, this) Variant(std::move(aValue));
654 return *this;
657 ~Variant() { Impl::destroy(*this); }
659 template <typename T, typename... Args>
660 T& emplace(Args&&... aTs) {
661 Impl::destroy(*this);
662 tag = Impl::template tag<T>();
663 ::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
664 return as<T>();
667 template <size_t N, typename... Args>
668 typename detail::Nth<N, Ts...>::Type& emplace(Args&&... aTs) {
669 using T = typename detail::Nth<N, Ts...>::Type;
670 Impl::destroy(*this);
671 tag = N;
672 ::new (KnownNotNull, ptr()) T(std::forward<Args>(aTs)...);
673 return as<N>();
676 /** Check which variant type is currently contained. */
677 template <typename T>
678 bool is() const {
679 static_assert(
680 detail::SelectVariantType<T, Ts...>::count == 1,
681 "provided a type not uniquely found in this Variant's type list");
682 return Impl::template tag<T>() == tag;
685 template <size_t N>
686 bool is() const {
687 static_assert(N < sizeof...(Ts),
688 "provided an index outside of this Variant's type list");
689 return N == size_t(tag);
693 * Operator == overload that defers to the variant type's operator==
694 * implementation if the rhs is tagged as the same type as this one.
696 bool operator==(const Variant& aRhs) const {
697 return tag == aRhs.tag && Impl::equal(*this, aRhs);
701 * Operator != overload that defers to the negation of the variant type's
702 * operator== implementation if the rhs is tagged as the same type as this
703 * one.
705 bool operator!=(const Variant& aRhs) const { return !(*this == aRhs); }
707 // Accessors for working with the contained variant value.
709 /** Mutable reference. */
710 template <typename T>
711 T& as() {
712 static_assert(
713 detail::SelectVariantType<T, Ts...>::count == 1,
714 "provided a type not uniquely found in this Variant's type list");
715 MOZ_RELEASE_ASSERT(is<T>());
716 return *static_cast<T*>(ptr());
719 template <size_t N>
720 typename detail::Nth<N, Ts...>::Type& as() {
721 static_assert(N < sizeof...(Ts),
722 "provided an index outside of this Variant's type list");
723 MOZ_RELEASE_ASSERT(is<N>());
724 return *static_cast<typename detail::Nth<N, Ts...>::Type*>(ptr());
727 /** Immutable const reference. */
728 template <typename T>
729 const T& as() const {
730 static_assert(detail::SelectVariantType<T, Ts...>::count == 1,
731 "provided a type not found in this Variant's type list");
732 MOZ_RELEASE_ASSERT(is<T>());
733 return *static_cast<const T*>(ptr());
736 template <size_t N>
737 const typename detail::Nth<N, Ts...>::Type& as() const {
738 static_assert(N < sizeof...(Ts),
739 "provided an index outside of this Variant's type list");
740 MOZ_RELEASE_ASSERT(is<N>());
741 return *static_cast<const typename detail::Nth<N, Ts...>::Type*>(ptr());
745 * Extract the contained variant value from this container into a temporary
746 * value. On completion, the value in the variant will be in a
747 * safely-destructible state, as determined by the behavior of T's move
748 * constructor when provided the variant's internal value.
750 template <typename T>
751 T extract() {
752 static_assert(
753 detail::SelectVariantType<T, Ts...>::count == 1,
754 "provided a type not uniquely found in this Variant's type list");
755 MOZ_ASSERT(is<T>());
756 return T(std::move(as<T>()));
759 template <size_t N>
760 typename detail::Nth<N, Ts...>::Type extract() {
761 static_assert(N < sizeof...(Ts),
762 "provided an index outside of this Variant's type list");
763 MOZ_RELEASE_ASSERT(is<N>());
764 return typename detail::Nth<N, Ts...>::Type(std::move(as<N>()));
767 // Exhaustive matching of all variant types on the contained value.
769 /** Match on an immutable const reference. */
770 template <typename Matcher>
771 decltype(auto) match(Matcher&& aMatcher) const {
772 return Impl::match(std::forward<Matcher>(aMatcher), *this);
775 template <typename M0, typename M1, typename... Ms>
776 decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) const {
777 static_assert(
778 2 + sizeof...(Ms) == sizeof...(Ts),
779 "Variant<T...>::match() takes either one callable argument that "
780 "accepts every type T; or one for each type T, in order");
781 static_assert(
782 tl::And<std::is_same_v<typename FunctionTypeTraits<M0>::ReturnType,
783 typename FunctionTypeTraits<M1>::ReturnType>,
784 std::is_same_v<
785 typename FunctionTypeTraits<M1>::ReturnType,
786 typename FunctionTypeTraits<Ms>::ReturnType>...>::value,
787 "all matchers must have the same return type");
788 return Impl::matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
789 std::forward<Ms>(aMs)...);
792 /** Match on a mutable non-const reference. */
793 template <typename Matcher>
794 decltype(auto) match(Matcher&& aMatcher) {
795 return Impl::match(std::forward<Matcher>(aMatcher), *this);
798 template <typename M0, typename M1, typename... Ms>
799 decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) {
800 static_assert(
801 2 + sizeof...(Ms) == sizeof...(Ts),
802 "Variant<T...>::match() takes either one callable argument that "
803 "accepts every type T; or one for each type T, in order");
804 static_assert(
805 tl::And<std::is_same_v<typename FunctionTypeTraits<M0>::ReturnType,
806 typename FunctionTypeTraits<M1>::ReturnType>,
807 std::is_same_v<
808 typename FunctionTypeTraits<M0>::ReturnType,
809 typename FunctionTypeTraits<Ms>::ReturnType>...>::value,
810 "all matchers must have the same return type");
811 return Impl::matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
812 std::forward<Ms>(aMs)...);
816 * Incorporate the current variant's tag into hashValue.
817 * Note that this does not hash the actual contents; you must take
818 * care of that yourself, perhaps by using a match.
820 mozilla::HashNumber addTagToHash(mozilla::HashNumber hashValue) const {
821 return mozilla::AddToHash(hashValue, tag);
826 * AsVariant() is used to construct a Variant<T,...> value containing the
827 * provided T value using type inference. It can be used to construct Variant
828 * values in expressions or return them from functions without specifying the
829 * entire Variant type.
831 * Because AsVariant() must copy or move the value into a temporary and this
832 * cannot necessarily be elided by the compiler, it's mostly appropriate only
833 * for use with primitive or very small types.
835 * AsVariant() returns a AsVariantTemporary value which is implicitly
836 * convertible to any Variant that can hold a value of type T.
838 template <typename T>
839 detail::AsVariantTemporary<T> AsVariant(T&& aValue) {
840 return detail::AsVariantTemporary<T>(std::forward<T>(aValue));
843 } // namespace mozilla
845 #endif /* mozilla_Variant_h */