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 // std::ranges::size 13 14 #include <ranges> 15 16 #include <cassert> 17 #include "test_macros.h" 18 #include "test_iterators.h" 19 20 using RangeSizeT = decltype(std::ranges::size); 21 22 static_assert(!std::is_invocable_v<RangeSizeT, int[]>); 23 static_assert( std::is_invocable_v<RangeSizeT, int[1]>); 24 static_assert( std::is_invocable_v<RangeSizeT, int (&&)[1]>); 25 static_assert( std::is_invocable_v<RangeSizeT, int (&)[1]>); 26 27 struct Incomplete; 28 static_assert(!std::is_invocable_v<RangeSizeT, Incomplete[]>); 29 static_assert(!std::is_invocable_v<RangeSizeT, Incomplete(&)[]>); 30 static_assert(!std::is_invocable_v<RangeSizeT, Incomplete(&&)[]>); 31 32 extern Incomplete array_of_incomplete[42]; 33 static_assert(std::ranges::size(array_of_incomplete) == 42); 34 static_assert(std::ranges::size(std::move(array_of_incomplete)) == 42); 35 static_assert(std::ranges::size(std::as_const(array_of_incomplete)) == 42); 36 static_assert(std::ranges::size(static_cast<const Incomplete(&&)[42]>(array_of_incomplete)) == 42); 37 38 struct SizeMember { 39 constexpr size_t size() { return 42; } 40 }; 41 42 struct StaticSizeMember { 43 constexpr static size_t size() { return 42; } 44 }; 45 46 static_assert(!std::is_invocable_v<RangeSizeT, const SizeMember>); 47 48 struct SizeFunction { 49 friend constexpr size_t size(SizeFunction) { return 42; } 50 }; 51 52 // Make sure the size member is preferred. 53 struct SizeMemberAndFunction { 54 constexpr size_t size() { return 42; } 55 friend constexpr size_t size(SizeMemberAndFunction) { return 0; } 56 }; 57 58 bool constexpr testArrayType() { 59 int a[4]; 60 int b[1]; 61 SizeMember c[4]; 62 SizeFunction d[4]; 63 64 assert(std::ranges::size(a) == 4); 65 ASSERT_SAME_TYPE(decltype(std::ranges::size(a)), size_t); 66 assert(std::ranges::size(b) == 1); 67 ASSERT_SAME_TYPE(decltype(std::ranges::size(b)), size_t); 68 assert(std::ranges::size(c) == 4); 69 ASSERT_SAME_TYPE(decltype(std::ranges::size(c)), size_t); 70 assert(std::ranges::size(d) == 4); 71 ASSERT_SAME_TYPE(decltype(std::ranges::size(d)), size_t); 72 73 return true; 74 } 75 76 struct SizeMemberConst { 77 constexpr size_t size() const { return 42; } 78 }; 79 80 struct SizeMemberSigned { 81 constexpr long size() { return 42; } 82 }; 83 84 bool constexpr testHasSizeMember() { 85 assert(std::ranges::size(SizeMember()) == 42); 86 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeMember())), size_t); 87 88 const SizeMemberConst sizeMemberConst; 89 assert(std::ranges::size(sizeMemberConst) == 42); 90 91 assert(std::ranges::size(SizeMemberAndFunction()) == 42); 92 93 assert(std::ranges::size(SizeMemberSigned()) == 42); 94 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeMemberSigned())), long); 95 96 assert(std::ranges::size(StaticSizeMember()) == 42); 97 ASSERT_SAME_TYPE(decltype(std::ranges::size(StaticSizeMember())), size_t); 98 99 return true; 100 } 101 102 struct MoveOnlySizeFunction { 103 MoveOnlySizeFunction() = default; 104 MoveOnlySizeFunction(MoveOnlySizeFunction &&) = default; 105 MoveOnlySizeFunction(MoveOnlySizeFunction const&) = delete; 106 107 friend constexpr size_t size(MoveOnlySizeFunction) { return 42; } 108 }; 109 110 enum EnumSizeFunction { 111 a, b 112 }; 113 114 constexpr size_t size(EnumSizeFunction) { return 42; } 115 116 struct SizeFunctionConst { 117 friend constexpr size_t size(const SizeFunctionConst) { return 42; } 118 }; 119 120 struct SizeFunctionRef { 121 friend constexpr size_t size(SizeFunctionRef&) { return 42; } 122 }; 123 124 struct SizeFunctionConstRef { 125 friend constexpr size_t size(SizeFunctionConstRef const&) { return 42; } 126 }; 127 128 struct SizeFunctionSigned { 129 friend constexpr long size(SizeFunctionSigned) { return 42; } 130 }; 131 132 bool constexpr testHasSizeFunction() { 133 assert(std::ranges::size(SizeFunction()) == 42); 134 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeFunction())), size_t); 135 static_assert(!std::is_invocable_v<RangeSizeT, MoveOnlySizeFunction>); 136 assert(std::ranges::size(EnumSizeFunction()) == 42); 137 assert(std::ranges::size(SizeFunctionConst()) == 42); 138 139 SizeFunctionRef a; 140 assert(std::ranges::size(a) == 42); 141 142 const SizeFunctionConstRef b; 143 assert(std::ranges::size(b) == 42); 144 145 assert(std::ranges::size(SizeFunctionSigned()) == 42); 146 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeFunctionSigned())), long); 147 148 return true; 149 } 150 151 struct Empty { }; 152 static_assert(!std::is_invocable_v<RangeSizeT, Empty>); 153 154 struct InvalidReturnTypeMember { 155 Empty size(); 156 }; 157 158 struct InvalidReturnTypeFunction { 159 friend Empty size(InvalidReturnTypeFunction); 160 }; 161 162 struct Convertible { 163 operator size_t(); 164 }; 165 166 struct ConvertibleReturnTypeMember { 167 Convertible size(); 168 }; 169 170 struct ConvertibleReturnTypeFunction { 171 friend Convertible size(ConvertibleReturnTypeFunction); 172 }; 173 174 struct BoolReturnTypeMember { 175 bool size() const; 176 }; 177 178 struct BoolReturnTypeFunction { 179 friend bool size(BoolReturnTypeFunction const&); 180 }; 181 182 static_assert(!std::is_invocable_v<RangeSizeT, InvalidReturnTypeMember>); 183 static_assert(!std::is_invocable_v<RangeSizeT, InvalidReturnTypeFunction>); 184 static_assert( std::is_invocable_v<RangeSizeT, InvalidReturnTypeMember (&)[4]>); 185 static_assert( std::is_invocable_v<RangeSizeT, InvalidReturnTypeFunction (&)[4]>); 186 static_assert(!std::is_invocable_v<RangeSizeT, ConvertibleReturnTypeMember>); 187 static_assert(!std::is_invocable_v<RangeSizeT, ConvertibleReturnTypeFunction>); 188 static_assert(!std::is_invocable_v<RangeSizeT, BoolReturnTypeMember const&>); 189 static_assert(!std::is_invocable_v<RangeSizeT, BoolReturnTypeFunction const&>); 190 191 struct SizeMemberDisabled { 192 size_t size() { return 42; } 193 }; 194 195 template <> 196 inline constexpr bool std::ranges::disable_sized_range<SizeMemberDisabled> = true; 197 198 struct ImproperlyDisabledMember { 199 size_t size() const { return 42; } 200 }; 201 202 // Intentionally disabling "const ConstSizeMemberDisabled". This doesn't disable anything 203 // because T is always uncvrefed before being checked. 204 template <> 205 inline constexpr bool std::ranges::disable_sized_range<const ImproperlyDisabledMember> = true; 206 207 struct SizeFunctionDisabled { 208 friend size_t size(SizeFunctionDisabled) { return 42; } 209 }; 210 211 template <> 212 inline constexpr bool std::ranges::disable_sized_range<SizeFunctionDisabled> = true; 213 214 struct ImproperlyDisabledFunction { 215 friend size_t size(ImproperlyDisabledFunction const&) { return 42; } 216 }; 217 218 template <> 219 inline constexpr bool std::ranges::disable_sized_range<const ImproperlyDisabledFunction> = true; 220 221 static_assert( std::is_invocable_v<RangeSizeT, ImproperlyDisabledMember&>); 222 static_assert( std::is_invocable_v<RangeSizeT, const ImproperlyDisabledMember&>); 223 static_assert(!std::is_invocable_v<RangeSizeT, ImproperlyDisabledFunction&>); 224 static_assert( std::is_invocable_v<RangeSizeT, const ImproperlyDisabledFunction&>); 225 226 // No begin end. 227 struct HasMinusOperator { 228 friend constexpr size_t operator-(HasMinusOperator, HasMinusOperator) { return 2; } 229 }; 230 static_assert(!std::is_invocable_v<RangeSizeT, HasMinusOperator>); 231 232 struct HasMinusBeginEnd { 233 struct sentinel { 234 friend bool operator==(sentinel, forward_iterator<int*>); 235 friend constexpr std::ptrdiff_t operator-(const sentinel, const forward_iterator<int*>) { return 2; } 236 friend constexpr std::ptrdiff_t operator-(const forward_iterator<int*>, const sentinel) { return 2; } 237 }; 238 239 friend constexpr forward_iterator<int*> begin(HasMinusBeginEnd) { return {}; } 240 friend constexpr sentinel end(HasMinusBeginEnd) { return {}; } 241 }; 242 243 struct other_forward_iterator : forward_iterator<int*> { }; 244 245 struct InvalidMinusBeginEnd { 246 struct sentinel { 247 friend bool operator==(sentinel, other_forward_iterator); 248 friend constexpr std::ptrdiff_t operator-(const sentinel, const other_forward_iterator) { return 2; } 249 friend constexpr std::ptrdiff_t operator-(const other_forward_iterator, const sentinel) { return 2; } 250 }; 251 252 friend constexpr other_forward_iterator begin(InvalidMinusBeginEnd) { return {}; } 253 friend constexpr sentinel end(InvalidMinusBeginEnd) { return {}; } 254 }; 255 256 // short is integer-like, but it is not other_forward_iterator's difference_type. 257 static_assert(!std::same_as<other_forward_iterator::difference_type, short>); 258 static_assert(!std::is_invocable_v<RangeSizeT, InvalidMinusBeginEnd>); 259 260 struct RandomAccessRange { 261 struct sentinel { 262 friend bool operator==(sentinel, random_access_iterator<int*>); 263 friend constexpr std::ptrdiff_t operator-(const sentinel, const random_access_iterator<int*>) { return 2; } 264 friend constexpr std::ptrdiff_t operator-(const random_access_iterator<int*>, const sentinel) { return 2; } 265 }; 266 267 constexpr random_access_iterator<int*> begin() { return {}; } 268 constexpr sentinel end() { return {}; } 269 }; 270 271 struct IntPtrBeginAndEnd { 272 int buff[8]; 273 constexpr int* begin() { return buff; } 274 constexpr int* end() { return buff + 8; } 275 }; 276 277 struct DisabledSizeRangeWithBeginEnd { 278 int buff[8]; 279 constexpr int* begin() { return buff; } 280 constexpr int* end() { return buff + 8; } 281 constexpr size_t size() { return 1; } 282 }; 283 284 template <> 285 inline constexpr bool std::ranges::disable_sized_range<DisabledSizeRangeWithBeginEnd> = true; 286 287 struct SizeBeginAndEndMembers { 288 int buff[8]; 289 constexpr int* begin() { return buff; } 290 constexpr int* end() { return buff + 8; } 291 constexpr size_t size() { return 1; } 292 }; 293 294 constexpr bool testRanges() { 295 HasMinusBeginEnd a; 296 assert(std::ranges::size(a) == 2); 297 // Ensure that this is converted to an *unsigned* type. 298 ASSERT_SAME_TYPE(decltype(std::ranges::size(a)), size_t); 299 300 IntPtrBeginAndEnd b; 301 assert(std::ranges::size(b) == 8); 302 303 DisabledSizeRangeWithBeginEnd c; 304 assert(std::ranges::size(c) == 8); 305 306 RandomAccessRange d; 307 assert(std::ranges::size(d) == 2); 308 ASSERT_SAME_TYPE(decltype(std::ranges::size(d)), size_t); 309 310 SizeBeginAndEndMembers e; 311 assert(std::ranges::size(e) == 1); 312 313 return true; 314 } 315 316 // Test ADL-proofing. 317 struct Incomplete; 318 template<class T> struct Holder { T t; }; 319 static_assert(!std::is_invocable_v<RangeSizeT, Holder<Incomplete>*>); 320 static_assert(!std::is_invocable_v<RangeSizeT, Holder<Incomplete>*&>); 321 322 int main(int, char**) { 323 testArrayType(); 324 static_assert(testArrayType()); 325 326 testHasSizeMember(); 327 static_assert(testHasSizeMember()); 328 329 testHasSizeFunction(); 330 static_assert(testHasSizeFunction()); 331 332 testRanges(); 333 static_assert(testRanges()); 334 335 return 0; 336 } 337