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 @@ -64,21 +64,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; @@ -145,6 +130,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< @@ -153,12 +139,14 @@ void *, pointer_union_detail::bitsRequired(sizeof...(PTs)), int, pointer_union_detail::PointerUnionUIntTraits>, 0, PTs...> { + static_assert(TypesAreDistinct(), + "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: @@ -175,10 +163,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{}; } /// 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,60 @@ struct function_traits : public function_traits {}; +namespace detail { +template +constexpr size_t getFirstIndexOfTypeImpl() { + constexpr bool Matches[] = {false, std::is_same{}...}; + // TODO: use llvm::find once it's constexpr (std::find is constexpr in C++20) + for (size_t I = 1; I <= sizeof...(Ts); ++I) + if (Matches[I]) + return I - 1; + return sizeof...(Ts); +} +template constexpr size_t getFirstIndexOfType() { + constexpr size_t Index = getFirstIndexOfTypeImpl(); + static_assert(Index != sizeof...(Ts), + "type T is not present in type pack Ts"); + return Index; +} +template static constexpr bool areTypesDistinct() { + constexpr size_t IndexOf[] = {0, getFirstIndexOfType()...}; + // TODO: use llvm::find once it's constexpr (std::find is constexpr in C++20) + for (size_t I = 1; I <= sizeof...(Ts); ++I) + if (I - 1 != IndexOf[I]) + return false; + return true; +} +} // 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. +template +using TypesAreDistinct = + std::integral_constant()>; + +/// Find the first index where a type appears in a list of types. +/// +/// FirstIndexOfType::value is the first index of T in Ts. +/// +/// 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{}). +/// +/// It is a compile-time error to instantiate when T is not present in Ts, i.e. +/// if is_one_of::value is false. +template +using 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 @@ -711,4 +711,40 @@ EXPECT_EQ(V4.size(), 4U); EXPECT_TRUE(llvm::all_of(V4, HasVal)); } + +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