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 // template<class I>
12 // unspecified iter_swap;
13 
14 #include <iterator>
15 
16 #include <array>
17 #include <cassert>
18 
19 #include "../unqualified_lookup_wrapper.h"
20 #include "test_iterators.h"
21 
22 using IterSwapT = decltype(std::ranges::iter_swap);
23 
24 struct HasIterSwap {
25   int &value_;
HasIterSwapHasIterSwap26   constexpr explicit HasIterSwap(int &value) : value_(value) { assert(value == 0); }
27 
iter_swap(HasIterSwap & a,HasIterSwap & b)28   friend constexpr void iter_swap(HasIterSwap& a, HasIterSwap& b) {
29     a.value_ = 1;
30     b.value_ = 1;
31   }
iter_swap(HasIterSwap & a,int & b)32   friend constexpr void iter_swap(HasIterSwap& a, int& b) {
33     a.value_ = 2;
34     b = 2;
35   }
36 };
37 
38 static_assert( std::is_invocable_v<IterSwapT, HasIterSwap&, HasIterSwap&>);
39 static_assert( std::is_invocable_v<IterSwapT, HasIterSwap&, int&>);
40 static_assert(!std::is_invocable_v<IterSwapT, int&, HasIterSwap&>);
41 
42 static_assert( std::is_invocable_v<IterSwapT&, HasIterSwap&, HasIterSwap&>);
43 static_assert( std::is_invocable_v<IterSwapT&, HasIterSwap&, int&>);
44 static_assert(!std::is_invocable_v<IterSwapT&, int&, HasIterSwap&>);
45 
46 static_assert( std::is_invocable_v<IterSwapT&&, HasIterSwap&, HasIterSwap&>);
47 static_assert( std::is_invocable_v<IterSwapT&&, HasIterSwap&, int&>);
48 static_assert(!std::is_invocable_v<IterSwapT&&, int&, HasIterSwap&>);
49 
50 struct StructWithNotMoreSpecializedIterSwap {
51   friend void iter_swap(auto&, auto&);
52 };
53 
54 static_assert(
55     !std::is_invocable_v<IterSwapT, StructWithNotMoreSpecializedIterSwap&, StructWithNotMoreSpecializedIterSwap&>);
56 
57 struct NodiscardIterSwap {
iter_swap(NodiscardIterSwap &,NodiscardIterSwap &)58   [[nodiscard]] friend int iter_swap(NodiscardIterSwap&, NodiscardIterSwap&) { return 0; }
59 };
60 
ensureVoidCast(NodiscardIterSwap & a,NodiscardIterSwap & b)61 void ensureVoidCast(NodiscardIterSwap& a, NodiscardIterSwap& b) { std::ranges::iter_swap(a, b); }
62 
63 struct HasRangesSwap {
64   int &value_;
HasRangesSwapHasRangesSwap65   constexpr explicit HasRangesSwap(int &value) : value_(value) { assert(value == 0); }
66 
swap(HasRangesSwap & a,HasRangesSwap & b)67   friend constexpr void swap(HasRangesSwap& a, HasRangesSwap& b) {
68     a.value_ = 1;
69     b.value_ = 1;
70   }
swap(HasRangesSwap & a,int & b)71   friend constexpr void swap(HasRangesSwap& a, int& b) {
72     a.value_ = 2;
73     b = 2;
74   }
75 };
76 
77 struct HasRangesSwapWrapper {
78   using value_type = HasRangesSwap;
79 
80   HasRangesSwap &value_;
HasRangesSwapWrapperHasRangesSwapWrapper81   constexpr explicit HasRangesSwapWrapper(HasRangesSwap &value) : value_(value) {}
82 
operator *HasRangesSwapWrapper83   constexpr HasRangesSwap& operator*() const { return value_; }
84 };
85 
86 static_assert( std::is_invocable_v<IterSwapT, HasRangesSwapWrapper&, HasRangesSwapWrapper&>);
87 // Does not satisfy swappable_with, even though swap(X, Y) is valid.
88 static_assert(!std::is_invocable_v<IterSwapT, HasRangesSwapWrapper&, int&>);
89 static_assert(!std::is_invocable_v<IterSwapT, int&, HasRangesSwapWrapper&>);
90 
91 struct B;
92 
93 struct A {
94   bool value = false;
operator =A95   constexpr A& operator=(const B&) {
96     value = true;
97     return *this;
98   };
99 };
100 
101 struct B {
102   bool value = false;
operator =B103   constexpr B& operator=(const A&) {
104     value = true;
105     return *this;
106   };
107 };
108 
109 struct MoveOnly2;
110 
111 struct MoveOnly1 {
112   bool value = false;
113 
114   MoveOnly1() = default;
115   MoveOnly1(MoveOnly1&&) = default;
116   MoveOnly1& operator=(MoveOnly1&&) = default;
117   MoveOnly1(const MoveOnly1&) = delete;
118   MoveOnly1& operator=(const MoveOnly1&) = delete;
119 
operator =MoveOnly1120   constexpr MoveOnly1& operator=(MoveOnly2 &&) {
121     value = true;
122     return *this;
123   };
124 };
125 
126 struct MoveOnly2 {
127   bool value = false;
128 
129   MoveOnly2() = default;
130   MoveOnly2(MoveOnly2&&) = default;
131   MoveOnly2& operator=(MoveOnly2&&) = default;
132   MoveOnly2(const MoveOnly2&) = delete;
133   MoveOnly2& operator=(const MoveOnly2&) = delete;
134 
operator =MoveOnly2135   constexpr MoveOnly2& operator=(MoveOnly1 &&) {
136     value = true;
137     return *this;
138   };
139 };
140 
test()141 constexpr bool test()
142 {
143   {
144     int value1 = 0;
145     int value2 = 0;
146     HasIterSwap a(value1), b(value2);
147     std::ranges::iter_swap(a, b);
148     assert(value1 == 1 && value2 == 1);
149   }
150   {
151     int value1 = 0;
152     int value2 = 0;
153     HasRangesSwap c(value1), d(value2);
154     HasRangesSwapWrapper cWrapper(c), dWrapper(d);
155     std::ranges::iter_swap(cWrapper, dWrapper);
156     assert(value1 == 1 && value2 == 1);
157   }
158   {
159     int value1 = 0;
160     int value2 = 0;
161     HasRangesSwap c(value1), d(value2);
162     std::ranges::iter_swap(HasRangesSwapWrapper(c), HasRangesSwapWrapper(d));
163     assert(value1 == 1 && value2 == 1);
164   }
165   {
166     A e; B f;
167     A *ePtr = &e;
168     B *fPtr = &f;
169     std::ranges::iter_swap(ePtr, fPtr);
170     assert(e.value && f.value);
171   }
172   {
173     MoveOnly1 g; MoveOnly2 h;
174     std::ranges::iter_swap(&g, &h);
175     assert(g.value && h.value);
176   }
177   {
178     auto arr = std::array<move_tracker, 2>();
179     std::ranges::iter_swap(arr.begin(), arr.begin() + 1);
180     if (std::is_constant_evaluated()) {
181       assert(arr[0].moves() == 1 && arr[1].moves() == 3);
182     } else {
183       assert(arr[0].moves() == 1 && arr[1].moves() == 2);
184     }
185   }
186   {
187     int buff[2] = {1, 2};
188     std::ranges::iter_swap(buff + 0, buff + 1);
189     assert(buff[0] == 2 && buff[1] == 1);
190 
191     std::ranges::iter_swap(cpp20_input_iterator(buff), cpp20_input_iterator(buff + 1));
192     assert(buff[0] == 1 && buff[1] == 2);
193 
194     std::ranges::iter_swap(cpp17_input_iterator(buff), cpp17_input_iterator(buff + 1));
195     assert(buff[0] == 2 && buff[1] == 1);
196 
197     std::ranges::iter_swap(forward_iterator(buff), forward_iterator(buff + 1));
198     assert(buff[0] == 1 && buff[1] == 2);
199 
200     std::ranges::iter_swap(bidirectional_iterator(buff), bidirectional_iterator(buff + 1));
201     assert(buff[0] == 2 && buff[1] == 1);
202 
203     std::ranges::iter_swap(random_access_iterator(buff), random_access_iterator(buff + 1));
204     assert(buff[0] == 1 && buff[1] == 2);
205 
206     std::ranges::iter_swap(contiguous_iterator(buff), contiguous_iterator(buff + 1));
207     assert(buff[0] == 2 && buff[1] == 1);
208   }
209   return true;
210 }
211 
212 static_assert(!std::is_invocable_v<IterSwapT, int*>); // too few arguments
213 static_assert(!std::is_invocable_v<IterSwapT, int*, int*, int*>); // too many arguments
214 static_assert(!std::is_invocable_v<IterSwapT, int, int*>);
215 static_assert(!std::is_invocable_v<IterSwapT, int*, int>);
216 static_assert(!std::is_invocable_v<IterSwapT, void*, void*>);
217 
218 // Test ADL-proofing.
219 struct Incomplete;
220 template<class T> struct Holder { T t; };
221 static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**, Holder<Incomplete>**>);
222 static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**, Holder<Incomplete>**&>);
223 static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**&, Holder<Incomplete>**>);
224 static_assert(std::is_invocable_v<IterSwapT, Holder<Incomplete>**&, Holder<Incomplete>**&>);
225 
main(int,char **)226 int main(int, char**)
227 {
228   test();
229   static_assert(test());
230 
231   return 0;
232 }
233