123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===- 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;
- };
- // TODO: Make this `inline` once we update to C++17 to avoid ORD violations.
- 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, typename 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,
- typename std::enable_if_t<std::is_enum<Enum>::value, bool> = 0>
- static CheckedInt from(Enum FromValue) {
- using type = typename std::underlying_type<Enum>::type;
- 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, typename 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,
- typename std::enable_if_t<std::is_enum<Enum>::value, bool> = 0>
- Enum to() const {
- using type = typename std::underlying_type<Enum>::type;
- 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 = T &;
- // 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
- value_type operator*() const { return SI.to<T>(); }
- // Indexing
- value_type 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
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|