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