[llvm] 36c812f - [ADT] Make enum iterators constexpr (#210405)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Jul 18 06:22:26 PDT 2026
Author: Krzysztof Parzyszek
Date: 2026-07-18T08:22:17-05:00
New Revision: 36c812f77142aa77b99b4e4d059ccba591a944c0
URL: https://github.com/llvm/llvm-project/commit/36c812f77142aa77b99b4e4d059ccba591a944c0
DIFF: https://github.com/llvm/llvm-project/commit/36c812f77142aa77b99b4e4d059ccba591a944c0.diff
LOG: [ADT] Make enum iterators constexpr (#210405)
<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Added:
Modified:
llvm/include/llvm/ADT/Sequence.h
llvm/unittests/ADT/SequenceTest.cpp
Removed:
################################################################################
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
diff erence_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);
}
diff --git a/llvm/unittests/ADT/SequenceTest.cpp b/llvm/unittests/ADT/SequenceTest.cpp
index ab50ad0bb5606..ac35798e90bd7 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
More information about the llvm-commits
mailing list