/* -*- 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/. */ #include #include "mozilla/UniquePtr.h" #include "mozilla/Variant.h" #include using mozilla::MakeUnique; using mozilla::UniquePtr; using mozilla::Variant; struct Destroyer { static int destroyedCount; ~Destroyer() { destroyedCount++; } }; int Destroyer::destroyedCount = 0; static void testDetails() { printf("testDetails\n"); using mozilla::detail::Nth; // Test Nth with a list of 1 item. static_assert(std::is_same_v::Type, int>, "Nth<0, int>::Type should be int"); // Test Nth with a list of more than 1 item. static_assert(std::is_same_v::Type, int>, "Nth<0, int, char>::Type should be int"); static_assert(std::is_same_v::Type, char>, "Nth<1, int, char>::Type should be char"); using mozilla::detail::SelectVariantType; // SelectVariantType for zero items (shouldn't happen, but `count` should // still work ok.) static_assert(SelectVariantType::count == 0, "SelectVariantType::count should be 0"); // SelectVariantType for 1 type, for all combinations from/to T, const T, // const T&, T&& // - type to type static_assert(std::is_same_v::Type, int>, "SelectVariantType::Type should be int"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - type to const type static_assert(std::is_same_v::Type, const int>, "SelectVariantType::Type should be const int"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - type to const type& static_assert( std::is_same_v::Type, const int&>, "SelectVariantType::Type should be const int&"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - type to type&& static_assert( std::is_same_v::Type, int&&>, "SelectVariantType::Type should be int&&"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - const type to type static_assert( std::is_same_v::Type, int>, "SelectVariantType::Type should be int"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - const type to const type static_assert( std::is_same_v::Type, const int>, "SelectVariantType::Type should be const int"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - const type to const type& static_assert( std::is_same_v::Type, const int&>, "SelectVariantType::Type should be const int&"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - const type to type&& static_assert( std::is_same_v::Type, int&&>, "SelectVariantType::Type should be int&&"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - const type& to type static_assert( std::is_same_v::Type, int>, "SelectVariantType::Type should be int"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - const type& to const type static_assert( std::is_same_v::Type, const int>, "SelectVariantType::Type should be const int"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - const type& to const type& static_assert( std::is_same_v::Type, const int&>, "SelectVariantType::Type should be const int&"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - const type& to type&& static_assert( std::is_same_v::Type, int&&>, "SelectVariantType::Type should be int&&"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - type&& to type static_assert( std::is_same_v::Type, int>, "SelectVariantType::Type should be int"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - type&& to const type static_assert( std::is_same_v::Type, const int>, "SelectVariantType::Type should be const int"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - type&& to const type& static_assert( std::is_same_v::Type, const int&>, "SelectVariantType::Type should be const int&"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // - type&& to type&& static_assert( std::is_same_v::Type, int&&>, "SelectVariantType::Type should be int&&"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // SelectVariantType for two different types. // (Don't test all combinations, trust that the above tests are sufficient.) static_assert( std::is_same_v::Type, int>, "SelectVariantType::Type should be int"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); static_assert( std::is_same_v::Type, char>, "SelectVariantType::Type should be char"); static_assert(SelectVariantType::count == 1, "SelectVariantType::count should be 1"); // SelectVariantType for two identical types. static_assert( std::is_same_v::Type, int>, "SelectVariantType::Type should be int"); static_assert(SelectVariantType::count == 2, "SelectVariantType::count should be 2"); // SelectVariantType for two identical types, with others around. static_assert( std::is_same_v::Type, int>, "SelectVariantType::Type should be int"); static_assert(SelectVariantType::count == 2, "SelectVariantType::count should be 2"); static_assert( std::is_same_v::Type, int>, "SelectVariantType::Type should be int"); static_assert(SelectVariantType::count == 2, "SelectVariantType::count should be 2"); static_assert( std::is_same_v::Type, int>, "SelectVariantType::Type should be int"); static_assert(SelectVariantType::count == 2, "SelectVariantType::count should be 2"); static_assert( std::is_same_v< typename SelectVariantType::Type, int>, "SelectVariantType::Type should be int"); static_assert( SelectVariantType::count == 2, "SelectVariantType::count should be 2"); // SelectVariantType for two identically-selectable types (first one wins!). static_assert( std::is_same_v::Type, int>, "SelectVariantType::Type should be int"); static_assert(SelectVariantType::count == 2, "SelectVariantType::count should be 2"); static_assert( std::is_same_v::Type, const int>, "SelectVariantType::Type should be const int"); static_assert(SelectVariantType::count == 2, "SelectVariantType::count should be 2"); static_assert( std::is_same_v::Type, const int>, "SelectVariantType::Type should be const int"); static_assert(SelectVariantType::count == 2, "SelectVariantType::count should be 2"); } static void testSimple() { printf("testSimple\n"); using V = Variant; // Non-const lvalue. V v(uint64_t(1)); MOZ_RELEASE_ASSERT(v.is()); MOZ_RELEASE_ASSERT(!v.is()); MOZ_RELEASE_ASSERT(v.as() == 1); MOZ_RELEASE_ASSERT(v.is<1>()); MOZ_RELEASE_ASSERT(!v.is<0>()); static_assert(std::is_same_v()), 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()); MOZ_RELEASE_ASSERT(!cv.is()); MOZ_RELEASE_ASSERT(cv.as() == 1); MOZ_RELEASE_ASSERT(cv.is<1>()); MOZ_RELEASE_ASSERT(!cv.is<0>()); static_assert(std::is_same_v()), 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()); MOZ_RELEASE_ASSERT(!MakeV().is()); MOZ_RELEASE_ASSERT(MakeV().as() == 1); MOZ_RELEASE_ASSERT(MakeV().is<1>()); MOZ_RELEASE_ASSERT(!MakeV().is<0>()); static_assert(std::is_same_v()), 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()); MOZ_RELEASE_ASSERT(!MakeCV().is()); MOZ_RELEASE_ASSERT(MakeCV().as() == 1); MOZ_RELEASE_ASSERT(MakeCV().is<1>()); MOZ_RELEASE_ASSERT(!MakeCV().is<0>()); static_assert(std::is_same_v()), const uint64_t&&>, "MakeCV().as<1>() should return a const uint64_t&&"); MOZ_RELEASE_ASSERT(MakeCV().as<1>() == 1); } static void testDuplicate() { printf("testDuplicate\n"); Variant v(uint64_t(1)); MOZ_RELEASE_ASSERT(v.is()); MOZ_RELEASE_ASSERT(v.as() == 1); // Note: uint32_t is not unique, so `v.is()` is not allowed. MOZ_RELEASE_ASSERT(v.is<1>()); MOZ_RELEASE_ASSERT(!v.is<0>()); MOZ_RELEASE_ASSERT(!v.is<2>()); static_assert(std::is_same_v()), uint32_t&>, "as<0>() should return a uint64_t"); static_assert(std::is_same_v()), uint64_t&>, "as<1>() should return a uint64_t"); static_assert(std::is_same_v()), uint32_t&>, "as<2>() should return a uint64_t"); MOZ_RELEASE_ASSERT(v.as<1>() == 1); MOZ_RELEASE_ASSERT(v.extract<1>() == 1); } static void testConstructionWithVariantType() { Variant v(mozilla::VariantType{}, 3); MOZ_RELEASE_ASSERT(v.is()); // MOZ_RELEASE_ASSERT(!v.is()); // uint32_t is not unique! MOZ_RELEASE_ASSERT(v.as() == 3); } static void testConstructionWithVariantIndex() { Variant v(mozilla::VariantIndex<2>{}, 2); MOZ_RELEASE_ASSERT(!v.is()); // Note: uint32_t is not unique, so `v.is()` is not allowed. MOZ_RELEASE_ASSERT(!v.is<1>()); MOZ_RELEASE_ASSERT(!v.is<0>()); MOZ_RELEASE_ASSERT(v.is<2>()); MOZ_RELEASE_ASSERT(v.as<2>() == 2); MOZ_RELEASE_ASSERT(v.extract<2>() == 2); } static void testEmplaceWithType() { printf("testEmplaceWithType\n"); Variant v1(mozilla::VariantIndex<0>{}, 0); v1.emplace(3); MOZ_RELEASE_ASSERT(v1.is()); MOZ_RELEASE_ASSERT(v1.as() == 3); Variant, char> v2('a'); v2.emplace>(); MOZ_RELEASE_ASSERT(v2.is>()); MOZ_RELEASE_ASSERT(!v2.as>().get()); Variant, char> v3('a'); v3.emplace>(MakeUnique(4)); MOZ_RELEASE_ASSERT(v3.is>()); MOZ_RELEASE_ASSERT(*v3.as>().get() == 4); } static void testEmplaceWithIndex() { printf("testEmplaceWithIndex\n"); Variant v1(mozilla::VariantIndex<1>{}, 0); v1.emplace<2>(2); MOZ_RELEASE_ASSERT(!v1.is()); MOZ_RELEASE_ASSERT(!v1.is<1>()); MOZ_RELEASE_ASSERT(!v1.is<0>()); MOZ_RELEASE_ASSERT(v1.is<2>()); MOZ_RELEASE_ASSERT(v1.as<2>() == 2); MOZ_RELEASE_ASSERT(v1.extract<2>() == 2); Variant, char> v2('a'); v2.emplace<0>(); MOZ_RELEASE_ASSERT(v2.is>()); MOZ_RELEASE_ASSERT(!v2.is<1>()); MOZ_RELEASE_ASSERT(v2.is<0>()); MOZ_RELEASE_ASSERT(!v2.as<0>().get()); MOZ_RELEASE_ASSERT(!v2.extract<0>().get()); Variant, char> v3('a'); v3.emplace<0>(MakeUnique(4)); MOZ_RELEASE_ASSERT(v3.is>()); MOZ_RELEASE_ASSERT(!v3.is<1>()); MOZ_RELEASE_ASSERT(v3.is<0>()); MOZ_RELEASE_ASSERT(*v3.as<0>().get() == 4); MOZ_RELEASE_ASSERT(*v3.extract<0>().get() == 4); } static void testCopy() { printf("testCopy\n"); Variant v1(uint64_t(1)); Variant v2(v1); MOZ_RELEASE_ASSERT(v2.is()); MOZ_RELEASE_ASSERT(!v2.is()); MOZ_RELEASE_ASSERT(v2.as() == 1); Variant v3(uint32_t(10)); v3 = v2; MOZ_RELEASE_ASSERT(v3.is()); MOZ_RELEASE_ASSERT(v3.as() == 1); } static void testMove() { printf("testMove\n"); Variant, char> v1(MakeUnique(5)); Variant, char> v2(std::move(v1)); MOZ_RELEASE_ASSERT(v2.is>()); MOZ_RELEASE_ASSERT(*v2.as>() == 5); MOZ_RELEASE_ASSERT(v1.is>()); MOZ_RELEASE_ASSERT(v1.as>() == nullptr); Destroyer::destroyedCount = 0; { Variant> v3(MakeUnique()); Variant> v4(std::move(v3)); Variant> v5('a'); v5 = std::move(v4); auto ptr = v5.extract>(); MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 0); } MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 1); } static void testDestructor() { printf("testDestructor\n"); Destroyer::destroyedCount = 0; { Destroyer d; { Variant, Destroyer> v1(d); MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 0); // None destroyed yet. } MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 1); // v1's copy of d is destroyed. { Variant, Destroyer> v2( mozilla::VariantIndex<2>{}); v2.emplace(d); MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 2); // v2's initial value is destroyed. } MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 3); // v2's second value is destroyed. } MOZ_RELEASE_ASSERT(Destroyer::destroyedCount == 4); // d is destroyed. } static void testEquality() { printf("testEquality\n"); using V = Variant; V v0('a'); V v1('b'); V v2('b'); V v3(42); V v4(27); V v5(27); V v6(int('b')); MOZ_RELEASE_ASSERT(v0 != v1); MOZ_RELEASE_ASSERT(v1 == v2); MOZ_RELEASE_ASSERT(v2 != v3); MOZ_RELEASE_ASSERT(v3 != v4); MOZ_RELEASE_ASSERT(v4 == v5); MOZ_RELEASE_ASSERT(v1 != v6); MOZ_RELEASE_ASSERT(v0 == v0); MOZ_RELEASE_ASSERT(v1 == v1); MOZ_RELEASE_ASSERT(v2 == v2); MOZ_RELEASE_ASSERT(v3 == v3); MOZ_RELEASE_ASSERT(v4 == v4); MOZ_RELEASE_ASSERT(v5 == v5); MOZ_RELEASE_ASSERT(v6 == v6); } // Matcher that returns a description of how its call-operator was invoked. struct Describer { enum class ParameterSize { NA, U8, U32, U64 }; enum class ParameterQualifier { NA, ParamLREF, ParamCLREF, ParamRREF, ParamCRREF }; enum class ThisQualifier { NA, ThisLREF, ThisCLREF, ThisRREF, ThisCRREF }; using Result = std::tuple; #define RESULT(SIZE, PQUAL, TQUAL, VALUE) \ Describer::Result(Describer::ParameterSize::SIZE, \ Describer::ParameterQualifier::PQUAL, \ Describer::ThisQualifier::TQUAL, VALUE) #define CALL(TYPE, SIZE, PQUAL, TREF, TQUAL) \ Result operator()(TYPE aValue) TREF { \ return RESULT(SIZE, PQUAL, TQUAL, aValue); \ } // 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) 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 Result operator()(const Other&) { MOZ_RELEASE_ASSERT(false); return RESULT(NA, NA, NA, 0); } }; static void testMatching() { printf("testMatching\n"); using V = Variant; 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)); 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; }; // 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() { printf("testMatchingLambda\n"); using V = Variant; // Note: Lambdas' call operators are const by default (unless the lambda is // 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) { if constexpr (std::is_same_v) { return RESULT(U8, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U8, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U8, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U8, ParamCRREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U32, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U32, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U32, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U32, ParamCRREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U64, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U64, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U64, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { 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)); 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(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() { printf("testMatchingLambdaWithIndex\n"); using V = Variant; // Note: Lambdas' call operators are const by default (unless the lambda is // declared `mutable`), hence the use of "...Const" strings below. // 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) { static_assert( std::is_same_v, "Expected a uint_fast8_t index for a Variant with 3 alternatives"); if constexpr (std::is_same_v) { MOZ_RELEASE_ASSERT(aIndex == 0); return RESULT(U8, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { MOZ_RELEASE_ASSERT(aIndex == 0); return RESULT(U8, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { MOZ_RELEASE_ASSERT(aIndex == 0); return RESULT(U8, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { MOZ_RELEASE_ASSERT(aIndex == 0); return RESULT(U8, ParamCRREF, NA, a); } else if constexpr (std::is_same_v) { MOZ_RELEASE_ASSERT(aIndex == 1); return RESULT(U32, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { MOZ_RELEASE_ASSERT(aIndex == 1); return RESULT(U32, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { MOZ_RELEASE_ASSERT(aIndex == 1); return RESULT(U32, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { MOZ_RELEASE_ASSERT(aIndex == 1); return RESULT(U32, ParamCRREF, NA, a); } else if constexpr (std::is_same_v) { MOZ_RELEASE_ASSERT(aIndex == 2); return RESULT(U64, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { MOZ_RELEASE_ASSERT(aIndex == 2); return RESULT(U64, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { MOZ_RELEASE_ASSERT(aIndex == 2); return RESULT(U64, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { 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); } }; V v1(uint8_t(1)); V v2(uint32_t(2)); V v3(uint64_t(3)); 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(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; auto desc8 = [](auto&& a) { if constexpr (std::is_same_v) { return RESULT(U8, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U8, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U8, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { 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) { return RESULT(U32, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U32, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U32, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { 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) { return RESULT(U64, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U64, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U64, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { 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)); 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) == RESULT(U8, ParamCLREF, NA, 1)); MOZ_RELEASE_ASSERT(constRef2.match(desc8, desc32, desc64) == RESULT(U32, ParamCLREF, NA, 2)); MOZ_RELEASE_ASSERT(constRef3.match(desc8, desc32, desc64) == 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; auto desc8 = [](size_t aIndex, auto&& a) { MOZ_RELEASE_ASSERT(aIndex == 0); if constexpr (std::is_same_v) { return RESULT(U8, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U8, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U8, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { 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, auto&& a) { MOZ_RELEASE_ASSERT(aIndex == 1); if constexpr (std::is_same_v) { return RESULT(U32, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U32, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U32, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { 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, auto&& a) { MOZ_RELEASE_ASSERT(aIndex == 2); if constexpr (std::is_same_v) { return RESULT(U64, ParamLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U64, ParamCLREF, NA, a); } else if constexpr (std::is_same_v) { return RESULT(U64, ParamRREF, NA, a); } else if constexpr (std::is_same_v) { 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)); 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) == RESULT(U8, ParamCLREF, NA, 1)); MOZ_RELEASE_ASSERT(constRef2.match(desc8, desc32, desc64) == RESULT(U32, ParamCLREF, NA, 2)); MOZ_RELEASE_ASSERT(constRef3.match(desc8, desc32, desc64) == 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; // We don't know what our hash function is, and these are certainly not all // true under all hash functions. But they are probably true under almost any // decent hash function, and our aim is simply to establish that the tag // *does* influence the hash value. { mozilla::HashNumber h8 = V(uint8_t(1)).addTagToHash(0); mozilla::HashNumber h16 = V(uint16_t(1)).addTagToHash(0); mozilla::HashNumber h32 = V(uint32_t(1)).addTagToHash(0); mozilla::HashNumber h64 = V(uint64_t(1)).addTagToHash(0); MOZ_RELEASE_ASSERT(h8 != h16 && h8 != h32 && h8 != h64); MOZ_RELEASE_ASSERT(h16 != h32 && h16 != h64); MOZ_RELEASE_ASSERT(h32 != h64); } { mozilla::HashNumber h8 = V(uint8_t(1)).addTagToHash(0x124356); mozilla::HashNumber h16 = V(uint16_t(1)).addTagToHash(0x124356); mozilla::HashNumber h32 = V(uint32_t(1)).addTagToHash(0x124356); mozilla::HashNumber h64 = V(uint64_t(1)).addTagToHash(0x124356); MOZ_RELEASE_ASSERT(h8 != h16 && h8 != h32 && h8 != h64); MOZ_RELEASE_ASSERT(h16 != h32 && h16 != h64); MOZ_RELEASE_ASSERT(h32 != h64); } } int main() { testDetails(); testSimple(); testDuplicate(); testConstructionWithVariantType(); testConstructionWithVariantIndex(); testEmplaceWithType(); testEmplaceWithIndex(); testCopy(); testMove(); testDestructor(); testEquality(); testMatching(); testMatchingLambda(); testMatchingLambdaWithIndex(); testMatchingLambdas(); testMatchingLambdasWithIndex(); testAddTagToHash(); printf("TestVariant OK!\n"); return 0; }