diff --git a/llvm/include/llvm/ADT/PointerUnion.h b/llvm/include/llvm/ADT/PointerUnion.h --- a/llvm/include/llvm/ADT/PointerUnion.h +++ b/llvm/include/llvm/ADT/PointerUnion.h @@ -16,6 +16,7 @@ #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include #include @@ -35,21 +36,6 @@ return std::min({PointerLikeTypeTraits::NumLowBitsAvailable...}); } - /// Find the index of a type in a list of types. TypeIndex::Index - /// is the index of T in Us, or sizeof...(Us) if T does not appear in the - /// list. - template struct TypeIndex; - template struct TypeIndex { - static constexpr int Index = 0; - }; - template - struct TypeIndex { - static constexpr int Index = 1 + TypeIndex::Index; - }; - template struct TypeIndex { - static constexpr int Index = 0; - }; - /// Find the first type in a list of types. template struct GetFirstType { using type = T; @@ -116,6 +102,7 @@ /// P = (float*)0; /// Y = P.get(); // ok. /// X = P.get(); // runtime assertion failure. +/// PointerUnion Q; // compile time failure. template class PointerUnion : public pointer_union_detail::PointerUnionMembers< @@ -124,12 +111,14 @@ void *, pointer_union_detail::bitsRequired(sizeof...(PTs)), int, pointer_union_detail::PointerUnionUIntTraits>, 0, PTs...> { + static_assert(TypesAreDistinct::value, + "PointerUnion alternative types cannot be repeated"); // The first type is special because we want to directly cast a pointer to a // default-initialized union to a pointer to the first type. But we don't // want PointerUnion to be a 'template ' // because it's much more convenient to have a name for the whole pack. So // split off the first type here. - using First = typename pointer_union_detail::GetFirstType::type; + using First = TypeAtIndex<0, PTs...>; using Base = typename PointerUnion::PointerUnionMembers; public: @@ -146,10 +135,7 @@ /// Test if the Union currently holds the type matching T. template bool is() const { - constexpr int Index = pointer_union_detail::TypeIndex::Index; - static_assert(Index < sizeof...(PTs), - "PointerUnion::is given type not in the union"); - return this->Val.getInt() == Index; + return this->Val.getInt() == FirstIndexOfType::value; } /// Returns the value of the specified pointer type. diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h --- a/llvm/include/llvm/ADT/STLExtras.h +++ b/llvm/include/llvm/ADT/STLExtras.h @@ -144,6 +144,61 @@ struct function_traits : public function_traits {}; +/// traits class for checking whether type T is one of any of the given +/// types in the variadic list. +template +using is_one_of = disjunction...>; + +/// traits class for checking whether type T is a base class for all +/// the given types in the variadic list. +template +using are_base_of = conjunction...>; + +namespace detail { +template struct TypesAreDistinct; +template +struct TypesAreDistinct + : std::integral_constant::value && + TypesAreDistinct::value> {}; +template struct TypesAreDistinct : std::true_type {}; +} // namespace detail + +/// Determine if all types in Ts are distinct. +/// +/// Useful to statically assert when Ts is intended to describe a non-multi set +/// of types. +/// +/// Expensive (currently quadratic in sizeof(Ts...)), and so should only be +/// asserted once per instantiation of a type which requires it. +template struct TypesAreDistinct; +template <> struct TypesAreDistinct<> : std::true_type {}; +template +struct TypesAreDistinct + : std::integral_constant::value> {}; + +/// Find the first index where a type appears in a list of types. +/// +/// FirstIndexOfType::value is the first index of T in Us. +/// +/// Typically only meaningful when it is otherwise statically known that the +/// type pack has no duplicate types. This should be guaranteed explicitly with +/// static_assert(TypesAreDistinct::value). +/// +/// It is a compile-time error to instantiate when T is not present in Us, i.e. +/// if is_one_of::value is false. +template struct FirstIndexOfType; +template +struct FirstIndexOfType + : std::integral_constant::value> {}; +template +struct FirstIndexOfType : std::integral_constant {}; + +/// Find the type at a given index in a list of types. +/// +/// TypeAtIndex is the type at index I in Ts. +template +using TypeAtIndex = std::tuple_element_t>; + //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// diff --git a/llvm/unittests/ADT/STLExtrasTest.cpp b/llvm/unittests/ADT/STLExtrasTest.cpp --- a/llvm/unittests/ADT/STLExtrasTest.cpp +++ b/llvm/unittests/ADT/STLExtrasTest.cpp @@ -905,4 +905,39 @@ all_of_zip(v1, v2, v_short, [](int, int, int) { return true; })); } +TEST(STLExtrasTest, TypesAreDistinct) { + EXPECT_TRUE((llvm::TypesAreDistinct<>::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_FALSE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_FALSE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_FALSE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); + EXPECT_TRUE((llvm::TypesAreDistinct::value)); +} + +TEST(STLExtrasTest, FirstIndexOfType) { + EXPECT_EQ((llvm::FirstIndexOfType::value), 0u); + EXPECT_EQ((llvm::FirstIndexOfType::value), 0u); + EXPECT_EQ((llvm::FirstIndexOfType::value), 1u); + EXPECT_EQ((llvm::FirstIndexOfType::value), + 2u); +} + +TEST(STLExtrasTest, TypeAtIndex) { + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>::value)); +} + } // namespace