fallible_iterator.h 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //===--- fallible_iterator.h - Wrapper for fallible iterators ---*- C++ -*-===//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #ifndef LLVM_ADT_FALLIBLE_ITERATOR_H
  14. #define LLVM_ADT_FALLIBLE_ITERATOR_H
  15. #include "llvm/ADT/PointerIntPair.h"
  16. #include "llvm/ADT/iterator_range.h"
  17. #include "llvm/Support/Error.h"
  18. #include <type_traits>
  19. namespace llvm {
  20. /// A wrapper class for fallible iterators.
  21. ///
  22. /// The fallible_iterator template wraps an underlying iterator-like class
  23. /// whose increment and decrement operations are replaced with fallible versions
  24. /// like:
  25. ///
  26. /// @code{.cpp}
  27. /// Error inc();
  28. /// Error dec();
  29. /// @endcode
  30. ///
  31. /// It produces an interface that is (mostly) compatible with a traditional
  32. /// c++ iterator, including ++ and -- operators that do not fail.
  33. ///
  34. /// Instances of the wrapper are constructed with an instance of the
  35. /// underlying iterator and (for non-end iterators) a reference to an Error
  36. /// instance. If the underlying increment/decrement operations fail, the Error
  37. /// is returned via this reference, and the resulting iterator value set to an
  38. /// end-of-range sentinel value. This enables the following loop idiom:
  39. ///
  40. /// @code{.cpp}
  41. /// class Archive { // E.g. Potentially malformed on-disk archive
  42. /// public:
  43. /// fallible_iterator<ArchiveChildItr> children_begin(Error &Err);
  44. /// fallible_iterator<ArchiveChildItr> children_end();
  45. /// iterator_range<fallible_iterator<ArchiveChildItr>>
  46. /// children(Error &Err) {
  47. /// return make_range(children_begin(Err), children_end());
  48. /// //...
  49. /// };
  50. ///
  51. /// void walk(Archive &A) {
  52. /// Error Err = Error::success();
  53. /// for (auto &C : A.children(Err)) {
  54. /// // Loop body only entered when increment succeeds.
  55. /// }
  56. /// if (Err) {
  57. /// // handle error.
  58. /// }
  59. /// }
  60. /// @endcode
  61. ///
  62. /// The wrapper marks the referenced Error as unchecked after each increment
  63. /// and/or decrement operation, and clears the unchecked flag when a non-end
  64. /// value is compared against end (since, by the increment invariant, not being
  65. /// an end value proves that there was no error, and is equivalent to checking
  66. /// that the Error is success). This allows early exits from the loop body
  67. /// without requiring redundant error checks.
  68. template <typename Underlying> class fallible_iterator {
  69. private:
  70. template <typename T>
  71. using enable_if_struct_deref_supported = std::enable_if<
  72. !std::is_void<decltype(std::declval<T>().operator->())>::value,
  73. decltype(std::declval<T>().operator->())>;
  74. public:
  75. /// Construct a fallible iterator that *cannot* be used as an end-of-range
  76. /// value.
  77. ///
  78. /// A value created by this method can be dereferenced, incremented,
  79. /// decremented and compared, providing the underlying type supports it.
  80. ///
  81. /// The error that is passed in will be initially marked as checked, so if the
  82. /// iterator is not used at all the Error need not be checked.
  83. static fallible_iterator itr(Underlying I, Error &Err) {
  84. (void)!!Err;
  85. return fallible_iterator(std::move(I), &Err);
  86. }
  87. /// Construct a fallible iterator that can be used as an end-of-range value.
  88. ///
  89. /// A value created by this method can be dereferenced (if the underlying
  90. /// value points at a valid value) and compared, but not incremented or
  91. /// decremented.
  92. static fallible_iterator end(Underlying I) {
  93. return fallible_iterator(std::move(I), nullptr);
  94. }
  95. /// Forward dereference to the underlying iterator.
  96. decltype(auto) operator*() { return *I; }
  97. /// Forward const dereference to the underlying iterator.
  98. decltype(auto) operator*() const { return *I; }
  99. /// Forward structure dereference to the underlying iterator (if the
  100. /// underlying iterator supports it).
  101. template <typename T = Underlying>
  102. typename enable_if_struct_deref_supported<T>::type operator->() {
  103. return I.operator->();
  104. }
  105. /// Forward const structure dereference to the underlying iterator (if the
  106. /// underlying iterator supports it).
  107. template <typename T = Underlying>
  108. typename enable_if_struct_deref_supported<const T>::type operator->() const {
  109. return I.operator->();
  110. }
  111. /// Increment the fallible iterator.
  112. ///
  113. /// If the underlying 'inc' operation fails, this will set the Error value
  114. /// and update this iterator value to point to end-of-range.
  115. ///
  116. /// The Error value is marked as needing checking, regardless of whether the
  117. /// 'inc' operation succeeds or fails.
  118. fallible_iterator &operator++() {
  119. assert(getErrPtr() && "Cannot increment end iterator");
  120. if (auto Err = I.inc())
  121. handleError(std::move(Err));
  122. else
  123. resetCheckedFlag();
  124. return *this;
  125. }
  126. /// Decrement the fallible iterator.
  127. ///
  128. /// If the underlying 'dec' operation fails, this will set the Error value
  129. /// and update this iterator value to point to end-of-range.
  130. ///
  131. /// The Error value is marked as needing checking, regardless of whether the
  132. /// 'dec' operation succeeds or fails.
  133. fallible_iterator &operator--() {
  134. assert(getErrPtr() && "Cannot decrement end iterator");
  135. if (auto Err = I.dec())
  136. handleError(std::move(Err));
  137. else
  138. resetCheckedFlag();
  139. return *this;
  140. }
  141. /// Compare fallible iterators for equality.
  142. ///
  143. /// Returns true if both LHS and RHS are end-of-range values, or if both are
  144. /// non-end-of-range values whose underlying iterator values compare equal.
  145. ///
  146. /// If this is a comparison between an end-of-range iterator and a
  147. /// non-end-of-range iterator, then the Error (referenced by the
  148. /// non-end-of-range value) is marked as checked: Since all
  149. /// increment/decrement operations result in an end-of-range value, comparing
  150. /// false against end-of-range is equivalent to checking that the Error value
  151. /// is success. This flag management enables early returns from loop bodies
  152. /// without redundant Error checks.
  153. friend bool operator==(const fallible_iterator &LHS,
  154. const fallible_iterator &RHS) {
  155. // If both iterators are in the end state they compare
  156. // equal, regardless of whether either is valid.
  157. if (LHS.isEnd() && RHS.isEnd())
  158. return true;
  159. assert(LHS.isValid() && RHS.isValid() &&
  160. "Invalid iterators can only be compared against end");
  161. bool Equal = LHS.I == RHS.I;
  162. // If the iterators differ and this is a comparison against end then mark
  163. // the Error as checked.
  164. if (!Equal) {
  165. if (LHS.isEnd())
  166. (void)!!*RHS.getErrPtr();
  167. else
  168. (void)!!*LHS.getErrPtr();
  169. }
  170. return Equal;
  171. }
  172. /// Compare fallible iterators for inequality.
  173. ///
  174. /// See notes for operator==.
  175. friend bool operator!=(const fallible_iterator &LHS,
  176. const fallible_iterator &RHS) {
  177. return !(LHS == RHS);
  178. }
  179. private:
  180. fallible_iterator(Underlying I, Error *Err)
  181. : I(std::move(I)), ErrState(Err, false) {}
  182. Error *getErrPtr() const { return ErrState.getPointer(); }
  183. bool isEnd() const { return getErrPtr() == nullptr; }
  184. bool isValid() const { return !ErrState.getInt(); }
  185. void handleError(Error Err) {
  186. *getErrPtr() = std::move(Err);
  187. ErrState.setPointer(nullptr);
  188. ErrState.setInt(true);
  189. }
  190. void resetCheckedFlag() {
  191. *getErrPtr() = Error::success();
  192. }
  193. Underlying I;
  194. mutable PointerIntPair<Error *, 1> ErrState;
  195. };
  196. /// Convenience wrapper to make a fallible_iterator value from an instance
  197. /// of an underlying iterator and an Error reference.
  198. template <typename Underlying>
  199. fallible_iterator<Underlying> make_fallible_itr(Underlying I, Error &Err) {
  200. return fallible_iterator<Underlying>::itr(std::move(I), Err);
  201. }
  202. /// Convenience wrapper to make a fallible_iterator end value from an instance
  203. /// of an underlying iterator.
  204. template <typename Underlying>
  205. fallible_iterator<Underlying> make_fallible_end(Underlying E) {
  206. return fallible_iterator<Underlying>::end(std::move(E));
  207. }
  208. template <typename Underlying>
  209. iterator_range<fallible_iterator<Underlying>>
  210. make_fallible_range(Underlying I, Underlying E, Error &Err) {
  211. return make_range(make_fallible_itr(std::move(I), Err),
  212. make_fallible_end(std::move(E)));
  213. }
  214. } // end namespace llvm
  215. #endif // LLVM_ADT_FALLIBLE_ITERATOR_H
  216. #ifdef __GNUC__
  217. #pragma GCC diagnostic pop
  218. #endif