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