From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- mfbt/Variant.h | 928 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 928 insertions(+) create mode 100644 mfbt/Variant.h (limited to 'mfbt/Variant.h') diff --git a/mfbt/Variant.h b/mfbt/Variant.h new file mode 100644 index 0000000000..d1db3a2cc9 --- /dev/null +++ b/mfbt/Variant.h @@ -0,0 +1,928 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A template class for tagged unions. */ + +#include +#include + +#include "mozilla/Assertions.h" +#include "mozilla/HashFunctions.h" +#include "mozilla/OperatorNewExtensions.h" +#include "mozilla/TemplateLib.h" +#include +#include + +#ifndef mozilla_Variant_h +# define mozilla_Variant_h + +namespace IPC { +template +struct ParamTraits; +} // namespace IPC + +namespace mozilla { + +namespace ipc { +template +struct IPDLParamTraits; +} // namespace ipc + +template +class Variant; + +namespace detail { + +// Nth::Type is the Nth type (0-based) in the list of types Ts. +template +struct Nth; + +template +struct Nth<0, T, Ts...> { + using Type = T; +}; + +template +struct Nth { + using Type = typename Nth::Type; +}; + +/// SelectVariantTypeHelper is used in the implementation of SelectVariantType. +template +struct SelectVariantTypeHelper; + +template +struct SelectVariantTypeHelper { + static constexpr size_t count = 0; +}; + +template +struct SelectVariantTypeHelper { + typedef T Type; + static constexpr size_t count = + 1 + SelectVariantTypeHelper::count; +}; + +template +struct SelectVariantTypeHelper { + typedef const T Type; + static constexpr size_t count = + 1 + SelectVariantTypeHelper::count; +}; + +template +struct SelectVariantTypeHelper { + typedef const T& Type; + static constexpr size_t count = + 1 + SelectVariantTypeHelper::count; +}; + +template +struct SelectVariantTypeHelper { + typedef T&& Type; + static constexpr size_t count = + 1 + SelectVariantTypeHelper::count; +}; + +template +struct SelectVariantTypeHelper + : public SelectVariantTypeHelper {}; + +/** + * SelectVariantType takes a type T and a list of variant types Variants and + * yields a type Type, selected from Variants, that can store a value of type T + * or a reference to type T. If no such type was found, Type is not defined. + * SelectVariantType also has a `count` member that contains the total number of + * selectable types (which will be used to check that a requested type is not + * ambiguously present twice.) + */ +template +struct SelectVariantType + : public SelectVariantTypeHelper< + std::remove_const_t>, Variants...> {}; + +// Compute a fast, compact type that can be used to hold integral values that +// distinctly map to every type in Ts. +template +struct VariantTag { + private: + static const size_t TypeCount = sizeof...(Ts); + + public: + using Type = std::conditional_t< + (TypeCount <= 2), bool, + std::conditional_t<(TypeCount <= size_t(UINT_FAST8_MAX)), uint_fast8_t, + size_t // stop caring past a certain + // point :-) + >>; +}; + +// TagHelper gets the given sentinel tag value for the given type T. This has to +// be split out from VariantImplementation because you can't nest a partial +// template specialization within a template class. + +template +struct TagHelper; + +// In the case where T != U, we continue recursion. +template +struct TagHelper { + static Tag tag() { return Next::template tag(); } +}; + +// In the case where T == U, return the tag number. +template +struct TagHelper { + static Tag tag() { return Tag(N); } +}; + +// The VariantImplementation template provides the guts of mozilla::Variant. We +// create a VariantImplementation for each T in Ts... which handles +// construction, destruction, etc for when the Variant's type is T. If the +// Variant's type isn't T, it punts the request on to the next +// VariantImplementation. + +template +struct VariantImplementation; + +// The singly typed Variant / recursion base case. +template +struct VariantImplementation { + template + static Tag tag() { + static_assert(std::is_same_v, "mozilla::Variant: tag: bad type!"); + return Tag(N); + } + + template + static void copyConstruct(void* aLhs, const Variant& aRhs) { + ::new (KnownNotNull, aLhs) T(aRhs.template as()); + } + + template + static void moveConstruct(void* aLhs, Variant&& aRhs) { + ::new (KnownNotNull, aLhs) T(aRhs.template extract()); + } + + template + static void destroy(Variant& aV) { + aV.template as().~T(); + } + + template + static bool equal(const Variant& aLhs, const Variant& aRhs) { + return aLhs.template as() == aRhs.template as(); + } + + template + static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant&& aV) { + if constexpr (std::is_invocable_v(aV) + .template as())>) { + return std::forward(aMatcher)( + Tag(N), std::forward(aV).template as()); + } else { + return std::forward(aMatcher)( + std::forward(aV).template as()); + } + } + + template + static decltype(auto) matchN(ConcreteVariant&& aV, Matcher&& aMatcher) { + if constexpr (std::is_invocable_v(aV) + .template as())>) { + return std::forward(aMatcher)( + Tag(N), std::forward(aV).template as()); + } else { + return std::forward(aMatcher)( + std::forward(aV).template as()); + } + } +}; + +// VariantImplementation for some variant type T. +template +struct VariantImplementation { + // The next recursive VariantImplementation. + using Next = VariantImplementation; + + template + static Tag tag() { + return TagHelper>::tag(); + } + + template + static void copyConstruct(void* aLhs, const Variant& aRhs) { + if (aRhs.template is()) { + ::new (KnownNotNull, aLhs) T(aRhs.template as()); + } else { + Next::copyConstruct(aLhs, aRhs); + } + } + + template + static void moveConstruct(void* aLhs, Variant&& aRhs) { + if (aRhs.template is()) { + ::new (KnownNotNull, aLhs) T(aRhs.template extract()); + } else { + Next::moveConstruct(aLhs, std::move(aRhs)); + } + } + + template + static void destroy(Variant& aV) { + if (aV.template is()) { + aV.template as().~T(); + } else { + Next::destroy(aV); + } + } + + template + static bool equal(const Variant& aLhs, const Variant& aRhs) { + if (aLhs.template is()) { + MOZ_ASSERT(aRhs.template is()); + return aLhs.template as() == aRhs.template as(); + } else { + return Next::equal(aLhs, aRhs); + } + } + + template + static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant&& aV) { + if (aV.template is()) { + if constexpr (std::is_invocable_v( + aV) + .template as())>) { + return std::forward(aMatcher)( + Tag(N), std::forward(aV).template as()); + } else { + return std::forward(aMatcher)( + std::forward(aV).template as()); + } + } else { + // If you're seeing compilation errors here like "no matching + // function for call to 'match'" then that means that the + // Matcher doesn't exhaust all variant types. There must exist a + // Matcher::operator()(T&) for every variant type T. + // + // If you're seeing compilation errors here like "cannot initialize + // return object of type <...> with an rvalue of type <...>" then that + // means that the Matcher::operator()(T&) overloads are returning + // different types. They must all return the same type. + return Next::match(std::forward(aMatcher), + std::forward(aV)); + } + } + + template + static decltype(auto) matchN(ConcreteVariant&& aV, Mi&& aMi, Ms&&... aMs) { + if (aV.template is()) { + if constexpr (std::is_invocable_v( + aV) + .template as())>) { + static_assert( + std::is_same_v< + decltype(std::forward(aMi)( + Tag(N), + std::forward(aV).template as())), + decltype(Next::matchN(std::forward(aV), + std::forward(aMs)...))>, + "all matchers must have the same return type"); + return std::forward(aMi)( + Tag(N), std::forward(aV).template as()); + } else { + static_assert( + std::is_same_v< + decltype(std::forward(aMi)( + std::forward(aV).template as())), + decltype(Next::matchN(std::forward(aV), + std::forward(aMs)...))>, + "all matchers must have the same return type"); + return std::forward(aMi)( + std::forward(aV).template as()); + } + } else { + // If you're seeing compilation errors here like "no matching + // function for call to 'match'" then that means that the + // Matchers don't exhaust all variant types. There must exist a + // Matcher (with its operator()(T&)) for every variant type T, in the + // exact same order. + return Next::matchN(std::forward(aV), + std::forward(aMs)...); + } + } +}; + +/** + * AsVariantTemporary stores a value of type T to allow construction of a + * Variant value via type inference. Because T is copied and there's no + * guarantee that the copy can be elided, AsVariantTemporary is best used with + * primitive or very small types. + */ +template +struct AsVariantTemporary { + explicit AsVariantTemporary(const T& aValue) : mValue(aValue) {} + + template + explicit AsVariantTemporary(U&& aValue) : mValue(std::forward(aValue)) {} + + AsVariantTemporary(const AsVariantTemporary& aOther) + : mValue(aOther.mValue) {} + + AsVariantTemporary(AsVariantTemporary&& aOther) + : mValue(std::move(aOther.mValue)) {} + + AsVariantTemporary() = delete; + void operator=(const AsVariantTemporary&) = delete; + void operator=(AsVariantTemporary&&) = delete; + + std::remove_const_t> mValue; +}; + +} // namespace detail + +// Used to unambiguously specify one of the Variant's type. +template +struct VariantType { + using Type = T; +}; + +// Used to specify one of the Variant's type by index. +template +struct VariantIndex { + static constexpr size_t index = N; +}; + +/** + * # mozilla::Variant + * + * A variant / tagged union / heterogenous disjoint union / sum-type template + * class. Similar in concept to (but not derived from) `boost::variant`. + * + * Sometimes, you may wish to use a C union with non-POD types. However, this is + * forbidden in C++ because it is not clear which type in the union should have + * its constructor and destructor run on creation and deletion + * respectively. This is the problem that `mozilla::Variant` solves. + * + * ## Usage + * + * A `mozilla::Variant` instance is constructed (via move or copy) from one of + * its variant types (ignoring const and references). It does *not* support + * construction from subclasses of variant types or types that coerce to one of + * the variant types. + * + * Variant v1('a'); + * Variant, B, C> v2(MakeUnique()); + * Variant v3(VariantType, 0); // disambiguation needed + * Variant v4(VariantIndex<1>, 0); // 2nd int + * + * Because specifying the full type of a Variant value is often verbose, + * there are two easier ways to construct values: + * + * A. AsVariant() can be used to construct a Variant value using type inference + * in contexts such as expressions or when returning values from functions. + * Because AsVariant() must copy or move the value into a temporary and this + * cannot necessarily be elided by the compiler, it's mostly appropriate only + * for use with primitive or very small types. + * + * Variant Foo() { return AsVariant('x'); } + * // ... + * Variant v1 = Foo(); // v1 holds char('x'). + * + * B. Brace-construction with VariantType or VariantIndex; this also allows + * in-place construction with any number of arguments. + * + * struct AB { AB(int, int){...} }; + * static Variant foo() + * { + * return {VariantIndex<0>{}, 1, 2}; + * } + * // ... + * Variant v0 = Foo(); // v0 holds AB(1,2). + * + * All access to the contained value goes through type-safe accessors. + * Either the stored type, or the type index may be provided. + * + * void + * Foo(Variant v) + * { + * if (v.is()) { + * A& ref = v.as(); + * ... + * } else (v.is<1>()) { // Instead of v.is. + * ... + * } else { + * ... + * } + * } + * + * In some situation, a Variant may be constructed from templated types, in + * which case it is possible that the same type could be given multiple times by + * an external developer. Or seemingly-different types could be aliases. + * In this case, repeated types can only be accessed through their index, to + * prevent ambiguous access by type. + * + * // Bad! + * template + * struct ResultOrError + * { + * Variant m; + * ResultOrError() : m(int(0)) {} // Error '0' by default + * ResultOrError(const T& r) : m(r) {} + * bool IsResult() const { return m.is(); } + * bool IsError() const { return m.is(); } + * }; + * // Now instantiante with the result being an int too: + * ResultOrError myResult(123); // Fail! + * // In Variant, which 'int' are we refering to, from inside + * // ResultOrError functions? + * + * // Good! + * template + * struct ResultOrError + * { + * Variant m; + * ResultOrError() : m(VariantIndex<1>{}, 0) {} // Error '0' by default + * ResultOrError(const T& r) : m(VariantIndex<0>{}, r) {} + * bool IsResult() const { return m.is<0>(); } // 0 -> T + * bool IsError() const { return m.is<1>(); } // 1 -> int + * }; + * // Now instantiante with the result being an int too: + * ResultOrError myResult(123); // It now works! + * + * Attempting to use the contained value as type `T1` when the `Variant` + * instance contains a value of type `T2` causes an assertion failure. + * + * A a; + * Variant v(a); + * v.as(); // <--- Assertion failure! + * + * Trying to use a `Variant` instance as some type `U` that is not a + * member of the set of `Ts...` is a compiler error. + * + * A a; + * Variant v(a); + * v.as(); // <--- Compiler error! + * + * Additionally, you can turn a `Variant` that `is` into a `T` by moving it + * out of the containing `Variant` instance with the `extract` method: + * + * Variant, B, C> v(MakeUnique()); + * auto ptr = v.extract>(); + * + * Finally, you can exhaustively match on the contained variant and branch into + * different code paths depending on which type is contained. This is preferred + * to manually checking every variant type T with is() because it provides + * compile-time checking that you handled every type, rather than runtime + * assertion failures. + * + * // Bad! + * char* foo(Variant& v) { + * if (v.is()) { + * return ...; + * } else if (v.is()) { + * return ...; + * } else { + * return doSomething(v.as()); // Forgot about case D! + * } + * } + * + * // Instead, a single function object (that can deal with all possible + * // options) may be provided: + * struct FooMatcher + * { + * // The return type of all matchers must be identical. + * char* operator()(A& a) { ... } + * char* operator()(B& b) { ... } + * char* operator()(C& c) { ... } + * char* operator()(D& d) { ... } // Compile-time error to forget D! + * } + * char* foo(Variant& v) { + * return v.match(FooMatcher()); + * } + * + * // In some situations, a single generic lambda may also be appropriate: + * char* foo(Variant& v) { + * return v.match([](auto&) {...}); + * } + * + * // Alternatively, multiple function objects may be provided, each one + * // corresponding to an option, in the same order: + * char* foo(Variant& v) { + * return v.match([](A&) { ... }, + * [](B&) { ... }, + * [](C&) { ... }, + * [](D&) { ... }); + * } + * + * // In rare cases, the index of the currently-active alternative is + * // needed, it may be obtained by adding a first parameter in the matcner + * // callback, which will receive the index in its most compact type (just + * // use `size_t` if the exact type is not important), e.g.: + * char* foo(Variant& v) { + * return v.match([](auto aIndex, auto& aAlternative) {...}); + * // --OR-- + * return v.match([](size_t aIndex, auto& aAlternative) {...}); + * } + * + * ## Examples + * + * A tree is either an empty leaf, or a node with a value and two children: + * + * struct Leaf { }; + * + * template + * struct Node + * { + * T value; + * Tree* left; + * Tree* right; + * }; + * + * template + * using Tree = Variant>; + * + * A copy-on-write string is either a non-owning reference to some existing + * string, or an owning reference to our copy: + * + * class CopyOnWriteString + * { + * Variant> string; + * + * ... + * }; + * + * Because Variant must be aligned suitable to hold any value stored within it, + * and because |alignas| requirements don't affect platform ABI with respect to + * how parameters are laid out in memory, Variant can't be used as the type of a + * function parameter. Pass Variant to functions by pointer or reference + * instead. + */ +template +class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant { + friend struct IPC::ParamTraits>; + friend struct mozilla::ipc::IPDLParamTraits>; + + using Tag = typename detail::VariantTag::Type; + using Impl = detail::VariantImplementation; + + static constexpr size_t RawDataAlignment = tl::Max::value; + static constexpr size_t RawDataSize = tl::Max::value; + + // Raw storage for the contained variant value. + alignas(RawDataAlignment) unsigned char rawData[RawDataSize]; + + // Each type is given a unique tag value that lets us keep track of the + // contained variant value's type. + Tag tag; + + // Some versions of GCC treat it as a -Wstrict-aliasing violation (ergo a + // -Werror compile error) to reinterpret_cast<> |rawData| to |T*|, even + // through |void*|. Placing the latter cast in these separate functions + // breaks the chain such that affected GCC versions no longer warn/error. + void* ptr() { return rawData; } + + const void* ptr() const { return rawData; } + + public: + /** Perfect forwarding construction for some variant type T. */ + template ::Type> + explicit Variant(RefT&& aT) : tag(Impl::template tag()) { + static_assert( + detail::SelectVariantType::count == 1, + "Variant can only be selected by type if that type is unique"); + ::new (KnownNotNull, ptr()) T(std::forward(aT)); + } + + /** + * Perfect forwarding construction for some variant type T, by + * explicitly giving the type. + * This is necessary to construct from any number of arguments, + * or to convert from a type that is not in the Variant's type list. + */ + template + MOZ_IMPLICIT Variant(const VariantType&, Args&&... aTs) + : tag(Impl::template tag()) { + ::new (KnownNotNull, ptr()) T(std::forward(aTs)...); + } + + /** + * Perfect forwarding construction for some variant type T, by + * explicitly giving the type index. + * This is necessary to construct from any number of arguments, + * or to convert from a type that is not in the Variant's type list, + * or to construct a type that is present more than once in the Variant. + */ + template + MOZ_IMPLICIT Variant(const VariantIndex&, Args&&... aTs) : tag(N) { + using T = typename detail::Nth::Type; + ::new (KnownNotNull, ptr()) T(std::forward(aTs)...); + } + + /** + * Constructs this Variant from an AsVariantTemporary such that T can be + * stored in one of the types allowable in this Variant. This is used in the + * implementation of AsVariant(). + */ + template + MOZ_IMPLICIT Variant(detail::AsVariantTemporary&& aValue) + : tag(Impl::template tag< + typename detail::SelectVariantType::Type>()) { + using T = typename detail::SelectVariantType::Type; + static_assert( + detail::SelectVariantType::count == 1, + "Variant can only be selected by type if that type is unique"); + ::new (KnownNotNull, ptr()) T(std::move(aValue.mValue)); + } + + /** Copy construction. */ + Variant(const Variant& aRhs) : tag(aRhs.tag) { + Impl::copyConstruct(ptr(), aRhs); + } + + /** Move construction. */ + Variant(Variant&& aRhs) : tag(aRhs.tag) { + Impl::moveConstruct(ptr(), std::move(aRhs)); + } + + /** Copy assignment. */ + Variant& operator=(const Variant& aRhs) { + MOZ_ASSERT(&aRhs != this, "self-assign disallowed"); + this->~Variant(); + ::new (KnownNotNull, this) Variant(aRhs); + return *this; + } + + /** Move assignment. */ + Variant& operator=(Variant&& aRhs) { + MOZ_ASSERT(&aRhs != this, "self-assign disallowed"); + this->~Variant(); + ::new (KnownNotNull, this) Variant(std::move(aRhs)); + return *this; + } + + /** Move assignment from AsVariant(). */ + template + Variant& operator=(detail::AsVariantTemporary&& aValue) { + static_assert( + detail::SelectVariantType::count == 1, + "Variant can only be selected by type if that type is unique"); + this->~Variant(); + ::new (KnownNotNull, this) Variant(std::move(aValue)); + return *this; + } + + ~Variant() { Impl::destroy(*this); } + + template + T& emplace(Args&&... aTs) { + Impl::destroy(*this); + tag = Impl::template tag(); + ::new (KnownNotNull, ptr()) T(std::forward(aTs)...); + return as(); + } + + template + typename detail::Nth::Type& emplace(Args&&... aTs) { + using T = typename detail::Nth::Type; + Impl::destroy(*this); + tag = N; + ::new (KnownNotNull, ptr()) T(std::forward(aTs)...); + return as(); + } + + /** Check which variant type is currently contained. */ + template + bool is() const { + static_assert( + detail::SelectVariantType::count == 1, + "provided a type not uniquely found in this Variant's type list"); + return Impl::template tag() == tag; + } + + template + bool is() const { + static_assert(N < sizeof...(Ts), + "provided an index outside of this Variant's type list"); + return N == size_t(tag); + } + + /** + * Operator == overload that defers to the variant type's operator== + * implementation if the rhs is tagged as the same type as this one. + */ + bool operator==(const Variant& aRhs) const { + return tag == aRhs.tag && Impl::equal(*this, aRhs); + } + + /** + * Operator != overload that defers to the negation of the variant type's + * operator== implementation if the rhs is tagged as the same type as this + * one. + */ + bool operator!=(const Variant& aRhs) const { return !(*this == aRhs); } + + // Accessors for working with the contained variant value. + + /** Mutable lvalue-reference. */ + template + T& as() & { + static_assert( + detail::SelectVariantType::count == 1, + "provided a type not uniquely found in this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return *static_cast(ptr()); + } + + template + typename detail::Nth::Type& as() & { + static_assert(N < sizeof...(Ts), + "provided an index outside of this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return *static_cast::Type*>(ptr()); + } + + /** Immutable const lvalue-reference. */ + template + const T& as() const& { + static_assert(detail::SelectVariantType::count == 1, + "provided a type not found in this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return *static_cast(ptr()); + } + + template + const typename detail::Nth::Type& as() const& { + static_assert(N < sizeof...(Ts), + "provided an index outside of this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return *static_cast::Type*>(ptr()); + } + + /** Mutable rvalue-reference. */ + template + T&& as() && { + static_assert( + detail::SelectVariantType::count == 1, + "provided a type not uniquely found in this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return std::move(*static_cast(ptr())); + } + + template + typename detail::Nth::Type&& as() && { + static_assert(N < sizeof...(Ts), + "provided an index outside of this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return std::move( + *static_cast::Type*>(ptr())); + } + + /** Immutable const rvalue-reference. */ + template + const T&& as() const&& { + static_assert(detail::SelectVariantType::count == 1, + "provided a type not found in this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return std::move(*static_cast(ptr())); + } + + template + const typename detail::Nth::Type&& as() const&& { + static_assert(N < sizeof...(Ts), + "provided an index outside of this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return std::move( + *static_cast::Type*>(ptr())); + } + + /** + * Extract the contained variant value from this container into a temporary + * value. On completion, the value in the variant will be in a + * safely-destructible state, as determined by the behavior of T's move + * constructor when provided the variant's internal value. + */ + template + T extract() { + static_assert( + detail::SelectVariantType::count == 1, + "provided a type not uniquely found in this Variant's type list"); + MOZ_ASSERT(is()); + return T(std::move(as())); + } + + template + typename detail::Nth::Type extract() { + static_assert(N < sizeof...(Ts), + "provided an index outside of this Variant's type list"); + MOZ_RELEASE_ASSERT(is()); + return typename detail::Nth::Type(std::move(as())); + } + + // Exhaustive matching of all variant types on the contained value. + + /** Match on an immutable const lvalue-reference. */ + template + decltype(auto) match(Matcher&& aMatcher) const& { + return Impl::match(std::forward(aMatcher), *this); + } + + template + decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) const& { + return matchN(*this, std::forward(aM0), std::forward(aM1), + std::forward(aMs)...); + } + + /** Match on a mutable non-const lvalue-reference. */ + template + decltype(auto) match(Matcher&& aMatcher) & { + return Impl::match(std::forward(aMatcher), *this); + } + + template + decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) & { + return matchN(*this, std::forward(aM0), std::forward(aM1), + std::forward(aMs)...); + } + + /** Match on an immutable const rvalue-reference. */ + template + decltype(auto) match(Matcher&& aMatcher) const&& { + return Impl::match(std::forward(aMatcher), std::move(*this)); + } + + template + decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) const&& { + return matchN(std::move(*this), std::forward(aM0), + std::forward(aM1), std::forward(aMs)...); + } + + /** Match on a mutable non-const rvalue-reference. */ + template + decltype(auto) match(Matcher&& aMatcher) && { + return Impl::match(std::forward(aMatcher), std::move(*this)); + } + + template + decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) && { + return matchN(std::move(*this), std::forward(aM0), + std::forward(aM1), std::forward(aMs)...); + } + + /** + * Incorporate the current variant's tag into hashValue. + * Note that this does not hash the actual contents; you must take + * care of that yourself, perhaps by using a match. + */ + mozilla::HashNumber addTagToHash(mozilla::HashNumber hashValue) const { + return mozilla::AddToHash(hashValue, tag); + } + + private: + template + static decltype(auto) matchN(ConcreteVariant&& aVariant, M0&& aM0, M1&& aM1, + Ms&&... aMs) { + static_assert( + 2 + sizeof...(Ms) == sizeof...(Ts), + "Variant::match() takes either one callable argument that " + "accepts every type T; or one for each type T, in order"); + return Impl::matchN(std::forward(aVariant), + std::forward(aM0), std::forward(aM1), + std::forward(aMs)...); + } +}; + +/* + * AsVariant() is used to construct a Variant value containing the + * provided T value using type inference. It can be used to construct Variant + * values in expressions or return them from functions without specifying the + * entire Variant type. + * + * Because AsVariant() must copy or move the value into a temporary and this + * cannot necessarily be elided by the compiler, it's mostly appropriate only + * for use with primitive or very small types. + * + * AsVariant() returns a AsVariantTemporary value which is implicitly + * convertible to any Variant that can hold a value of type T. + */ +template +detail::AsVariantTemporary AsVariant(T&& aValue) { + return detail::AsVariantTemporary(std::forward(aValue)); +} + +} // namespace mozilla + +#endif /* mozilla_Variant_h */ -- cgit v1.2.3