xref: /llvm-project/libcxx/test/std/ranges/range.access/end.pass.cpp (revision a70fe03961ddb483607861a79ce569a24757919f)
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-no-concepts
11 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
12 
13 // std::ranges::end
14 // std::ranges::cend
15 
16 #include <ranges>
17 
18 #include <cassert>
19 #include "test_macros.h"
20 #include "test_iterators.h"
21 
22 using RangeEndT = decltype(std::ranges::end);
23 using RangeCEndT = decltype(std::ranges::cend);
24 
25 static int globalBuff[8];
26 
27 static_assert(!std::is_invocable_v<RangeEndT, int (&&)[]>);
28 static_assert(!std::is_invocable_v<RangeEndT, int (&)[]>);
29 static_assert(!std::is_invocable_v<RangeEndT, int (&&)[10]>);
30 static_assert( std::is_invocable_v<RangeEndT, int (&)[10]>);
31 
32 struct Incomplete;
33 static_assert(!std::is_invocable_v<RangeEndT, Incomplete(&&)[]>);
34 static_assert(!std::is_invocable_v<RangeEndT, Incomplete(&&)[42]>);
35 static_assert(!std::is_invocable_v<RangeCEndT, Incomplete(&&)[]>);
36 static_assert(!std::is_invocable_v<RangeCEndT, Incomplete(&&)[42]>);
37 
38 struct EndMember {
39   int x;
40   const int *begin() const;
41   constexpr const int *end() const { return &x; }
42 };
43 
44 // Ensure that we can't call with rvalues with borrowing disabled.
45 static_assert( std::is_invocable_v<RangeEndT, EndMember &>);
46 static_assert(!std::is_invocable_v<RangeEndT, EndMember &&>);
47 static_assert( std::is_invocable_v<RangeEndT, EndMember const&>);
48 static_assert(!std::is_invocable_v<RangeEndT, EndMember const&&>);
49 static_assert( std::is_invocable_v<RangeCEndT, EndMember &>);
50 static_assert(!std::is_invocable_v<RangeCEndT, EndMember &&>);
51 static_assert( std::is_invocable_v<RangeCEndT, EndMember const&>);
52 static_assert(!std::is_invocable_v<RangeCEndT, EndMember const&&>);
53 
54 constexpr bool testReturnTypes() {
55   {
56     int *x[2];
57     ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), int**);
58     ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), int* const*);
59   }
60   {
61     int x[2][2];
62     ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), int(*)[2]);
63     ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), const int(*)[2]);
64   }
65   {
66     struct Different {
67       char *begin();
68       sentinel_wrapper<char*>& end();
69       short *begin() const;
70       sentinel_wrapper<short*>& end() const;
71     } x;
72     ASSERT_SAME_TYPE(decltype(std::ranges::end(x)), sentinel_wrapper<char*>);
73     ASSERT_SAME_TYPE(decltype(std::ranges::cend(x)), sentinel_wrapper<short*>);
74   }
75   return true;
76 }
77 
78 constexpr bool testArray() {
79   int a[2];
80   assert(std::ranges::end(a) == a + 2);
81   assert(std::ranges::cend(a) == a + 2);
82 
83   int b[2][2];
84   assert(std::ranges::end(b) == b + 2);
85   assert(std::ranges::cend(b) == b + 2);
86 
87   EndMember c[2];
88   assert(std::ranges::end(c) == c + 2);
89   assert(std::ranges::cend(c) == c + 2);
90 
91   return true;
92 }
93 
94 struct EndMemberFunction {
95   int x;
96   constexpr const int *begin() const { return nullptr; }
97   constexpr const int *end() const { return &x; }
98   friend constexpr int *end(EndMemberFunction const&);
99 };
100 
101 struct EndMemberReturnsInt {
102   int begin() const;
103   int end() const;
104 };
105 
106 static_assert(!std::is_invocable_v<RangeEndT, EndMemberReturnsInt const&>);
107 
108 struct EndMemberReturnsVoidPtr {
109   const void *begin() const;
110   const void *end() const;
111 };
112 
113 static_assert(!std::is_invocable_v<RangeEndT, EndMemberReturnsVoidPtr const&>);
114 
115 struct Empty { };
116 struct EmptyEndMember {
117   Empty begin() const;
118   Empty end() const;
119 };
120 struct EmptyPtrEndMember {
121   Empty x;
122   constexpr const Empty *begin() const { return nullptr; }
123   constexpr const Empty *end() const { return &x; }
124 };
125 
126 static_assert(!std::is_invocable_v<RangeEndT, EmptyEndMember const&>);
127 
128 struct PtrConvertible {
129   operator int*() const;
130 };
131 struct PtrConvertibleEndMember {
132   PtrConvertible begin() const;
133   PtrConvertible end() const;
134 };
135 
136 static_assert(!std::is_invocable_v<RangeEndT, PtrConvertibleEndMember const&>);
137 
138 struct NoBeginMember {
139   constexpr const int *end();
140 };
141 
142 static_assert(!std::is_invocable_v<RangeEndT, NoBeginMember const&>);
143 
144 struct NonConstEndMember {
145   int x;
146   constexpr int *begin() { return nullptr; }
147   constexpr int *end() { return &x; }
148 };
149 
150 static_assert( std::is_invocable_v<RangeEndT,  NonConstEndMember &>);
151 static_assert(!std::is_invocable_v<RangeEndT,  NonConstEndMember const&>);
152 static_assert(!std::is_invocable_v<RangeCEndT, NonConstEndMember &>);
153 static_assert(!std::is_invocable_v<RangeCEndT, NonConstEndMember const&>);
154 
155 struct EnabledBorrowingEndMember {
156   constexpr int *begin() const { return nullptr; }
157   constexpr int *end() const { return &globalBuff[0]; }
158 };
159 
160 template<>
161 inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingEndMember> = true;
162 
163 constexpr bool testEndMember() {
164   EndMember a;
165   assert(std::ranges::end(a) == &a.x);
166   assert(std::ranges::cend(a) == &a.x);
167 
168   NonConstEndMember b;
169   assert(std::ranges::end(b) == &b.x);
170   static_assert(!std::is_invocable_v<RangeCEndT, decltype((b))>);
171 
172   EnabledBorrowingEndMember c;
173   assert(std::ranges::end(std::move(c)) == &globalBuff[0]);
174   assert(std::ranges::cend(std::move(c)) == &globalBuff[0]);
175 
176   EndMemberFunction d;
177   assert(std::ranges::end(d) == &d.x);
178   assert(std::ranges::cend(d) == &d.x);
179 
180   EmptyPtrEndMember e;
181   assert(std::ranges::end(e) == &e.x);
182   assert(std::ranges::cend(e) == &e.x);
183 
184   return true;
185 }
186 
187 struct EndFunction {
188   int x;
189   friend constexpr const int *begin(EndFunction const&) { return nullptr; }
190   friend constexpr const int *end(EndFunction const& bf) { return &bf.x; }
191 };
192 
193 static_assert( std::is_invocable_v<RangeEndT, EndFunction const&>);
194 static_assert(!std::is_invocable_v<RangeEndT, EndFunction &&>);
195 
196 static_assert( std::is_invocable_v<RangeEndT,  EndFunction const&>);
197 static_assert(!std::is_invocable_v<RangeEndT,  EndFunction &&>);
198 static_assert(!std::is_invocable_v<RangeEndT,  EndFunction &>);
199 static_assert( std::is_invocable_v<RangeCEndT, EndFunction const&>);
200 static_assert( std::is_invocable_v<RangeCEndT, EndFunction &>);
201 
202 struct EndFunctionWithDataMember {
203   int x;
204   int end;
205   friend constexpr const int *begin(EndFunctionWithDataMember const&) { return nullptr; }
206   friend constexpr const int *end(EndFunctionWithDataMember const& bf) { return &bf.x; }
207 };
208 
209 struct EndFunctionWithPrivateEndMember : private EndMember {
210   int y;
211   friend constexpr const int *begin(EndFunctionWithPrivateEndMember const&) { return nullptr; }
212   friend constexpr const int *end(EndFunctionWithPrivateEndMember const& bf) { return &bf.y; }
213 };
214 
215 struct EndFunctionReturnsEmptyPtr {
216   Empty x;
217   friend constexpr const Empty *begin(EndFunctionReturnsEmptyPtr const&) { return nullptr; }
218   friend constexpr const Empty *end(EndFunctionReturnsEmptyPtr const& bf) { return &bf.x; }
219 };
220 
221 struct EndFunctionByValue {
222   friend constexpr int *begin(EndFunctionByValue) { return nullptr; }
223   friend constexpr int *end(EndFunctionByValue) { return &globalBuff[1]; }
224 };
225 
226 static_assert(!std::is_invocable_v<RangeCEndT, EndFunctionByValue>);
227 
228 struct EndFunctionEnabledBorrowing {
229   friend constexpr int *begin(EndFunctionEnabledBorrowing) { return nullptr; }
230   friend constexpr int *end(EndFunctionEnabledBorrowing) { return &globalBuff[2]; }
231 };
232 
233 template<>
234 inline constexpr bool std::ranges::enable_borrowed_range<EndFunctionEnabledBorrowing> = true;
235 
236 struct EndFunctionReturnsInt {
237   friend constexpr int begin(EndFunctionReturnsInt const&);
238   friend constexpr int end(EndFunctionReturnsInt const&);
239 };
240 
241 static_assert(!std::is_invocable_v<RangeEndT, EndFunctionReturnsInt const&>);
242 
243 struct EndFunctionReturnsVoidPtr {
244   friend constexpr void *begin(EndFunctionReturnsVoidPtr const&);
245   friend constexpr void *end(EndFunctionReturnsVoidPtr const&);
246 };
247 
248 static_assert(!std::is_invocable_v<RangeEndT, EndFunctionReturnsVoidPtr const&>);
249 
250 struct EndFunctionReturnsEmpty {
251   friend constexpr Empty begin(EndFunctionReturnsEmpty const&);
252   friend constexpr Empty end(EndFunctionReturnsEmpty const&);
253 };
254 
255 static_assert(!std::is_invocable_v<RangeEndT, EndFunctionReturnsEmpty const&>);
256 
257 struct EndFunctionReturnsPtrConvertible {
258   friend constexpr PtrConvertible begin(EndFunctionReturnsPtrConvertible const&);
259   friend constexpr PtrConvertible end(EndFunctionReturnsPtrConvertible const&);
260 };
261 
262 static_assert(!std::is_invocable_v<RangeEndT, EndFunctionReturnsPtrConvertible const&>);
263 
264 struct NoBeginFunction {
265   friend constexpr const int *end(NoBeginFunction const&);
266 };
267 
268 static_assert(!std::is_invocable_v<RangeEndT, NoBeginFunction const&>);
269 
270 struct BeginMemberEndFunction {
271   int x;
272   constexpr const int *begin() const { return nullptr; }
273   friend constexpr const int *end(BeginMemberEndFunction const& bf) { return &bf.x; }
274 };
275 
276 constexpr bool testEndFunction() {
277   const EndFunction a{};
278   assert(std::ranges::end(a) == &a.x);
279   assert(std::ranges::cend(a) == &a.x);
280   EndFunction aa{};
281   static_assert(!std::is_invocable_v<RangeEndT, decltype((aa))>);
282   assert(std::ranges::cend(aa) == &aa.x);
283 
284   EndFunctionByValue b;
285   assert(std::ranges::end(b) == &globalBuff[1]);
286   assert(std::ranges::cend(b) == &globalBuff[1]);
287 
288   EndFunctionEnabledBorrowing c;
289   assert(std::ranges::end(std::move(c)) == &globalBuff[2]);
290   assert(std::ranges::cend(std::move(c)) == &globalBuff[2]);
291 
292   const EndFunctionReturnsEmptyPtr d{};
293   assert(std::ranges::end(d) == &d.x);
294   assert(std::ranges::cend(d) == &d.x);
295   EndFunctionReturnsEmptyPtr dd{};
296   static_assert(!std::is_invocable_v<RangeEndT, decltype((dd))>);
297   assert(std::ranges::cend(dd) == &dd.x);
298 
299   const EndFunctionWithDataMember e{};
300   assert(std::ranges::end(e) == &e.x);
301   assert(std::ranges::cend(e) == &e.x);
302   EndFunctionWithDataMember ee{};
303   static_assert(!std::is_invocable_v<RangeEndT, decltype((ee))>);
304   assert(std::ranges::cend(ee) == &ee.x);
305 
306   const EndFunctionWithPrivateEndMember f{};
307   assert(std::ranges::end(f) == &f.y);
308   assert(std::ranges::cend(f) == &f.y);
309   EndFunctionWithPrivateEndMember ff{};
310   static_assert(!std::is_invocable_v<RangeEndT, decltype((ff))>);
311   assert(std::ranges::cend(ff) == &ff.y);
312 
313   const BeginMemberEndFunction g{};
314   assert(std::ranges::end(g) == &g.x);
315   assert(std::ranges::cend(g) == &g.x);
316   BeginMemberEndFunction gg{};
317   static_assert(!std::is_invocable_v<RangeEndT, decltype((gg))>);
318   assert(std::ranges::cend(gg) == &gg.x);
319 
320   return true;
321 }
322 
323 
324 ASSERT_NOEXCEPT(std::ranges::end(std::declval<int (&)[10]>()));
325 ASSERT_NOEXCEPT(std::ranges::cend(std::declval<int (&)[10]>()));
326 
327 struct NoThrowMemberEnd {
328   ThrowingIterator<int> begin() const;
329   ThrowingIterator<int> end() const noexcept; // auto(t.end()) doesn't throw
330 } ntme;
331 static_assert(noexcept(std::ranges::end(ntme)));
332 static_assert(noexcept(std::ranges::cend(ntme)));
333 
334 struct NoThrowADLEnd {
335   ThrowingIterator<int> begin() const;
336   friend ThrowingIterator<int> end(NoThrowADLEnd&) noexcept;  // auto(end(t)) doesn't throw
337   friend ThrowingIterator<int> end(const NoThrowADLEnd&) noexcept;
338 } ntae;
339 static_assert(noexcept(std::ranges::end(ntae)));
340 static_assert(noexcept(std::ranges::cend(ntae)));
341 
342 struct NoThrowMemberEndReturnsRef {
343   ThrowingIterator<int> begin() const;
344   ThrowingIterator<int>& end() const noexcept; // auto(t.end()) may throw
345 } ntmerr;
346 static_assert(!noexcept(std::ranges::end(ntmerr)));
347 static_assert(!noexcept(std::ranges::cend(ntmerr)));
348 
349 struct EndReturnsArrayRef {
350     auto begin() const noexcept -> int(&)[10];
351     auto end() const noexcept -> int(&)[10];
352 } erar;
353 static_assert(noexcept(std::ranges::end(erar)));
354 static_assert(noexcept(std::ranges::cend(erar)));
355 
356 // Test ADL-proofing.
357 struct Incomplete;
358 template<class T> struct Holder { T t; };
359 static_assert(!std::is_invocable_v<RangeEndT, Holder<Incomplete>*>);
360 static_assert(!std::is_invocable_v<RangeCEndT, Holder<Incomplete>*>);
361 
362 int main(int, char**) {
363   static_assert(testReturnTypes());
364 
365   testArray();
366   static_assert(testArray());
367 
368   testEndMember();
369   static_assert(testEndMember());
370 
371   testEndFunction();
372   static_assert(testEndFunction());
373 
374   return 0;
375 }
376