| //===- Sequence.h - Utility for producing sequences of values ---*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// \file |
| /// Provides some synthesis utilities to produce sequences of values. The names |
| /// are intentionally kept very short as they tend to occur in common and |
| /// widely used contexts. |
| /// |
| /// The `seq(A, B)` function produces a sequence of values from `A` to up to |
| /// (but not including) `B`, i.e., [`A`, `B`), that can be safely iterated over. |
| /// `seq` supports both integral (e.g., `int`, `char`, `uint32_t`) and enum |
| /// types. `seq_inclusive(A, B)` produces a sequence of values from `A` to `B`, |
| /// including `B`. |
| /// |
| /// Examples with integral types: |
| /// ``` |
| /// for (int x : seq(0, 3)) |
| /// outs() << x << " "; |
| /// ``` |
| /// |
| /// Prints: `0 1 2 `. |
| /// |
| /// ``` |
| /// for (int x : seq_inclusive(0, 3)) |
| /// outs() << x << " "; |
| /// ``` |
| /// |
| /// Prints: `0 1 2 3 `. |
| /// |
| /// Similar to `seq` and `seq_inclusive`, the `enum_seq` and |
| /// `enum_seq_inclusive` functions produce sequences of enum values that can be |
| /// iterated over. |
| /// To enable iteration with enum types, you need to either mark enums as safe |
| /// to iterate on by specializing `enum_iteration_traits`, or opt into |
| /// potentially unsafe iteration at every callsite by passing |
| /// `force_iteration_on_noniterable_enum`. |
| /// |
| /// Examples with enum types: |
| /// ``` |
| /// namespace X { |
| /// enum class MyEnum : unsigned {A = 0, B, C}; |
| /// } // namespace X |
| /// |
| /// template <> struct enum_iteration_traits<X::MyEnum> { |
| /// static contexpr bool is_iterable = true; |
| /// }; |
| /// |
| /// class MyClass { |
| /// public: |
| /// enum Safe { D = 3, E, F }; |
| /// enum MaybeUnsafe { G = 1, H = 2, I = 4 }; |
| /// }; |
| /// |
| /// template <> struct enum_iteration_traits<MyClass::Safe> { |
| /// static contexpr bool is_iterable = true; |
| /// }; |
| /// ``` |
| /// |
| /// ``` |
| /// for (auto v : enum_seq(MyClass::Safe::D, MyClass::Safe::F)) |
| /// outs() << int(v) << " "; |
| /// ``` |
| /// |
| /// Prints: `3 4 `. |
| /// |
| /// ``` |
| /// for (auto v : enum_seq(MyClass::MaybeUnsafe::H, MyClass::MaybeUnsafe::I, |
| /// force_iteration_on_noniterable_enum)) |
| /// outs() << int(v) << " "; |
| /// ``` |
| /// |
| /// Prints: `2 3 `. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_ADT_SEQUENCE_H |
| #define LLVM_ADT_SEQUENCE_H |
| |
| #include <cassert> // assert |
| #include <iterator> // std::random_access_iterator_tag |
| #include <limits> // std::numeric_limits |
| #include <type_traits> // std::is_integral, std::is_enum, std::underlying_type, |
| // std::enable_if |
| |
| #include "llvm/Support/MathExtras.h" // AddOverflow / SubOverflow |
| |
| namespace llvm { |
| |
| // Enum traits that marks enums as safe or unsafe to iterate over. |
| // By default, enum types are *not* considered safe for iteration. |
| // To allow iteration for your enum type, provide a specialization with |
| // `is_iterable` set to `true` in the `llvm` namespace. |
| // Alternatively, you can pass the `force_iteration_on_noniterable_enum` tag |
| // to `enum_seq` or `enum_seq_inclusive`. |
| template <typename EnumT> struct enum_iteration_traits { |
| static constexpr bool is_iterable = false; |
| }; |
| |
| struct force_iteration_on_noniterable_enum_t { |
| explicit force_iteration_on_noniterable_enum_t() = default; |
| }; |
| |
| inline constexpr force_iteration_on_noniterable_enum_t |
| force_iteration_on_noniterable_enum; |
| |
| 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) { |
| 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()); |
| const uintmax_t TopU = uintmax_t(std::numeric_limits<U>::max()); |
| return !((BotT > BotU && Value < static_cast<U>(BotT)) || |
| (TopT < TopU && Value > static_cast<U>(TopT))); |
| } |
| |
| // An integer type that asserts when: |
| // - constructed from a value that doesn't fit into intmax_t, |
| // - casted to a type that cannot hold the current value, |
| // - its internal representation overflows. |
| 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) { |
| if (!canTypeFitValue<intmax_t>(FromValue)) |
| assertOutOfBounds(); |
| CheckedInt Result; |
| Result.Value = static_cast<intmax_t>(FromValue); |
| return Result; |
| } |
| |
| // 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) { |
| using type = std::underlying_type_t<Enum>; |
| return from<type>(static_cast<type>(FromValue)); |
| } |
| |
| // Equality |
| bool operator==(const CheckedInt &O) const { return Value == O.Value; } |
| bool operator!=(const CheckedInt &O) const { return Value != O.Value; } |
| |
| CheckedInt operator+(intmax_t Offset) const { |
| CheckedInt Result; |
| if (AddOverflow(Value, Offset, Result.Value)) |
| assertOutOfBounds(); |
| return Result; |
| } |
| |
| intmax_t operator-(CheckedInt Other) const { |
| intmax_t Result; |
| if (SubOverflow(Value, Other.Value, Result)) |
| assertOutOfBounds(); |
| return Result; |
| } |
| |
| // 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 { |
| if (!canTypeFitValue<Integral>(Value)) |
| assertOutOfBounds(); |
| return static_cast<Integral>(Value); |
| } |
| |
| // Convert to enum, asserts if Value cannot be represented as Enum's |
| // underlying type. |
| template <typename Enum, |
| std::enable_if_t<std::is_enum<Enum>::value, bool> = 0> |
| Enum to() const { |
| using type = std::underlying_type_t<Enum>; |
| return Enum(to<type>()); |
| } |
| |
| private: |
| static void assertOutOfBounds() { assert(false && "Out of bounds"); } |
| |
| intmax_t Value; |
| }; |
| |
| template <typename T, bool IsReverse> struct SafeIntIterator { |
| using iterator_category = std::random_access_iterator_tag; |
| using value_type = T; |
| using difference_type = intmax_t; |
| using pointer = T *; |
| using reference = value_type; // The iterator does not reference memory. |
| |
| // Construct from T. |
| explicit SafeIntIterator(T Value) : SI(CheckedInt::from<T>(Value)) {} |
| // Construct from other direction. |
| SafeIntIterator(const SafeIntIterator<T, !IsReverse> &O) : SI(O.SI) {} |
| |
| // Dereference |
| reference operator*() const { return SI.to<T>(); } |
| // Indexing |
| 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; } |
| // 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; } |
| |
| // Pre Increment/Decrement |
| void operator++() { offset(1); } |
| void operator--() { offset(-1); } |
| |
| // Post Increment/Decrement |
| SafeIntIterator operator++(int) { |
| const auto Copy = *this; |
| ++*this; |
| return Copy; |
| } |
| 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); } |
| |
| // Arithmetic |
| SafeIntIterator operator+(intmax_t Offset) const { return add(Offset); } |
| SafeIntIterator operator-(intmax_t Offset) const { return add(-Offset); } |
| |
| // Difference |
| intmax_t operator-(const SafeIntIterator &O) const { |
| return IsReverse ? O.SI - SI : SI - O.SI; |
| } |
| |
| private: |
| SafeIntIterator(const CheckedInt &SI) : SI(SI) {} |
| |
| static intmax_t getOffset(intmax_t Offset) { |
| return IsReverse ? -Offset : Offset; |
| } |
| |
| CheckedInt add(intmax_t Offset) const { return SI + getOffset(Offset); } |
| |
| void offset(intmax_t Offset) { SI = SI + getOffset(Offset); } |
| |
| CheckedInt SI; |
| |
| // To allow construction from the other direction. |
| template <typename, bool> friend struct SafeIntIterator; |
| }; |
| |
| } // namespace detail |
| |
| template <typename T> struct iota_range { |
| using value_type = T; |
| using reference = T &; |
| using const_reference = const T &; |
| using iterator = detail::SafeIntIterator<value_type, false>; |
| using const_iterator = iterator; |
| using reverse_iterator = detail::SafeIntIterator<value_type, true>; |
| using const_reverse_iterator = reverse_iterator; |
| using difference_type = intmax_t; |
| using size_type = std::size_t; |
| |
| explicit 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; } |
| |
| auto begin() const { return const_iterator(BeginValue); } |
| 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); } |
| |
| private: |
| static_assert(std::is_integral<T>::value || std::is_enum<T>::value, |
| "T must be an integral or enum type"); |
| static_assert(std::is_same<T, std::remove_cv_t<T>>::value, |
| "T must not be const nor volatile"); |
| |
| iterator BeginValue; |
| iterator PastEndValue; |
| }; |
| |
| /// Iterate over an integral type from Begin up to - but not including - End. |
| /// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX] for |
| /// forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX] for reverse |
| /// 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) { |
| return iota_range<T>(Begin, End, false); |
| } |
| |
| /// Iterate over an integral type from Begin to End inclusive. |
| /// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX - 1] |
| /// for forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX - 1] for reverse |
| /// 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) { |
| return iota_range<T>(Begin, End, true); |
| } |
| |
| /// Iterate over an enum type from Begin up to - but not including - End. |
| /// Note: `enum_seq` will generate each consecutive value, even if no |
| /// enumerator with that value exists. |
| /// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX] for |
| /// forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX] for reverse |
| /// iteration). |
| template <typename EnumT, |
| typename = std::enable_if_t<std::is_enum<EnumT>::value>> |
| 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); |
| } |
| |
| /// Iterate over an enum type from Begin up to - but not including - End, even |
| /// when `EnumT` is not marked as safely iterable by `enum_iteration_traits`. |
| /// Note: `enum_seq` will generate each consecutive value, even if no |
| /// enumerator with that value exists. |
| /// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX] for |
| /// forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX] for reverse |
| /// 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) { |
| return iota_range<EnumT>(Begin, End, false); |
| } |
| |
| /// Iterate over an enum type from Begin to End inclusive. |
| /// Note: `enum_seq_inclusive` will generate each consecutive value, even if no |
| /// enumerator with that value exists. |
| /// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX - 1] |
| /// for forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX - 1] for reverse |
| /// iteration). |
| template <typename EnumT, |
| typename = std::enable_if_t<std::is_enum<EnumT>::value>> |
| 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); |
| } |
| |
| /// Iterate over an enum type from Begin to End inclusive, even when `EnumT` |
| /// is not marked as safely iterable by `enum_iteration_traits`. |
| /// Note: `enum_seq_inclusive` will generate each consecutive value, even if no |
| /// enumerator with that value exists. |
| /// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX - 1] |
| /// for forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX - 1] for reverse |
| /// 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) { |
| return iota_range<EnumT>(Begin, End, true); |
| } |
| |
| } // end namespace llvm |
| |
| #endif // LLVM_ADT_SEQUENCE_H |