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