Index: llvm/include/llvm/ADT/STLExtras.h =================================================================== --- llvm/include/llvm/ADT/STLExtras.h +++ llvm/include/llvm/ADT/STLExtras.h @@ -21,6 +21,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/Config/abi-breaking.h" #include "llvm/Support/ErrorHandling.h" #include #include @@ -418,6 +419,89 @@ std::end(std::forward(Range)), Pred)); } +/// A pseudo-iterator adaptor that is designed to implement "early increment" +/// style loops. +/// +/// This is *not a normal iterator* and should almost never be used directly. It +/// is intended primarily to be used with range based for loops and some range +/// algorithms. +/// +/// The iterator isn't quite an `OutputIterator` or an `InputIterator` but +/// somewhere between them. The constraints of these iterators are: +/// +/// - On construction or after being incremented, it is comparable and +/// dereferencable. It is *not* incrementable. +/// - After being dereferenced, it is neither comparable nor dereferencable, it +/// is only incrementable. +/// +/// This means you can only dereference the iterator once, and you can only +/// increment it once between dereferences. +template +class early_inc_iterator_impl + : public iterator_adaptor_base, + WrappedIteratorT, std::input_iterator_tag> { + using BaseT = + iterator_adaptor_base, + WrappedIteratorT, std::input_iterator_tag>; + + using PointerT = typename std::iterator_traits::pointer; + +protected: +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + bool IsEarlyIncremented = false; +#endif + +public: + early_inc_iterator_impl(WrappedIteratorT I) : BaseT(I) {} + + using BaseT::operator*; + typename BaseT::reference operator*() { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + assert(!IsEarlyIncremented && "Cannot dereference twice!"); + IsEarlyIncremented = true; +#endif + return *(this->I)++; + } + + using BaseT::operator++; + early_inc_iterator_impl &operator++() { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + assert(IsEarlyIncremented && "Cannot increment before dereferencing!"); + IsEarlyIncremented = false; +#endif + return *this; + } + + using BaseT::operator==; + bool operator==(const early_inc_iterator_impl &RHS) const { +#if LLVM_ENABLE_ABI_BREAKING_CHECKS + assert(!IsEarlyIncremented && "Cannot compare after dereferencing!"); +#endif + return BaseT::operator==(RHS); + } +}; + +/// Make a range that does early increment to allow mutation of the underlying +/// range without disrupting iteration. +/// +/// The underlying iterator will be incremented immediately after it is +/// dereferenced, allowing deletion of the current node or insertion of nodes to +/// not disrupt iteration provided they do not invalidate the *next* iterator -- +/// the current iterator can be invalidated. +/// +/// This requires a very exact pattern of use that is only really suitable to +/// range based for loops and other range algorithms that explicitly guarantee +/// to dereference exactly once each element, and to increment exactly once each +/// element. +template +iterator_range>> +make_early_inc_range(RangeT &&Range) { + using EarlyIncIteratorT = + early_inc_iterator_impl>; + return make_range(EarlyIncIteratorT(std::begin(std::forward(Range))), + EarlyIncIteratorT(std::end(std::forward(Range)))); +} + // forward declarations required by zip_shortest/zip_first template bool all_of(R &&range, UnaryPredicate P); Index: llvm/unittests/ADT/STLExtrasTest.cpp =================================================================== --- llvm/unittests/ADT/STLExtrasTest.cpp +++ llvm/unittests/ADT/STLExtrasTest.cpp @@ -364,4 +364,55 @@ EXPECT_EQ(5, count); } +TEST(STLExtrasTest, EarlyIncrementTest) { + std::list L = {1, 2, 3, 4}; + + auto EIR = make_early_inc_range(L); + + auto I = EIR.begin(); + auto EI = EIR.end(); + EXPECT_NE(I, EI); + + EXPECT_EQ(1, *I); +#if LLVM_ENABLE_ABI_BREAKING_CHECKS +#ifndef NDEBUG + // Repeated dereferences are not allowed. + EXPECT_DEATH(*I, "Cannot dereference"); + // Comparison after dereference is not allowed. + EXPECT_DEATH((void)(I == EI), "Cannot compare"); + EXPECT_DEATH((void)(I != EI), "Cannot compare"); +#endif +#endif + + ++I; + EXPECT_NE(I, EI); +#if LLVM_ENABLE_ABI_BREAKING_CHECKS +#ifndef NDEBUG + // You cannot increment prior to dereference. + EXPECT_DEATH(++I, "Cannot increment"); +#endif +#endif + EXPECT_EQ(2, *I); +#if LLVM_ENABLE_ABI_BREAKING_CHECKS +#ifndef NDEBUG + // Repeated dereferences are not allowed. + EXPECT_DEATH(*I, "Cannot dereference"); +#endif +#endif + + // Inserting shouldn't break anything. We should be able to keep dereferencing + // the currrent iterator and increment. The increment to go to the "next" + // iterator from before we inserted. + L.insert(std::next(L.begin(), 2), -1); + ++I; + EXPECT_EQ(3, *I); + + // Erasing the front including the current doesn't break incrementing. + L.erase(L.begin(), std::prev(L.end())); + ++I; + EXPECT_EQ(4, *I); + ++I; + EXPECT_EQ(EIR.end(), I); +} + } // namespace