Index: libcxx/include/__ranges/access.h =================================================================== --- libcxx/include/__ranges/access.h +++ libcxx/include/__ranges/access.h @@ -9,13 +9,14 @@ #ifndef _LIBCPP___RANGES_ACCESS_H #define _LIBCPP___RANGES_ACCESS_H +#include <__concepts/class_or_enum.h> #include <__config> #include <__iterator/concepts.h> -#include <__iterator/readable_traits.h> #include <__ranges/enable_borrowed_range.h> #include <__utility/as_const.h> #include <__utility/auto_cast.h> -#include +#include <__utility/forward.h> +#include <__utility/priority_tag.h> #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -26,173 +27,199 @@ #if !defined(_LIBCPP_HAS_NO_RANGES) -namespace ranges { - template - concept __can_borrow = - is_lvalue_reference_v<_Tp> || enable_borrowed_range >; -} // namespace ranges - -// [range.access.begin] -namespace ranges::__begin { - template - concept __member_begin = - __can_borrow<_Tp> && - requires(_Tp&& __t) { - { _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator; - }; +// [ranges.access.begin] +namespace ranges { +namespace __begin { void begin(auto&) = delete; void begin(const auto&) = delete; template - concept __unqualified_begin = - !__member_begin<_Tp> && - __can_borrow<_Tp> && - __class_or_enum > && - requires(_Tp && __t) { + concept __basic_requirements = + is_array_v> || + requires (_Tp __t) { + requires __class_or_enum>; + __t.begin(); + { _LIBCPP_AUTO_CAST(__t.begin()) } -> input_or_output_iterator; + } || + requires (_Tp __t) { + requires __class_or_enum>; + begin(__t); { _LIBCPP_AUTO_CAST(begin(__t)) } -> input_or_output_iterator; }; struct __fn { template - requires is_array_v> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const noexcept - { - return __t; - } - - template - requires __member_begin<_Tp> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.begin()))) - { - return _LIBCPP_AUTO_CAST(__t.begin()); - } + requires is_rvalue_reference_v<_Tp&&> && (!enable_borrowed_range>) + _LIBCPP_HIDE_FROM_ABI + static constexpr void __go(_Tp&&, __priority_tag<3>) = delete; + + template >>* = nullptr> + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<2>) + noexcept(noexcept(_LIBCPP_AUTO_CAST(__t))) + -> decltype( _LIBCPP_AUTO_CAST(__t)) + { return _LIBCPP_AUTO_CAST(__t); } + + template >>* = nullptr> + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<1>) + noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.begin()))) + -> decltype( _LIBCPP_AUTO_CAST(__t.begin())) + requires input_or_output_iterator + { return _LIBCPP_AUTO_CAST(__t.begin()); } + + template >>* = nullptr> + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<0>) + noexcept(noexcept(_LIBCPP_AUTO_CAST(begin(__t)))) + -> decltype( _LIBCPP_AUTO_CAST(begin(__t))) + requires input_or_output_iterator + { return _LIBCPP_AUTO_CAST(begin(__t)); } template - requires __unqualified_begin<_Tp> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(_LIBCPP_AUTO_CAST(begin(__t)))) - { - return _LIBCPP_AUTO_CAST(begin(__t)); - } - - void operator()(auto&&) const = delete; + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator()(_Tp&& __t) const + noexcept(noexcept(__go(_VSTD::forward<_Tp>(__t), __priority_tag<3>()))) + requires __basic_requirements<_Tp&&> && + (requires { __go(_VSTD::forward<_Tp>(__t), __priority_tag<3>()); }) + { return __go(_VSTD::forward<_Tp>(__t), __priority_tag<3>()); } }; -} // namespace ranges::__begin +} -namespace ranges { - inline namespace __cpo { - inline constexpr auto begin = __begin::__fn{}; - } // namespace __cpo +inline namespace __cpo { + inline constexpr auto begin = __begin::__fn{}; +} // namespace __cpo +} // namespace ranges +namespace ranges { template using iterator_t = decltype(ranges::begin(declval<_Tp&>())); } // namespace ranges -// [range.access.end] -namespace ranges::__end { - template - concept __member_end = - __can_borrow<_Tp> && - requires(_Tp&& __t) { - typename iterator_t<_Tp>; - { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for >; - }; +// [ranges.access.end] +namespace ranges { +namespace __end { void end(auto&) = delete; void end(const auto&) = delete; template - concept __unqualified_end = - !__member_end<_Tp> && - __can_borrow<_Tp> && - __class_or_enum > && - requires(_Tp && __t) { - typename iterator_t<_Tp>; - { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for >; + concept __basic_requirements = + is_bounded_array_v> || + requires (_Tp __t) { + requires __class_or_enum>; + __t.end(); + { _LIBCPP_AUTO_CAST(__t.end()) } -> sentinel_for>>; + } || + requires (_Tp __t) { + requires __class_or_enum>; + end(__t); + { _LIBCPP_AUTO_CAST(end(__t)) } -> sentinel_for>>; }; - class __fn { - public: - template - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept - requires (sizeof(*__t) != 0) // Disallow incomplete element types. - { - return __t + _Np; - } + struct __fn { + template + requires is_rvalue_reference_v<_Tp&&> && (!enable_borrowed_range>) + _LIBCPP_HIDE_FROM_ABI + static constexpr void __go(_Tp&&, __priority_tag<4>) = delete; template - requires __member_end<_Tp> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.end()))) - { - return _LIBCPP_AUTO_CAST(__t.end()); - } + requires is_unbounded_array_v> + _LIBCPP_HIDE_FROM_ABI + static constexpr void __go(_Tp&&, __priority_tag<3>) = delete; + + template >>* = nullptr> + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<2>) + noexcept(noexcept(__t + extent_v>)) + -> decltype( __t + extent_v>) + { return __t + extent_v>; } + + template >>* = nullptr> + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<1>) + noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.end()))) + -> decltype( _LIBCPP_AUTO_CAST(__t.end())) + requires sentinel_for>> + { return _LIBCPP_AUTO_CAST(__t.end()); } + + template >>* = nullptr> + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<0>) + noexcept(noexcept(_LIBCPP_AUTO_CAST(end(__t)))) + -> decltype( _LIBCPP_AUTO_CAST(end(__t))) + requires sentinel_for>> + { return _LIBCPP_AUTO_CAST(end(__t)); } template - requires __unqualified_end<_Tp> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(_LIBCPP_AUTO_CAST(end(__t)))) - { - return _LIBCPP_AUTO_CAST(end(__t)); - } - - void operator()(auto&&) const = delete; + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator()(_Tp&& __t) const + noexcept(noexcept(__go(_VSTD::forward<_Tp>(__t), __priority_tag<4>()))) + requires __basic_requirements<_Tp&&> && + (requires { __go(_VSTD::forward<_Tp>(__t), __priority_tag<4>()); }) + { return __go(_VSTD::forward<_Tp>(__t), __priority_tag<4>()); } }; -} // namespace ranges::__end +} -namespace ranges::inline __cpo { +inline namespace __cpo { inline constexpr auto end = __end::__fn{}; -} // namespace ranges::__cpo +} // namespace __cpo +} // namespace ranges -namespace ranges::__cbegin { +// [range.access.cbegin] + +namespace ranges { +namespace __cbegin { struct __fn { template - requires invocable - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const - noexcept(noexcept(ranges::begin(_VSTD::as_const(__t)))) - { - return ranges::begin(_VSTD::as_const(__t)); - } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Tp& __t) const + noexcept(noexcept(ranges::begin(static_cast(__t)))) + -> decltype( ranges::begin(static_cast(__t))) + { return ranges::begin(static_cast(__t)); } template - requires is_rvalue_reference_v<_Tp> && invocable - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(ranges::begin(static_cast<_Tp const&&>(__t)))) - { - return ranges::begin(static_cast<_Tp const&&>(__t)); - } + requires is_rvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::begin(static_cast(__t)))) + -> decltype( ranges::begin(static_cast(__t))) + { return ranges::begin(static_cast(__t)); } }; -} // namespace ranges::__cbegin +} -namespace ranges::inline __cpo { +inline namespace __cpo { inline constexpr auto cbegin = __cbegin::__fn{}; -} // namespace ranges::__cpo +} // namespace __cpo +} // namespace ranges -namespace ranges::__cend { +// [range.access.cend] + +namespace ranges { +namespace __cend { struct __fn { template - requires invocable - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const - noexcept(noexcept(ranges::end(_VSTD::as_const(__t)))) - { - return ranges::end(_VSTD::as_const(__t)); - } + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Tp& __t) const + noexcept(noexcept(ranges::end(static_cast(__t)))) + -> decltype( ranges::end(static_cast(__t))) + { return ranges::end(static_cast(__t)); } template - requires is_rvalue_reference_v<_Tp> && invocable - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const - noexcept(noexcept(ranges::end(static_cast<_Tp const&&>(__t)))) - { - return ranges::end(static_cast<_Tp const&&>(__t)); - } + requires is_rvalue_reference_v<_Tp&&> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::end(static_cast(__t)))) + -> decltype( ranges::end(static_cast(__t))) + { return ranges::end(static_cast(__t)); } }; -} // namespace ranges::__cend +} -namespace ranges::inline __cpo { +inline namespace __cpo { inline constexpr auto cend = __cend::__fn{}; -} // namespace ranges::__cpo +} // namespace __cpo +} // namespace ranges #endif // !defined(_LIBCPP_HAS_NO_RANGES) Index: libcxx/include/__ranges/data.h =================================================================== --- libcxx/include/__ranges/data.h +++ libcxx/include/__ranges/data.h @@ -9,13 +9,14 @@ #ifndef _LIBCPP___RANGES_DATA_H #define _LIBCPP___RANGES_DATA_H +#include <__concepts/class_or_enum.h> #include <__config> #include <__iterator/concepts.h> -#include <__iterator/iterator_traits.h> #include <__memory/pointer_traits.h> #include <__ranges/access.h> +#include <__ranges/enable_borrowed_range.h> #include <__utility/forward.h> -#include +#include <__utility/priority_tag.h> #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -26,45 +27,49 @@ #if !defined(_LIBCPP_HAS_NO_RANGES) -namespace ranges { // [range.prim.data] -namespace __data { - template - concept __ptr_to_object = is_pointer_v<_Tp> && is_object_v>; - - template - concept __member_data = - requires(_Tp&& __t) { - { _VSTD::forward<_Tp>(__t) } -> __can_borrow; - { __t.data() } -> __ptr_to_object; - }; +namespace ranges { +namespace __data { template - concept __ranges_begin_invocable = - !__member_data<_Tp> && - requires(_Tp&& __t) { - { _VSTD::forward<_Tp>(__t) } -> __can_borrow; - { ranges::begin(_VSTD::forward<_Tp>(__t)) } -> contiguous_iterator; - }; + concept __basic_requirements = + requires (_Tp __t) { + requires __class_or_enum>; + __t.data(); + } || + requires (_Tp __t) { { ranges::begin(__t) } -> contiguous_iterator; }; struct __fn { - template <__member_data _Tp> - requires __can_borrow<_Tp> + template + requires is_rvalue_reference_v<_Tp&&> && (!enable_borrowed_range>) _LIBCPP_HIDE_FROM_ABI - constexpr __ptr_to_object auto operator()(_Tp&& __t) const - noexcept(noexcept(__t.data())) { - return __t.data(); - } + static constexpr void __go(_Tp&&, __priority_tag<2>) = delete; - template<__ranges_begin_invocable _Tp> - requires __can_borrow<_Tp> + template >>* = nullptr> _LIBCPP_HIDE_FROM_ABI - constexpr __ptr_to_object auto operator()(_Tp&& __t) const - noexcept(noexcept(_VSTD::to_address(ranges::begin(_VSTD::forward<_Tp>(__t))))) { - return _VSTD::to_address(ranges::begin(_VSTD::forward<_Tp>(__t))); - } + static constexpr auto __go(_Tp&& __t, __priority_tag<1>) + noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.data()))) + -> decltype( _LIBCPP_AUTO_CAST(__t.data())) + requires is_pointer_v().data()))> && + is_object_v().data()))>> + { return _LIBCPP_AUTO_CAST(__t.data()); } + + template >>* = nullptr> + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<0>) + noexcept(noexcept(_VSTD::to_address(ranges::begin(__t)))) + -> decltype( _VSTD::to_address(ranges::begin(__t))) + { return _VSTD::to_address(ranges::begin(__t)); } + + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator()(_Tp&& __t) const + noexcept(noexcept(__go(_VSTD::forward<_Tp>(__t), __priority_tag<2>()))) + requires __basic_requirements<_Tp&&> && + (requires { __go(_VSTD::forward<_Tp>(__t), __priority_tag<2>()); }) + { return __go(_VSTD::forward<_Tp>(__t), __priority_tag<2>()); } }; -} // end namespace __data +} inline namespace __cpo { inline constexpr auto data = __data::__fn{}; Index: libcxx/include/__ranges/empty.h =================================================================== --- libcxx/include/__ranges/empty.h +++ libcxx/include/__ranges/empty.h @@ -9,10 +9,13 @@ #ifndef _LIBCPP___RANGES_EMPTY_H #define _LIBCPP___RANGES_EMPTY_H +#include <__concepts/class_or_enum.h> #include <__config> #include <__iterator/concepts.h> #include <__ranges/access.h> #include <__ranges/size.h> +#include <__utility/forward.h> +#include <__utility/priority_tag.h> #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -23,46 +26,57 @@ #if !defined(_LIBCPP_HAS_NO_RANGES) -namespace ranges { // [range.prim.empty] -namespace __empty { - template - concept __member_empty = requires(_Tp&& __t) { - bool(__t.empty()); - }; - - template - concept __can_invoke_size = - !__member_empty<_Tp> && - requires(_Tp&& __t) { ranges::size(__t); }; +namespace ranges { +namespace __empty { template - concept __can_compare_begin_end = - !__member_empty<_Tp> && - !__can_invoke_size<_Tp> && - requires(_Tp&& __t) { - bool(ranges::begin(__t) == ranges::end(__t)); + concept __basic_requirements = + requires (_Tp __t) { + requires __class_or_enum>; + bool(__t.empty()); + } || + requires (_Tp __t) { ranges::size(__t) == 0; } || + requires (_Tp __t) { { ranges::begin(__t) } -> forward_iterator; + bool(ranges::begin(__t) == ranges::end(__t)); }; struct __fn { - template <__member_empty _Tp> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(_Tp&& __t) const - noexcept(noexcept(bool(__t.empty()))) { - return bool(__t.empty()); - } + template + requires is_unbounded_array_v> + _LIBCPP_HIDE_FROM_ABI + static constexpr void __go(_Tp&&, __priority_tag<3>) = delete; + + template >>* = nullptr> + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<2>) + noexcept(noexcept(bool(__t.empty()))) + -> decltype( bool(__t.empty())) + { return bool(__t.empty()); } + + template + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<1>) + noexcept(noexcept(ranges::size(__t) == 0)) + -> decltype( ranges::size(__t) == 0) + { return ranges::size(__t) == 0; } - template <__can_invoke_size _Tp> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(_Tp&& __t) const - noexcept(noexcept(ranges::size(__t))) { - return ranges::size(__t) == 0; - } + template + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<0>) + noexcept(noexcept(bool(ranges::begin(__t) == ranges::end(__t)))) + -> decltype( bool(ranges::begin(__t) == ranges::end(__t))) + requires forward_iterator + { return bool(ranges::begin(__t) == ranges::end(__t)); } - template<__can_compare_begin_end _Tp> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(_Tp&& __t) const - noexcept(noexcept(bool(ranges::begin(__t) == ranges::end(__t)))) { - return ranges::begin(__t) == ranges::end(__t); - } + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator()(_Tp&& __t) const + noexcept(noexcept(__go(_VSTD::forward<_Tp>(__t), __priority_tag<3>()))) + requires __basic_requirements<_Tp&&> && + (requires { __go(_VSTD::forward<_Tp>(__t), __priority_tag<3>()); }) + { return __go(_VSTD::forward<_Tp>(__t), __priority_tag<3>()); } }; } Index: libcxx/include/__ranges/size.h =================================================================== --- libcxx/include/__ranges/size.h +++ libcxx/include/__ranges/size.h @@ -9,12 +9,14 @@ #ifndef _LIBCPP___RANGES_SIZE_H #define _LIBCPP___RANGES_SIZE_H +#include <__concepts/class_or_enum.h> #include <__config> #include <__iterator/concepts.h> -#include <__iterator/iterator_traits.h> #include <__ranges/access.h> #include <__utility/auto_cast.h> -#include +#include <__utility/declval.h> +#include <__utility/forward.h> +#include <__utility/priority_tag.h> #include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -25,88 +27,101 @@ #if !defined(_LIBCPP_HAS_NO_RANGES) +// [range.prim.size] + namespace ranges { + template inline constexpr bool disable_sized_range = false; -// [range.prim.size] namespace __size { void size(auto&) = delete; void size(const auto&) = delete; template - concept __size_enabled = !disable_sized_range>; - - template - concept __member_size = __size_enabled<_Tp> && requires(_Tp&& __t) { - { _LIBCPP_AUTO_CAST(__t.size()) } -> __integer_like; - }; - - template - concept __unqualified_size = - __size_enabled<_Tp> && - !__member_size<_Tp> && - __class_or_enum> && - requires(_Tp&& __t) { - { _LIBCPP_AUTO_CAST(size(__t)) } -> __integer_like; - }; - - template - concept __difference = - !__member_size<_Tp> && - !__unqualified_size<_Tp> && - __class_or_enum> && - requires(_Tp&& __t) { - { ranges::begin(__t) } -> forward_iterator; - { ranges::end(__t) } -> sized_sentinel_for()))>; - }; + concept __basic_requirements = + is_bounded_array_v> || + requires (_Tp __t) { + requires __class_or_enum>; + __t.size(); + } || + requires (_Tp __t) { + requires __class_or_enum>; + size(__t); + } || + requires (_Tp __t) { ranges::end(__t) - ranges::begin(__t); }; struct __fn { - template - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr size_t operator()(_Tp (&&)[_Sz]) const noexcept { - return _Sz; - } - - template - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr size_t operator()(_Tp (&)[_Sz]) const noexcept { - return _Sz; - } - - template <__member_size _Tp> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const - noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.size()))) { - return _LIBCPP_AUTO_CAST(__t.size()); - } - - template <__unqualified_size _Tp> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const - noexcept(noexcept(_LIBCPP_AUTO_CAST(size(__t)))) { - return _LIBCPP_AUTO_CAST(size(__t)); - } - - template<__difference _Tp> - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __integer_like auto operator()(_Tp&& __t) const - noexcept(noexcept(ranges::end(__t) - ranges::begin(__t))) { - return _VSTD::__to_unsigned_like(ranges::end(__t) - ranges::begin(__t)); - } + template + requires is_unbounded_array_v> + _LIBCPP_HIDE_FROM_ABI + static constexpr void __go(_Tp&&, __priority_tag<4>) = delete; + + template + requires is_bounded_array_v> + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&&, __priority_tag<3>) + noexcept(noexcept(_LIBCPP_AUTO_CAST(extent_v>))) + -> decltype( _LIBCPP_AUTO_CAST(extent_v>)) + { return _LIBCPP_AUTO_CAST(extent_v>); } + + template >>* = nullptr> + requires (!disable_sized_range>) + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<2>) + noexcept(noexcept(_LIBCPP_AUTO_CAST(__t.size()))) + -> decltype( _LIBCPP_AUTO_CAST(__t.size())) + requires __integer_like + { return _LIBCPP_AUTO_CAST(__t.size()); } + + template >>* = nullptr> + requires __class_or_enum> && + (!disable_sized_range>) + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<1>) + noexcept(noexcept(_LIBCPP_AUTO_CAST(size(__t)))) + -> decltype( _LIBCPP_AUTO_CAST(size(__t))) + requires __integer_like + { return _LIBCPP_AUTO_CAST(size(__t)); } + + template ()))> + _LIBCPP_HIDE_FROM_ABI + static constexpr auto __go(_Tp&& __t, __priority_tag<0>) + noexcept(noexcept(_VSTD::__to_unsigned_like(ranges::end(__t) - ranges::begin(__t)))) + -> decltype( _VSTD::__to_unsigned_like(ranges::end(__t) - ranges::begin(__t))) + requires forward_iterator<_It> && sized_sentinel_for + { return _VSTD::__to_unsigned_like(ranges::end(__t) - ranges::begin(__t)); } + + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator()(_Tp&& __t) const + noexcept(noexcept(__go(_VSTD::forward<_Tp>(__t), __priority_tag<4>()))) + requires __basic_requirements<_Tp&&> && + (requires { __go(_VSTD::forward<_Tp>(__t), __priority_tag<4>()); }) + { return __go(_VSTD::forward<_Tp>(__t), __priority_tag<4>()); } }; -} // end namespace __size +} inline namespace __cpo { inline constexpr auto size = __size::__fn{}; } // namespace __cpo +} // namespace ranges + +// [range.prim.ssize] +namespace ranges { namespace __ssize { struct __fn { - template - requires requires (_Tp&& __t) { ranges::size(__t); } - [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr integral auto operator()(_Tp&& __t) const - noexcept(noexcept(ranges::size(__t))) { - using _Signed = make_signed_t; - if constexpr (sizeof(ptrdiff_t) > sizeof(_Signed)) + template()))>> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Tp&& __t) const + noexcept(noexcept(ranges::size(__t))) + requires requires { ranges::size(__t); } + { + if constexpr (sizeof(ptrdiff_t) > sizeof(_Dp)) return static_cast(ranges::size(__t)); else - return static_cast<_Signed>(ranges::size(__t)); + return static_cast<_Dp>(ranges::size(__t)); } }; } Index: libcxx/test/libcxx/ranges/range.access/begin.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/ranges/range.access/begin.verify.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::begin +// Substitution failure should give reasonably nice diagnostics. + +#include +#include + +void nonclasstype() { + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{'is_array_v >' evaluated to false}} + // expected-note@*:* {{does not satisfy '__class_or_enum'}} + // expected-note@*:* {{does not satisfy '__class_or_enum'}} + std::ranges::begin(42); +} + +void classtype() { + struct S {} s; + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{'is_array_v >' evaluated to false}} + // expected-note@*:* {{'__t.begin()' would be invalid}} + // expected-note@*:* {{'begin(__t)' would be invalid}} + std::ranges::begin(s); +} + +void baditerator() { + struct S { int begin(); } s; + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{'is_array_v >' evaluated to false}} + // expected-note@*:* {{'typename decay::type' (aka 'int') does not satisfy 'input_or_output_iterator'}} + // expected-note@*:* {{'begin(__t)' would be invalid}} + std::ranges::begin(s); +} + +void rvalue() { + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{call to deleted function '__go'}} + std::ranges::begin(std::string()); +} Index: libcxx/test/libcxx/ranges/range.access/cbegin.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/ranges/range.access/cbegin.verify.cpp @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::cbegin +// Substitution failure should give reasonably nice diagnostics. + +#include + +void f() { + // expected-error@*:* {{no matching function for call}} + std::ranges::cbegin(42); +} Index: libcxx/test/libcxx/ranges/range.access/cend.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/ranges/range.access/cend.verify.cpp @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::cend +// Substitution failure should give reasonably nice diagnostics. + +#include + +void f() { + // expected-error@*:* {{no matching function for call}} + std::ranges::cend(42); +} Index: libcxx/test/libcxx/ranges/range.access/data.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/ranges/range.access/data.verify.cpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::data +// Substitution failure should give reasonably nice diagnostics. + +#include + +void f() { + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{does not satisfy '__class_or_enum'}} + // expected-note@*:* {{'ranges::begin(__t)' would be invalid}} + std::ranges::data(42); +} + +void g() { + struct S {} s; + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{'__t.data()' would be invalid}} + // expected-note@*:* {{'ranges::begin(__t)' would be invalid}} + std::ranges::data(s); +} Index: libcxx/test/libcxx/ranges/range.access/empty.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/ranges/range.access/empty.verify.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::empty +// Substitution failure should give reasonably nice diagnostics. + +#include + +void f() { + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{does not satisfy '__class_or_enum'}} + // expected-note@*:* {{'ranges::size(__t) == 0' would be invalid}} + // expected-note@*:* {{'ranges::begin(__t)' would be invalid}} + std::ranges::empty(42); +} + +void g() { + struct S {} s; + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{'bool(__t.empty())' would be invalid}} + // expected-note@*:* {{'ranges::size(__t) == 0' would be invalid}} + // expected-note@*:* {{'ranges::begin(__t)' would be invalid}} + std::ranges::empty(s); +} Index: libcxx/test/libcxx/ranges/range.access/end.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/ranges/range.access/end.verify.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::end +// Substitution failure should give reasonably nice diagnostics. + +#include +#include + +void nonclasstype() { + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{'is_bounded_array_v >' evaluated to false}} + // expected-note@*:* {{does not satisfy '__class_or_enum'}} + // expected-note@*:* {{does not satisfy '__class_or_enum'}} + std::ranges::end(42); +} + +void classtype() { + struct S {} s; + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{'is_bounded_array_v >' evaluated to false}} + // expected-note@*:* {{'__t.end()' would be invalid}} + // expected-note@*:* {{'end(__t)' would be invalid}} + std::ranges::end(s); +} + +void baditerator() { + struct S { int *begin(); int end(); } s; + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{'is_bounded_array_v >' evaluated to false}} + // expected-note@*:* {{type constraint 'sentinel_for > >' was not satisfied}} + // expected-note@*:* {{'end(__t)' would be invalid}} + std::ranges::end(s); +} + +void rvalue() { + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{call to deleted function '__go'}} + std::ranges::end(std::string()); +} Index: libcxx/test/libcxx/ranges/range.access/size.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/ranges/range.access/size.verify.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::size +// Substitution failure should give reasonably nice diagnostics. + +#include + +void f() { + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{'is_bounded_array_v >' evaluated to false}} + // expected-note@*:* {{does not satisfy '__class_or_enum'}} + // expected-note@*:* {{does not satisfy '__class_or_enum'}} + // expected-note@*:* {{'ranges::end(__t) - ranges::begin(__t)' would be invalid}} + std::ranges::size(42); +} + +void g() { + struct S {} s; + // expected-error@*:* {{no matching function for call}} + // expected-note@*:* {{'is_bounded_array_v >' evaluated to false}} + // expected-note@*:* {{'__t.size()' would be invalid}} + // expected-note@*:* {{'size(__t)' would be invalid}} + // expected-note@*:* {{'ranges::end(__t) - ranges::begin(__t)' would be invalid}} + std::ranges::size(s); +} Index: libcxx/test/libcxx/ranges/range.access/ssize.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/ranges/range.access/ssize.verify.cpp @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::ssize +// Substitution failure should give reasonably nice diagnostics. + +#include + +void f() { + // expected-error@*:* {{no matching function for call}} + std::ranges::ssize(42); +} Index: libcxx/test/std/ranges/range.access/begin.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/begin.pass.cpp +++ libcxx/test/std/ranges/range.access/begin.pass.cpp @@ -11,6 +11,7 @@ // UNSUPPORTED: libcpp-has-no-incomplete-ranges // std::ranges::begin +// std::ranges::cbegin #include @@ -18,8 +19,8 @@ #include "test_macros.h" #include "test_iterators.h" -using RangeBeginT = decltype(std::ranges::begin)&; -using RangeCBeginT = decltype(std::ranges::cbegin)&; +using RangeBeginT = decltype(std::ranges::begin); +using RangeCBeginT = decltype(std::ranges::cbegin); static int globalBuff[8]; @@ -118,12 +119,18 @@ BeginMember a; assert(std::ranges::begin(a) == &a.x); assert(std::ranges::cbegin(a) == &a.x); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); NonConstBeginMember b; assert(std::ranges::begin(b) == &b.x); + static_assert(!std::is_invocable_v); EnabledBorrowingBeginMember c; + assert(std::ranges::begin(c) == &globalBuff[0]); + assert(std::ranges::cbegin(c) == &globalBuff[0]); assert(std::ranges::begin(std::move(c)) == &globalBuff[0]); + assert(std::ranges::cbegin(std::move(c)) == &globalBuff[0]); BeginMemberFunction d; assert(std::ranges::begin(d) == &d.x); @@ -202,44 +209,44 @@ constexpr bool testBeginFunction() { BeginFunction a{}; const BeginFunction aa{}; - static_assert(!std::invocable); - assert(std::ranges::begin(aa) == &aa.x); + static_assert(!std::invocable); assert(std::ranges::cbegin(a) == &a.x); + assert(std::ranges::begin(aa) == &aa.x); assert(std::ranges::cbegin(aa) == &aa.x); BeginFunctionByValue b{}; const BeginFunctionByValue bb{}; assert(std::ranges::begin(b) == &globalBuff[1]); - assert(std::ranges::begin(bb) == &globalBuff[1]); assert(std::ranges::cbegin(b) == &globalBuff[1]); + assert(std::ranges::begin(bb) == &globalBuff[1]); assert(std::ranges::cbegin(bb) == &globalBuff[1]); BeginFunctionEnabledBorrowing c{}; const BeginFunctionEnabledBorrowing cc{}; assert(std::ranges::begin(std::move(c)) == &globalBuff[2]); - static_assert(!std::invocable); + assert(std::ranges::cbegin(std::move(c)) == &globalBuff[2]); assert(std::ranges::begin(std::move(cc)) == &globalBuff[2]); assert(std::ranges::cbegin(std::move(cc)) == &globalBuff[2]); BeginFunctionReturnsEmptyPtr d{}; const BeginFunctionReturnsEmptyPtr dd{}; - static_assert(!std::invocable); - assert(std::ranges::begin(dd) == &dd.x); + static_assert(!std::invocable); assert(std::ranges::cbegin(d) == &d.x); + assert(std::ranges::begin(dd) == &dd.x); assert(std::ranges::cbegin(dd) == &dd.x); BeginFunctionWithDataMember e{}; const BeginFunctionWithDataMember ee{}; - static_assert(!std::invocable); + static_assert(!std::invocable); assert(std::ranges::begin(ee) == &ee.x); assert(std::ranges::cbegin(e) == &e.x); assert(std::ranges::cbegin(ee) == &ee.x); BeginFunctionWithPrivateBeginMember f{}; const BeginFunctionWithPrivateBeginMember ff{}; - static_assert(!std::invocable); - assert(std::ranges::begin(ff) == &ff.y); + static_assert(!std::invocable); assert(std::ranges::cbegin(f) == &f.y); + assert(std::ranges::begin(ff) == &ff.y); assert(std::ranges::cbegin(ff) == &ff.y); return true; @@ -274,6 +281,11 @@ static_assert(noexcept(std::ranges::begin(brar))); static_assert(noexcept(std::ranges::cbegin(brar))); +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*>); int main(int, char**) { testArray(); Index: libcxx/test/std/ranges/range.access/data.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/data.pass.cpp +++ libcxx/test/std/ranges/range.access/data.pass.cpp @@ -116,9 +116,10 @@ random_access_iterator begin() const; }; -static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); struct BeginFriendContiguousIterator { int buff[8]; @@ -135,9 +136,10 @@ struct BeginFriendRandomAccess { friend random_access_iterator begin(const BeginFriendRandomAccess iter); }; -static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); -static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); struct BeginMemberRvalue { int buff[8]; @@ -173,6 +175,17 @@ return true; } +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); + +struct RandomButNotContiguous { + random_access_iterator begin() const; + random_access_iterator end() const; +}; +static_assert(!std::is_invocable_v); + int main(int, char**) { testDataMember(); static_assert(testDataMember()); Index: libcxx/test/std/ranges/range.access/empty.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/empty.pass.cpp +++ libcxx/test/std/ranges/range.access/empty.pass.cpp @@ -168,6 +168,11 @@ return true; } +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); + int main(int, char**) { testEmptyMember(); static_assert(testEmptyMember()); Index: libcxx/test/std/ranges/range.access/end.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/end.pass.cpp +++ libcxx/test/std/ranges/range.access/end.pass.cpp @@ -11,6 +11,7 @@ // UNSUPPORTED: libcpp-has-no-incomplete-ranges // std::ranges::end +// std::ranges::cend #include @@ -18,8 +19,8 @@ #include "test_macros.h" #include "test_iterators.h" -using RangeEndT = decltype(std::ranges::end)&; -using RangeCEndT = decltype(std::ranges::cend)&; +using RangeEndT = decltype(std::ranges::end); +using RangeCEndT = decltype(std::ranges::cend); static int globalBuff[8]; @@ -139,9 +140,11 @@ NonConstEndMember b; assert(std::ranges::end(b) == &b.x); + static_assert(!std::is_invocable_v); EnabledBorrowingEndMember c; assert(std::ranges::end(std::move(c)) == &globalBuff[0]); + assert(std::ranges::cend(std::move(c)) == &globalBuff[0]); EndMemberFunction d; assert(std::ranges::end(d) == &d.x); @@ -246,7 +249,9 @@ constexpr bool testEndFunction() { const EndFunction a{}; assert(std::ranges::end(a) == &a.x); + assert(std::ranges::cend(a) == &a.x); EndFunction aa{}; + static_assert(!std::is_invocable_v); assert(std::ranges::cend(aa) == &aa.x); EndFunctionByValue b; @@ -255,25 +260,34 @@ EndFunctionEnabledBorrowing c; assert(std::ranges::end(std::move(c)) == &globalBuff[2]); + assert(std::ranges::cend(std::move(c)) == &globalBuff[2]); const EndFunctionReturnsEmptyPtr d{}; assert(std::ranges::end(d) == &d.x); + assert(std::ranges::cend(d) == &d.x); EndFunctionReturnsEmptyPtr dd{}; + static_assert(!std::is_invocable_v); assert(std::ranges::cend(dd) == &dd.x); const EndFunctionWithDataMember e{}; assert(std::ranges::end(e) == &e.x); + assert(std::ranges::cend(e) == &e.x); EndFunctionWithDataMember ee{}; + static_assert(!std::is_invocable_v); assert(std::ranges::cend(ee) == &ee.x); const EndFunctionWithPrivateEndMember f{}; assert(std::ranges::end(f) == &f.y); + assert(std::ranges::cend(f) == &f.y); EndFunctionWithPrivateEndMember ff{}; + static_assert(!std::is_invocable_v); assert(std::ranges::cend(ff) == &ff.y); const BeginMemberEndFunction g{}; assert(std::ranges::end(g) == &g.x); + assert(std::ranges::cend(g) == &g.x); BeginMemberEndFunction gg{}; + static_assert(!std::is_invocable_v); assert(std::ranges::cend(gg) == &gg.x); return true; @@ -312,6 +326,12 @@ static_assert(noexcept(std::ranges::end(erar))); static_assert(noexcept(std::ranges::cend(erar))); +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); +static_assert(!std::is_invocable_v*>); + int main(int, char**) { testArray(); static_assert(testArray()); Index: libcxx/test/std/ranges/range.access/size.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/size.pass.cpp +++ libcxx/test/std/ranges/range.access/size.pass.cpp @@ -316,6 +316,11 @@ return true; } +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); + int main(int, char**) { testArrayType(); static_assert(testArrayType()); Index: libcxx/test/std/ranges/range.access/ssize.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.access/ssize.pass.cpp +++ libcxx/test/std/ranges/range.access/ssize.pass.cpp @@ -80,6 +80,11 @@ return true; } +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::is_invocable_v*>); + int main(int, char**) { test(); static_assert(test()); Index: libcxx/test/std/ranges/range.req/range.range/range.compile.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.req/range.range/range.compile.pass.cpp +++ libcxx/test/std/ranges/range.req/range.range/range.compile.pass.cpp @@ -46,3 +46,8 @@ int* end(); }; static_assert(!std::ranges::range); + +// Test ADL-proofing. +struct Incomplete; +template struct Holder { T t; }; +static_assert(!std::ranges::range*>); Index: libcxx/test/std/ranges/range.req/range.sized/sized_range.compile.pass.cpp =================================================================== --- libcxx/test/std/ranges/range.req/range.sized/sized_range.compile.pass.cpp +++ libcxx/test/std/ranges/range.req/range.sized/sized_range.compile.pass.cpp @@ -17,13 +17,14 @@ #include "test_iterators.h" - - static_assert(std::ranges::sized_range); static_assert(std::ranges::sized_range); static_assert(!std::ranges::sized_range); static_assert(!std::ranges::sized_range); +struct Incomplete; +static_assert(!std::ranges::sized_range); + struct range_has_size { bidirectional_iterator begin(); bidirectional_iterator end();