diff --git a/stl/inc/xutility b/stl/inc/xutility index 7dc28b57a1..2b1061c8e4 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -2479,17 +2479,34 @@ namespace ranges { _NODISCARD constexpr auto _As_const_pointer(const _Ty* _Ptr) noexcept { return _Ptr; } + + template + using _Begin_on_const = decltype(_RANGES begin(_RANGES _Possibly_const_range(_STD declval<_Ty&>()))); + + // TRANSITION, LLVM-55945 + template + concept _Range_accessible_and_begin_adaptable = + _Should_range_access<_Ty> + && requires( + _Ty& _Val) { const_iterator<_Begin_on_const<_Ty>>{_RANGES begin(_RANGES _Possibly_const_range(_Val))}; }; + + template + using _End_on_const = decltype(_RANGES end(_RANGES _Possibly_const_range(_STD declval<_Ty&>()))); + + // TRANSITION, LLVM-55945 + template + concept _Range_accessible_and_end_adaptable = + _Should_range_access<_Ty> + && requires( + _Ty& _Val) { const_sentinel<_End_on_const<_Ty>>{_RANGES end(_RANGES _Possibly_const_range(_Val))}; }; #endif // _HAS_CXX23 struct _Cbegin_fn { #if _HAS_CXX23 - template <_Should_range_access _Ty, - class _Uty = decltype(_RANGES begin(_RANGES _Possibly_const_range(_STD declval<_Ty&>())))> - _NODISCARD constexpr auto operator()(_Ty&& _Val) const - noexcept(noexcept(const_iterator<_Uty>{_RANGES begin(_RANGES _Possibly_const_range(_Val))})) - requires requires { const_iterator<_Uty>{_RANGES begin(_RANGES _Possibly_const_range(_Val))}; } - { - return const_iterator<_Uty>{_RANGES begin(_RANGES _Possibly_const_range(_Val))}; + template <_Range_accessible_and_begin_adaptable _Ty> + _NODISCARD constexpr auto operator()(_Ty&& _Val) const noexcept( + noexcept(const_iterator<_Begin_on_const<_Ty>>{_RANGES begin(_RANGES _Possibly_const_range(_Val))})) { + return const_iterator<_Begin_on_const<_Ty>>{_RANGES begin(_RANGES _Possibly_const_range(_Val))}; } #else // ^^^ C++23 / C++20 vvv template > @@ -2508,13 +2525,10 @@ namespace ranges { struct _Cend_fn { #if _HAS_CXX23 - template <_Should_range_access _Ty, - class _Uty = decltype(_RANGES end(_RANGES _Possibly_const_range(_STD declval<_Ty&>())))> + template <_Range_accessible_and_end_adaptable _Ty> _NODISCARD constexpr auto operator()(_Ty&& _Val) const - noexcept(noexcept(const_sentinel<_Uty>{_RANGES end(_RANGES _Possibly_const_range(_Val))})) - requires requires { const_sentinel<_Uty>{_RANGES end(_RANGES _Possibly_const_range(_Val))}; } - { - return const_sentinel<_Uty>{_RANGES end(_RANGES _Possibly_const_range(_Val))}; + noexcept(noexcept(const_sentinel<_End_on_const<_Ty>>{_RANGES end(_RANGES _Possibly_const_range(_Val))})) { + return const_sentinel<_End_on_const<_Ty>>{_RANGES end(_RANGES _Possibly_const_range(_Val))}; } #else // ^^^ C++23 / C++20 vvv template > @@ -2670,15 +2684,34 @@ namespace ranges { _EXPORT_STD inline constexpr _Rend::_Cpo rend; } +#if _HAS_CXX23 + template + using _Rbegin_on_const = decltype(_RANGES rbegin(_RANGES _Possibly_const_range(_STD declval<_Ty&>()))); + + // TRANSITION, LLVM-55945 + template + concept _Range_accessible_and_rbegin_adaptable = + _Should_range_access<_Ty> + && requires( + _Ty& _Val) { const_iterator<_Rbegin_on_const<_Ty>>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))}; }; + + template + using _Rend_on_const = decltype(_RANGES rend(_RANGES _Possibly_const_range(_STD declval<_Ty&>()))); + + // TRANSITION, LLVM-55945 + template + concept _Range_accessible_and_rend_adaptable = + _Should_range_access<_Ty> + && requires( + _Ty& _Val) { const_sentinel<_Rend_on_const<_Ty>>{_RANGES rend(_RANGES _Possibly_const_range(_Val))}; }; +#endif // _HAS_CXX23 + struct _Crbegin_fn { #if _HAS_CXX23 - template <_Should_range_access _Ty, - class _Uty = decltype(_RANGES rbegin(_RANGES _Possibly_const_range(_STD declval<_Ty&>())))> - _NODISCARD constexpr auto operator()(_Ty&& _Val) const - noexcept(noexcept(const_iterator<_Uty>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))})) - requires requires { const_iterator<_Uty>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))}; } - { - return const_iterator<_Uty>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))}; + template <_Range_accessible_and_rbegin_adaptable _Ty> + _NODISCARD constexpr auto operator()(_Ty&& _Val) const noexcept( + noexcept(const_iterator<_Rbegin_on_const<_Ty>>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))})) { + return const_iterator<_Rbegin_on_const<_Ty>>{_RANGES rbegin(_RANGES _Possibly_const_range(_Val))}; } #else // ^^^ C++23 / C++20 vvv template > @@ -2697,13 +2730,10 @@ namespace ranges { struct _Crend_fn { #if _HAS_CXX23 - template <_Should_range_access _Ty, - class _Uty = decltype(_RANGES rend(_RANGES _Possibly_const_range(_STD declval<_Ty&>())))> + template <_Range_accessible_and_rend_adaptable _Ty> _NODISCARD constexpr auto operator()(_Ty&& _Val) const - noexcept(noexcept(const_sentinel<_Uty>{_RANGES rend(_RANGES _Possibly_const_range(_Val))})) - requires requires { const_sentinel<_Uty>{_RANGES rend(_RANGES _Possibly_const_range(_Val))}; } - { - return const_sentinel<_Uty>{_RANGES rend(_RANGES _Possibly_const_range(_Val))}; + noexcept(noexcept(const_sentinel<_Rend_on_const<_Ty>>{_RANGES rend(_RANGES _Possibly_const_range(_Val))})) { + return const_sentinel<_Rend_on_const<_Ty>>{_RANGES rend(_RANGES _Possibly_const_range(_Val))}; } #else // ^^^ C++23 / C++20 vvv template > @@ -2922,13 +2952,19 @@ namespace ranges { _EXPORT_STD inline constexpr _Data::_Cpo data; } +#if _HAS_CXX23 + // TRANSITION, LLVM-55945 + template + concept _Range_accessible_and_data_adaptable = + _Should_range_access<_Ty> + && requires(_Ty& _Val) { _RANGES _As_const_pointer(_RANGES data(_RANGES _Possibly_const_range(_Val))); }; +#endif // _HAS_CXX23 + struct _Cdata_fn { #if _HAS_CXX23 - template <_Should_range_access _Ty> + template <_Range_accessible_and_data_adaptable _Ty> _NODISCARD constexpr auto operator()(_Ty&& _Val) const - noexcept(noexcept(_RANGES data(_RANGES _Possibly_const_range(_Val)))) - requires requires { _RANGES _As_const_pointer(_RANGES data(_RANGES _Possibly_const_range(_Val))); } - { + noexcept(noexcept(_RANGES data(_RANGES _Possibly_const_range(_Val)))) { return _RANGES _As_const_pointer(_RANGES data(_RANGES _Possibly_const_range(_Val))); } #else // ^^^ C++23 / C++20 vvv diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 6b7aa8923c..083e1348d6 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -660,6 +660,20 @@ struct initially_incomplete; extern initially_incomplete array_of_incomplete[42]; STATIC_ASSERT(ranges::size(array_of_incomplete) == 42); STATIC_ASSERT(!ranges::empty(array_of_incomplete)); + +// begin, end, rbegin, rend, and data (and their c variations) should reject rvalues of array of incomplete elements +// with substitution failures +STATIC_ASSERT(!CanBegin); +STATIC_ASSERT(!CanCBegin); +STATIC_ASSERT(!CanEnd); +STATIC_ASSERT(!CanCEnd); +STATIC_ASSERT(!CanRBegin); +STATIC_ASSERT(!CanCRBegin); +STATIC_ASSERT(!CanREnd); +STATIC_ASSERT(!CanCREnd); +STATIC_ASSERT(!CanData); +STATIC_ASSERT(!CanCData); + struct initially_incomplete {}; initially_incomplete array_of_incomplete[42];