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