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