[libcxx-commits] [libcxx] [libc++] Fix _CopySegment helper in ranges::copy(join_view, out) when called in a static assertion context (PR #69593)

Rajveer Singh Bharadwaj via libcxx-commits libcxx-commits at lists.llvm.org
Mon Oct 23 00:24:17 PDT 2023


https://github.com/Rajveer100 updated https://github.com/llvm/llvm-project/pull/69593

>From d81a475e4d17b62f5d0797ace2969af33f83b358 Mon Sep 17 00:00:00 2001
From: Rajveer <rajveer.developer at icloud.com>
Date: Mon, 23 Oct 2023 12:44:22 +0530
Subject: [PATCH] Squashed commit of the following:

commit 8f4f90a925d63a975de17df0bd89460b545f7dc0
Author: Rajveer <rajveer.developer at icloud.com>
Date:   Mon Oct 23 12:33:19 2023 +0530

    [libc++] Make _CopySegment constexpr-friendly, and add some tests

    Fixes #69083

commit 5df4f7ac87bf7c876e1de4b3d0c331291e0c9164
Author: Rajveer <rajveer.developer at icloud.com>
Date:   Thu Oct 19 17:08:57 2023 +0530

    [libc++] Fix _CopySegment helper in ranges::copy(join_view, out) when called in a static assertion context

     Resolves Issue #69083
---
 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 dfe9898c6480cf8..b35c6fa04a38149 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 01aeef4e177e814..e0da07117e66c9f 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 abb9157df9abbbb..50fb479afcd0640 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<int>, std::deque<int>>();
-  test_containers<std::deque<int>, std::vector<int>>();
-  test_containers<std::vector<int>, std::deque<int>>();
+constexpr bool test_constexpr() {
   test_containers<std::vector<int>, std::vector<int>>();
 
   types::for_each(types::forward_iterator_list<int*>{}, []<class Iter> {
@@ -103,6 +100,16 @@ int main(int, char**) {
     test_join_view<Iter, sentinel_wrapper<Iter>>();
     test_join_view<Iter, sized_sentinel<Iter>>();
   });
+  return true;
+}
+
+int main(int, char**) {
+  test_containers<std::deque<int>, std::deque<int>>();
+  test_containers<std::deque<int>, std::vector<int>>();
+  test_containers<std::vector<int>, std::deque<int>>();
+
+  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 000000000000000..c434cea1208cfe8
--- /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 <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <deque>
+#include <ranges>
+#include <vector>
+
+#include "test_iterators.h"
+#include "type_algorithms.h"
+
+template <class InContainer, class OutContainer>
+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<std::ranges::in_out_result<InIter, OutIter>> 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<std::ranges::in_out_result<InIter, OutIter>> 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 <class Iter, class Sent>
+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<std::vector<int>> vectors = {};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+    std::array<int, 0> arr;
+
+    std::ranges::copy_backward(subrange_vector | std::views::join, arr.end());
+    assert(std::ranges::equal(arr, std::array<int, 0>{}));
+  }
+  { // segmented -> contiguous
+    std::vector<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+    std::array<int, 10> 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<std::vector<int>> vectors = {{0, 0, 0, 0}, {0, 0}, {0, 0, 0, 0}, {}};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> 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<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+    auto range1                           = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range1.begin(), range1.end());
+    std::vector<std::vector<int>> to_vectors = {{0, 0, 0, 0}, {0, 0, 0, 0}, {}, {0, 0}};
+    auto range2                              = to_vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> 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<int>, std::vector<int>>();
+
+  types::for_each(types::bidirectional_iterator_list<int*>{}, []<class Iter> { test_join_view<Iter, Iter>(); });
+  return true;
+}
+
+int main(int, char**) {
+  test_containers<std::deque<int>, std::deque<int>>();
+  test_containers<std::deque<int>, std::vector<int>>();
+  test_containers<std::vector<int>, std::deque<int>>();
+
+  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 000000000000000..eae40cefa663f82
--- /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 <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <deque>
+#include <ranges>
+#include <vector>
+
+#include "test_iterators.h"
+#include "type_algorithms.h"
+
+template <class InContainer, class OutContainer>
+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<std::ranges::in_out_result<InIter, OutIter>> 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 <class Iter, class Sent>
+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<std::vector<int>> vectors = {};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+    std::array<int, 0> 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<int, 0>{}));
+  }
+  { // segmented -> contiguous
+    std::vector<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+    std::array<int, 10> 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<std::vector<int>> vectors = {{0, 0, 0, 0}, {0, 0}, {0, 0, 0, 0}, {}};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> 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<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+    auto range1                           = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range1.begin(), range1.end());
+    std::vector<std::vector<int>> to_vectors = {{0, 0, 0, 0}, {0, 0, 0, 0}, {}, {0, 0}};
+    auto range2                              = to_vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> 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<int>, std::vector<int>>();
+
+  // TODO: this should be cpp20_input_iterator_list, not forward_iterator_list
+  types::for_each(types::forward_iterator_list<int*>{}, []<class Iter> {
+    test_join_view<Iter, Iter>();
+    test_join_view<Iter, sentinel_wrapper<Iter>>();
+    test_join_view<Iter, sized_sentinel<Iter>>();
+  });
+  return true;
+}
+
+int main(int, char**) {
+  test_containers<std::deque<int>, std::deque<int>>();
+  test_containers<std::deque<int>, std::vector<int>>();
+  test_containers<std::vector<int>, std::deque<int>>();
+
+  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 000000000000000..2df6a10b18504ce
--- /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 <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <deque>
+#include <ranges>
+#include <vector>
+
+#include "test_iterators.h"
+#include "type_algorithms.h"
+
+template <class InContainer, class OutContainer>
+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<std::ranges::in_out_result<InIter, OutIter>> 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<std::ranges::in_out_result<InIter, OutIter>> 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 <class Iter, class Sent>
+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<std::vector<int>> vectors = {};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+    std::array<int, 0> arr;
+
+    std::ranges::move(subrange_vector | std::views::join, arr.begin());
+    assert(std::ranges::equal(arr, std::array<int, 0>{}));
+  }
+  { // segmented -> contiguous
+    std::vector<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+    std::array<int, 10> 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<std::vector<int>> vectors = {{0, 0, 0, 0}, {0, 0}, {0, 0, 0, 0}, {}};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> 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<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+    auto range1                           = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range1.begin(), range1.end());
+    std::vector<std::vector<int>> to_vectors = {{0, 0, 0, 0}, {0, 0, 0, 0}, {}, {0, 0}};
+    auto range2                              = to_vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> 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<int>, std::vector<int>>();
+
+  types::for_each(types::forward_iterator_list<int*>{}, []<class Iter> {
+    test_join_view<Iter, Iter>();
+    test_join_view<Iter, sentinel_wrapper<Iter>>();
+    test_join_view<Iter, sized_sentinel<Iter>>();
+  });
+  return true;
+}
+
+int main(int, char**) {
+  test_containers<std::deque<int>, std::deque<int>>();
+  test_containers<std::deque<int>, std::vector<int>>();
+  test_containers<std::vector<int>, std::deque<int>>();
+
+  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 000000000000000..0f0a71439a10dd1
--- /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 <algorithm>
+#include <array>
+#include <cassert>
+#include <concepts>
+#include <deque>
+#include <ranges>
+#include <vector>
+
+#include "test_iterators.h"
+#include "type_algorithms.h"
+
+template <class InContainer, class OutContainer>
+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<std::ranges::in_out_result<InIter, OutIter>> 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<std::ranges::in_out_result<InIter, OutIter>> 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 <class Iter, class Sent>
+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<std::vector<int>> vectors = {};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+    std::array<int, 0> arr;
+
+    std::ranges::move_backward(subrange_vector | std::views::join, arr.end());
+    assert(std::ranges::equal(arr, std::array<int, 0>{}));
+  }
+  { // segmented -> contiguous
+    std::vector<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range.begin(), range.end());
+    std::array<int, 10> 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<std::vector<int>> vectors = {{0, 0, 0, 0}, {0, 0}, {0, 0, 0, 0}, {}};
+    auto range                            = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> 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<std::vector<int>> vectors = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}, {}};
+    auto range1                           = vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> subrange_vector(range1.begin(), range1.end());
+    std::vector<std::vector<int>> to_vectors = {{0, 0, 0, 0}, {0, 0, 0, 0}, {}, {0, 0}};
+    auto range2                              = to_vectors | to_subranges;
+    std::vector<std::ranges::subrange<Iter, Sent>> 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<int>, std::vector<int>>();
+
+  types::for_each(types::bidirectional_iterator_list<int*>{}, []<class Iter> { test_join_view<Iter, Iter>(); });
+  return true;
+}
+
+int main(int, char**) {
+  test_containers<std::deque<int>, std::deque<int>>();
+  test_containers<std::deque<int>, std::vector<int>>();
+  test_containers<std::vector<int>, std::deque<int>>();
+
+  test_constexpr();
+  static_assert(test_constexpr());
+
+  return 0;
+}



More information about the libcxx-commits mailing list