diff options
author | Nikolas Klauser <nikolasklauser@berlin.de> | 2022-07-27 23:52:45 +0200 |
---|---|---|
committer | Tom Stellard <tstellar@redhat.com> | 2022-08-02 21:48:48 -0700 |
commit | 9e126d6fd2628b187ee356e66694df686817e125 (patch) | |
tree | 92b2663ff19f9e9860481f98f9c93f76a270ba11 | |
parent | e38b9de601b36b26754854fda60b11817fd54b17 (diff) |
[libc++] Fix unwrapping ranges with different iterators and sentinels
Reviewed By: ldionne, huixie90, #libc
Spies: arichardson, sstefan1, libcxx-commits, mgorny
Differential Revision: https://reviews.llvm.org/D129040
(cherry picked from commit e01b4fe956dd038fed71cf3c552d3383905d022a)
-rw-r--r-- | libcxx/include/CMakeLists.txt | 1 | ||||
-rw-r--r-- | libcxx/include/__algorithm/copy.h | 10 | ||||
-rw-r--r-- | libcxx/include/__algorithm/unwrap_range.h | 97 | ||||
-rw-r--r-- | libcxx/include/module.modulemap.in | 1 | ||||
-rw-r--r-- | libcxx/test/libcxx/private_headers.verify.cpp | 1 | ||||
-rw-r--r-- | libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp | 15 |
6 files changed, 117 insertions, 8 deletions
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index 371f0c0cf223..36eb70d39d76 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -181,6 +181,7 @@ set(files __algorithm/unique.h __algorithm/unique_copy.h __algorithm/unwrap_iter.h + __algorithm/unwrap_range.h __algorithm/upper_bound.h __assert __availability diff --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h index 5428baa68859..f7535a81547a 100644 --- a/libcxx/include/__algorithm/copy.h +++ b/libcxx/include/__algorithm/copy.h @@ -10,6 +10,7 @@ #define _LIBCPP___ALGORITHM_COPY_H #include <__algorithm/unwrap_iter.h> +#include <__algorithm/unwrap_range.h> #include <__config> #include <__iterator/iterator_traits.h> #include <__iterator/reverse_iterator.h> @@ -88,10 +89,11 @@ template <class _InIter, class _Sent, class _OutIter, && is_copy_constructible<_Sent>::value && is_copy_constructible<_OutIter>::value, int> = 0> inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11 -pair<_InIter, _OutIter> -__copy(_InIter __first, _Sent __last, _OutIter __result) { - auto __ret = std::__copy_impl(std::__unwrap_iter(__first), std::__unwrap_iter(__last), std::__unwrap_iter(__result)); - return std::make_pair(std::__rewrap_iter(__first, __ret.first), std::__rewrap_iter(__result, __ret.second)); +pair<_InIter, _OutIter> __copy(_InIter __first, _Sent __last, _OutIter __result) { + auto __range = std::__unwrap_range(__first, __last); + auto __ret = std::__copy_impl(std::move(__range.first), std::move(__range.second), std::__unwrap_iter(__result)); + return std::make_pair( + std::__rewrap_range<_Sent>(__first, __ret.first), std::__rewrap_iter(__result, __ret.second)); } template <class _InputIterator, class _OutputIterator> diff --git a/libcxx/include/__algorithm/unwrap_range.h b/libcxx/include/__algorithm/unwrap_range.h new file mode 100644 index 000000000000..745906a96e15 --- /dev/null +++ b/libcxx/include/__algorithm/unwrap_range.h @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// 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_UNWRAP_RANGE_H +#define _LIBCPP___ALGORITHM_UNWRAP_RANGE_H + +#include <__algorithm/unwrap_iter.h> +#include <__concepts/constructible.h> +#include <__config> +#include <__iterator/concepts.h> +#include <__iterator/next.h> +#include <__utility/declval.h> +#include <__utility/move.h> +#include <__utility/pair.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +// __unwrap_range and __rewrap_range are used to unwrap ranges which may have different iterator and sentinel types. +// __unwrap_iter and __rewrap_iter don't work for this, because they assume that the iterator and sentinel have +// the same type. __unwrap_range tries to get two iterators and then forward to __unwrap_iter. + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) +template <class _Iter, class _Sent> +struct __unwrap_range_impl { + _LIBCPP_HIDE_FROM_ABI static constexpr auto __unwrap(_Iter __first, _Sent __sent) + requires random_access_iterator<_Iter> && sized_sentinel_for<_Sent, _Iter> + { + auto __last = ranges::next(__first, __sent); + return pair{std::__unwrap_iter(std::move(__first)), std::__unwrap_iter(std::move(__last))}; + } + + _LIBCPP_HIDE_FROM_ABI static constexpr auto __unwrap(_Iter __first, _Sent __last) { + return pair{std::move(__first), std::move(__last)}; + } + + _LIBCPP_HIDE_FROM_ABI static constexpr auto + __rewrap(_Iter __orig_iter, decltype(std::__unwrap_iter(__orig_iter)) __iter) + requires random_access_iterator<_Iter> && sized_sentinel_for<_Sent, _Iter> + { + return std::__rewrap_iter(std::move(__orig_iter), std::move(__iter)); + } + + _LIBCPP_HIDE_FROM_ABI static constexpr auto __rewrap(const _Iter&, _Iter __iter) + requires (!(random_access_iterator<_Iter> && sized_sentinel_for<_Sent, _Iter>)) + { + return __iter; + } +}; + +template <class _Iter> +struct __unwrap_range_impl<_Iter, _Iter> { + _LIBCPP_HIDE_FROM_ABI static constexpr auto __unwrap(_Iter __first, _Iter __last) { + return pair{std::__unwrap_iter(std::move(__first)), std::__unwrap_iter(std::move(__last))}; + } + + _LIBCPP_HIDE_FROM_ABI static constexpr auto + __rewrap(_Iter __orig_iter, decltype(std::__unwrap_iter(__orig_iter)) __iter) { + return std::__rewrap_iter(std::move(__orig_iter), std::move(__iter)); + } +}; + +template <class _Iter, class _Sent> +_LIBCPP_HIDE_FROM_ABI constexpr auto __unwrap_range(_Iter __first, _Sent __last) { + return __unwrap_range_impl<_Iter, _Sent>::__unwrap(std::move(__first), std::move(__last)); +} + +template < + class _Sent, + class _Iter, + class _Unwrapped = decltype(std::__unwrap_range(std::declval<_Iter>(), std::declval<_Sent>()))> +_LIBCPP_HIDE_FROM_ABI constexpr _Iter __rewrap_range(_Iter __orig_iter, _Unwrapped __iter) { + return __unwrap_range_impl<_Iter, _Sent>::__rewrap(std::move(__orig_iter), std::move(__iter)); +} +#else // _LIBCPP_STD_VER > 17 +template <class _Iter, class _Unwrapped = decltype(std::__unwrap_iter(std::declval<_Iter>()))> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR pair<_Unwrapped, _Unwrapped> __unwrap_range(_Iter __first, _Iter __last) { + return std::make_pair(std::__unwrap_iter(std::move(__first)), std::__unwrap_iter(std::move(__last))); +} + +template <class _Iter, class _Unwrapped = decltype(std::__unwrap_iter(std::declval<_Iter>()))> +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iter __rewrap_range(_Iter __orig_iter, _Unwrapped __iter) { + return std::__rewrap_iter(std::move(__orig_iter), std::move(__iter)); +} +#endif // _LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___ALGORITHM_UNWRAP_RANGE_H diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index 1cfd4ae3bfef..88f4d152063b 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -420,6 +420,7 @@ module std [system] { module unique { private header "__algorithm/unique.h" } module unique_copy { private header "__algorithm/unique_copy.h" } module unwrap_iter { private header "__algorithm/unwrap_iter.h" } + module unwrap_range { private header "__algorithm/unwrap_range.h" } module upper_bound { private header "__algorithm/upper_bound.h" } } } diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp index 5b3e65be251d..516991b2ac26 100644 --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -218,6 +218,7 @@ END-SCRIPT #include <__algorithm/unique.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/unique.h'}} #include <__algorithm/unique_copy.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/unique_copy.h'}} #include <__algorithm/unwrap_iter.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/unwrap_iter.h'}} +#include <__algorithm/unwrap_range.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/unwrap_range.h'}} #include <__algorithm/upper_bound.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/upper_bound.h'}} #include <__availability> // expected-error@*:* {{use of private header from outside its module: '__availability'}} #include <__bit/bit_cast.h> // expected-error@*:* {{use of private header from outside its module: '__bit/bit_cast.h'}} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp index 390bafed7aa5..cc58dd2732c4 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp @@ -95,13 +95,20 @@ constexpr void test_iterators() { } } +template <class In, class Out> +constexpr void test_sentinels() { + test_iterators<In, Out>(); + test_iterators<In, Out, sized_sentinel<In>>(); + test_iterators<In, Out, sentinel_wrapper<In>>(); +} + template <class Out> constexpr void test_in_iterators() { test_iterators<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>(); - test_iterators<forward_iterator<int*>, Out>(); - test_iterators<bidirectional_iterator<int*>, Out>(); - test_iterators<random_access_iterator<int*>, Out>(); - test_iterators<contiguous_iterator<int*>, Out>(); + test_sentinels<forward_iterator<int*>, Out>(); + test_sentinels<bidirectional_iterator<int*>, Out>(); + test_sentinels<random_access_iterator<int*>, Out>(); + test_sentinels<contiguous_iterator<int*>, Out>(); } template <class Out> |