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