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