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::begin 14 // std::ranges::cbegin 15 16 #include <ranges> 17 18 #include <cassert> 19 #include "test_macros.h" 20 #include "test_iterators.h" 21 22 using RangeBeginT = decltype(std::ranges::begin); 23 using RangeCBeginT = decltype(std::ranges::cbegin); 24 25 static int globalBuff[8]; 26 27 static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[10]>); 28 static_assert( std::is_invocable_v<RangeBeginT, int (&)[10]>); 29 static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[]>); 30 static_assert( std::is_invocable_v<RangeBeginT, int (&)[]>); 31 32 struct Incomplete; 33 static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[]>); 34 static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[]>); 35 static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[]>); 36 static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[]>); 37 38 static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[10]>); 39 static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[10]>); 40 static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[10]>); 41 static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[10]>); 42 43 // This case is IFNDR; we handle it SFINAE-friendly. 44 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[]>); 45 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[]>); 46 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[]>); 47 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[]>); 48 49 // This case is IFNDR; we handle it SFINAE-friendly. 50 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[10]>); 51 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[10]>); 52 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[10]>); 53 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[10]>); 54 55 struct BeginMember { 56 int x; 57 constexpr const int *begin() const { return &x; } 58 }; 59 60 // Ensure that we can't call with rvalues with borrowing disabled. 61 static_assert( std::is_invocable_v<RangeBeginT, BeginMember &>); 62 static_assert(!std::is_invocable_v<RangeBeginT, BeginMember &&>); 63 static_assert( std::is_invocable_v<RangeBeginT, BeginMember const&>); 64 static_assert(!std::is_invocable_v<RangeBeginT, BeginMember const&&>); 65 static_assert( std::is_invocable_v<RangeCBeginT, BeginMember &>); 66 static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember &&>); 67 static_assert( std::is_invocable_v<RangeCBeginT, BeginMember const&>); 68 static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember const&&>); 69 70 constexpr bool testReturnTypes() { 71 { 72 int *x[2]; 73 ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int**); 74 ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), int* const*); 75 } 76 { 77 int x[2][2]; 78 ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int(*)[2]); 79 ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), const int(*)[2]); 80 } 81 { 82 struct Different { 83 char*& begin(); 84 short*& begin() const; 85 } x; 86 ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), char*); 87 ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), short*); 88 } 89 return true; 90 } 91 92 constexpr bool testArray() { 93 int a[2]; 94 assert(std::ranges::begin(a) == a); 95 assert(std::ranges::cbegin(a) == a); 96 97 int b[2][2]; 98 assert(std::ranges::begin(b) == b); 99 assert(std::ranges::cbegin(b) == b); 100 101 BeginMember c[2]; 102 assert(std::ranges::begin(c) == c); 103 assert(std::ranges::cbegin(c) == c); 104 105 return true; 106 } 107 108 struct BeginMemberFunction { 109 int x; 110 constexpr const int *begin() const { return &x; } 111 friend int *begin(BeginMemberFunction const&); 112 }; 113 114 struct BeginMemberReturnsInt { 115 int begin() const; 116 }; 117 static_assert(!std::is_invocable_v<RangeBeginT, BeginMemberReturnsInt const&>); 118 119 struct BeginMemberReturnsVoidPtr { 120 const void *begin() const; 121 }; 122 static_assert(!std::is_invocable_v<RangeBeginT, BeginMemberReturnsVoidPtr const&>); 123 124 struct EmptyBeginMember { 125 struct iterator {}; 126 iterator begin() const; 127 }; 128 static_assert(!std::is_invocable_v<RangeBeginT, EmptyBeginMember const&>); 129 130 struct EmptyPtrBeginMember { 131 struct Empty {}; 132 Empty x; 133 constexpr const Empty *begin() const { return &x; } 134 }; 135 136 struct PtrConvertibleBeginMember { 137 struct iterator { operator int*() const; }; 138 iterator begin() const; 139 }; 140 static_assert(!std::is_invocable_v<RangeBeginT, PtrConvertibleBeginMember const&>); 141 142 struct NonConstBeginMember { 143 int x; 144 constexpr int *begin() { return &x; } 145 }; 146 static_assert( std::is_invocable_v<RangeBeginT, NonConstBeginMember &>); 147 static_assert(!std::is_invocable_v<RangeBeginT, NonConstBeginMember const&>); 148 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember &>); 149 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember const&>); 150 151 struct EnabledBorrowingBeginMember { 152 constexpr int *begin() const { return &globalBuff[0]; } 153 }; 154 template<> 155 inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingBeginMember> = true; 156 157 constexpr bool testBeginMember() { 158 BeginMember a; 159 assert(std::ranges::begin(a) == &a.x); 160 assert(std::ranges::cbegin(a) == &a.x); 161 static_assert(!std::is_invocable_v<RangeBeginT, BeginMember&&>); 162 static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember&&>); 163 164 NonConstBeginMember b; 165 assert(std::ranges::begin(b) == &b.x); 166 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember&>); 167 168 EnabledBorrowingBeginMember c; 169 assert(std::ranges::begin(c) == &globalBuff[0]); 170 assert(std::ranges::cbegin(c) == &globalBuff[0]); 171 assert(std::ranges::begin(std::move(c)) == &globalBuff[0]); 172 assert(std::ranges::cbegin(std::move(c)) == &globalBuff[0]); 173 174 BeginMemberFunction d; 175 assert(std::ranges::begin(d) == &d.x); 176 assert(std::ranges::cbegin(d) == &d.x); 177 178 EmptyPtrBeginMember e; 179 assert(std::ranges::begin(e) == &e.x); 180 assert(std::ranges::cbegin(e) == &e.x); 181 182 return true; 183 } 184 185 186 struct BeginFunction { 187 int x; 188 friend constexpr const int *begin(BeginFunction const& bf) { return &bf.x; } 189 }; 190 static_assert( std::is_invocable_v<RangeBeginT, BeginFunction const&>); 191 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunction &&>); 192 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunction &>); 193 static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction const&>); 194 static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction &>); 195 196 struct BeginFunctionWithDataMember { 197 int x; 198 int begin; 199 friend constexpr const int *begin(BeginFunctionWithDataMember const& bf) { return &bf.x; } 200 }; 201 202 struct BeginFunctionWithPrivateBeginMember { 203 int y; 204 friend constexpr const int *begin(BeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; } 205 private: 206 const int *begin() const; 207 }; 208 209 struct BeginFunctionReturnsEmptyPtr { 210 struct Empty {}; 211 Empty x; 212 friend constexpr const Empty *begin(BeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; } 213 }; 214 215 struct BeginFunctionByValue { 216 friend constexpr int *begin(BeginFunctionByValue) { return &globalBuff[1]; } 217 }; 218 static_assert(!std::is_invocable_v<RangeCBeginT, BeginFunctionByValue>); 219 220 struct BeginFunctionEnabledBorrowing { 221 friend constexpr int *begin(BeginFunctionEnabledBorrowing) { return &globalBuff[2]; } 222 }; 223 template<> 224 inline constexpr bool std::ranges::enable_borrowed_range<BeginFunctionEnabledBorrowing> = true; 225 226 struct BeginFunctionReturnsInt { 227 friend int begin(BeginFunctionReturnsInt const&); 228 }; 229 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsInt const&>); 230 231 struct BeginFunctionReturnsVoidPtr { 232 friend void *begin(BeginFunctionReturnsVoidPtr const&); 233 }; 234 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsVoidPtr const&>); 235 236 struct BeginFunctionReturnsEmpty { 237 struct Empty {}; 238 friend Empty begin(BeginFunctionReturnsEmpty const&); 239 }; 240 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsEmpty const&>); 241 242 struct BeginFunctionReturnsPtrConvertible { 243 struct iterator { operator int*() const; }; 244 friend iterator begin(BeginFunctionReturnsPtrConvertible const&); 245 }; 246 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsPtrConvertible const&>); 247 248 constexpr bool testBeginFunction() { 249 BeginFunction a{}; 250 const BeginFunction aa{}; 251 static_assert(!std::invocable<RangeBeginT, decltype((a))>); 252 assert(std::ranges::cbegin(a) == &a.x); 253 assert(std::ranges::begin(aa) == &aa.x); 254 assert(std::ranges::cbegin(aa) == &aa.x); 255 256 BeginFunctionByValue b{}; 257 const BeginFunctionByValue bb{}; 258 assert(std::ranges::begin(b) == &globalBuff[1]); 259 assert(std::ranges::cbegin(b) == &globalBuff[1]); 260 assert(std::ranges::begin(bb) == &globalBuff[1]); 261 assert(std::ranges::cbegin(bb) == &globalBuff[1]); 262 263 BeginFunctionEnabledBorrowing c{}; 264 const BeginFunctionEnabledBorrowing cc{}; 265 assert(std::ranges::begin(std::move(c)) == &globalBuff[2]); 266 assert(std::ranges::cbegin(std::move(c)) == &globalBuff[2]); 267 assert(std::ranges::begin(std::move(cc)) == &globalBuff[2]); 268 assert(std::ranges::cbegin(std::move(cc)) == &globalBuff[2]); 269 270 BeginFunctionReturnsEmptyPtr d{}; 271 const BeginFunctionReturnsEmptyPtr dd{}; 272 static_assert(!std::invocable<RangeBeginT, decltype((d))>); 273 assert(std::ranges::cbegin(d) == &d.x); 274 assert(std::ranges::begin(dd) == &dd.x); 275 assert(std::ranges::cbegin(dd) == &dd.x); 276 277 BeginFunctionWithDataMember e{}; 278 const BeginFunctionWithDataMember ee{}; 279 static_assert(!std::invocable<RangeBeginT, decltype((e))>); 280 assert(std::ranges::begin(ee) == &ee.x); 281 assert(std::ranges::cbegin(e) == &e.x); 282 assert(std::ranges::cbegin(ee) == &ee.x); 283 284 BeginFunctionWithPrivateBeginMember f{}; 285 const BeginFunctionWithPrivateBeginMember ff{}; 286 static_assert(!std::invocable<RangeBeginT, decltype((f))>); 287 assert(std::ranges::cbegin(f) == &f.y); 288 assert(std::ranges::begin(ff) == &ff.y); 289 assert(std::ranges::cbegin(ff) == &ff.y); 290 291 return true; 292 } 293 294 295 ASSERT_NOEXCEPT(std::ranges::begin(std::declval<int (&)[10]>())); 296 ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval<int (&)[10]>())); 297 298 struct NoThrowMemberBegin { 299 ThrowingIterator<int> begin() const noexcept; // auto(t.begin()) doesn't throw 300 } ntmb; 301 static_assert(noexcept(std::ranges::begin(ntmb))); 302 static_assert(noexcept(std::ranges::cbegin(ntmb))); 303 304 struct NoThrowADLBegin { 305 friend ThrowingIterator<int> begin(NoThrowADLBegin&) noexcept; // auto(begin(t)) doesn't throw 306 friend ThrowingIterator<int> begin(const NoThrowADLBegin&) noexcept; 307 } ntab; 308 static_assert(noexcept(std::ranges::begin(ntab))); 309 static_assert(noexcept(std::ranges::cbegin(ntab))); 310 311 struct NoThrowMemberBeginReturnsRef { 312 ThrowingIterator<int>& begin() const noexcept; // auto(t.begin()) may throw 313 } ntmbrr; 314 static_assert(!noexcept(std::ranges::begin(ntmbrr))); 315 static_assert(!noexcept(std::ranges::cbegin(ntmbrr))); 316 317 struct BeginReturnsArrayRef { 318 auto begin() const noexcept -> int(&)[10]; 319 } brar; 320 static_assert(noexcept(std::ranges::begin(brar))); 321 static_assert(noexcept(std::ranges::cbegin(brar))); 322 323 // Test ADL-proofing. 324 struct Incomplete; 325 template<class T> struct Holder { T t; }; 326 static_assert(!std::is_invocable_v<RangeBeginT, Holder<Incomplete>*>); 327 static_assert(!std::is_invocable_v<RangeBeginT, Holder<Incomplete>*&>); 328 static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*>); 329 static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*&>); 330 331 int main(int, char**) { 332 static_assert(testReturnTypes()); 333 334 testArray(); 335 static_assert(testArray()); 336 337 testBeginMember(); 338 static_assert(testBeginMember()); 339 340 testBeginFunction(); 341 static_assert(testBeginFunction()); 342 343 return 0; 344 } 345