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