https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/210405
>From 8b931740b452c8bb438d3ba761259f4cf6881016 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <[email protected]> Date: Fri, 17 Jul 2026 11:01:43 -0500 Subject: [PATCH 1/2] [ADT] Make enum iterators constexpr --- llvm/include/llvm/ADT/Sequence.h | 142 +++++++++++++++++++------------ 1 file changed, 87 insertions(+), 55 deletions(-) diff --git a/llvm/include/llvm/ADT/Sequence.h b/llvm/include/llvm/ADT/Sequence.h index ae446df345ee0..b79c794526a8e 100644 --- a/llvm/include/llvm/ADT/Sequence.h +++ b/llvm/include/llvm/ADT/Sequence.h @@ -102,7 +102,7 @@ template <typename EnumT> struct enum_iteration_traits { }; struct force_iteration_on_noniterable_enum_t { - explicit force_iteration_on_noniterable_enum_t() = default; + explicit constexpr force_iteration_on_noniterable_enum_t() = default; }; inline constexpr force_iteration_on_noniterable_enum_t @@ -111,7 +111,8 @@ inline constexpr force_iteration_on_noniterable_enum_t namespace detail { // Returns whether a value of type U can be represented with type T. -template <typename T, typename U> bool canTypeFitValue(const U Value) { +template <typename T, typename U> +constexpr bool canTypeFitValue(const U Value) { const intmax_t BotT = intmax_t(std::numeric_limits<T>::min()); const intmax_t BotU = intmax_t(std::numeric_limits<U>::min()); const uintmax_t TopT = uintmax_t(std::numeric_limits<T>::max()); @@ -128,7 +129,7 @@ struct CheckedInt { // Integral constructor, asserts if Value cannot be represented as intmax_t. template <typename Integral, std::enable_if_t<std::is_integral<Integral>::value, bool> = 0> - static CheckedInt from(Integral FromValue) { + static constexpr CheckedInt from(Integral FromValue) { if (!canTypeFitValue<intmax_t>(FromValue)) assertOutOfBounds(); CheckedInt Result; @@ -139,24 +140,28 @@ struct CheckedInt { // Enum constructor, asserts if Value cannot be represented as intmax_t. template <typename Enum, std::enable_if_t<std::is_enum<Enum>::value, bool> = 0> - static CheckedInt from(Enum FromValue) { + static constexpr CheckedInt from(Enum FromValue) { return from(llvm::to_underlying(FromValue)); } // Equality - bool operator==(const CheckedInt &O) const { return Value == O.Value; } - bool operator!=(const CheckedInt &O) const { return Value != O.Value; } + constexpr bool operator==(const CheckedInt &O) const { + return Value == O.Value; + } + constexpr bool operator!=(const CheckedInt &O) const { + return Value != O.Value; + } - CheckedInt operator+(intmax_t Offset) const { - CheckedInt Result; - if (AddOverflow(Value, Offset, Result.Value)) + constexpr CheckedInt operator+(intmax_t Offset) const { + auto [Result, Overflow] = AddOverflow(Value, Offset); + if (Overflow) assertOutOfBounds(); - return Result; + return CheckedInt::from(Result); } - intmax_t operator-(CheckedInt Other) const { - intmax_t Result; - if (SubOverflow(Value, Other.Value, Result)) + constexpr intmax_t operator-(CheckedInt Other) const { + auto [Result, Overflow] = SubOverflow(Value, Other.Value); + if (Overflow) assertOutOfBounds(); return Result; } @@ -164,7 +169,7 @@ struct CheckedInt { // Convert to integral, asserts if Value cannot be represented as Integral. template <typename Integral, std::enable_if_t<std::is_integral<Integral>::value, bool> = 0> - Integral to() const { + constexpr Integral to() const { if (!canTypeFitValue<Integral>(Value)) assertOutOfBounds(); return static_cast<Integral>(Value); @@ -174,15 +179,17 @@ struct CheckedInt { // underlying type. template <typename Enum, std::enable_if_t<std::is_enum<Enum>::value, bool> = 0> - Enum to() const { + constexpr Enum to() const { using type = std::underlying_type_t<Enum>; return Enum(to<type>()); } private: - static void assertOutOfBounds() { assert(false && "Out of bounds"); } + [[noreturn]] static void assertOutOfBounds() { + assert(false && "Out of bounds"); + } - intmax_t Value; + intmax_t Value = 0; }; template <typename T, bool IsReverse> struct SafeIntIterator { @@ -193,63 +200,85 @@ template <typename T, bool IsReverse> struct SafeIntIterator { using reference = value_type; // The iterator does not reference memory. // Construct from T. - explicit SafeIntIterator(T Value) : SI(CheckedInt::from<T>(Value)) {} + explicit constexpr SafeIntIterator(T Value) + : SI(CheckedInt::from<T>(Value)) {} // Construct from other direction. - SafeIntIterator(const SafeIntIterator<T, !IsReverse> &O) : SI(O.SI) {} + constexpr SafeIntIterator(const SafeIntIterator<T, !IsReverse> &O) + : SI(O.SI) {} // Dereference - reference operator*() const { return SI.to<T>(); } + constexpr reference operator*() const { return SI.to<T>(); } // Indexing - reference operator[](intmax_t Offset) const { return *(*this + Offset); } + constexpr reference operator[](intmax_t Offset) const { + return *(*this + Offset); + } // Can be compared for equivalence using the equality/inequality operators. - bool operator==(const SafeIntIterator &O) const { return SI == O.SI; } - bool operator!=(const SafeIntIterator &O) const { return SI != O.SI; } + constexpr bool operator==(const SafeIntIterator &O) const { + return SI == O.SI; + } + constexpr bool operator!=(const SafeIntIterator &O) const { + return SI != O.SI; + } // Comparison - bool operator<(const SafeIntIterator &O) const { return (*this - O) < 0; } - bool operator>(const SafeIntIterator &O) const { return (*this - O) > 0; } - bool operator<=(const SafeIntIterator &O) const { return (*this - O) <= 0; } - bool operator>=(const SafeIntIterator &O) const { return (*this - O) >= 0; } + constexpr bool operator<(const SafeIntIterator &O) const { + return (*this - O) < 0; + } + constexpr bool operator>(const SafeIntIterator &O) const { + return (*this - O) > 0; + } + constexpr bool operator<=(const SafeIntIterator &O) const { + return (*this - O) <= 0; + } + constexpr bool operator>=(const SafeIntIterator &O) const { + return (*this - O) >= 0; + } // Pre Increment/Decrement - void operator++() { offset(1); } - void operator--() { offset(-1); } + constexpr void operator++() { offset(1); } + constexpr void operator--() { offset(-1); } // Post Increment/Decrement - SafeIntIterator operator++(int) { + constexpr SafeIntIterator operator++(int) { const auto Copy = *this; ++*this; return Copy; } - SafeIntIterator operator--(int) { + constexpr SafeIntIterator operator--(int) { const auto Copy = *this; --*this; return Copy; } // Compound assignment operators - void operator+=(intmax_t Offset) { offset(Offset); } - void operator-=(intmax_t Offset) { offset(-Offset); } + constexpr void operator+=(intmax_t Offset) { offset(Offset); } + constexpr void operator-=(intmax_t Offset) { offset(-Offset); } // Arithmetic - SafeIntIterator operator+(intmax_t Offset) const { return add(Offset); } - SafeIntIterator operator-(intmax_t Offset) const { return add(-Offset); } + constexpr SafeIntIterator operator+(intmax_t Offset) const { + return add(Offset); + } + constexpr SafeIntIterator operator-(intmax_t Offset) const { + return add(-Offset); + } // Difference - intmax_t operator-(const SafeIntIterator &O) const { + constexpr intmax_t operator-(const SafeIntIterator &O) const { return IsReverse ? O.SI - SI : SI - O.SI; } private: - SafeIntIterator(const CheckedInt &SI) : SI(SI) {} + constexpr SafeIntIterator(const CheckedInt &SI) : SI(SI) {} - static intmax_t getOffset(intmax_t Offset) { + static constexpr intmax_t getOffset(intmax_t Offset) { return IsReverse ? -Offset : Offset; } - CheckedInt add(intmax_t Offset) const { return SI + getOffset(Offset); } + constexpr CheckedInt add(intmax_t Offset) const { + return SI + getOffset(Offset); + } - void offset(intmax_t Offset) { SI = SI + getOffset(Offset); } + constexpr void offset(intmax_t Offset) { SI = SI + getOffset(Offset); } CheckedInt SI; @@ -270,21 +299,23 @@ template <typename T> struct iota_range { using difference_type = intmax_t; using size_type = std::size_t; - explicit iota_range(T Begin, T End, bool Inclusive) + explicit constexpr iota_range(T Begin, T End, bool Inclusive) : BeginValue(Begin), PastEndValue(End) { assert(Begin <= End && "Begin must be less or equal to End."); if (Inclusive) ++PastEndValue; } - size_t size() const { return PastEndValue - BeginValue; } - bool empty() const { return BeginValue == PastEndValue; } + constexpr size_t size() const { return PastEndValue - BeginValue; } + constexpr bool empty() const { return BeginValue == PastEndValue; } - auto begin() const { return const_iterator(BeginValue); } - auto end() const { return const_iterator(PastEndValue); } + constexpr auto begin() const { return const_iterator(BeginValue); } + constexpr auto end() const { return const_iterator(PastEndValue); } - auto rbegin() const { return const_reverse_iterator(PastEndValue - 1); } - auto rend() const { return const_reverse_iterator(BeginValue - 1); } + constexpr auto rbegin() const { + return const_reverse_iterator(PastEndValue - 1); + } + constexpr auto rend() const { return const_reverse_iterator(BeginValue - 1); } private: static_assert(std::is_integral<T>::value || std::is_enum<T>::value, @@ -302,7 +333,7 @@ template <typename T> struct iota_range { /// iteration). template <typename T, typename = std::enable_if_t<std::is_integral<T>::value && !std::is_enum<T>::value>> -auto seq(T Begin, T End) { +constexpr auto seq(T Begin, T End) { return iota_range<T>(Begin, End, false); } @@ -312,7 +343,7 @@ auto seq(T Begin, T End) { /// iteration). template <typename T, typename = std::enable_if_t<std::is_integral<T>::value && !std::is_enum<T>::value>> -auto seq(T Size) { +constexpr auto seq(T Size) { return seq<T>(0, Size); } @@ -322,7 +353,7 @@ auto seq(T Size) { /// iteration). template <typename T, typename = std::enable_if_t<std::is_integral<T>::value && !std::is_enum<T>::value>> -auto seq_inclusive(T Begin, T End) { +constexpr auto seq_inclusive(T Begin, T End) { return iota_range<T>(Begin, End, true); } @@ -334,7 +365,7 @@ auto seq_inclusive(T Begin, T End) { /// iteration). template <typename EnumT, typename = std::enable_if_t<std::is_enum<EnumT>::value>> -auto enum_seq(EnumT Begin, EnumT End) { +constexpr auto enum_seq(EnumT Begin, EnumT End) { static_assert(enum_iteration_traits<EnumT>::is_iterable, "Enum type is not marked as iterable."); return iota_range<EnumT>(Begin, End, false); @@ -349,7 +380,8 @@ auto enum_seq(EnumT Begin, EnumT End) { /// iteration). template <typename EnumT, typename = std::enable_if_t<std::is_enum<EnumT>::value>> -auto enum_seq(EnumT Begin, EnumT End, force_iteration_on_noniterable_enum_t) { +constexpr auto enum_seq(EnumT Begin, EnumT End, + force_iteration_on_noniterable_enum_t) { return iota_range<EnumT>(Begin, End, false); } @@ -361,7 +393,7 @@ auto enum_seq(EnumT Begin, EnumT End, force_iteration_on_noniterable_enum_t) { /// iteration). template <typename EnumT, typename = std::enable_if_t<std::is_enum<EnumT>::value>> -auto enum_seq_inclusive(EnumT Begin, EnumT End) { +constexpr auto enum_seq_inclusive(EnumT Begin, EnumT End) { static_assert(enum_iteration_traits<EnumT>::is_iterable, "Enum type is not marked as iterable."); return iota_range<EnumT>(Begin, End, true); @@ -376,8 +408,8 @@ auto enum_seq_inclusive(EnumT Begin, EnumT End) { /// iteration). template <typename EnumT, typename = std::enable_if_t<std::is_enum<EnumT>::value>> -auto enum_seq_inclusive(EnumT Begin, EnumT End, - force_iteration_on_noniterable_enum_t) { +constexpr auto enum_seq_inclusive(EnumT Begin, EnumT End, + force_iteration_on_noniterable_enum_t) { return iota_range<EnumT>(Begin, End, true); } >From bb45367afd2a2e08317c5e18523dfe9c30afa90c Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek <[email protected]> Date: Fri, 17 Jul 2026 17:55:41 -0500 Subject: [PATCH 2/2] Add constexpr checks --- llvm/unittests/ADT/SequenceTest.cpp | 171 +++++++++++++++++++++++----- 1 file changed, 141 insertions(+), 30 deletions(-) diff --git a/llvm/unittests/ADT/SequenceTest.cpp b/llvm/unittests/ADT/SequenceTest.cpp index ab50ad0bb5606..28ad26ee46379 100644 --- a/llvm/unittests/ADT/SequenceTest.cpp +++ b/llvm/unittests/ADT/SequenceTest.cpp @@ -25,6 +25,17 @@ namespace { using detail::canTypeFitValue; using detail::CheckedInt; +namespace elem { +template <typename Elem, Elem... Values, typename Range> +constexpr bool ElementsAre(Range &&R) { + if (std::distance(R.begin(), R.end()) != sizeof...(Values)) + return false; + + int Idx = 0; + return ((*(R.begin() + (Idx++)) == Values) && ...); +} +} // namespace elem + using IntegralTypes = testing::Types<uint8_t, // 0 uint16_t, // 1 uint32_t, // 2 @@ -41,34 +52,50 @@ template <class T> class StrongIntTest : public testing::Test {}; TYPED_TEST_SUITE(StrongIntTest, IntegralTypes, ); TYPED_TEST(StrongIntTest, Operations) { using T = TypeParam; - auto Max = std::numeric_limits<T>::max(); - auto Min = std::numeric_limits<T>::min(); + constexpr auto Max = std::numeric_limits<T>::max(); + constexpr auto Min = std::numeric_limits<T>::min(); // We bail out for types that are not entirely representable within intmax_t. - if (!canTypeFitValue<intmax_t>(Max) || !canTypeFitValue<intmax_t>(Min)) - return; - - // All representable values convert back and forth. - EXPECT_EQ(CheckedInt::from(Min).template to<T>(), Min); - EXPECT_EQ(CheckedInt::from(Max).template to<T>(), Max); - - // Addition -2, -1, 0, 1, 2. - const T Expected = Max / 2; - const CheckedInt Actual = CheckedInt::from(Expected); - EXPECT_EQ((Actual + -2).template to<T>(), Expected - 2); - EXPECT_EQ((Actual + -1).template to<T>(), Expected - 1); - EXPECT_EQ((Actual + 0).template to<T>(), Expected); - EXPECT_EQ((Actual + 1).template to<T>(), Expected + 1); - EXPECT_EQ((Actual + 2).template to<T>(), Expected + 2); - - // EQ/NEQ - EXPECT_EQ(Actual, Actual); - EXPECT_NE(Actual, Actual + 1); - - // Difference - EXPECT_EQ(Actual - Actual, 0); - EXPECT_EQ((Actual + 1) - Actual, 1); - EXPECT_EQ(Actual - (Actual + 2), -2); + if constexpr (canTypeFitValue<intmax_t>(Max) && + canTypeFitValue<intmax_t>(Min)) { + // All representable values convert back and forth. + EXPECT_EQ(CheckedInt::from(Min).template to<T>(), Min); + EXPECT_EQ(CheckedInt::from(Max).template to<T>(), Max); + + static_assert(CheckedInt::from(Min).template to<T>() == Min); + static_assert(CheckedInt::from(Max).template to<T>() == Max); + + // Addition -2, -1, 0, 1, 2. + constexpr T Expected = Max / 2; + constexpr CheckedInt Actual = CheckedInt::from(Expected); + EXPECT_EQ((Actual + -2).template to<T>(), Expected - 2); + EXPECT_EQ((Actual + -1).template to<T>(), Expected - 1); + EXPECT_EQ((Actual + 0).template to<T>(), Expected); + EXPECT_EQ((Actual + 1).template to<T>(), Expected + 1); + EXPECT_EQ((Actual + 2).template to<T>(), Expected + 2); + + static_assert((Actual + -2).template to<T>() == Expected - 2); + static_assert((Actual + -1).template to<T>() == Expected - 1); + static_assert((Actual + 0).template to<T>() == Expected); + static_assert((Actual + 1).template to<T>() == Expected + 1); + static_assert((Actual + 2).template to<T>() == Expected + 2); + + // EQ/NEQ + EXPECT_EQ(Actual, Actual); + EXPECT_NE(Actual, Actual + 1); + + static_assert(Actual == Actual); + static_assert(Actual != Actual + 1); + + // Difference + EXPECT_EQ(Actual - Actual, 0); + EXPECT_EQ((Actual + 1) - Actual, 1); + EXPECT_EQ(Actual - (Actual + 2), -2); + + static_assert(Actual - Actual == 0); + static_assert((Actual + 1) - Actual == 1); + static_assert(Actual - (Actual + 2) == -2); + } } #if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) @@ -88,6 +115,9 @@ TEST(StrongIntDeathTest, OutOfBounds) { #endif TEST(SafeIntIteratorTest, Operations) { + constexpr detail::SafeIntIterator<int, false> CForward(0); + constexpr detail::SafeIntIterator<int, true> CReverse(0); + detail::SafeIntIterator<int, false> Forward(0); detail::SafeIntIterator<int, true> Reverse(0); @@ -114,16 +144,38 @@ TEST(SafeIntIteratorTest, Operations) { EXPECT_GE(Reverse, Reverse); EXPECT_GE(Reverse + 1, Reverse); + static_assert(CForward == CForward); + static_assert(CForward - 1 < CForward); + static_assert(CForward <= CForward); + static_assert(CForward - 1 <= CForward); + static_assert(CForward + 1 > CForward); + static_assert(CForward >= CForward); + static_assert(CForward + 1 >= CForward); + + static_assert(CReverse == CReverse); + static_assert(CReverse - 1 < CReverse); + static_assert(CReverse <= CReverse); + static_assert(CReverse - 1 <= CReverse); + static_assert(CReverse + 1 > CReverse); + static_assert(CReverse >= CReverse); + static_assert(CReverse + 1 >= CReverse); + // Dereference SetToZero(); EXPECT_EQ(*Forward, 0); EXPECT_EQ(*Reverse, 0); + static_assert(*CForward == 0); + static_assert(*CReverse == 0); + // Indexing SetToZero(); EXPECT_EQ(Forward[2], 2); EXPECT_EQ(Reverse[2], -2); + static_assert(CForward[2] == 2); + static_assert(CReverse[2] == -2); + // Pre-increment SetToZero(); ++Forward; @@ -168,9 +220,13 @@ TEST(SafeIntIteratorTest, Operations) { SetToZero(); EXPECT_EQ(*(Forward + 3), 3); EXPECT_EQ(*(Reverse + 3), -3); + static_assert(*(CForward + 3) == 3); + static_assert(*(CReverse + 3) == -3); SetToZero(); EXPECT_EQ(*(Forward - 4), -4); EXPECT_EQ(*(Reverse - 4), 4); + static_assert(*(CForward - 4) == -4); + static_assert(*(CReverse - 4) == 4); // Difference SetToZero(); @@ -180,33 +236,53 @@ TEST(SafeIntIteratorTest, Operations) { EXPECT_EQ(Forward - (Forward + 1), -1); EXPECT_EQ((Reverse + 1) - Reverse, 1); EXPECT_EQ(Reverse - (Reverse + 1), -1); + + static_assert(CForward - CForward == 0); + static_assert(CReverse - CReverse == 0); + static_assert((CForward + 1) - CForward == 1); + static_assert(CForward - (CForward + 1) == -1); + static_assert((CReverse + 1) - CReverse == 1); + static_assert(CReverse - (CReverse + 1) == -1); } TEST(SequenceTest, Iteration) { EXPECT_THAT(seq(5), ElementsAre(0, 1, 2, 3, 4)); + static_assert(elem::ElementsAre<int, 0, 1, 2, 3, 4>(seq(5))); EXPECT_THAT(seq(-4, 5), ElementsAre(-4, -3, -2, -1, 0, 1, 2, 3, 4)); + static_assert( + elem::ElementsAre<int, -4, -3, -2, -1, 0, 1, 2, 3, 4>(seq(-4, 5))); EXPECT_THAT(reverse(seq(-4, 5)), ElementsAre(4, 3, 2, 1, 0, -1, -2, -3, -4)); EXPECT_THAT(seq_inclusive(-4, 5), ElementsAre(-4, -3, -2, -1, 0, 1, 2, 3, 4, 5)); + static_assert(elem::ElementsAre<int, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5>( + seq_inclusive(-4, 5))); EXPECT_THAT(reverse(seq_inclusive(-4, 5)), ElementsAre(5, 4, 3, 2, 1, 0, -1, -2, -3, -4)); + // reverse is not constexpr } TEST(SequenceTest, Distance) { - const auto Forward = seq(0, 10); + constexpr auto Forward = seq(0, 10); EXPECT_EQ(std::distance(Forward.begin(), Forward.end()), 10); EXPECT_EQ(std::distance(Forward.rbegin(), Forward.rend()), 10); + static_assert(std::distance(Forward.begin(), Forward.end()) == 10); + static_assert(std::distance(Forward.rbegin(), Forward.rend()) == 10); } TEST(SequenceTest, Dereference) { - const auto Forward = seq(0, 10).begin(); + constexpr auto Forward = seq(0, 10).begin(); EXPECT_EQ(Forward[0], 0); EXPECT_EQ(Forward[2], 2); - const auto Backward = seq(0, 10).rbegin(); + static_assert(Forward[0] == 0); + static_assert(Forward[2] == 2); + + constexpr auto Backward = seq(0, 10).rbegin(); EXPECT_EQ(Backward[0], 9); EXPECT_EQ(Backward[2], 7); + static_assert(Backward[0] == 9); + static_assert(Backward[2] == 7); } enum UntypedEnum { A = 3 }; @@ -225,7 +301,7 @@ struct S { friend struct llvm::enum_iteration_traits<NestedEnum3>; public: - static auto getNestedEnum3() { return NestedEnum3::F; } + static constexpr auto getNestedEnum3() { return NestedEnum3::F; } }; } // namespace @@ -261,42 +337,77 @@ TEST(StrongIntTest, Enums) { EXPECT_EQ(CheckedInt::from(B).to<TypedEnum>(), B); EXPECT_EQ(CheckedInt::from(X::ScopedEnum::C).to<X::ScopedEnum>(), X::ScopedEnum::C); + + static_assert(CheckedInt::from(A).to<UntypedEnum>() == A); + static_assert(CheckedInt::from(B).to<TypedEnum>() == B); + static_assert(CheckedInt::from(X::ScopedEnum::C).to<X::ScopedEnum>() == + X::ScopedEnum::C); } TEST(SequenceTest, IterableEnums) { EXPECT_THAT(enum_seq(UntypedEnum::A, UntypedEnum::A), IsEmpty()); + static_assert( + elem::ElementsAre<UntypedEnum>(enum_seq(UntypedEnum::A, UntypedEnum::A))); + EXPECT_THAT(enum_seq_inclusive(UntypedEnum::A, UntypedEnum::A), ElementsAre(UntypedEnum::A)); + static_assert(elem::ElementsAre<UntypedEnum, UntypedEnum::A>( + enum_seq_inclusive(UntypedEnum::A, UntypedEnum::A))); EXPECT_THAT(enum_seq(TypedEnum::B, TypedEnum::B), IsEmpty()); + static_assert( + elem::ElementsAre<TypedEnum>(enum_seq(TypedEnum::B, TypedEnum::B))); EXPECT_THAT(enum_seq_inclusive(TypedEnum::B, TypedEnum::B), ElementsAre(TypedEnum::B)); + static_assert(elem::ElementsAre<TypedEnum, TypedEnum::B>( + enum_seq_inclusive(TypedEnum::B, TypedEnum::B))); EXPECT_THAT(enum_seq(X::ScopedEnum::C, X::ScopedEnum::C), IsEmpty()); + static_assert( + elem::ElementsAre<X::ScopedEnum>(enum_seq(X::ScopedEnum::C, X::ScopedEnum::C))); EXPECT_THAT(enum_seq_inclusive(X::ScopedEnum::C, X::ScopedEnum::C), ElementsAre(X::ScopedEnum::C)); + static_assert(elem::ElementsAre<X::ScopedEnum, X::ScopedEnum::C>( + enum_seq_inclusive(X::ScopedEnum::C, X::ScopedEnum::C))); EXPECT_THAT(enum_seq_inclusive(S::NestedEnum::D, S::NestedEnum::D), ElementsAre(S::NestedEnum::D)); + static_assert(elem::ElementsAre<S::NestedEnum, S::NestedEnum::D>( + enum_seq_inclusive(S::NestedEnum::D, S::NestedEnum::D))); EXPECT_THAT(enum_seq_inclusive(S::getNestedEnum3(), S::getNestedEnum3()), ElementsAre(S::getNestedEnum3())); + static_assert( + elem::ElementsAre<decltype(S::getNestedEnum3()), S::getNestedEnum3()>( + enum_seq_inclusive(S::getNestedEnum3(), S::getNestedEnum3()))); } TEST(SequenceTest, NonIterableEnums) { EXPECT_THAT(enum_seq(S::NestedEnum2::E, S::NestedEnum2::E, force_iteration_on_noniterable_enum), IsEmpty()); + static_assert(elem::ElementsAre<S::NestedEnum2>( + enum_seq(S::NestedEnum2::E, S::NestedEnum2::E, + force_iteration_on_noniterable_enum))); + EXPECT_THAT(enum_seq_inclusive(S::NestedEnum2::E, S::NestedEnum2::E, force_iteration_on_noniterable_enum), ElementsAre(S::NestedEnum2::E)); + static_assert(elem::ElementsAre<S::NestedEnum2, S::NestedEnum2::E>( + enum_seq_inclusive(S::NestedEnum2::E, S::NestedEnum2::E, + force_iteration_on_noniterable_enum))); // Check that this also works with enums marked as iterable. EXPECT_THAT(enum_seq(UntypedEnum::A, UntypedEnum::A, force_iteration_on_noniterable_enum), IsEmpty()); + static_assert(elem::ElementsAre<UntypedEnum>(enum_seq( + UntypedEnum::A, UntypedEnum::A, force_iteration_on_noniterable_enum))); EXPECT_THAT(enum_seq_inclusive(UntypedEnum::A, UntypedEnum::A, force_iteration_on_noniterable_enum), ElementsAre(UntypedEnum::A)); + static_assert(elem::ElementsAre<UntypedEnum, UntypedEnum::A>( + enum_seq_inclusive(UntypedEnum::A, UntypedEnum::A, + force_iteration_on_noniterable_enum))); } // Reproducer for https://github.com/llvm/llvm-project/issues/61122 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
