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 decltype(auto) iter_move(const inner-iterator& i)
13 // noexcept(noexcept(ranges::iter_move(i.i_.<current>)));
14
15 #include <iterator>
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 = ptrdiff_t;
28
29 value_type* ptr_ = nullptr;
30 int* iter_move_invocations_ = nullptr;
31
32 constexpr MaybeNoexceptIterator() = default;
MaybeNoexceptIteratoradl::MaybeNoexceptIterator33 constexpr explicit MaybeNoexceptIterator(int* p, int& iter_moves) : ptr_(p), iter_move_invocations_(&iter_moves) {}
34
operator *adl::MaybeNoexceptIterator35 constexpr 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 constexpr MaybeNoexceptIterator& operator--() { --ptr_; return *this; }
operator --adl::MaybeNoexceptIterator45 constexpr MaybeNoexceptIterator operator--(int) {
46 MaybeNoexceptIterator prev = *this;
47 --ptr_;
48 return prev;
49 }
50
iter_move(MaybeNoexceptIterator iter)51 constexpr friend value_type&& iter_move(MaybeNoexceptIterator iter) noexcept(IsNoexcept) {
52 if (iter.iter_move_invocations_) {
53 ++(*iter.iter_move_invocations_);
54 }
55 return std::move(*iter);
56 }
57
operator ==(const MaybeNoexceptIterator & lhs,const MaybeNoexceptIterator & rhs)58 friend bool operator==(const MaybeNoexceptIterator& lhs, const MaybeNoexceptIterator& rhs) { return lhs.ptr_ == rhs.ptr_; }
59 };
60
61 template <bool IsNoexcept = false>
62 struct View : std::ranges::view_base {
63 static constexpr int N = 3;
64 int a[N] = {0, 1, 2};
65 int* iter_moves = nullptr;
66
67 constexpr View() = default;
Viewadl::View68 constexpr View(int& iter_move_invocations) : iter_moves(&iter_move_invocations) {
69 }
70
beginadl::View71 constexpr adl::MaybeNoexceptIterator<IsNoexcept> begin() {
72 return adl::MaybeNoexceptIterator<IsNoexcept>(a, *iter_moves);
73 }
endadl::View74 constexpr adl::MaybeNoexceptIterator<IsNoexcept> end() {
75 return adl::MaybeNoexceptIterator<IsNoexcept>(a + N, *iter_moves);
76 }
77 };
78
79 } // namespace adl
80
test()81 constexpr bool test() {
82 // Can use `iter_move` with `inner-iterator`; `View` is a forward range.
83 {
84 SplitViewForward v("abc def", " ");
85 auto segment = *v.begin();
86
87 // Non-const iterator.
88 {
89 auto i = segment.begin();
90 static_assert(std::same_as<decltype(iter_move(i)), const char &&>);
91 assert(iter_move(i) == 'a');
92 }
93
94 // Const iterator.
95 {
96 const auto i = segment.begin();
97 static_assert(std::same_as<decltype(iter_move(i)), const char &&>);
98 assert(iter_move(i) == 'a');
99 }
100 }
101
102 // Can use `iter_move` with `inner-iterator`, `View` is an input range.
103 {
104 SplitViewInput v("abc def", ' ');
105 auto segment = *v.begin();
106
107 // Non-const iterator.
108 {
109 auto i = segment.begin();
110 static_assert(std::same_as<decltype(iter_move(i)), char &&>);
111 assert(iter_move(i) == 'a');
112 }
113
114 // Const iterator.
115 {
116 const auto i = segment.begin();
117 static_assert(std::same_as<decltype(iter_move(i)), char &&>);
118 assert(iter_move(i) == 'a');
119 }
120 }
121
122 // Ensure the `iter_move` customization point is being used.
123 {
124 int iter_move_invocations = 0;
125 adl::View<> input(iter_move_invocations);
126 std::ranges::lazy_split_view<adl::View<>, adl::View<>> v(input, adl::View<>());
127
128 auto segment = *v.begin();
129 auto i = segment.begin();
130 int x = iter_move(i);
131 assert(x == 0);
132 assert(iter_move_invocations == 1);
133 }
134
135 // Check the `noexcept` specification.
136 {
137 {
138 using ThrowingSplitView = std::ranges::lazy_split_view<adl::View<false>, adl::View<false>>;
139 using ThrowingValueType = std::ranges::iterator_t<ThrowingSplitView>::value_type;
140 using ThrowingIter = std::ranges::iterator_t<ThrowingValueType>;
141 ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(std::declval<adl::MaybeNoexceptIterator<false>>()));
142 ASSERT_NOT_NOEXCEPT(iter_move(std::declval<ThrowingIter>()));
143 }
144
145 {
146 using NoexceptSplitView = std::ranges::lazy_split_view<adl::View<true>, adl::View<true>>;
147 using NoexceptValueType = std::ranges::iterator_t<NoexceptSplitView>::value_type;
148 using NoexceptIter = std::ranges::iterator_t<NoexceptValueType>;
149 ASSERT_NOEXCEPT(std::ranges::iter_move(std::declval<adl::MaybeNoexceptIterator<true>>()));
150 ASSERT_NOEXCEPT(iter_move(std::declval<NoexceptIter>()));
151 }
152 }
153
154 return true;
155 }
156
main(int,char **)157 int main(int, char**) {
158 test();
159 static_assert(test());
160
161 return 0;
162 }
163