Bug 1500811 - Variant::as()&&, match()&& - r=sg

`as<...>()` on an rvalue variant returns the contained value as rvalue-reference.
`match()` on an rvalue variant forwards the contained value as rvalue-reference.
So now the contained values can be efficiently moved-from.

There are also equivalents for `const&&`, which should be rare.

For `match(2+ matchers)`, the static_assert for same return types has been moved to `Impl::matchN`. Also, it doesn't rely on `FunctionTypeTraits` anymore, which has the added benefit of allowing `auto` in individual matchers.

Differential Revision: https://phabricator.services.mozilla.com/D98050
This commit is contained in:
Gerald Squelart 2020-12-02 00:17:46 +00:00
Родитель 32c0d65819
Коммит 0e7e45ed66
2 изменённых файлов: 686 добавлений и 175 удалений

Просмотреть файл

@ -10,7 +10,6 @@
#include <stdint.h>
#include "mozilla/Assertions.h"
#include "mozilla/FunctionTypeTraits.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/OperatorNewExtensions.h"
#include "mozilla/TemplateLib.h"
@ -179,22 +178,28 @@ struct VariantImplementation<Tag, N, T> {
}
template <typename Matcher, typename ConcreteVariant>
static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant& aV) {
static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant&& aV) {
if constexpr (std::is_invocable_v<Matcher, Tag,
decltype(aV.template as<N>())>) {
return std::forward<Matcher>(aMatcher)(Tag(N), aV.template as<N>());
decltype(std::forward<ConcreteVariant>(aV)
.template as<N>())>) {
return std::forward<Matcher>(aMatcher)(
Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
} else {
return std::forward<Matcher>(aMatcher)(aV.template as<N>());
return std::forward<Matcher>(aMatcher)(
std::forward<ConcreteVariant>(aV).template as<N>());
}
}
template <typename ConcreteVariant, typename Matcher>
static decltype(auto) matchN(ConcreteVariant& aV, Matcher&& aMatcher) {
static decltype(auto) matchN(ConcreteVariant&& aV, Matcher&& aMatcher) {
if constexpr (std::is_invocable_v<Matcher, Tag,
decltype(aV.template as<N>())>) {
return std::forward<Matcher>(aMatcher)(Tag(N), aV.template as<N>());
decltype(std::forward<ConcreteVariant>(aV)
.template as<N>())>) {
return std::forward<Matcher>(aMatcher)(
Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
} else {
return std::forward<Matcher>(aMatcher)(aV.template as<N>());
return std::forward<Matcher>(aMatcher)(
std::forward<ConcreteVariant>(aV).template as<N>());
}
}
};
@ -248,13 +253,17 @@ struct VariantImplementation<Tag, N, T, Ts...> {
}
template <typename Matcher, typename ConcreteVariant>
static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant& aV) {
static decltype(auto) match(Matcher&& aMatcher, ConcreteVariant&& aV) {
if (aV.template is<N>()) {
if constexpr (std::is_invocable_v<Matcher, Tag,
decltype(aV.template as<N>())>) {
return std::forward<Matcher>(aMatcher)(Tag(N), aV.template as<N>());
decltype(
std::forward<ConcreteVariant>(aV)
.template as<N>())>) {
return std::forward<Matcher>(aMatcher)(
Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
} else {
return std::forward<Matcher>(aMatcher)(aV.template as<N>());
return std::forward<Matcher>(aMatcher)(
std::forward<ConcreteVariant>(aV).template as<N>());
}
} else {
// If you're seeing compilation errors here like "no matching
@ -266,18 +275,38 @@ struct VariantImplementation<Tag, N, T, Ts...> {
// 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<Matcher>(aMatcher), aV);
return Next::match(std::forward<Matcher>(aMatcher),
std::forward<ConcreteVariant>(aV));
}
}
template <typename ConcreteVariant, typename Mi, typename... Ms>
static decltype(auto) matchN(ConcreteVariant& aV, Mi&& aMi, Ms&&... aMs) {
static decltype(auto) matchN(ConcreteVariant&& aV, Mi&& aMi, Ms&&... aMs) {
if (aV.template is<N>()) {
if constexpr (std::is_invocable_v<Mi, Tag,
decltype(aV.template as<N>())>) {
return std::forward<Mi>(aMi)(Tag(N), aV.template as<N>());
decltype(
std::forward<ConcreteVariant>(aV)
.template as<N>())>) {
static_assert(
std::is_same_v<
decltype(std::forward<Mi>(aMi)(
Tag(N),
std::forward<ConcreteVariant>(aV).template as<N>())),
decltype(Next::matchN(std::forward<ConcreteVariant>(aV),
std::forward<Ms>(aMs)...))>,
"all matchers must have the same return type");
return std::forward<Mi>(aMi)(
Tag(N), std::forward<ConcreteVariant>(aV).template as<N>());
} else {
return std::forward<Mi>(aMi)(aV.template as<N>());
static_assert(
std::is_same_v<
decltype(std::forward<Mi>(aMi)(
std::forward<ConcreteVariant>(aV).template as<N>())),
decltype(Next::matchN(std::forward<ConcreteVariant>(aV),
std::forward<Ms>(aMs)...))>,
"all matchers must have the same return type");
return std::forward<Mi>(aMi)(
std::forward<ConcreteVariant>(aV).template as<N>());
}
} else {
// If you're seeing compilation errors here like "no matching
@ -285,7 +314,8 @@ struct VariantImplementation<Tag, N, T, Ts...> {
// 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(aV, std::forward<Ms>(aMs)...);
return Next::matchN(std::forward<ConcreteVariant>(aV),
std::forward<Ms>(aMs)...);
}
}
};
@ -706,9 +736,9 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant {
// Accessors for working with the contained variant value.
/** Mutable reference. */
/** Mutable lvalue-reference. */
template <typename T>
T& as() {
T& as() & {
static_assert(
detail::SelectVariantType<T, Ts...>::count == 1,
"provided a type not uniquely found in this Variant's type list");
@ -717,16 +747,16 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant {
}
template <size_t N>
typename detail::Nth<N, Ts...>::Type& as() {
typename detail::Nth<N, Ts...>::Type& as() & {
static_assert(N < sizeof...(Ts),
"provided an index outside of this Variant's type list");
MOZ_RELEASE_ASSERT(is<N>());
return *static_cast<typename detail::Nth<N, Ts...>::Type*>(ptr());
}
/** Immutable const reference. */
/** Immutable const lvalue-reference. */
template <typename T>
const T& as() const {
const T& as() const& {
static_assert(detail::SelectVariantType<T, Ts...>::count == 1,
"provided a type not found in this Variant's type list");
MOZ_RELEASE_ASSERT(is<T>());
@ -734,13 +764,50 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant {
}
template <size_t N>
const typename detail::Nth<N, Ts...>::Type& as() const {
const typename detail::Nth<N, Ts...>::Type& as() const& {
static_assert(N < sizeof...(Ts),
"provided an index outside of this Variant's type list");
MOZ_RELEASE_ASSERT(is<N>());
return *static_cast<const typename detail::Nth<N, Ts...>::Type*>(ptr());
}
/** Mutable rvalue-reference. */
template <typename T>
T&& as() && {
static_assert(
detail::SelectVariantType<T, Ts...>::count == 1,
"provided a type not uniquely found in this Variant's type list");
MOZ_RELEASE_ASSERT(is<T>());
return std::move(*static_cast<T*>(ptr()));
}
template <size_t N>
typename detail::Nth<N, Ts...>::Type&& as() && {
static_assert(N < sizeof...(Ts),
"provided an index outside of this Variant's type list");
MOZ_RELEASE_ASSERT(is<N>());
return std::move(
*static_cast<typename detail::Nth<N, Ts...>::Type*>(ptr()));
}
/** Immutable const rvalue-reference. */
template <typename T>
const T&& as() const&& {
static_assert(detail::SelectVariantType<T, Ts...>::count == 1,
"provided a type not found in this Variant's type list");
MOZ_RELEASE_ASSERT(is<T>());
return std::move(*static_cast<const T*>(ptr()));
}
template <size_t N>
const typename detail::Nth<N, Ts...>::Type&& as() const&& {
static_assert(N < sizeof...(Ts),
"provided an index outside of this Variant's type list");
MOZ_RELEASE_ASSERT(is<N>());
return std::move(
*static_cast<const typename detail::Nth<N, Ts...>::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
@ -766,50 +833,52 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant {
// Exhaustive matching of all variant types on the contained value.
/** Match on an immutable const reference. */
/** Match on an immutable const lvalue-reference. */
template <typename Matcher>
decltype(auto) match(Matcher&& aMatcher) const {
decltype(auto) match(Matcher&& aMatcher) const& {
return Impl::match(std::forward<Matcher>(aMatcher), *this);
}
template <typename M0, typename M1, typename... Ms>
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) const {
static_assert(
2 + sizeof...(Ms) == sizeof...(Ts),
"Variant<T...>::match() takes either one callable argument that "
"accepts every type T; or one for each type T, in order");
static_assert(
tl::And<std::is_same_v<typename FunctionTypeTraits<M0>::ReturnType,
typename FunctionTypeTraits<M1>::ReturnType>,
std::is_same_v<
typename FunctionTypeTraits<M1>::ReturnType,
typename FunctionTypeTraits<Ms>::ReturnType>...>::value,
"all matchers must have the same return type");
return Impl::matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
std::forward<Ms>(aMs)...);
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) const& {
return matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
std::forward<Ms>(aMs)...);
}
/** Match on a mutable non-const reference. */
/** Match on a mutable non-const lvalue-reference. */
template <typename Matcher>
decltype(auto) match(Matcher&& aMatcher) {
decltype(auto) match(Matcher&& aMatcher) & {
return Impl::match(std::forward<Matcher>(aMatcher), *this);
}
template <typename M0, typename M1, typename... Ms>
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) {
static_assert(
2 + sizeof...(Ms) == sizeof...(Ts),
"Variant<T...>::match() takes either one callable argument that "
"accepts every type T; or one for each type T, in order");
static_assert(
tl::And<std::is_same_v<typename FunctionTypeTraits<M0>::ReturnType,
typename FunctionTypeTraits<M1>::ReturnType>,
std::is_same_v<
typename FunctionTypeTraits<M0>::ReturnType,
typename FunctionTypeTraits<Ms>::ReturnType>...>::value,
"all matchers must have the same return type");
return Impl::matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
std::forward<Ms>(aMs)...);
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) & {
return matchN(*this, std::forward<M0>(aM0), std::forward<M1>(aM1),
std::forward<Ms>(aMs)...);
}
/** Match on an immutable const rvalue-reference. */
template <typename Matcher>
decltype(auto) match(Matcher&& aMatcher) const&& {
return Impl::match(std::forward<Matcher>(aMatcher), std::move(*this));
}
template <typename M0, typename M1, typename... Ms>
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) const&& {
return matchN(std::move(*this), std::forward<M0>(aM0),
std::forward<M1>(aM1), std::forward<Ms>(aMs)...);
}
/** Match on a mutable non-const rvalue-reference. */
template <typename Matcher>
decltype(auto) match(Matcher&& aMatcher) && {
return Impl::match(std::forward<Matcher>(aMatcher), std::move(*this));
}
template <typename M0, typename M1, typename... Ms>
decltype(auto) match(M0&& aM0, M1&& aM1, Ms&&... aMs) && {
return matchN(std::move(*this), std::forward<M0>(aM0),
std::forward<M1>(aM1), std::forward<Ms>(aMs)...);
}
/**
@ -820,6 +889,19 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant {
mozilla::HashNumber addTagToHash(mozilla::HashNumber hashValue) const {
return mozilla::AddToHash(hashValue, tag);
}
private:
template <typename ConcreteVariant, typename M0, typename M1, typename... Ms>
static decltype(auto) matchN(ConcreteVariant&& aVariant, M0&& aM0, M1&& aM1,
Ms&&... aMs) {
static_assert(
2 + sizeof...(Ms) == sizeof...(Ts),
"Variant<T...>::match() takes either one callable argument that "
"accepts every type T; or one for each type T, in order");
return Impl::matchN(std::forward<ConcreteVariant>(aVariant),
std::forward<M0>(aM0), std::forward<M1>(aM1),
std::forward<Ms>(aMs)...);
}
};
/*

Просмотреть файл

@ -9,6 +9,8 @@
#include "mozilla/UniquePtr.h"
#include "mozilla/Variant.h"
#include <tuple>
using mozilla::MakeUnique;
using mozilla::UniquePtr;
using mozilla::Variant;
@ -237,7 +239,10 @@ static void testDetails() {
static void testSimple() {
printf("testSimple\n");
Variant<uint32_t, uint64_t> v(uint64_t(1));
using V = Variant<uint32_t, uint64_t>;
// Non-const lvalue.
V v(uint64_t(1));
MOZ_RELEASE_ASSERT(v.is<uint64_t>());
MOZ_RELEASE_ASSERT(!v.is<uint32_t>());
MOZ_RELEASE_ASSERT(v.as<uint64_t>() == 1);
@ -245,8 +250,44 @@ static void testSimple() {
MOZ_RELEASE_ASSERT(v.is<1>());
MOZ_RELEASE_ASSERT(!v.is<0>());
static_assert(std::is_same_v<decltype(v.as<1>()), uint64_t&>,
"as<1>() should return a uint64_t");
"v.as<1>() should return a uint64_t&");
MOZ_RELEASE_ASSERT(v.as<1>() == 1);
// Const lvalue.
const V& cv = v;
MOZ_RELEASE_ASSERT(cv.is<uint64_t>());
MOZ_RELEASE_ASSERT(!cv.is<uint32_t>());
MOZ_RELEASE_ASSERT(cv.as<uint64_t>() == 1);
MOZ_RELEASE_ASSERT(cv.is<1>());
MOZ_RELEASE_ASSERT(!cv.is<0>());
static_assert(std::is_same_v<decltype(cv.as<1>()), const uint64_t&>,
"cv.as<1>() should return a const uint64_t&");
MOZ_RELEASE_ASSERT(cv.as<1>() == 1);
// Non-const rvalue, using a function to create a temporary.
auto MakeV = []() { return V(uint64_t(1)); };
MOZ_RELEASE_ASSERT(MakeV().is<uint64_t>());
MOZ_RELEASE_ASSERT(!MakeV().is<uint32_t>());
MOZ_RELEASE_ASSERT(MakeV().as<uint64_t>() == 1);
MOZ_RELEASE_ASSERT(MakeV().is<1>());
MOZ_RELEASE_ASSERT(!MakeV().is<0>());
static_assert(std::is_same_v<decltype(MakeV().as<1>()), uint64_t&&>,
"MakeV().as<1>() should return a uint64_t&&");
MOZ_RELEASE_ASSERT(MakeV().as<1>() == 1);
// Const rvalue, using a function to create a temporary.
auto MakeCV = []() -> const V { return V(uint64_t(1)); };
MOZ_RELEASE_ASSERT(MakeCV().is<uint64_t>());
MOZ_RELEASE_ASSERT(!MakeCV().is<uint32_t>());
MOZ_RELEASE_ASSERT(MakeCV().as<uint64_t>() == 1);
MOZ_RELEASE_ASSERT(MakeCV().is<1>());
MOZ_RELEASE_ASSERT(!MakeCV().is<0>());
static_assert(std::is_same_v<decltype(MakeCV().as<1>()), const uint64_t&&>,
"MakeCV().as<1>() should return a const uint64_t&&");
MOZ_RELEASE_ASSERT(MakeCV().as<1>() == 1);
}
static void testDuplicate() {
@ -432,90 +473,249 @@ static void testEquality() {
MOZ_RELEASE_ASSERT(v6 == v6);
}
// Matcher that returns a description of how its call-operator was invoked.
struct Describer {
static const char* littleNonConst;
static const char* mediumNonConst;
static const char* bigNonConst;
enum class ParameterSize { NA, U8, U32, U64 };
enum class ParameterQualifier {
NA,
ParamLREF,
ParamCLREF,
ParamRREF,
ParamCRREF
};
enum class ThisQualifier { NA, ThisLREF, ThisCLREF, ThisRREF, ThisCRREF };
static const char* littleConst;
static const char* mediumConst;
static const char* bigConst;
using Result =
std::tuple<ParameterSize, ParameterQualifier, ThisQualifier, uint64_t>;
static const char* littleRRef;
static const char* mediumRRef;
static const char* bigRRef;
#define RESULT(SIZE, PQUAL, TQUAL, VALUE) \
Describer::Result(Describer::ParameterSize::SIZE, \
Describer::ParameterQualifier::PQUAL, \
Describer::ThisQualifier::TQUAL, VALUE)
const char* operator()(const uint8_t&) & { return littleNonConst; }
const char* operator()(const uint32_t&) & { return mediumNonConst; }
const char* operator()(const uint64_t&) & { return bigNonConst; }
#define CALL(TYPE, SIZE, PQUAL, TREF, TQUAL) \
Result operator()(TYPE aValue) TREF { \
return RESULT(SIZE, PQUAL, TQUAL, aValue); \
}
const char* operator()(const uint8_t&) const& { return littleConst; }
const char* operator()(const uint32_t&) const& { return mediumConst; }
const char* operator()(const uint64_t&) const& { return bigConst; }
// All combinations of possible call operators:
// Every line, the parameter integer type changes.
// Every 3 lines, the parameter type changes constness.
// Every 6 lines, the parameter changes reference l/r-valueness.
// Every 12 lines, the member function qualifier changes constness.
// After 24 lines, the member function qualifier changes ref l/r-valueness.
CALL(uint8_t&, U8, ParamLREF, &, ThisLREF)
CALL(uint32_t&, U32, ParamLREF, &, ThisLREF)
CALL(uint64_t&, U64, ParamLREF, &, ThisLREF)
const char* operator()(const uint8_t&) && { return littleRRef; }
const char* operator()(const uint32_t&) && { return mediumRRef; }
const char* operator()(const uint64_t&) && { return bigRRef; }
CALL(const uint8_t&, U8, ParamCLREF, &, ThisLREF)
CALL(const uint32_t&, U32, ParamCLREF, &, ThisLREF)
CALL(const uint64_t&, U64, ParamCLREF, &, ThisLREF)
CALL(uint8_t&&, U8, ParamRREF, &, ThisLREF)
CALL(uint32_t&&, U32, ParamRREF, &, ThisLREF)
CALL(uint64_t&&, U64, ParamRREF, &, ThisLREF)
CALL(const uint8_t&&, U8, ParamCRREF, &, ThisLREF)
CALL(const uint32_t&&, U32, ParamCRREF, &, ThisLREF)
CALL(const uint64_t&&, U64, ParamCRREF, &, ThisLREF)
CALL(uint8_t&, U8, ParamLREF, const&, ThisCLREF)
CALL(uint32_t&, U32, ParamLREF, const&, ThisCLREF)
CALL(uint64_t&, U64, ParamLREF, const&, ThisCLREF)
CALL(const uint8_t&, U8, ParamCLREF, const&, ThisCLREF)
CALL(const uint32_t&, U32, ParamCLREF, const&, ThisCLREF)
CALL(const uint64_t&, U64, ParamCLREF, const&, ThisCLREF)
CALL(uint8_t&&, U8, ParamRREF, const&, ThisCLREF)
CALL(uint32_t&&, U32, ParamRREF, const&, ThisCLREF)
CALL(uint64_t&&, U64, ParamRREF, const&, ThisCLREF)
CALL(const uint8_t&&, U8, ParamCRREF, const&, ThisCLREF)
CALL(const uint32_t&&, U32, ParamCRREF, const&, ThisCLREF)
CALL(const uint64_t&&, U64, ParamCRREF, const&, ThisCLREF)
CALL(uint8_t&, U8, ParamLREF, &&, ThisRREF)
CALL(uint32_t&, U32, ParamLREF, &&, ThisRREF)
CALL(uint64_t&, U64, ParamLREF, &&, ThisRREF)
CALL(const uint8_t&, U8, ParamCLREF, &&, ThisRREF)
CALL(const uint32_t&, U32, ParamCLREF, &&, ThisRREF)
CALL(const uint64_t&, U64, ParamCLREF, &&, ThisRREF)
CALL(uint8_t&&, U8, ParamRREF, &&, ThisRREF)
CALL(uint32_t&&, U32, ParamRREF, &&, ThisRREF)
CALL(uint64_t&&, U64, ParamRREF, &&, ThisRREF)
CALL(const uint8_t&&, U8, ParamCRREF, &&, ThisRREF)
CALL(const uint32_t&&, U32, ParamCRREF, &&, ThisRREF)
CALL(const uint64_t&&, U64, ParamCRREF, &&, ThisRREF)
CALL(uint8_t&, U8, ParamLREF, const&&, ThisCRREF)
CALL(uint32_t&, U32, ParamLREF, const&&, ThisCRREF)
CALL(uint64_t&, U64, ParamLREF, const&&, ThisCRREF)
CALL(const uint8_t&, U8, ParamCLREF, const&&, ThisCRREF)
CALL(const uint32_t&, U32, ParamCLREF, const&&, ThisCRREF)
CALL(const uint64_t&, U64, ParamCLREF, const&&, ThisCRREF)
CALL(uint8_t&&, U8, ParamRREF, const&&, ThisCRREF)
CALL(uint32_t&&, U32, ParamRREF, const&&, ThisCRREF)
CALL(uint64_t&&, U64, ParamRREF, const&&, ThisCRREF)
CALL(const uint8_t&&, U8, ParamCRREF, const&&, ThisCRREF)
CALL(const uint32_t&&, U32, ParamCRREF, const&&, ThisCRREF)
CALL(const uint64_t&&, U64, ParamCRREF, const&&, ThisCRREF)
#undef CALL
// Catch-all, to verify that there is no call with any type other than the
// expected ones above.
template <typename Other>
const char* operator()(const Other&) {
Result operator()(const Other&) {
MOZ_RELEASE_ASSERT(false);
return "uh?";
return RESULT(NA, NA, NA, 0);
}
};
const char* Describer::littleNonConst = "little non-const";
const char* Describer::mediumNonConst = "medium non-const";
const char* Describer::bigNonConst = "big non-const";
const char* Describer::littleConst = "little const";
const char* Describer::mediumConst = "medium const";
const char* Describer::bigConst = "big const";
const char* Describer::littleRRef = "little rvalue-ref";
const char* Describer::mediumRRef = "medium rvalue-ref";
const char* Describer::bigRRef = "big rvalue-ref";
static void testMatching() {
printf("testMatching\n");
using V = Variant<uint8_t, uint32_t, uint64_t>;
Describer desc;
const Describer descConst;
auto MakeDescriber = []() { return Describer(); };
auto MakeConstDescriber = []() -> const Describer { return Describer(); };
V v1(uint8_t(1));
V v2(uint32_t(2));
V v3(uint64_t(3));
MOZ_RELEASE_ASSERT(v1.match(desc) == Describer::littleNonConst);
MOZ_RELEASE_ASSERT(v2.match(desc) == Describer::mediumNonConst);
MOZ_RELEASE_ASSERT(v3.match(desc) == Describer::bigNonConst);
MOZ_RELEASE_ASSERT(v1.match(descConst) == Describer::littleConst);
MOZ_RELEASE_ASSERT(v2.match(descConst) == Describer::mediumConst);
MOZ_RELEASE_ASSERT(v3.match(descConst) == Describer::bigConst);
MOZ_RELEASE_ASSERT(v1.match(Describer()) == Describer::littleRRef);
MOZ_RELEASE_ASSERT(v2.match(Describer()) == Describer::mediumRRef);
MOZ_RELEASE_ASSERT(v3.match(Describer()) == Describer::bigRRef);
const V& constRef1 = v1;
const V& constRef2 = v2;
const V& constRef3 = v3;
MOZ_RELEASE_ASSERT(constRef1.match(desc) == Describer::littleNonConst);
MOZ_RELEASE_ASSERT(constRef2.match(desc) == Describer::mediumNonConst);
MOZ_RELEASE_ASSERT(constRef3.match(desc) == Describer::bigNonConst);
// Create a temporary variant by returning a copy of one.
auto CopyV = [](const V& aV) { return aV; };
MOZ_RELEASE_ASSERT(constRef1.match(descConst) == Describer::littleConst);
MOZ_RELEASE_ASSERT(constRef2.match(descConst) == Describer::mediumConst);
MOZ_RELEASE_ASSERT(constRef3.match(descConst) == Describer::bigConst);
// Create a temporary variant by returning a const copy of one.
auto CopyConstV = [](const V& aV) -> const V { return aV; };
MOZ_RELEASE_ASSERT(constRef1.match(Describer()) == Describer::littleRRef);
MOZ_RELEASE_ASSERT(constRef2.match(Describer()) == Describer::mediumRRef);
MOZ_RELEASE_ASSERT(constRef3.match(Describer()) == Describer::bigRRef);
// All combinations of possible calls:
// Every line, the variant integer type changes.
// Every 3 lines, the variant type changes constness.
// Every 6 lines, the variant changes reference l/r-valueness.
// Every 12 lines, the matcher changes constness.
// After 24 lines, the matcher changes ref l/r-valueness.
MOZ_RELEASE_ASSERT(v1.match(desc) == RESULT(U8, ParamLREF, ThisLREF, 1));
MOZ_RELEASE_ASSERT(v2.match(desc) == RESULT(U32, ParamLREF, ThisLREF, 2));
MOZ_RELEASE_ASSERT(v3.match(desc) == RESULT(U64, ParamLREF, ThisLREF, 3));
MOZ_RELEASE_ASSERT(constRef1.match(desc) ==
RESULT(U8, ParamCLREF, ThisLREF, 1));
MOZ_RELEASE_ASSERT(constRef2.match(desc) ==
RESULT(U32, ParamCLREF, ThisLREF, 2));
MOZ_RELEASE_ASSERT(constRef3.match(desc) ==
RESULT(U64, ParamCLREF, ThisLREF, 3));
MOZ_RELEASE_ASSERT(CopyV(v1).match(desc) ==
RESULT(U8, ParamRREF, ThisLREF, 1));
MOZ_RELEASE_ASSERT(CopyV(v2).match(desc) ==
RESULT(U32, ParamRREF, ThisLREF, 2));
MOZ_RELEASE_ASSERT(CopyV(v3).match(desc) ==
RESULT(U64, ParamRREF, ThisLREF, 3));
MOZ_RELEASE_ASSERT(CopyConstV(v1).match(desc) ==
RESULT(U8, ParamCRREF, ThisLREF, 1));
MOZ_RELEASE_ASSERT(CopyConstV(v2).match(desc) ==
RESULT(U32, ParamCRREF, ThisLREF, 2));
MOZ_RELEASE_ASSERT(CopyConstV(v3).match(desc) ==
RESULT(U64, ParamCRREF, ThisLREF, 3));
MOZ_RELEASE_ASSERT(v1.match(descConst) ==
RESULT(U8, ParamLREF, ThisCLREF, 1));
MOZ_RELEASE_ASSERT(v2.match(descConst) ==
RESULT(U32, ParamLREF, ThisCLREF, 2));
MOZ_RELEASE_ASSERT(v3.match(descConst) ==
RESULT(U64, ParamLREF, ThisCLREF, 3));
MOZ_RELEASE_ASSERT(constRef1.match(descConst) ==
RESULT(U8, ParamCLREF, ThisCLREF, 1));
MOZ_RELEASE_ASSERT(constRef2.match(descConst) ==
RESULT(U32, ParamCLREF, ThisCLREF, 2));
MOZ_RELEASE_ASSERT(constRef3.match(descConst) ==
RESULT(U64, ParamCLREF, ThisCLREF, 3));
MOZ_RELEASE_ASSERT(CopyV(v1).match(descConst) ==
RESULT(U8, ParamRREF, ThisCLREF, 1));
MOZ_RELEASE_ASSERT(CopyV(v2).match(descConst) ==
RESULT(U32, ParamRREF, ThisCLREF, 2));
MOZ_RELEASE_ASSERT(CopyV(v3).match(descConst) ==
RESULT(U64, ParamRREF, ThisCLREF, 3));
MOZ_RELEASE_ASSERT(CopyConstV(v1).match(descConst) ==
RESULT(U8, ParamCRREF, ThisCLREF, 1));
MOZ_RELEASE_ASSERT(CopyConstV(v2).match(descConst) ==
RESULT(U32, ParamCRREF, ThisCLREF, 2));
MOZ_RELEASE_ASSERT(CopyConstV(v3).match(descConst) ==
RESULT(U64, ParamCRREF, ThisCLREF, 3));
MOZ_RELEASE_ASSERT(v1.match(MakeDescriber()) ==
RESULT(U8, ParamLREF, ThisRREF, 1));
MOZ_RELEASE_ASSERT(v2.match(MakeDescriber()) ==
RESULT(U32, ParamLREF, ThisRREF, 2));
MOZ_RELEASE_ASSERT(v3.match(MakeDescriber()) ==
RESULT(U64, ParamLREF, ThisRREF, 3));
MOZ_RELEASE_ASSERT(constRef1.match(MakeDescriber()) ==
RESULT(U8, ParamCLREF, ThisRREF, 1));
MOZ_RELEASE_ASSERT(constRef2.match(MakeDescriber()) ==
RESULT(U32, ParamCLREF, ThisRREF, 2));
MOZ_RELEASE_ASSERT(constRef3.match(MakeDescriber()) ==
RESULT(U64, ParamCLREF, ThisRREF, 3));
MOZ_RELEASE_ASSERT(CopyV(v1).match(MakeDescriber()) ==
RESULT(U8, ParamRREF, ThisRREF, 1));
MOZ_RELEASE_ASSERT(CopyV(v2).match(MakeDescriber()) ==
RESULT(U32, ParamRREF, ThisRREF, 2));
MOZ_RELEASE_ASSERT(CopyV(v3).match(MakeDescriber()) ==
RESULT(U64, ParamRREF, ThisRREF, 3));
MOZ_RELEASE_ASSERT(CopyConstV(v1).match(MakeDescriber()) ==
RESULT(U8, ParamCRREF, ThisRREF, 1));
MOZ_RELEASE_ASSERT(CopyConstV(v2).match(MakeDescriber()) ==
RESULT(U32, ParamCRREF, ThisRREF, 2));
MOZ_RELEASE_ASSERT(CopyConstV(v3).match(MakeDescriber()) ==
RESULT(U64, ParamCRREF, ThisRREF, 3));
MOZ_RELEASE_ASSERT(v1.match(MakeConstDescriber()) ==
RESULT(U8, ParamLREF, ThisCRREF, 1));
MOZ_RELEASE_ASSERT(v2.match(MakeConstDescriber()) ==
RESULT(U32, ParamLREF, ThisCRREF, 2));
MOZ_RELEASE_ASSERT(v3.match(MakeConstDescriber()) ==
RESULT(U64, ParamLREF, ThisCRREF, 3));
MOZ_RELEASE_ASSERT(constRef1.match(MakeConstDescriber()) ==
RESULT(U8, ParamCLREF, ThisCRREF, 1));
MOZ_RELEASE_ASSERT(constRef2.match(MakeConstDescriber()) ==
RESULT(U32, ParamCLREF, ThisCRREF, 2));
MOZ_RELEASE_ASSERT(constRef3.match(MakeConstDescriber()) ==
RESULT(U64, ParamCLREF, ThisCRREF, 3));
MOZ_RELEASE_ASSERT(CopyV(v1).match(MakeConstDescriber()) ==
RESULT(U8, ParamRREF, ThisCRREF, 1));
MOZ_RELEASE_ASSERT(CopyV(v2).match(MakeConstDescriber()) ==
RESULT(U32, ParamRREF, ThisCRREF, 2));
MOZ_RELEASE_ASSERT(CopyV(v3).match(MakeConstDescriber()) ==
RESULT(U64, ParamRREF, ThisCRREF, 3));
MOZ_RELEASE_ASSERT(CopyConstV(v1).match(MakeConstDescriber()) ==
RESULT(U8, ParamCRREF, ThisCRREF, 1));
MOZ_RELEASE_ASSERT(CopyConstV(v2).match(MakeConstDescriber()) ==
RESULT(U32, ParamCRREF, ThisCRREF, 2));
MOZ_RELEASE_ASSERT(CopyConstV(v3).match(MakeConstDescriber()) ==
RESULT(U64, ParamCRREF, ThisCRREF, 3));
}
static void testMatchingLambda() {
@ -523,21 +723,42 @@ static void testMatchingLambda() {
using V = Variant<uint8_t, uint32_t, uint64_t>;
// Note: Lambdas' call operators are const by default (unless the lambda is
// declared `mutable`), hence the use of "...Const" strings below.
// declared `mutable`).
// There is no need to test mutable lambdas, nor rvalue lambda, because there
// would be no way to distinguish how each lambda is actually invoked because
// there is only one choice of call operator in each overload set.
auto desc = [](auto& a) {
switch (sizeof(a)) {
case 1:
return Describer::littleConst;
case 4:
return Describer::mediumConst;
case 8:
return Describer::bigConst;
default:
MOZ_RELEASE_ASSERT(false);
return "";
auto desc = [](auto&& a) {
if constexpr (std::is_same_v<decltype(a), uint8_t&>) {
return RESULT(U8, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint8_t&>) {
return RESULT(U8, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint8_t&&>) {
return RESULT(U8, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint8_t&&>) {
return RESULT(U8, ParamCRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint32_t&>) {
return RESULT(U32, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint32_t&>) {
return RESULT(U32, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint32_t&&>) {
return RESULT(U32, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint32_t&&>) {
return RESULT(U32, ParamCRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint64_t&>) {
return RESULT(U64, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint64_t&>) {
return RESULT(U64, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint64_t&&>) {
return RESULT(U64, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint64_t&&>) {
return RESULT(U64, ParamCRREF, NA, a);
} else {
// We don't expect any other type.
// Tech note: We can't just do `static_assert(false)` which would always
// fail during the initial parsing. So we depend on the templated
// parameter to delay computing `false` until actual instantiation.
static_assert(sizeof(a) == size_t(-1));
return RESULT(NA, NA, NA, 0);
}
};
@ -545,17 +766,34 @@ static void testMatchingLambda() {
V v2(uint32_t(2));
V v3(uint64_t(3));
MOZ_RELEASE_ASSERT(v1.match(desc) == Describer::littleConst);
MOZ_RELEASE_ASSERT(v2.match(desc) == Describer::mediumConst);
MOZ_RELEASE_ASSERT(v3.match(desc) == Describer::bigConst);
const V& constRef1 = v1;
const V& constRef2 = v2;
const V& constRef3 = v3;
MOZ_RELEASE_ASSERT(constRef1.match(desc) == Describer::littleConst);
MOZ_RELEASE_ASSERT(constRef2.match(desc) == Describer::mediumConst);
MOZ_RELEASE_ASSERT(constRef3.match(desc) == Describer::bigConst);
// Create a temporary variant by returning a copy of one.
auto CopyV = [](const V& aV) { return aV; };
// Create a temporary variant by returning a const copy of one.
auto CopyConstV = [](const V& aV) -> const V { return aV; };
MOZ_RELEASE_ASSERT(v1.match(desc) == RESULT(U8, ParamLREF, NA, 1));
MOZ_RELEASE_ASSERT(v2.match(desc) == RESULT(U32, ParamLREF, NA, 2));
MOZ_RELEASE_ASSERT(v3.match(desc) == RESULT(U64, ParamLREF, NA, 3));
MOZ_RELEASE_ASSERT(constRef1.match(desc) == RESULT(U8, ParamCLREF, NA, 1));
MOZ_RELEASE_ASSERT(constRef2.match(desc) == RESULT(U32, ParamCLREF, NA, 2));
MOZ_RELEASE_ASSERT(constRef3.match(desc) == RESULT(U64, ParamCLREF, NA, 3));
MOZ_RELEASE_ASSERT(CopyV(v1).match(desc) == RESULT(U8, ParamRREF, NA, 1));
MOZ_RELEASE_ASSERT(CopyV(v2).match(desc) == RESULT(U32, ParamRREF, NA, 2));
MOZ_RELEASE_ASSERT(CopyV(v3).match(desc) == RESULT(U64, ParamRREF, NA, 3));
MOZ_RELEASE_ASSERT(CopyConstV(v1).match(desc) ==
RESULT(U8, ParamCRREF, NA, 1));
MOZ_RELEASE_ASSERT(CopyConstV(v2).match(desc) ==
RESULT(U32, ParamCRREF, NA, 2));
MOZ_RELEASE_ASSERT(CopyConstV(v3).match(desc) ==
RESULT(U64, ParamCRREF, NA, 3));
}
static void testMatchingLambdaWithIndex() {
@ -567,21 +805,51 @@ static void testMatchingLambdaWithIndex() {
// There is no need to test mutable lambdas, nor rvalue lambda, because there
// would be no way to distinguish how each lambda is actually invoked because
// there is only one choice of call operator in each overload set.
auto desc = [](auto aIndex, auto& a) {
auto desc = [](auto aIndex, auto&& a) {
static_assert(sizeof(aIndex) < sizeof(size_t), "Expected small index type");
switch (sizeof(a)) {
case 1:
MOZ_RELEASE_ASSERT(aIndex == 0);
return Describer::littleConst;
case 4:
MOZ_RELEASE_ASSERT(aIndex == 1);
return Describer::mediumConst;
case 8:
MOZ_RELEASE_ASSERT(aIndex == 2);
return Describer::bigConst;
default:
MOZ_RELEASE_ASSERT(false);
return "";
if constexpr (std::is_same_v<decltype(a), uint8_t&>) {
MOZ_RELEASE_ASSERT(aIndex == 0);
return RESULT(U8, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint8_t&>) {
MOZ_RELEASE_ASSERT(aIndex == 0);
return RESULT(U8, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint8_t&&>) {
MOZ_RELEASE_ASSERT(aIndex == 0);
return RESULT(U8, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint8_t&&>) {
MOZ_RELEASE_ASSERT(aIndex == 0);
return RESULT(U8, ParamCRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint32_t&>) {
MOZ_RELEASE_ASSERT(aIndex == 1);
return RESULT(U32, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint32_t&>) {
MOZ_RELEASE_ASSERT(aIndex == 1);
return RESULT(U32, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint32_t&&>) {
MOZ_RELEASE_ASSERT(aIndex == 1);
return RESULT(U32, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint32_t&&>) {
MOZ_RELEASE_ASSERT(aIndex == 1);
return RESULT(U32, ParamCRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint64_t&>) {
MOZ_RELEASE_ASSERT(aIndex == 2);
return RESULT(U64, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint64_t&>) {
MOZ_RELEASE_ASSERT(aIndex == 2);
return RESULT(U64, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint64_t&&>) {
MOZ_RELEASE_ASSERT(aIndex == 2);
return RESULT(U64, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint64_t&&>) {
MOZ_RELEASE_ASSERT(aIndex == 2);
return RESULT(U64, ParamCRREF, NA, a);
} else {
// We don't expect any other type.
// Tech note: We can't just do `static_assert(false)` which would always
// fail during the initial parsing. So we depend on the templated
// parameter to delay computing `false` until actual instantiation.
static_assert(sizeof(a) == size_t(-1));
return RESULT(NA, NA, NA, 0);
}
};
@ -589,84 +857,245 @@ static void testMatchingLambdaWithIndex() {
V v2(uint32_t(2));
V v3(uint64_t(3));
MOZ_RELEASE_ASSERT(v1.match(desc) == Describer::littleConst);
MOZ_RELEASE_ASSERT(v2.match(desc) == Describer::mediumConst);
MOZ_RELEASE_ASSERT(v3.match(desc) == Describer::bigConst);
const V& constRef1 = v1;
const V& constRef2 = v2;
const V& constRef3 = v3;
MOZ_RELEASE_ASSERT(constRef1.match(desc) == Describer::littleConst);
MOZ_RELEASE_ASSERT(constRef2.match(desc) == Describer::mediumConst);
MOZ_RELEASE_ASSERT(constRef3.match(desc) == Describer::bigConst);
// Create a temporary variant by returning a copy of one.
auto CopyV = [](const V& aV) { return aV; };
// Create a temporary variant by returning a const copy of one.
auto CopyConstV = [](const V& aV) -> const V { return aV; };
MOZ_RELEASE_ASSERT(v1.match(desc) == RESULT(U8, ParamLREF, NA, 1));
MOZ_RELEASE_ASSERT(v2.match(desc) == RESULT(U32, ParamLREF, NA, 2));
MOZ_RELEASE_ASSERT(v3.match(desc) == RESULT(U64, ParamLREF, NA, 3));
MOZ_RELEASE_ASSERT(constRef1.match(desc) == RESULT(U8, ParamCLREF, NA, 1));
MOZ_RELEASE_ASSERT(constRef2.match(desc) == RESULT(U32, ParamCLREF, NA, 2));
MOZ_RELEASE_ASSERT(constRef3.match(desc) == RESULT(U64, ParamCLREF, NA, 3));
MOZ_RELEASE_ASSERT(CopyV(v1).match(desc) == RESULT(U8, ParamRREF, NA, 1));
MOZ_RELEASE_ASSERT(CopyV(v2).match(desc) == RESULT(U32, ParamRREF, NA, 2));
MOZ_RELEASE_ASSERT(CopyV(v3).match(desc) == RESULT(U64, ParamRREF, NA, 3));
MOZ_RELEASE_ASSERT(CopyConstV(v1).match(desc) ==
RESULT(U8, ParamCRREF, NA, 1));
MOZ_RELEASE_ASSERT(CopyConstV(v2).match(desc) ==
RESULT(U32, ParamCRREF, NA, 2));
MOZ_RELEASE_ASSERT(CopyConstV(v3).match(desc) ==
RESULT(U64, ParamCRREF, NA, 3));
}
static void testMatchingLambdas() {
printf("testMatchingLambdas\n");
using V = Variant<uint8_t, uint32_t, uint64_t>;
auto desc8 = [](const uint8_t& a) { return Describer::littleConst; };
auto desc32 = [](const uint32_t& a) { return Describer::mediumConst; };
auto desc64 = [](const uint64_t& a) { return Describer::bigConst; };
auto desc8 = [](auto&& a) {
if constexpr (std::is_same_v<decltype(a), uint8_t&>) {
return RESULT(U8, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint8_t&>) {
return RESULT(U8, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint8_t&&>) {
return RESULT(U8, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint8_t&&>) {
return RESULT(U8, ParamCRREF, NA, a);
} else {
// We don't expect any other type.
// Tech note: We can't just do `static_assert(false)` which would always
// fail during the initial parsing. So we depend on the templated
// parameter to delay computing `false` until actual instantiation.
static_assert(sizeof(a) == size_t(-1));
return RESULT(NA, NA, NA, 0);
}
};
auto desc32 = [](auto&& a) {
if constexpr (std::is_same_v<decltype(a), uint32_t&>) {
return RESULT(U32, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint32_t&>) {
return RESULT(U32, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint32_t&&>) {
return RESULT(U32, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint32_t&&>) {
return RESULT(U32, ParamCRREF, NA, a);
} else {
// We don't expect any other type.
// Tech note: We can't just do `static_assert(false)` which would always
// fail during the initial parsing. So we depend on the templated
// parameter to delay computing `false` until actual instantiation.
static_assert(sizeof(a) == size_t(-1));
return RESULT(NA, NA, NA, 0);
}
};
auto desc64 = [](auto&& a) {
if constexpr (std::is_same_v<decltype(a), uint64_t&>) {
return RESULT(U64, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint64_t&>) {
return RESULT(U64, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint64_t&&>) {
return RESULT(U64, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint64_t&&>) {
return RESULT(U64, ParamCRREF, NA, a);
} else {
// We don't expect any other type.
// Tech note: We can't just do `static_assert(false)` which would always
// fail during the initial parsing. So we depend on the templated
// parameter to delay computing `false` until actual instantiation.
static_assert(sizeof(a) == size_t(-1));
return RESULT(NA, NA, NA, 0);
}
};
V v1(uint8_t(1));
V v2(uint32_t(2));
V v3(uint64_t(3));
MOZ_RELEASE_ASSERT(v1.match(desc8, desc32, desc64) == Describer::littleConst);
MOZ_RELEASE_ASSERT(v2.match(desc8, desc32, desc64) == Describer::mediumConst);
MOZ_RELEASE_ASSERT(v3.match(desc8, desc32, desc64) == Describer::bigConst);
const V& constRef1 = v1;
const V& constRef2 = v2;
const V& constRef3 = v3;
// Create a temporary variant by returning a copy of one.
auto CopyV = [](const V& aV) { return aV; };
// Create a temporary variant by returning a const copy of one.
auto CopyConstV = [](const V& aV) -> const V { return aV; };
MOZ_RELEASE_ASSERT(v1.match(desc8, desc32, desc64) ==
RESULT(U8, ParamLREF, NA, 1));
MOZ_RELEASE_ASSERT(v2.match(desc8, desc32, desc64) ==
RESULT(U32, ParamLREF, NA, 2));
MOZ_RELEASE_ASSERT(v3.match(desc8, desc32, desc64) ==
RESULT(U64, ParamLREF, NA, 3));
MOZ_RELEASE_ASSERT(constRef1.match(desc8, desc32, desc64) ==
Describer::littleConst);
RESULT(U8, ParamCLREF, NA, 1));
MOZ_RELEASE_ASSERT(constRef2.match(desc8, desc32, desc64) ==
Describer::mediumConst);
RESULT(U32, ParamCLREF, NA, 2));
MOZ_RELEASE_ASSERT(constRef3.match(desc8, desc32, desc64) ==
Describer::bigConst);
RESULT(U64, ParamCLREF, NA, 3));
MOZ_RELEASE_ASSERT(CopyV(v1).match(desc8, desc32, desc64) ==
RESULT(U8, ParamRREF, NA, 1));
MOZ_RELEASE_ASSERT(CopyV(v2).match(desc8, desc32, desc64) ==
RESULT(U32, ParamRREF, NA, 2));
MOZ_RELEASE_ASSERT(CopyV(v3).match(desc8, desc32, desc64) ==
RESULT(U64, ParamRREF, NA, 3));
MOZ_RELEASE_ASSERT(CopyConstV(v1).match(desc8, desc32, desc64) ==
RESULT(U8, ParamCRREF, NA, 1));
MOZ_RELEASE_ASSERT(CopyConstV(v2).match(desc8, desc32, desc64) ==
RESULT(U32, ParamCRREF, NA, 2));
MOZ_RELEASE_ASSERT(CopyConstV(v3).match(desc8, desc32, desc64) ==
RESULT(U64, ParamCRREF, NA, 3));
}
static void testMatchingLambdasWithIndex() {
printf("testMatchingLambdasWithIndex\n");
using V = Variant<uint8_t, uint32_t, uint64_t>;
auto desc8 = [](size_t aIndex, const uint8_t& a) {
auto desc8 = [](size_t aIndex, auto&& a) {
MOZ_RELEASE_ASSERT(aIndex == 0);
return Describer::littleConst;
if constexpr (std::is_same_v<decltype(a), uint8_t&>) {
return RESULT(U8, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint8_t&>) {
return RESULT(U8, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint8_t&&>) {
return RESULT(U8, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint8_t&&>) {
return RESULT(U8, ParamCRREF, NA, a);
} else {
// We don't expect any other type.
// Tech note: We can't just do `static_assert(false)` which would always
// fail during the initial parsing. So we depend on the templated
// parameter to delay computing `false` until actual instantiation.
static_assert(sizeof(a) == size_t(-1));
return RESULT(NA, NA, NA, 0);
}
};
auto desc32 = [](size_t aIndex, const uint32_t& a) {
auto desc32 = [](size_t aIndex, auto&& a) {
MOZ_RELEASE_ASSERT(aIndex == 1);
return Describer::mediumConst;
if constexpr (std::is_same_v<decltype(a), uint32_t&>) {
return RESULT(U32, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint32_t&>) {
return RESULT(U32, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint32_t&&>) {
return RESULT(U32, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint32_t&&>) {
return RESULT(U32, ParamCRREF, NA, a);
} else {
// We don't expect any other type.
// Tech note: We can't just do `static_assert(false)` which would always
// fail during the initial parsing. So we depend on the templated
// parameter to delay computing `false` until actual instantiation.
static_assert(sizeof(a) == size_t(-1));
return RESULT(NA, NA, NA, 0);
}
};
auto desc64 = [](size_t aIndex, const uint64_t& a) {
auto desc64 = [](size_t aIndex, auto&& a) {
MOZ_RELEASE_ASSERT(aIndex == 2);
return Describer::bigConst;
if constexpr (std::is_same_v<decltype(a), uint64_t&>) {
return RESULT(U64, ParamLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint64_t&>) {
return RESULT(U64, ParamCLREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), uint64_t&&>) {
return RESULT(U64, ParamRREF, NA, a);
} else if constexpr (std::is_same_v<decltype(a), const uint64_t&&>) {
return RESULT(U64, ParamCRREF, NA, a);
} else {
// We don't expect any other type.
// Tech note: We can't just do `static_assert(false)` which would always
// fail during the initial parsing. So we depend on the templated
// parameter to delay computing `false` until actual instantiation.
static_assert(sizeof(a) == size_t(-1));
return RESULT(NA, NA, NA, 0);
}
};
V v1(uint8_t(1));
V v2(uint32_t(2));
V v3(uint64_t(3));
MOZ_RELEASE_ASSERT(v1.match(desc8, desc32, desc64) == Describer::littleConst);
MOZ_RELEASE_ASSERT(v2.match(desc8, desc32, desc64) == Describer::mediumConst);
MOZ_RELEASE_ASSERT(v3.match(desc8, desc32, desc64) == Describer::bigConst);
const V& constRef1 = v1;
const V& constRef2 = v2;
const V& constRef3 = v3;
// Create a temporary variant by returning a copy of one.
auto CopyV = [](const V& aV) { return aV; };
// Create a temporary variant by returning a const copy of one.
auto CopyConstV = [](const V& aV) -> const V { return aV; };
MOZ_RELEASE_ASSERT(v1.match(desc8, desc32, desc64) ==
RESULT(U8, ParamLREF, NA, 1));
MOZ_RELEASE_ASSERT(v2.match(desc8, desc32, desc64) ==
RESULT(U32, ParamLREF, NA, 2));
MOZ_RELEASE_ASSERT(v3.match(desc8, desc32, desc64) ==
RESULT(U64, ParamLREF, NA, 3));
MOZ_RELEASE_ASSERT(constRef1.match(desc8, desc32, desc64) ==
Describer::littleConst);
RESULT(U8, ParamCLREF, NA, 1));
MOZ_RELEASE_ASSERT(constRef2.match(desc8, desc32, desc64) ==
Describer::mediumConst);
RESULT(U32, ParamCLREF, NA, 2));
MOZ_RELEASE_ASSERT(constRef3.match(desc8, desc32, desc64) ==
Describer::bigConst);
RESULT(U64, ParamCLREF, NA, 3));
MOZ_RELEASE_ASSERT(CopyV(v1).match(desc8, desc32, desc64) ==
RESULT(U8, ParamRREF, NA, 1));
MOZ_RELEASE_ASSERT(CopyV(v2).match(desc8, desc32, desc64) ==
RESULT(U32, ParamRREF, NA, 2));
MOZ_RELEASE_ASSERT(CopyV(v3).match(desc8, desc32, desc64) ==
RESULT(U64, ParamRREF, NA, 3));
MOZ_RELEASE_ASSERT(CopyConstV(v1).match(desc8, desc32, desc64) ==
RESULT(U8, ParamCRREF, NA, 1));
MOZ_RELEASE_ASSERT(CopyConstV(v2).match(desc8, desc32, desc64) ==
RESULT(U32, ParamCRREF, NA, 2));
MOZ_RELEASE_ASSERT(CopyConstV(v3).match(desc8, desc32, desc64) ==
RESULT(U64, ParamCRREF, NA, 3));
}
#undef RESULT
static void testAddTagToHash() {
printf("testAddToHash\n");
using V = Variant<uint8_t, uint16_t, uint32_t, uint64_t>;