From dd4891318c72d3d0503801a2f78da77fa00c6dcf Mon Sep 17 00:00:00 2001 From: Rajveer Singh Bharadwaj Date: Fri, 27 Oct 2023 14:37:12 +0530 Subject: [PATCH] [libc++] Fix _CopySegment helper in ranges::copy(join_view, out) when called in a static assertion context (#69593) Resolves Issue #69083 The `_CopySegment` helper for `ranges::copy(join_view, out)` is not `constexpr` causing rejection in `libc++` in a static assertion context as in the issue snippet. --- libcxx/include/__algorithm/copy.h | 5 +- libcxx/include/__algorithm/move.h | 5 +- .../alg.copy/ranges.copy.segmented.pass.cpp | 15 ++- .../ranges.copy_backward.segmented.pass.cpp | 111 +++++++++++++++++ .../alg.copy/ranges.copy_n.segmented.pass.cpp | 114 +++++++++++++++++ .../alg.move/ranges.move.segmented.pass.cpp | 115 ++++++++++++++++++ .../ranges.move_backward.segmented.pass.cpp | 111 +++++++++++++++++ 7 files changed, 468 insertions(+), 8 deletions(-) create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.segmented.pass.cpp create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.segmented.pass.cpp create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.segmented.pass.cpp create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.segmented.pass.cpp diff --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h index dfe9898c6480..b35c6fa04a38 100644 --- a/libcxx/include/__algorithm/copy.h +++ b/libcxx/include/__algorithm/copy.h @@ -51,9 +51,10 @@ struct __copy_loop { _OutIter& __result_; - _LIBCPP_HIDE_FROM_ABI _CopySegment(_OutIter& __result) : __result_(__result) {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit _CopySegment(_OutIter& __result) + : __result_(__result) {} - _LIBCPP_HIDE_FROM_ABI void + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void operator()(typename _Traits::__local_iterator __lfirst, typename _Traits::__local_iterator __llast) { __result_ = std::__copy<_AlgPolicy>(__lfirst, __llast, std::move(__result_)).second; } diff --git a/libcxx/include/__algorithm/move.h b/libcxx/include/__algorithm/move.h index 01aeef4e177e..e0da07117e66 100644 --- a/libcxx/include/__algorithm/move.h +++ b/libcxx/include/__algorithm/move.h @@ -52,9 +52,10 @@ struct __move_loop { _OutIter& __result_; - _LIBCPP_HIDE_FROM_ABI _MoveSegment(_OutIter& __result) : __result_(__result) {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 explicit _MoveSegment(_OutIter& __result) + : __result_(__result) {} - _LIBCPP_HIDE_FROM_ABI void + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void operator()(typename _Traits::__local_iterator __lfirst, typename _Traits::__local_iterator __llast) { __result_ = std::__move<_AlgPolicy>(__lfirst, __llast, std::move(__result_)).second; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp index abb9157df9ab..50fb479afcd0 100644 --- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp @@ -92,10 +92,7 @@ constexpr void test_join_view() { } } -int main(int, char**) { - test_containers, std::deque>(); - test_containers, std::vector>(); - test_containers, std::deque>(); +constexpr bool test_constexpr() { test_containers, std::vector>(); types::for_each(types::forward_iterator_list{}, [] { @@ -103,6 +100,16 @@ int main(int, char**) { test_join_view>(); test_join_view>(); }); + return true; +} + +int main(int, char**) { + test_containers, std::deque>(); + test_containers, std::vector>(); + test_containers, std::deque>(); + + test_constexpr(); + static_assert(test_constexpr()); return 0; } diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.segmented.pass.cpp new file mode 100644 index 000000000000..c434cea1208c --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.segmented.pass.cpp @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// TODO: make `join_view` non-experimental once D2770 is implemented. +// UNSUPPORTED: !c++experimental + +#include +#include +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "type_algorithms.h" + +template +constexpr void test_containers() { + using InIter = typename InContainer::iterator; + using OutIter = typename OutContainer::iterator; + + { + InContainer in{1, 2, 3, 4}; + OutContainer out(4); + + std::same_as> auto ret = + std::ranges::copy_backward(in.begin(), in.end(), out.end()); + assert(std::ranges::equal(in, out)); + assert(ret.in == in.end()); + assert(ret.out == out.begin()); + } + { + InContainer in{1, 2, 3, 4}; + OutContainer out(4); + std::same_as> auto ret = std::ranges::copy_backward(in, out.end()); + assert(std::ranges::equal(in, out)); + assert(ret.in == in.end()); + assert(ret.out == out.begin()); + } +} + +template +constexpr void test_join_view() { + auto to_subranges = std::views::transform([](auto& vec) { + return std::ranges::subrange(Iter(vec.data()), Sent(Iter(vec.data() + vec.size()))); + }); + + { // segmented -> contiguous + std::vector> vectors = {}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr; + + std::ranges::copy_backward(subrange_vector | std::views::join, arr.end()); + assert(std::ranges::equal(arr, std::array{})); + } + { // segmented -> contiguous + std::vector> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr; + + std::ranges::copy_backward(subrange_vector | std::views::join, arr.end()); + assert(std::ranges::equal(arr, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } + { // contiguous -> segmented + std::vector> vectors = {{0, 0, 0, 0}, {0, 0}, {0, 0, 0, 0}, {}}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + std::ranges::copy_backward(arr, (subrange_vector | std::views::join).end()); + assert(std::ranges::equal(subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } + { // segmented -> segmented + std::vector> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}}; + auto range1 = vectors | to_subranges; + std::vector> subrange_vector(range1.begin(), range1.end()); + std::vector> to_vectors = {{0, 0, 0, 0}, {0, 0, 0, 0}, {}, {0, 0}}; + auto range2 = to_vectors | to_subranges; + std::vector> to_subrange_vector(range2.begin(), range2.end()); + + std::ranges::copy_backward(subrange_vector | std::views::join, (to_subrange_vector | std::views::join).end()); + assert(std::ranges::equal(to_subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } +} + +constexpr bool test_constexpr() { + test_containers, std::vector>(); + + types::for_each(types::bidirectional_iterator_list{}, [] { test_join_view(); }); + return true; +} + +int main(int, char**) { + test_containers, std::deque>(); + test_containers, std::vector>(); + test_containers, std::deque>(); + + test_constexpr(); + static_assert(test_constexpr()); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.segmented.pass.cpp new file mode 100644 index 000000000000..eae40cefa663 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.segmented.pass.cpp @@ -0,0 +1,114 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// TODO: make `join_view` non-experimental once D2770 is implemented. +// UNSUPPORTED: !c++experimental + +#include +#include +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "type_algorithms.h" + +template +constexpr void test_containers() { + using InIter = typename InContainer::iterator; + using OutIter = typename OutContainer::iterator; + + { + InContainer in{1, 2, 3, 4}; + OutContainer out(4); + + std::same_as> auto ret = + std::ranges::copy_n(in.begin(), in.size(), out.begin()); + assert(std::ranges::equal(in, out)); + assert(ret.in == in.end()); + assert(ret.out == out.end()); + } +} + +template +constexpr void test_join_view() { + auto to_subranges = std::views::transform([](auto& vec) { + return std::ranges::subrange(Iter(vec.data()), Sent(Iter(vec.data() + vec.size()))); + }); + + { // segmented -> contiguous + std::vector> vectors = {}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr; + + std::ranges::copy_n((subrange_vector | std::views::join).begin(), + std::ranges::distance(subrange_vector | std::views::join), + arr.begin()); + assert(std::ranges::equal(arr, std::array{})); + } + { // segmented -> contiguous + std::vector> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr; + + std::ranges::copy_n((subrange_vector | std::views::join).begin(), + std::ranges::distance(subrange_vector | std::views::join), + arr.begin()); + assert(std::ranges::equal(arr, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } + { // contiguous -> segmented + std::vector> vectors = {{0, 0, 0, 0}, {0, 0}, {0, 0, 0, 0}, {}}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + std::ranges::copy_n(arr.begin(), arr.size(), (subrange_vector | std::views::join).begin()); + assert(std::ranges::equal(subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } + { // segmented -> segmented + std::vector> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}}; + auto range1 = vectors | to_subranges; + std::vector> subrange_vector(range1.begin(), range1.end()); + std::vector> to_vectors = {{0, 0, 0, 0}, {0, 0, 0, 0}, {}, {0, 0}}; + auto range2 = to_vectors | to_subranges; + std::vector> to_subrange_vector(range2.begin(), range2.end()); + + std::ranges::copy_n((subrange_vector | std::views::join).begin(), + std::ranges::distance(subrange_vector | std::views::join), + (to_subrange_vector | std::views::join).begin()); + assert(std::ranges::equal(to_subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } +} + +constexpr bool test_constexpr() { + test_containers, std::vector>(); + + // TODO: this should be cpp20_input_iterator_list, not forward_iterator_list + types::for_each(types::forward_iterator_list{}, [] { + test_join_view(); + test_join_view>(); + test_join_view>(); + }); + return true; +} + +int main(int, char**) { + test_containers, std::deque>(); + test_containers, std::vector>(); + test_containers, std::deque>(); + + test_constexpr(); + static_assert(test_constexpr()); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.segmented.pass.cpp new file mode 100644 index 000000000000..2df6a10b1850 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.segmented.pass.cpp @@ -0,0 +1,115 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// TODO: make `join_view` non-experimental once D2770 is implemented. +// UNSUPPORTED: !c++experimental + +#include +#include +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "type_algorithms.h" + +template +constexpr void test_containers() { + using InIter = typename InContainer::iterator; + using OutIter = typename OutContainer::iterator; + + { + InContainer in{1, 2, 3, 4}; + OutContainer out(4); + + std::same_as> auto ret = + std::ranges::move(in.begin(), in.end(), out.begin()); + assert(std::ranges::equal(in, out)); + assert(ret.in == in.end()); + assert(ret.out == out.end()); + } + { + InContainer in{1, 2, 3, 4}; + OutContainer out(4); + std::same_as> auto ret = std::ranges::move(in, out.begin()); + assert(std::ranges::equal(in, out)); + assert(ret.in == in.end()); + assert(ret.out == out.end()); + } +} + +template +constexpr void test_join_view() { + auto to_subranges = std::views::transform([](auto& vec) { + return std::ranges::subrange(Iter(vec.data()), Sent(Iter(vec.data() + vec.size()))); + }); + + { // segmented -> contiguous + std::vector> vectors = {}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr; + + std::ranges::move(subrange_vector | std::views::join, arr.begin()); + assert(std::ranges::equal(arr, std::array{})); + } + { // segmented -> contiguous + std::vector> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr; + + std::ranges::move(subrange_vector | std::views::join, arr.begin()); + assert(std::ranges::equal(arr, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } + { // contiguous -> segmented + std::vector> vectors = {{0, 0, 0, 0}, {0, 0}, {0, 0, 0, 0}, {}}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + std::ranges::move(arr, (subrange_vector | std::views::join).begin()); + assert(std::ranges::equal(subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } + { // segmented -> segmented + std::vector> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}}; + auto range1 = vectors | to_subranges; + std::vector> subrange_vector(range1.begin(), range1.end()); + std::vector> to_vectors = {{0, 0, 0, 0}, {0, 0, 0, 0}, {}, {0, 0}}; + auto range2 = to_vectors | to_subranges; + std::vector> to_subrange_vector(range2.begin(), range2.end()); + + std::ranges::move(subrange_vector | std::views::join, (to_subrange_vector | std::views::join).begin()); + assert(std::ranges::equal(to_subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } +} + +constexpr bool test_constexpr() { + test_containers, std::vector>(); + + types::for_each(types::forward_iterator_list{}, [] { + test_join_view(); + test_join_view>(); + test_join_view>(); + }); + return true; +} + +int main(int, char**) { + test_containers, std::deque>(); + test_containers, std::vector>(); + test_containers, std::deque>(); + + test_constexpr(); + static_assert(test_constexpr()); + + return 0; +} diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.segmented.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.segmented.pass.cpp new file mode 100644 index 000000000000..0f0a71439a10 --- /dev/null +++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.segmented.pass.cpp @@ -0,0 +1,111 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// TODO: make `join_view` non-experimental once D2770 is implemented. +// UNSUPPORTED: !c++experimental + +#include +#include +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "type_algorithms.h" + +template +constexpr void test_containers() { + using InIter = typename InContainer::iterator; + using OutIter = typename OutContainer::iterator; + + { + InContainer in{1, 2, 3, 4}; + OutContainer out(4); + + std::same_as> auto ret = + std::ranges::move_backward(in.begin(), in.end(), out.end()); + assert(std::ranges::equal(in, out)); + assert(ret.in == in.end()); + assert(ret.out == out.begin()); + } + { + InContainer in{1, 2, 3, 4}; + OutContainer out(4); + std::same_as> auto ret = std::ranges::move_backward(in, out.end()); + assert(std::ranges::equal(in, out)); + assert(ret.in == in.end()); + assert(ret.out == out.begin()); + } +} + +template +constexpr void test_join_view() { + auto to_subranges = std::views::transform([](auto& vec) { + return std::ranges::subrange(Iter(vec.data()), Sent(Iter(vec.data() + vec.size()))); + }); + + { // segmented -> contiguous + std::vector> vectors = {}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr; + + std::ranges::move_backward(subrange_vector | std::views::join, arr.end()); + assert(std::ranges::equal(arr, std::array{})); + } + { // segmented -> contiguous + std::vector> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr; + + std::ranges::move_backward(subrange_vector | std::views::join, arr.end()); + assert(std::ranges::equal(arr, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } + { // contiguous -> segmented + std::vector> vectors = {{0, 0, 0, 0}, {0, 0}, {0, 0, 0, 0}, {}}; + auto range = vectors | to_subranges; + std::vector> subrange_vector(range.begin(), range.end()); + std::array arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + std::ranges::move_backward(arr, (subrange_vector | std::views::join).end()); + assert(std::ranges::equal(subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } + { // segmented -> segmented + std::vector> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}}; + auto range1 = vectors | to_subranges; + std::vector> subrange_vector(range1.begin(), range1.end()); + std::vector> to_vectors = {{0, 0, 0, 0}, {0, 0, 0, 0}, {}, {0, 0}}; + auto range2 = to_vectors | to_subranges; + std::vector> to_subrange_vector(range2.begin(), range2.end()); + + std::ranges::move_backward(subrange_vector | std::views::join, (to_subrange_vector | std::views::join).end()); + assert(std::ranges::equal(to_subrange_vector | std::views::join, std::array{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})); + } +} + +constexpr bool test_constexpr() { + test_containers, std::vector>(); + + types::for_each(types::bidirectional_iterator_list{}, [] { test_join_view(); }); + return true; +} + +int main(int, char**) { + test_containers, std::deque>(); + test_containers, std::vector>(); + test_containers, std::deque>(); + + test_constexpr(); + static_assert(test_constexpr()); + + return 0; +}