diff --git a/mfbt/Variant.h b/mfbt/Variant.h index 947a57a6bbba..b74fe04e66c3 100644 --- a/mfbt/Variant.h +++ b/mfbt/Variant.h @@ -41,81 +41,42 @@ struct Nth using Type = typename Nth::Type; }; -template -struct FirstTypeIsInRest; - -template -struct FirstTypeIsInRest : FalseType {}; - -template -struct FirstTypeIsInRest -{ - static constexpr bool value = - IsSame::value || - FirstTypeIsInRest::value; -}; - -template -struct TypesAreDistinct; - -template <> -struct TypesAreDistinct<> : TrueType { }; - -template -struct TypesAreDistinct -{ - static constexpr bool value = - !FirstTypeIsInRest::value && - TypesAreDistinct::value; -}; - -// The `IsVariant` helper is used in conjunction with static_assert and -// `mozilla::EnableIf` to catch passing non-variant types to `Variant::is()` -// and friends at compile time, rather than at runtime. It ensures that the -// given type `Needle` is one of the types in the set of types `Haystack`. - -template -struct IsVariant; - -template -struct IsVariant : FalseType {}; - -template -struct IsVariant : TrueType {}; - -template -struct IsVariant : public IsVariant { }; - /// 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 @@ -127,6 +88,9 @@ struct 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 @@ -373,13 +337,43 @@ struct AsVariantTemporary * if (v.is()) { * A& ref = v.as(); * ... - * } else (v.is<1>()) { // Same as v.is in this case. + * } 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; + * bool IsResult() const { return m.is(); } + * bool IsError() const { return m.is(); } + * }; + * // Now instantiante with the result being an int too: + * ResultOrError myResult; // Fail! + * // In Variant, which 'int' are we refering to, from inside + * // ResultOrError functions? + * + * // Good! + * template + * struct ResultOrError + * { + * Variant m; + * 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; // 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. * @@ -466,9 +460,6 @@ struct AsVariantTemporary template class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS MOZ_NON_PARAM Variant { - static_assert(detail::TypesAreDistinct::value, - "Variant with duplicate types is not supported"); - using Tag = typename detail::VariantTag::Type; using Impl = detail::VariantImplementation; @@ -505,6 +496,8 @@ public: 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(Forward(aT)); } @@ -518,6 +511,8 @@ public: MOZ_IMPLICIT Variant(detail::AsVariantTemporary&& aValue) : 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(Move(aValue.mValue)); } @@ -555,6 +550,8 @@ public: 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(Move(aValue)); return *this; @@ -568,8 +565,8 @@ public: /** Check which variant type is currently contained. */ template bool is() const { - static_assert(detail::IsVariant::value, - "provided a type not found in this Variant's type list"); + static_assert(detail::SelectVariantType::count == 1, + "provided a type not uniquely found in this Variant's type list"); return Impl::template tag() == tag; } @@ -603,8 +600,8 @@ public: /** Mutable reference. */ template T& as() { - static_assert(detail::IsVariant::value, - "provided a type not found in this Variant's type list"); + 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()); } @@ -621,7 +618,7 @@ public: /** Immutable const reference. */ template const T& as() const { - static_assert(detail::IsVariant::value, + 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()); @@ -644,8 +641,8 @@ public: */ template T extract() { - static_assert(detail::IsVariant::value, - "provided a type not found in this Variant's type list"); + static_assert(detail::SelectVariantType::count == 1, + "provided a type not uniquely found in this Variant's type list"); MOZ_ASSERT(is()); return T(Move(as())); } diff --git a/mfbt/tests/TestVariant.cpp b/mfbt/tests/TestVariant.cpp index 3d9ee0c430f4..e3da3fd469f0 100644 --- a/mfbt/tests/TestVariant.cpp +++ b/mfbt/tests/TestVariant.cpp @@ -36,6 +36,28 @@ testSimple() MOZ_RELEASE_ASSERT(v.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(mozilla::IsSame()), uint32_t&>::value, + "as<0>() should return a uint64_t"); + static_assert(mozilla::IsSame()), uint64_t&>::value, + "as<1>() should return a uint64_t"); + static_assert(mozilla::IsSame()), uint32_t&>::value, + "as<2>() should return a uint64_t"); + MOZ_RELEASE_ASSERT(v.as<1>() == 1); + MOZ_RELEASE_ASSERT(v.extract<1>() == 1); +} + static void testCopy() { @@ -182,6 +204,7 @@ int main() { testSimple(); + testDuplicate(); testCopy(); testMove(); testDestructor();