diff --git a/stl/inc/algorithm b/stl/inc/algorithm index ed4466cfcd8..e625cdb5326 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -3402,16 +3402,13 @@ namespace ranges { _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2) const { _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); - auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); - auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); - auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); - auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); - for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1, (void) ++_UFirst2) { - _RANGES iter_swap(_UFirst1, _UFirst2); - } - _Seek_wrapped(_First1, _STD move(_UFirst1)); - _Seek_wrapped(_First2, _STD move(_UFirst2)); + auto _UResult = + _Swap_ranges_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2))); + + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); return {_STD move(_First1), _STD move(_First2)}; } @@ -3419,9 +3416,33 @@ namespace ranges { requires indirectly_swappable, iterator_t<_Rng2>> constexpr swap_ranges_result, borrowed_iterator_t<_Rng2>> operator()( _Rng1&& _Range1, _Rng2&& _Range2) const { - return (*this)(_RANGES begin(_Range1), _RANGES end(_Range1), _RANGES begin(_Range2), _RANGES end(_Range2)); + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); + + auto _UResult = _Swap_ranges_unchecked( + _Get_unwrapped(_STD move(_First1)), _Uend(_Range1), _Get_unwrapped(_STD move(_First2)), _Uend(_Range2)); + + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + return {_STD move(_First1), _STD move(_First2)}; } // clang-format on + private: + template + _NODISCARD static constexpr swap_ranges_result<_It1, _It2> _Swap_ranges_unchecked( + _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_swappable<_It1, _It2>); + + for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, (void) ++_First2) { + _RANGES iter_swap(_First1, _First2); + } + + return {_STD move(_First1), _STD move(_First2)}; + } }; inline constexpr _Swap_ranges_fn swap_ranges{_Not_quite_object::_Construct_tag{}}; diff --git a/stl/inc/xutility b/stl/inc/xutility index b22d75d57ef..0330cc3d9be 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3243,25 +3243,32 @@ namespace ranges { return _Last - _First; } else { _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(static_cast<_It&&>(_First)); - const auto _ULast = _Get_unwrapped(static_cast<_Se&&>(_Last)); - iter_difference_t<_It> _Count = 0; - for (; _UFirst != _ULast; ++_UFirst) { - ++_Count; - } - - return _Count; + return _Distance_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last))); } } template - _NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Val) const { + _NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range) const { if constexpr (sized_range<_Rng>) { - return static_cast>(_RANGES size(_Val)); + return static_cast>(_RANGES size(_Range)); } else { - return (*this)(_RANGES begin(_Val), _RANGES end(_Val)); + return _Distance_unchecked(_Ubegin(_Range), _Uend(_Range)); } } + + private: + template + _NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) { + _STL_INTERNAL_STATIC_ASSERT(input_or_output_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + + iter_difference_t<_It> _Count = 0; + for (; _First != _Last; ++_First) { + ++_Count; + } + + return _Count; + } }; // VARIABLE ranges::distance diff --git a/tests/std/tests/P0896R4_ranges_alg_includes/test.cpp b/tests/std/tests/P0896R4_ranges_alg_includes/test.cpp index 082ca91f38e..54929f26822 100644 --- a/tests/std/tests/P0896R4_ranges_alg_includes/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_includes/test.cpp @@ -68,7 +68,7 @@ int main() { #else // ^^^ test all permutations of range properties / test only interesting permutations vvv template using test_range = test::range}, IsProxyRef>; + test::CanCompare{derived_from}, IsProxyRef>; constexpr void run_tests() { using namespace test; diff --git a/tests/std/tests/P0896R4_ranges_alg_swap_ranges/test.cpp b/tests/std/tests/P0896R4_ranges_alg_swap_ranges/test.cpp index b30d2c6466b..534280d29d1 100644 --- a/tests/std/tests/P0896R4_ranges_alg_swap_ranges/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_swap_ranges/test.cpp @@ -2,65 +2,105 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include -#include #include #include #include #include #include +using namespace std; -constexpr void smoke_test() { - using ranges::swap_ranges, ranges::swap_ranges_result, ranges::iterator_t; - using std::same_as; - using I1 = iterator_t>; +// Validate that swap_ranges_result aliases in_in_result +STATIC_ASSERT(same_as, ranges::in_in_result>); - // Validate that swap_ranges_result aliases in_in_result - STATIC_ASSERT(same_as, ranges::in_in_result>); +// Validate dangling story +STATIC_ASSERT(same_as{}, borrowed{})), + ranges::swap_ranges_result>); +STATIC_ASSERT(same_as{}, borrowed{})), + ranges::swap_ranges_result>); +STATIC_ASSERT(same_as{}, borrowed{})), + ranges::swap_ranges_result>); +STATIC_ASSERT( + same_as{}, borrowed{})), ranges::swap_ranges_result>); - { // Validate ranges overload - using R = std::array; - R range1 = {13, 53, 1876}; - R range2 = {34, 243, 9366}; - int const expected_output1[] = {34, 243, 9366}; - int const expected_output2[] = {13, 53, 1876}; - auto result = swap_ranges(basic_borrowed_range{range1}, basic_borrowed_range{range2}); - STATIC_ASSERT(same_as>); - assert(result.in1 == basic_borrowed_range{range1}.end()); - assert(result.in2 == basic_borrowed_range{range2}.end()); - assert(ranges::equal(range1, expected_output1)); - assert(ranges::equal(range2, expected_output2)); - } - { // Validate iterator + sentinel overload - int range1[] = {13, 53, 1876}; - int range2[] = {34, 243, 9366}; - basic_borrowed_range wrapped_range1{range1}; - basic_borrowed_range wrapped_range2{range2}; - int const expected_output1[] = {34, 243, 9366}; - int const expected_output2[] = {13, 53, 1876}; - auto result = - swap_ranges(wrapped_range1.begin(), wrapped_range1.end(), wrapped_range2.begin(), wrapped_range2.end()); - STATIC_ASSERT(same_as>); - assert(result.in1 == wrapped_range1.end()); - assert(result.in2 == wrapped_range2.end()); - assert(ranges::equal(range1, expected_output1)); - assert(ranges::equal(range2, expected_output2)); +struct instantiator { + static constexpr int expected_output1[3] = {34, 243, 9366}; + static constexpr int expected_output2[3] = {13, 53, 1876}; + + template + static constexpr void call() { + using ranges::swap_ranges, ranges::swap_ranges_result, ranges::iterator_t, ranges::equal; + { // Validate iterator + sentinel overload + int input1[3] = {13, 53, 1876}; + int input2[3] = {34, 243, 9366}; + ReadWrite1 wrapped_input1{input1}; + ReadWrite2 wrapped_input2{input2}; + + auto result = + swap_ranges(wrapped_input1.begin(), wrapped_input1.end(), wrapped_input2.begin(), wrapped_input2.end()); + STATIC_ASSERT( + same_as, iterator_t>>); + assert(result.in1 == wrapped_input1.end()); + assert(result.in2 == wrapped_input2.end()); + assert(equal(input1, expected_output1)); + assert(equal(input2, expected_output2)); + } + { // Validate range overload + int input1[3] = {13, 53, 1876}; + int input2[3] = {34, 243, 9366}; + ReadWrite1 wrapped_input1{input1}; + ReadWrite2 wrapped_input2{input2}; + + auto result = swap_ranges(wrapped_input1, wrapped_input2); + STATIC_ASSERT( + same_as, iterator_t>>); + assert(result.in1 == wrapped_input1.end()); + assert(result.in2 == wrapped_input2.end()); + assert(equal(input1, expected_output1)); + assert(equal(input2, expected_output2)); + } } +}; + +#ifdef TEST_EVERYTHING +int main() { + // no constexpr tests here; the below exceeds constexpr step limits + test_in_in(); +} +#else // ^^^ test all permutations of range properties / test only interesting permutations vvv +template +using test_range = test::range}, IsProxyRef>; + +constexpr void run_tests() { + using namespace test; + // The algorithm is completely oblivious to: + // * categories stronger than input + // * whether the end sentinel is an iterator + // * size information + // * iterator and/or sentinel differencing + // so let's vary proxyness for coverage and add a range of each category out of paranoia. + + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); + + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); + + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); + instantiator::call, test_range>(); } int main() { #if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-938163 - STATIC_ASSERT((smoke_test(), true)); + STATIC_ASSERT((run_tests(), true)); #endif // TRANSITION, VSO-938163 - smoke_test(); + run_tests(); } - -struct instantiator { - template - static void call(In1 in1 = {}, In2 in2 = {}) { - (void) ranges::swap_ranges(in1, in2); - (void) ranges::swap_ranges(ranges::begin(in1), ranges::end(in1), ranges::begin(in2), ranges::end(in2)); - } -}; - -template void test_in_in(); +#endif // TEST_EVERYTHING