1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // UNSUPPORTED: c++03, c++11, c++14, c++17 10 // UNSUPPORTED: libcpp-has-no-incomplete-ranges 11 12 // friend constexpr void iter_swap(const inner-iterator& x, const inner-iterator& y) 13 // noexcept(noexcept(ranges::iter_swap(x.i_.<current>, y.i_.<current>))) 14 // requires indirectly_swappable<iterator_t<Base>>; 15 16 #include <ranges> 17 18 #include <cassert> 19 #include <type_traits> 20 #include <utility> 21 #include "../types.h" 22 23 namespace adl { 24 25 template <bool IsNoexcept = false> 26 struct MaybeNoexceptIterator { 27 using value_type = int; 28 using difference_type = ptrdiff_t; 29 30 value_type* ptr_ = nullptr; 31 int* iter_swap_invocations_ = nullptr; 32 33 constexpr MaybeNoexceptIterator() = default; 34 constexpr explicit MaybeNoexceptIterator(int& iter_swaps) : iter_swap_invocations_(&iter_swaps) {} 35 36 value_type& operator*() const { return *ptr_; } 37 38 MaybeNoexceptIterator& operator++() { ++ptr_; return *this; } 39 MaybeNoexceptIterator operator++(int) { 40 MaybeNoexceptIterator prev = *this; 41 ++ptr_; 42 return prev; 43 } 44 45 MaybeNoexceptIterator& operator--() { --ptr_; return *this; } 46 MaybeNoexceptIterator operator--(int) { 47 MaybeNoexceptIterator prev = *this; 48 --ptr_; 49 return prev; 50 } 51 52 constexpr friend void iter_swap(MaybeNoexceptIterator a, MaybeNoexceptIterator) noexcept(IsNoexcept) { 53 if (a.iter_swap_invocations_) { 54 ++(*a.iter_swap_invocations_); 55 } 56 } 57 58 friend bool operator==(const MaybeNoexceptIterator& lhs, const MaybeNoexceptIterator& rhs) { 59 return lhs.ptr_ == rhs.ptr_; 60 } 61 }; 62 63 template <bool IsNoexcept = false> 64 struct View : std::ranges::view_base { 65 int* iter_swaps = nullptr; 66 67 constexpr View() = default; 68 constexpr View(int& iter_swap_invocations) : iter_swaps(&iter_swap_invocations) { 69 } 70 71 constexpr adl::MaybeNoexceptIterator<IsNoexcept> begin() { 72 return adl::MaybeNoexceptIterator<IsNoexcept>(*iter_swaps); 73 } 74 constexpr adl::MaybeNoexceptIterator<IsNoexcept> end() { 75 return adl::MaybeNoexceptIterator<IsNoexcept>(*iter_swaps); 76 } 77 }; 78 79 } // namespace adl 80 81 constexpr bool test() { 82 // Can use `iter_swap` with `inner-iterator`; `View` is a forward range. 83 { 84 // Non-const iterator. 85 { 86 SplitViewDiff v("abc def", " "); 87 auto segment = *v.begin(); 88 89 auto i1 = segment.begin(); 90 auto i2 = i1++; 91 static_assert(std::is_void_v<decltype(iter_swap(i1, i2))>); 92 assert(*i1 == 'b'); 93 assert(*i2 == 'a'); 94 95 iter_swap(i1, i2); 96 assert(*i1 == 'a'); 97 assert(*i2 == 'b'); 98 // Note that `iter_swap` swaps characters in the actual underlying range. 99 assert(*v.base().begin() == 'b'); 100 } 101 102 // Const iterator. 103 { 104 SplitViewDiff v("abc def", " "); 105 auto segment = *v.begin(); 106 107 auto i1 = segment.begin(); 108 const auto i2 = i1++; 109 static_assert(std::is_void_v<decltype(iter_swap(i1, i2))>); 110 static_assert(std::is_void_v<decltype(iter_swap(i2, i2))>); 111 assert(*i1 == 'b'); 112 assert(*i2 == 'a'); 113 114 iter_swap(i1, i2); 115 assert(*i1 == 'a'); 116 assert(*i2 == 'b'); 117 assert(*v.base().begin() == 'b'); 118 } 119 } 120 121 // Can use `iter_swap` with `inner-iterator`; `View` is an input range. 122 { 123 124 // Non-const iterator. 125 { 126 // Iterators belong to the same view. 127 { 128 SplitViewInput v("abc def", ' '); 129 auto segment = *v.begin(); 130 131 auto i1 = segment.begin(); 132 auto i2 = i1; 133 ++i1; 134 static_assert(std::is_void_v<decltype(iter_swap(i1, i2))>); 135 assert(*i1 == 'b'); 136 // For an input view, all inner iterators are essentially thin proxies to the same underlying iterator. 137 assert(*i2 == 'b'); 138 139 iter_swap(i1, i2); 140 assert(*i1 == 'b'); 141 assert(*i2 == 'b'); 142 } 143 144 // Iterators belong to different views. 145 { 146 SplitViewInput v1("abc def", ' '); 147 auto val1 = *v1.begin(); 148 SplitViewInput v2 = v1; 149 auto val2 = *v2.begin(); 150 151 auto i1 = val1.begin(); 152 auto i2 = val2.begin(); 153 ++i1; 154 assert(*i1 == 'b'); 155 assert(*i2 == 'a'); 156 157 iter_swap(i1, i2); 158 assert(*i1 == 'a'); 159 assert(*i2 == 'b'); 160 } 161 } 162 163 // Const iterator. 164 { 165 SplitViewInput v("abc def", ' '); 166 auto segment = *v.begin(); 167 168 const auto i1 = segment.begin(); 169 const auto i2 = i1; 170 static_assert(std::is_void_v<decltype(iter_swap(i1, i2))>); 171 assert(*i1 == 'a'); 172 assert(*i2 == 'a'); 173 174 iter_swap(i1, i2); 175 assert(*i1 == 'a'); 176 assert(*i2 == 'a'); 177 } 178 } 179 180 // Ensure the `iter_swap` customization point is being used. 181 { 182 int iter_swap_invocations = 0; 183 adl::View<> input(iter_swap_invocations); 184 std::ranges::lazy_split_view<adl::View<>, adl::View<>> v(input, adl::View<>()); 185 186 auto segment = *v.begin(); 187 auto i = segment.begin(); 188 iter_swap(i, i); 189 assert(iter_swap_invocations == 1); 190 } 191 192 // Check the `noexcept` specification. 193 { 194 { 195 using ThrowingSplitView = std::ranges::lazy_split_view<adl::View<false>, adl::View<false>>; 196 using ThrowingValueType = std::ranges::iterator_t<ThrowingSplitView>::value_type; 197 using ThrowingIter = std::ranges::iterator_t<ThrowingValueType>; 198 ASSERT_NOT_NOEXCEPT(std::ranges::iter_swap( 199 std::declval<adl::MaybeNoexceptIterator<false>>(), 200 std::declval<adl::MaybeNoexceptIterator<false>>())); 201 ASSERT_NOT_NOEXCEPT(iter_swap(std::declval<ThrowingIter>(), std::declval<ThrowingIter>())); 202 } 203 204 { 205 using NoexceptSplitView = std::ranges::lazy_split_view<adl::View<true>, adl::View<true>>; 206 using NoexceptValueType = std::ranges::iterator_t<NoexceptSplitView>::value_type; 207 using NoexceptIter = std::ranges::iterator_t<NoexceptValueType>; 208 ASSERT_NOEXCEPT(std::ranges::iter_swap( 209 std::declval<adl::MaybeNoexceptIterator<true>>(), 210 std::declval<adl::MaybeNoexceptIterator<true>>())); 211 ASSERT_NOEXCEPT(iter_swap(std::declval<NoexceptIter>(), std::declval<NoexceptIter>())); 212 } 213 } 214 215 return true; 216 } 217 218 int main(int, char**) { 219 test(); 220 static_assert(test()); 221 222 return 0; 223 } 224