diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index d2b76cb96e866..0f65770a4fa14 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -394,6 +394,8 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_reference_from_temporary`` *unimplemented* ---------------------------------------------------------- ----------------- + ``__cpp_lib_shift`` ``202202L`` + ---------------------------------------------------------- ----------------- ``__cpp_lib_spanstream`` *unimplemented* ---------------------------------------------------------- ----------------- ``__cpp_lib_stacktrace`` *unimplemented* diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst index bec665c167ce3..ffc586d9630a3 100644 --- a/libcxx/docs/ReleaseNotes/23.rst +++ b/libcxx/docs/ReleaseNotes/23.rst @@ -38,7 +38,7 @@ What's New in Libc++ 23.0.0? Implemented Papers ------------------ -- P2440R1 (partial): ``ranges::iota`` and ``ranges::shift_left`` are supported (`Github `__) +- P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github `__) Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index a787a4a6215ec..fd99ff635aa00 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -46,7 +46,7 @@ "`P2255R2 `__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 `__","Implemented the type traits only." "`P2273R3 `__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16","`#105182 `__","" "`P2387R3 `__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 `__","" -"`P2440R1 `__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 `__","``ranges::iota`` and ``ranges::shift_left`` are implemented." +"`P2440R1 `__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 `__","" "`P2441R2 `__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 `__","" "`P2442R1 `__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 `__","" "`P2443R1 `__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 `__","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 1ceba14329cb2..b0b1487eedddb 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -158,6 +158,7 @@ set(files __algorithm/ranges_set_symmetric_difference.h __algorithm/ranges_set_union.h __algorithm/ranges_shift_left.h + __algorithm/ranges_shift_right.h __algorithm/ranges_shuffle.h __algorithm/ranges_sort.h __algorithm/ranges_sort_heap.h diff --git a/libcxx/include/__algorithm/ranges_shift_right.h b/libcxx/include/__algorithm/ranges_shift_right.h new file mode 100644 index 0000000000000..d3521b958271d --- /dev/null +++ b/libcxx/include/__algorithm/ranges_shift_right.h @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_RANGES_SHIFT_RIGHT_H +#define _LIBCPP___ALGORITHM_RANGES_SHIFT_RIGHT_H + +#include <__algorithm/iterator_operations.h> +#include <__algorithm/shift_right.h> +#include <__config> +#include <__iterator/concepts.h> +#include <__iterator/distance.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/permutable.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/subrange.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER >= 23 + +namespace ranges { +namespace __shift_right { + +struct __fn { + template _Sent> + _LIBCPP_HIDE_FROM_ABI static constexpr subrange<_Iter> + operator()(_Iter __first, _Sent __last, iter_difference_t<_Iter> __n) { + auto __ret = std::__shift_right<_RangeAlgPolicy>(std::move(__first), std::move(__last), std::move(__n)); + return {std::move(__ret.first), std::move(__ret.second)}; + } + + template + requires permutable> + _LIBCPP_HIDE_FROM_ABI static constexpr borrowed_subrange_t<_Range> + operator()(_Range&& __range, range_difference_t<_Range> __n) { + if constexpr (sized_range<_Range>) { + if (__n >= ranges::distance(__range)) { + auto __iter = ranges::begin(__range); + auto __end = ranges::end(__range); + ranges::advance(__iter, __end); + return {__iter, std::move(__iter)}; + } + } + + auto __ret = std::__shift_right<_RangeAlgPolicy>(ranges::begin(__range), ranges::end(__range), std::move(__n)); + return {std::move(__ret.first), std::move(__ret.second)}; + } +}; +} // namespace __shift_right + +inline namespace __cpo { +inline constexpr auto shift_right = __shift_right::__fn{}; +} // namespace __cpo +} // namespace ranges + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___ALGORITHM_RANGES_SHIFT_RIGHT_H diff --git a/libcxx/include/__algorithm/shift_right.h b/libcxx/include/__algorithm/shift_right.h index 01853057fc478..917e255965cfe 100644 --- a/libcxx/include/__algorithm/shift_right.h +++ b/libcxx/include/__algorithm/shift_right.h @@ -9,11 +9,17 @@ #ifndef _LIBCPP___ALGORITHM_SHIFT_RIGHT_H #define _LIBCPP___ALGORITHM_SHIFT_RIGHT_H +#include <__algorithm/iterator_operations.h> #include <__algorithm/move.h> #include <__algorithm/move_backward.h> #include <__algorithm/swap_ranges.h> +#include <__assert> +#include <__concepts/derived_from.h> #include <__config> +#include <__iterator/concepts.h> #include <__iterator/iterator_traits.h> +#include <__utility/move.h> +#include <__utility/pair.h> #include <__utility/swap.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) @@ -27,36 +33,48 @@ _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER >= 20 -template -inline _LIBCPP_HIDE_FROM_ABI constexpr _ForwardIterator -shift_right(_ForwardIterator __first, - _ForwardIterator __last, - typename iterator_traits<_ForwardIterator>::difference_type __n) { +template +_LIBCPP_HIDE_FROM_ABI constexpr pair<_Iter, _Iter> +__shift_right(_Iter __first, _Sent __last, typename _IterOps<_AlgPolicy>::template __difference_type<_Iter> __n) { + _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n >= 0, "Providing a negative shift amount to shift_right is UB"); if (__n == 0) { - return __first; + _Iter __end = _IterOps<_AlgPolicy>::next(__first, __last); + return pair<_Iter, _Iter>(std::move(__first), std::move(__end)); } - if constexpr (__has_random_access_iterator_category<_ForwardIterator>::value) { - decltype(__n) __d = __last - __first; - if (__n >= __d) { - return __last; + using _IterCategory = typename _IterOps<_AlgPolicy>::template __iterator_category<_Iter>; + + if constexpr (derived_from<_IterCategory, random_access_iterator_tag>) { + _Iter __end = _IterOps<_AlgPolicy>::next(__first, __last); + auto __size = __end - __first; + if (__n >= __size) { + return pair<_Iter, _Iter>(__end, std::move(__end)); + } + _Iter __m = __first; + _IterOps<_AlgPolicy>::advance(__m, (__size - __n)); + auto __ret = std::__move_backward<_AlgPolicy>(std::move(__first), std::move(__m), __end); + return pair<_Iter, _Iter>(std::move(__ret.second), std::move(__end)); + } else if constexpr (derived_from<_IterCategory, bidirectional_iterator_tag>) { + _Iter __end = _IterOps<_AlgPolicy>::next(__first, __last); + if constexpr (sized_sentinel_for<_Sent, _Iter>) { + if (__n >= ranges::distance(__first, __last)) { + return pair<_Iter, _Iter>(__end, std::move(__end)); + } } - _ForwardIterator __m = __first + (__d - __n); - return std::move_backward(__first, __m, __last); - } else if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) { - _ForwardIterator __m = __last; + _Iter __m = __end; for (; __n > 0; --__n) { if (__m == __first) { - return __last; + return pair<_Iter, _Iter>(__end, std::move(__end)); } --__m; } - return std::move_backward(__first, __m, __last); + auto __ret = std::__move_backward<_AlgPolicy>(std::move(__first), std::move(__m), __end); + return pair<_Iter, _Iter>(std::move(__ret.second), std::move(__end)); } else { - _ForwardIterator __ret = __first; + _Iter __ret = __first; for (; __n > 0; --__n) { if (__ret == __last) { - return __last; + return pair<_Iter, _Iter>(__ret, std::move(__ret)); } ++__ret; } @@ -64,28 +82,28 @@ shift_right(_ForwardIterator __first, // We have an __n-element scratch space from __first to __ret. // Slide an __n-element window [__trail, __lead) from left to right. // We're essentially doing swap_ranges(__first, __ret, __trail, __lead) - // over and over; but once __lead reaches __last we needn't bother - // to save the values of elements [__trail, __last). + // over and over; but once __lead reaches __end we needn't bother + // to save the values of elements [__trail, __end). auto __trail = __first; auto __lead = __ret; while (__trail != __ret) { if (__lead == __last) { - std::move(__first, __trail, __ret); - return __ret; + std::__move<_AlgPolicy>(std::move(__first), std::move(__trail), __ret); + return pair<_Iter, _Iter>(__ret, std::move(__lead)); } ++__trail; ++__lead; } - _ForwardIterator __mid = __first; + _Iter __mid = __first; while (true) { if (__lead == __last) { - __trail = std::move(__mid, __ret, __trail); - std::move(__first, __mid, __trail); - return __ret; + __trail = std::__move<_AlgPolicy>(__mid, __ret, __trail).second; + std::__move<_AlgPolicy>(std::move(__first), std::move(__mid), std::move(__trail)); + return pair<_Iter, _Iter>(__ret, std::move(__lead)); } - swap(*__mid, *__trail); + _IterOps<_AlgPolicy>::iter_swap(__mid, __trail); ++__mid; ++__trail; ++__lead; @@ -96,6 +114,14 @@ shift_right(_ForwardIterator __first, } } +template +_LIBCPP_HIDE_FROM_ABI constexpr _ForwardIterator +shift_right(_ForwardIterator __first, + _ForwardIterator __last, + typename iterator_traits<_ForwardIterator>::difference_type __n) { + return std::__shift_right<_ClassicAlgPolicy>(std::move(__first), std::move(__last), __n).first; +} + #endif // _LIBCPP_STD_VER >= 20 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm index 5dd7b9dc990e1..2200ce6219eb4 100644 --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -1496,6 +1496,13 @@ template shift_right(ForwardIterator first, ForwardIterator last, typename iterator_traits::difference_type n); // C++20 + template S> + constexpr subrange ranges::shift_right(I first, S last, iter_difference_t n); // since C++23 + + template + requires permutable> + constexpr borrowed_subrange_t ranges::shift_right(R&& r, range_difference_t n) // since C++23 + template constexpr bool // constexpr since C++20 is_partitioned(InputIterator first, InputIterator last, Predicate pred); @@ -2043,6 +2050,7 @@ template # include <__algorithm/ranges_find_last.h> # include <__algorithm/ranges_fold.h> # include <__algorithm/ranges_shift_left.h> +# include <__algorithm/ranges_shift_right.h> # include <__algorithm/ranges_starts_with.h> # endif // _LIBCPP_STD_VER >= 23 diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 83b0259cac9e4..a1dbf4bad3559 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -763,6 +763,9 @@ module std [system] { module ranges_shift_left { header "__algorithm/ranges_shift_left.h" } + module ranges_shift_right { + header "__algorithm/ranges_shift_right.h" + } module ranges_shuffle { header "__algorithm/ranges_shuffle.h" } diff --git a/libcxx/include/version b/libcxx/include/version index 7d77677a012ce..562538c5393cc 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -236,7 +236,8 @@ __cpp_lib_shared_ptr_arrays 201707L 201611L // C++17 __cpp_lib_shared_ptr_weak_type 201606L __cpp_lib_shared_timed_mutex 201402L -__cpp_lib_shift 201806L +__cpp_lib_shift 202202L + 201806L // C++20 __cpp_lib_smart_ptr_for_overwrite 202002L __cpp_lib_smart_ptr_owner_equality 202306L __cpp_lib_source_location 201907L @@ -533,6 +534,8 @@ __cpp_lib_void_t 201411L # define __cpp_lib_ranges_to_container 202202L # define __cpp_lib_ranges_zip 202110L // # define __cpp_lib_reference_from_temporary 202202L +# undef __cpp_lib_shift +# define __cpp_lib_shift 202202L // # define __cpp_lib_spanstream 202106L // # define __cpp_lib_stacktrace 202011L # define __cpp_lib_stdatomic_h 202011L diff --git a/libcxx/modules/std/algorithm.inc b/libcxx/modules/std/algorithm.inc index 540bcc3879678..5cca7636da1df 100644 --- a/libcxx/modules/std/algorithm.inc +++ b/libcxx/modules/std/algorithm.inc @@ -364,9 +364,11 @@ export namespace std { using std::shift_right; +#if _LIBCPP_STD_VER >= 23 namespace ranges { - // using std::ranges::shift_right; + using std::ranges::shift_right; } +#endif // _LIBCPP_STD_VER >= 23 // [alg.sorting], sorting and related operations // [alg.sort], sorting diff --git a/libcxx/test/libcxx/algorithms/alg.modifying.operations/alg.shift/assert.shift_right.pass.cpp b/libcxx/test/libcxx/algorithms/alg.modifying.operations/alg.shift/assert.shift_right.pass.cpp new file mode 100644 index 0000000000000..2c6f54304eacf --- /dev/null +++ b/libcxx/test/libcxx/algorithms/alg.modifying.operations/alg.shift/assert.shift_right.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// REQUIRES: has-unix-headers +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 +// UNSUPPORTED: libcpp-hardening-mode=none +// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing + +#include +#include + +#include "check_assertion.h" + +int main(int, char**) { + std::array arr = {1, 2, 3, 4, 5}; + TEST_LIBCPP_ASSERT_FAILURE( + std::ranges::shift_right(arr, -2), "Providing a negative shift amount to shift_right is UB"); + TEST_LIBCPP_ASSERT_FAILURE( + std::ranges::shift_right(arr.begin(), arr.end(), -2), "Providing a negative shift amount to shift_right is UB"); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.shift/ranges_shift_right.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.shift/ranges_shift_right.pass.cpp new file mode 100644 index 0000000000000..48bb35f4b4589 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.shift/ranges_shift_right.pass.cpp @@ -0,0 +1,307 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++20 +// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=9000000 + +// + +// template S> +// constexpr subrange ranges::shift_right(I first, S last, iter_difference_t n); + +// template +// requires permutable> +// constexpr borrowed_subrange_t ranges::shift_right(R&& r, range_difference_t n) + +#include +#include +#include +#include +#include + +#include "almost_satisfies_types.h" +#include "test_macros.h" +#include "test_iterators.h" +#include "MoveOnly.h" + +struct InvalidDifferenceT {}; + +template > +concept HasShiftRightIt = requires(Iter iter, Sent sent, N n) { std::ranges::shift_right(iter, sent, n); }; + +static_assert(HasShiftRightIt); +static_assert(HasShiftRightIt>); +static_assert(HasShiftRightIt>); +static_assert(!HasShiftRightIt); +static_assert(!HasShiftRightIt); + +static_assert(!HasShiftRightIt); +static_assert(!HasShiftRightIt); +static_assert(!HasShiftRightIt); + +template > +concept HasShiftRightR = requires(Range range, N n) { std::ranges::shift_right(range, n); }; + +static_assert(HasShiftRightR>); +static_assert(!HasShiftRightR, InvalidDifferenceT>); + +static_assert(!HasShiftRightR); +static_assert(!HasShiftRightR); +static_assert(!HasShiftRightR); + +struct TrackCopyMove { + mutable int copy_count = 0; + int move_count = 0; + + constexpr TrackCopyMove() = default; + constexpr TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) { + ++copy_count; + ++other.copy_count; + } + + constexpr TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) { + ++move_count; + ++other.move_count; + } + constexpr TrackCopyMove& operator=(const TrackCopyMove& other) { + ++copy_count; + ++other.copy_count; + return *this; + } + constexpr TrackCopyMove& operator=(TrackCopyMove&& other) noexcept { + ++move_count; + ++other.move_count; + return *this; + } +}; + +template +constexpr void test_iter_sent() { + { + const std::array original = {3, 1, 4, 1, 5, 9, 2, 6}; + + // (iterator, sentinel) overload + for (size_t n = 0; n <= original.size(); ++n) { + for (size_t k = 0; k <= n + 2; ++k) { + std::vector scratch(n); + auto begin = Iter(scratch.data()); + auto end = Sent(Iter(scratch.data() + n)); + std::ranges::copy(original.begin(), original.begin() + n, begin); + std::same_as> decltype(auto) result = std::ranges::shift_right(begin, end, k); + + assert(result.end() == end); + if (k < n) { + assert(result.begin() == Iter(scratch.data() + k)); + assert(std::ranges::equal(original.begin(), original.begin() + n - k, result.begin(), result.end())); + } else { + assert(result.begin() == end); + assert(std::ranges::equal(original.begin(), original.begin() + n, begin, end)); + } + } + } + + // (range) overload + for (size_t n = 0; n <= original.size(); ++n) { + for (size_t k = 0; k <= n + 2; ++k) { + std::vector scratch(n); + auto begin = Iter(scratch.data()); + auto end = Sent(Iter(scratch.data() + n)); + std::ranges::copy(original.begin(), original.begin() + n, begin); + auto range = std::ranges::subrange(begin, end); + std::same_as> decltype(auto) result = std::ranges::shift_right(range, k); + + assert(result.end() == end); + if (k < n) { + assert(result.begin() == Iter(scratch.data() + k)); + assert(std::ranges::equal(original.begin(), original.begin() + n - k, result.begin(), result.end())); + } else { + assert(result.begin() == end); + assert(std::ranges::equal(original.begin(), original.begin() + n, begin, end)); + } + } + } + } + // n == 0 + { + std::array input = {0, 1, 2}; + const std::array expected = {0, 1, 2}; + + { // (iterator, sentinel) overload + auto in = input; + auto begin = Iter(in.data()); + auto end = Sent(Iter(in.data() + in.size())); + std::same_as> decltype(auto) result = std::ranges::shift_right(begin, end, 0); + assert(std::ranges::equal(expected, result)); + assert(result.begin() == begin); + assert(result.end() == end); + } + + { // (range) overload + auto in = input; + auto begin = Iter(in.data()); + auto end = Sent(Iter(in.data() + in.size())); + auto range = std::ranges::subrange(begin, end); + std::same_as> decltype(auto) result = std::ranges::shift_right(range, 0); + assert(std::ranges::equal(expected, result)); + assert(result.begin() == begin); + assert(result.end() == end); + } + } + + // n == len + { + std::array input = {0, 1, 2}; + const std::array expected = {0, 1, 2}; + + { // (iterator, sentinel) overload + auto in = input; + auto begin = Iter(in.data()); + auto end = Sent(Iter(in.data() + in.size())); + std::same_as> decltype(auto) result = + std::ranges::shift_right(begin, end, input.size()); + assert(std::ranges::equal(expected, input)); + assert(result.begin() == end); + assert(result.end() == end); + } + + { // (range) overload + auto in = input; + auto begin = Iter(in.data()); + auto end = Sent(Iter(in.data() + in.size())); + auto range = std::ranges::subrange(begin, end); + std::same_as> decltype(auto) result = std::ranges::shift_right(range, input.size()); + assert(std::ranges::equal(expected, input)); + assert(result.begin() == end); + assert(result.end() == end); + } + } + + // n > len + { + std::array input = {0, 1, 2}; + const std::array expected = {0, 1, 2}; + + { // (iterator, sentinel) overload + auto in = input; + auto begin = Iter(in.data()); + auto end = Sent(Iter(in.data() + in.size())); + std::same_as> decltype(auto) result = + std::ranges::shift_right(begin, end, input.size() + 1); + assert(std::ranges::equal(expected, input)); + assert(result.begin() == end); + assert(result.end() == end); + } + + { // (range) overload + auto in = input; + auto begin = Iter(in.data()); + auto end = Sent(Iter(in.data() + in.size())); + auto range = std::ranges::subrange(begin, end); + std::same_as> decltype(auto) result = + std::ranges::shift_right(range, input.size() + 1); + assert(std::ranges::equal(expected, input)); + assert(result.begin() == end); + assert(result.end() == end); + } + } + + // empty range + { + std::vector input = {}; + const std::vector expected = {}; + { // (iterator, sentinel) overload + auto in = input; + auto begin = Iter(in.data()); + auto end = Sent(Iter(in.data() + in.size())); + std::same_as> decltype(auto) result = + std::ranges::shift_right(begin, end, input.size() + 1); + assert(std::ranges::equal(expected, input)); + assert(result.begin() == begin); + assert(result.end() == begin); + } + + { // (range) overload + auto in = input; + auto begin = Iter(in.data()); + auto end = Sent(Iter(in.data() + in.size())); + auto range = std::ranges::subrange(begin, end); + std::same_as> decltype(auto) result = + std::ranges::shift_right(range, input.size() + 1); + assert(std::ranges::equal(expected, input)); + assert(result.begin() == begin); + assert(result.end() == begin); + } + } +} + +constexpr bool test() { + types::for_each(types::forward_iterator_list{}, [] { + test_iter_sent(); + test_iter_sent>(); + test_iter_sent>(); + }); + + // Complexity: At most (last - first) - n assignments or swaps. + { + constexpr int length = 100; + constexpr int n = length / 2; + auto make_vec = []() { + std::vector vec; + vec.reserve(length); + for (int i = 0; i < length; ++i) { + vec.emplace_back(); + } + return vec; + }; + + { // (iterator, sentinel) overload + auto input = make_vec(); + auto result = std::ranges::shift_right(input.begin(), input.end(), n); + assert(result.begin() == input.begin() + length - n); + assert(result.end() == input.end()); + + auto total_copies = 0; + auto total_moves = 0; + for (auto it = result.begin(); it != result.end(); ++it) { + const auto& item = *it; + total_copies += item.copy_count; + total_moves += item.move_count; + } + + assert(total_copies == 0); + assert(total_moves <= length - n); + } + + { // (range) overload + auto input = make_vec(); + auto result = std::ranges::shift_right(input, n); + assert(result.begin() == input.begin() + length - n); + assert(result.end() == input.end()); + + auto total_copies = 0; + auto total_moves = 0; + for (auto it = result.begin(); it != result.end(); ++it) { + const auto& item = *it; + total_copies += item.copy_count; + total_moves += item.move_count; + } + + assert(total_copies == 0); + assert(total_moves <= length - n); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp index 17037acfe7184..a4536b7455c46 100644 --- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp @@ -225,6 +225,7 @@ constexpr bool test_all() { #if TEST_STD_VER >= 23 dangling_1st>(std::ranges::iota, in, x); dangling_1st(std::ranges::shift_left, in, x); + dangling_1st(std::ranges::shift_right, in, x); #endif return true; diff --git a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp index a442743333ea8..c77d4cd9f420a 100644 --- a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp +++ b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp @@ -193,7 +193,9 @@ constexpr void run_tests() { test(std::ranges::next_permutation, in); #if TEST_STD_VER >= 23 test(std::ranges::shift_left, in, count); + test(std::ranges::shift_right, in, count); #endif + // The algorithms that work on uninitialized memory have constraints that prevent proxy iterators from being used with // them. } diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp index bc479f1bcb1e0..beecf87cd4aa7 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp @@ -348,8 +348,8 @@ # ifndef __cpp_lib_shift # error "__cpp_lib_shift should be defined in c++23" # endif -# if __cpp_lib_shift != 201806L -# error "__cpp_lib_shift should have the value 201806L in c++23" +# if __cpp_lib_shift != 202202L +# error "__cpp_lib_shift should have the value 202202L in c++23" # endif #elif TEST_STD_VER > 23 @@ -452,8 +452,8 @@ # ifndef __cpp_lib_shift # error "__cpp_lib_shift should be defined in c++26" # endif -# if __cpp_lib_shift != 201806L -# error "__cpp_lib_shift should have the value 201806L in c++26" +# if __cpp_lib_shift != 202202L +# error "__cpp_lib_shift should have the value 202202L in c++26" # endif #endif // TEST_STD_VER > 23 diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp index f442b22c6d752..e74c2b58717a9 100644 --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -5916,8 +5916,8 @@ # ifndef __cpp_lib_shift # error "__cpp_lib_shift should be defined in c++23" # endif -# if __cpp_lib_shift != 201806L -# error "__cpp_lib_shift should have the value 201806L in c++23" +# if __cpp_lib_shift != 202202L +# error "__cpp_lib_shift should have the value 202202L in c++23" # endif # ifndef __cpp_lib_smart_ptr_for_overwrite @@ -7868,8 +7868,8 @@ # ifndef __cpp_lib_shift # error "__cpp_lib_shift should be defined in c++26" # endif -# if __cpp_lib_shift != 201806L -# error "__cpp_lib_shift should have the value 201806L in c++26" +# if __cpp_lib_shift != 202202L +# error "__cpp_lib_shift should have the value 202202L in c++26" # endif # ifndef __cpp_lib_smart_ptr_for_overwrite diff --git a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp index c799af04fc688..d78d513e92d3f 100644 --- a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp +++ b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp @@ -157,6 +157,7 @@ static_assert(test(std::ranges::set_symmetric_difference, a, a, a)); static_assert(test(std::ranges::set_union, a, a, a)); #if TEST_STD_VER >= 23 static_assert(test(std::ranges::shift_left, a, 42)); +static_assert(test(std::ranges::shift_right, a, 42)); #endif static_assert(test(std::ranges::shuffle, a, g)); static_assert(test(std::ranges::sort, a)); diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py index 88431c76a00f5..9d2e095ab9ff7 100644 --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -1270,7 +1270,7 @@ def add_version_header(tc): }, { "name": "__cpp_lib_shift", - "values": {"c++20": 201806}, + "values": {"c++20": 201806, "c++23": 202202}, "headers": ["algorithm"], }, {