337 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710 11// std::ranges::size12 13#include <ranges>14 15#include <cassert>16#include "test_macros.h"17#include "test_iterators.h"18 19using RangeSizeT = decltype(std::ranges::size);20 21static_assert(!std::is_invocable_v<RangeSizeT, int[]>);22static_assert( std::is_invocable_v<RangeSizeT, int[1]>);23static_assert( std::is_invocable_v<RangeSizeT, int (&&)[1]>);24static_assert( std::is_invocable_v<RangeSizeT, int (&)[1]>);25 26struct Incomplete;27static_assert(!std::is_invocable_v<RangeSizeT, Incomplete[]>);28static_assert(!std::is_invocable_v<RangeSizeT, Incomplete(&)[]>);29static_assert(!std::is_invocable_v<RangeSizeT, Incomplete(&&)[]>);30 31extern Incomplete array_of_incomplete[42];32static_assert(std::ranges::size(array_of_incomplete) == 42);33static_assert(std::ranges::size(std::move(array_of_incomplete)) == 42);34static_assert(std::ranges::size(std::as_const(array_of_incomplete)) == 42);35static_assert(std::ranges::size(static_cast<const Incomplete(&&)[42]>(array_of_incomplete)) == 42);36 37struct SizeMember {38 constexpr std::size_t size() { return 42; }39};40 41struct StaticSizeMember {42 constexpr static std::size_t size() { return 42; }43};44 45static_assert(!std::is_invocable_v<RangeSizeT, const SizeMember>);46 47struct SizeFunction {48 friend constexpr std::size_t size(SizeFunction) { return 42; }49};50 51// Make sure the size member is preferred.52struct SizeMemberAndFunction {53 constexpr std::size_t size() { return 42; }54 friend constexpr std::size_t size(SizeMemberAndFunction) { return 0; }55};56 57bool constexpr testArrayType() {58 int a[4];59 int b[1];60 SizeMember c[4];61 SizeFunction d[4];62 63 assert(std::ranges::size(a) == 4);64 ASSERT_SAME_TYPE(decltype(std::ranges::size(a)), std::size_t);65 assert(std::ranges::size(b) == 1);66 ASSERT_SAME_TYPE(decltype(std::ranges::size(b)), std::size_t);67 assert(std::ranges::size(c) == 4);68 ASSERT_SAME_TYPE(decltype(std::ranges::size(c)), std::size_t);69 assert(std::ranges::size(d) == 4);70 ASSERT_SAME_TYPE(decltype(std::ranges::size(d)), std::size_t);71 72 return true;73}74 75struct SizeMemberConst {76 constexpr std::size_t size() const { return 42; }77};78 79struct SizeMemberSigned {80 constexpr long size() { return 42; }81};82 83bool constexpr testHasSizeMember() {84 assert(std::ranges::size(SizeMember()) == 42);85 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeMember())), std::size_t);86 87 const SizeMemberConst sizeMemberConst;88 assert(std::ranges::size(sizeMemberConst) == 42);89 90 assert(std::ranges::size(SizeMemberAndFunction()) == 42);91 92 assert(std::ranges::size(SizeMemberSigned()) == 42);93 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeMemberSigned())), long);94 95 assert(std::ranges::size(StaticSizeMember()) == 42);96 ASSERT_SAME_TYPE(decltype(std::ranges::size(StaticSizeMember())), std::size_t);97 98 return true;99}100 101struct MoveOnlySizeFunction {102 MoveOnlySizeFunction() = default;103 MoveOnlySizeFunction(MoveOnlySizeFunction &&) = default;104 MoveOnlySizeFunction(MoveOnlySizeFunction const&) = delete;105 106 friend constexpr std::size_t size(MoveOnlySizeFunction) { return 42; }107};108 109enum EnumSizeFunction {110 a, b111};112 113constexpr std::size_t size(EnumSizeFunction) { return 42; }114 115struct SizeFunctionConst {116 friend constexpr std::size_t size(const SizeFunctionConst) { return 42; }117};118 119struct SizeFunctionRef {120 friend constexpr std::size_t size(SizeFunctionRef&) { return 42; }121};122 123struct SizeFunctionConstRef {124 friend constexpr std::size_t size(SizeFunctionConstRef const&) { return 42; }125};126 127struct SizeFunctionSigned {128 friend constexpr long size(SizeFunctionSigned) { return 42; }129};130 131bool constexpr testHasSizeFunction() {132 assert(std::ranges::size(SizeFunction()) == 42);133 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeFunction())), std::size_t);134 static_assert(!std::is_invocable_v<RangeSizeT, MoveOnlySizeFunction>);135 assert(std::ranges::size(EnumSizeFunction()) == 42);136 assert(std::ranges::size(SizeFunctionConst()) == 42);137 138 SizeFunctionRef a;139 assert(std::ranges::size(a) == 42);140 141 const SizeFunctionConstRef b;142 assert(std::ranges::size(b) == 42);143 144 assert(std::ranges::size(SizeFunctionSigned()) == 42);145 ASSERT_SAME_TYPE(decltype(std::ranges::size(SizeFunctionSigned())), long);146 147 return true;148}149 150struct Empty { };151static_assert(!std::is_invocable_v<RangeSizeT, Empty>);152 153struct InvalidReturnTypeMember {154 Empty size();155};156 157struct InvalidReturnTypeFunction {158 friend Empty size(InvalidReturnTypeFunction);159};160 161struct Convertible {162 operator std::size_t();163};164 165struct ConvertibleReturnTypeMember {166 Convertible size();167};168 169struct ConvertibleReturnTypeFunction {170 friend Convertible size(ConvertibleReturnTypeFunction);171};172 173struct BoolReturnTypeMember {174 bool size() const;175};176 177struct BoolReturnTypeFunction {178 friend bool size(BoolReturnTypeFunction const&);179};180 181static_assert(!std::is_invocable_v<RangeSizeT, InvalidReturnTypeMember>);182static_assert(!std::is_invocable_v<RangeSizeT, InvalidReturnTypeFunction>);183static_assert( std::is_invocable_v<RangeSizeT, InvalidReturnTypeMember (&)[4]>);184static_assert( std::is_invocable_v<RangeSizeT, InvalidReturnTypeFunction (&)[4]>);185static_assert(!std::is_invocable_v<RangeSizeT, ConvertibleReturnTypeMember>);186static_assert(!std::is_invocable_v<RangeSizeT, ConvertibleReturnTypeFunction>);187static_assert(!std::is_invocable_v<RangeSizeT, BoolReturnTypeMember const&>);188static_assert(!std::is_invocable_v<RangeSizeT, BoolReturnTypeFunction const&>);189 190struct SizeMemberDisabled {191 std::size_t size() { return 42; }192};193 194template <>195inline constexpr bool std::ranges::disable_sized_range<SizeMemberDisabled> = true;196 197struct ImproperlyDisabledMember {198 std::size_t size() const { return 42; }199};200 201// Intentionally disabling "const ConstSizeMemberDisabled". This doesn't disable anything202// because T is always uncvrefed before being checked.203template <>204inline constexpr bool std::ranges::disable_sized_range<const ImproperlyDisabledMember> = true;205 206struct SizeFunctionDisabled {207 friend std::size_t size(SizeFunctionDisabled) { return 42; }208};209 210template <>211inline constexpr bool std::ranges::disable_sized_range<SizeFunctionDisabled> = true;212 213struct ImproperlyDisabledFunction {214 friend std::size_t size(ImproperlyDisabledFunction const&) { return 42; }215};216 217template <>218inline constexpr bool std::ranges::disable_sized_range<const ImproperlyDisabledFunction> = true;219 220static_assert( std::is_invocable_v<RangeSizeT, ImproperlyDisabledMember&>);221static_assert( std::is_invocable_v<RangeSizeT, const ImproperlyDisabledMember&>);222static_assert(std::is_invocable_v<RangeSizeT,223 ImproperlyDisabledFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic224static_assert( std::is_invocable_v<RangeSizeT, const ImproperlyDisabledFunction&>);225 226// No begin end.227struct HasMinusOperator {228 friend constexpr std::size_t operator-(HasMinusOperator, HasMinusOperator) { return 2; }229};230static_assert(!std::is_invocable_v<RangeSizeT, HasMinusOperator>);231 232struct 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 243struct other_forward_iterator : forward_iterator<int*> { };244 245struct 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.257static_assert(!std::same_as<other_forward_iterator::difference_type, short>);258static_assert(!std::is_invocable_v<RangeSizeT, InvalidMinusBeginEnd>);259 260struct 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 271struct IntPtrBeginAndEnd {272 int buff[8];273 constexpr int* begin() { return buff; }274 constexpr int* end() { return buff + 8; }275};276 277struct DisabledSizeRangeWithBeginEnd {278 int buff[8];279 constexpr int* begin() { return buff; }280 constexpr int* end() { return buff + 8; }281 constexpr std::size_t size() { return 1; }282};283 284template <>285inline constexpr bool std::ranges::disable_sized_range<DisabledSizeRangeWithBeginEnd> = true;286 287struct SizeBeginAndEndMembers {288 int buff[8];289 constexpr int* begin() { return buff; }290 constexpr int* end() { return buff + 8; }291 constexpr std::size_t size() { return 1; }292};293 294constexpr 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)), std::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)), std::size_t);309 310 SizeBeginAndEndMembers e;311 assert(std::ranges::size(e) == 1);312 313 return true;314}315 316// Test ADL-proofing.317struct Incomplete;318template<class T> struct Holder { T t; };319static_assert(!std::is_invocable_v<RangeSizeT, Holder<Incomplete>*>);320static_assert(!std::is_invocable_v<RangeSizeT, Holder<Incomplete>*&>);321 322int 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