[libcxx-commits] [PATCH] D130695: [libc++][ranges]Refactor `copy{, _backward}` and `move{, _backward}`

Konstantin Varlamov via Phabricator via libcxx-commits libcxx-commits at lists.llvm.org
Tue Aug 30 03:02:12 PDT 2022


var-const created this revision.
Herald added a subscriber: mgorny.
Herald added a project: All.
var-const updated this revision to Diff 448547.
var-const added a comment.
var-const updated this revision to Diff 451022.
var-const retitled this revision from "[libc++][ranges][NFC] Refactor `move` and `move_backward`." to "[libc++][ranges][NFC] Refactor `copy{,_backward}` and `move{,_backward}`".
var-const edited the summary of this revision.
var-const updated this revision to Diff 451023.
var-const updated this revision to Diff 451024.
var-const updated this revision to Diff 451199.
var-const marked 7 inline comments as done.
var-const updated this revision to Diff 451203.
var-const updated this revision to Diff 456586.
var-const marked 7 inline comments as done.
var-const marked an inline comment as done.
var-const retitled this revision from "[libc++][ranges][NFC] Refactor `copy{,_backward}` and `move{,_backward}`" to "[libc++][ranges]Refactor `copy{,_backward}` and `move{,_backward}`".
var-const published this revision for review.
Herald added a reviewer: jdoerfert.
Herald added subscribers: libcxx-commits, sstefan1.
Herald added a project: libc++.
Herald added a reviewer: libc++.

Address feedback


var-const added a comment.

Finalize


var-const added a comment.

Fix the CI.


var-const added a comment.

Fix the CI.


var-const added a comment.

Address feedback


var-const added a comment.

Fix the CI.


var-const added a comment.

Feedback, add tests.



================
Comment at: libcxx/include/__algorithm/copy.h:65
-                      && is_trivially_copy_assignable<__iter_value_type<_OutIter> >::value
-                      && __is_reverse_iterator<_InIter>::value
-                      && __is_reverse_iterator<_OutIter>::value, int> = 0>
----------------
I think we can get rid of `__is_reverse_iterator` in this patch too.

A folllow-up patch should get rid of `__unconstrained_reverse_iterator`, but that may require a bit of additional work in e.g. `inplace_merge`. Still worth doing.


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:1
+//===----------------------------------------------------------------------===//
+//
----------------
Draft from our discussion just now:

```
template <class _AlgPolicy>
struct __copy_loop {
  template <class _InIter, class _Sentinel, class _OutIter>
  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
  pair<_InIter, _OutIter>
  operator()(_InIter __first, _Sentinel __last, _OutIter __result) const {
    while (__first != __last) {
      *__result = *__first;
      ++__first;
      ++__result;
    }

    return pair<_InIter, _OutIter>(std::move(__first), std::move(__result));
  }
};

template <class _AlgPolicy>
struct __copy_trivial {
  // At this point, the iterators have been unwrapped so any contiguous_iterator
  // has been unwrapped to a pointer.
  template <class _In, class _Out, class = __enable_if_t<
    is_trivially_assignable<_Out&, _In&>::value
  > >
  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
  pair<_In*, _Out*>
  operator()(_In* __first, _In* __last, _Out* __result) const {
    const size_t __n = static_cast<size_t>(__last - __first);
    std::memmove(__result, __first, __n * sizeof(_Out));

    return pair<_In*, _Out*>(__last, __result + __n);
  }
};

template <class _F1, class _F2>
struct __overload : _F1, _F2 {
    using _F1::operator();
    using _F2::operator();
};

template <class _Algorithm, class _InIter, class _Sent, class _OutIter,
        __enable_if_t<__can_rewrap<_InIter, _Sent, _OutIter>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
pair<_InIter, _OutIter>
__unwrap_and_dispatch(_InIter __first, _Sent __last, _OutIter __out_first) {
  auto __range = std::__unwrap_range(__first, std::move(__last));
  auto __result = _Algorithm()(std::move(__range.first),
                               std::move(__range.second),
                               std::__unwrap_iter(__out_first));
  return pair<_InIter, _OutIter>(
      std::__rewrap_range<_Sent>(std::move(__first), std::move(__result.first)),
      std::__rewrap_iter(std::move(__out_first), std::move(__result.second)));
}

template <class _Algorithm, class _InIter, class _Sent, class _OutIter,
          __enable_if_t<!__can_rewrap<_InIter, _Sent, _OutIter>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
pair<_InIter, _OutIter>
__unwrap_and_dispatch(_InIter __first, _Sent __last, _OutIter __out_first) {
  return _Algorithm()(std::move(__first), std::move(__last), std::move(__out_first));
}

template <class _NaiveAlgorithm, class _OptimizedAlgorithm, class _InIter, class _Sent, class _OutIter>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
pair<_InIter, _OutIter>
__dispatch_copy_or_move(_InIter __first, _Sent __last, _OutIter __out_first) {
  if (__libcpp_is_constant_evaluated()) {
    return std::__unwrap_and_dispatch<_NaiveAlgorithm>(std::move(__first), std::move(__last), std::move(__out_first));
  }

  using _Algorithm = std::__overload<_NaiveAlgorithm, _OptimizedAlgorithm>;
  return std::__unwrap_and_dispatch<_Algorithm>(std::move(__first), std::move(__last), std::move(__out_first));
}


template <class _AlgPolicy, class _InIter, class _Sent, class _OutIter>
pair<_InIter, _OutIter>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
__copy(_InIter __first, _Sent __last, _OutIter __result) {
  return std::__dispatch_copy_or_move<__copy_loop<_AlgPolicy>, __copy_trivial<_AlgPolicy> >(
      std::move(__first), std::move(__last), std::move(__result));
}

template <class _InputIterator, class _OutputIterator>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
_OutputIterator
copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result) {
  return std::__copy<_ClassicAlgPolicy>(__first, __last, __result).second;
}
```



================
Comment at: libcxx/include/__algorithm/copy_move_common.h:97
+template <class _Iter, class _OutIter, class = void>
+struct __can_lower_move_to_memmove {
+  static const bool value = false;
----------------
Per your comment just now: maybe we can take `T*` in the trivial version of `__run` since contiguous iterators are already unwrapped by `unwrap_iter` by the time we get there. That would simplify the sfinae and perhaps more.


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:42
+    const size_t __n = static_cast<size_t>(__last - __first);
+    ::__builtin_memmove(__result, __first, __n * sizeof(_Out));
+
----------------
I would use `std::memmove` instead. We're not using this optimization during constexpr anyway.


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:106
+template <class _InIter, class _Sent, class _OutIter>
+struct __can_unwrap<_InIter, _Sent, _OutIter, __enable_if_t<
+    is_copy_constructible<_InIter>::value &&
----------------
I think this is actually insufficient. There's another bug that we rewrap iterators and sentinels, leading to running the algorithm on a non-sensical range (delimited by `[unwrapped-iterator, wrapped-sentinel)`).

See the `TODO: some algorithms calls std::__copy` in `libcxx/test/std/algorithms/alg.sorting/sortable_helpers.h`. I'm not 100% sure what's the right fix, but I would maybe explore querying whether `__unwrap_range` can be called on `[iter, sent)` -- that would require changes to the semantics of `__unwrap_range`, though. Or maybe you could call `__unwrap_range` unconditionally and have it be a no-op if it can't unwrap.

That also makes me think that `__can_unwrap` should actually be called `__can_rewrap`, and then everything makes a bit more sense.

TLDR: Rename `__can_unwrap` to `__can_rewrap`, and then use `__unwrap_range` instead of `__unwrap_iter` for `__first` and `__last`. That way, we'll either unwrap both or none of them, and we'll know that we're able to rewrap them after.


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:106
+template <class _InIter, class _Sent, class _OutIter>
+struct __can_unwrap<_InIter, _Sent, _OutIter, __enable_if_t<
+    is_copy_constructible<_InIter>::value &&
----------------
ldionne wrote:
> I think this is actually insufficient. There's another bug that we rewrap iterators and sentinels, leading to running the algorithm on a non-sensical range (delimited by `[unwrapped-iterator, wrapped-sentinel)`).
> 
> See the `TODO: some algorithms calls std::__copy` in `libcxx/test/std/algorithms/alg.sorting/sortable_helpers.h`. I'm not 100% sure what's the right fix, but I would maybe explore querying whether `__unwrap_range` can be called on `[iter, sent)` -- that would require changes to the semantics of `__unwrap_range`, though. Or maybe you could call `__unwrap_range` unconditionally and have it be a no-op if it can't unwrap.
> 
> That also makes me think that `__can_unwrap` should actually be called `__can_rewrap`, and then everything makes a bit more sense.
> 
> TLDR: Rename `__can_unwrap` to `__can_rewrap`, and then use `__unwrap_range` instead of `__unwrap_iter` for `__first` and `__last`. That way, we'll either unwrap both or none of them, and we'll know that we're able to rewrap them after.
Renamed the trait and replaced `__unwrap_iter` with `__unwrap_range`.

> Or maybe you could call `__unwrap_range` unconditionally and have it be a no-op if it can't unwrap.

I might be misreading it, but it seems to already be the case. `__unwrap_range` only calls `__unwrap_iter` either if both the iterator and the sentinel are the same type or if the end iterator can be created in constant time; otherwise, it simply returns the given arguments.



================
Comment at: libcxx/include/__algorithm/copy_move_common.h:127-131
+template <class _AlgImpl, class _InIter, class _Sent, class _OutIter>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
+__enable_if_t<
+    !__can_unwrap<_InIter, _Sent, _OutIter>::value,
+    pair<_InIter, _OutIter>>
----------------
Same above. It's easier to read `enable_if` when they are in the template argument list, and it's what we try to do in new code elsewhere in the library.


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:127-131
+template <class _AlgImpl, class _InIter, class _Sent, class _OutIter>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14
+__enable_if_t<
+    !__can_unwrap<_InIter, _Sent, _OutIter>::value,
+    pair<_InIter, _OutIter>>
----------------
ldionne wrote:
> Same above. It's easier to read `enable_if` when they are in the template argument list, and it's what we try to do in new code elsewhere in the library.
Thanks for explaining!


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:28-29
+
+template <class _In, class _Out>
+struct __can_memmove<_In, _Out, __enable_if_t<
+    is_same<typename remove_const<_In>::type, _Out>::value &&
----------------
`_In` and `_Out` should be iterators, not `value_type`s. That way, you can check for `__is_cpp17_contiguous_iterator`. What I had locally was:


```
template <class _Iter, class _OutIter, class = void>
struct __can_optimize_move_to_memmove {
  static constexpr bool value = false;
};

template <class _Iter, class _OutIter>
struct __can_optimize_move_to_memmove<_Iter, _OutIter, __enable_if_t<
  __is_cpp17_contiguous_iterator<_Iter>::value && __is_cpp17_contiguous_iterator<_OutIter>::value
> > {
  using _InReference = typename iterator_traits<_Iter>::reference;
  using _OutReference = typename iterator_traits<_OutIter>::reference;
  static constexpr bool value = is_trivially_assignable<_OutReference, __move_result_whatever_t<_InReference> >::value;
};
```

Where `__move_result_whatever_t<T>` is basically `decltype(std::move(T))`. At the end of the day, we're trying to do `bool value = __is_trivial(*out = std::move(*in))`. It might be relevant to capture that in a comment.


Furthermore, I think we can do *almost* the same thing for `std::copy`, but by simply using `is_trivially_assignable<_OutReference, _InReference>::value`, i.e. by dropping the `__move_result_whatever_t` part. I think that's a nice generalization that allows simplifying stuff.



================
Comment at: libcxx/include/__algorithm/copy_move_common.h:28-29
+
+template <class _In, class _Out>
+struct __can_memmove<_In, _Out, __enable_if_t<
+    is_same<typename remove_const<_In>::type, _Out>::value &&
----------------
ldionne wrote:
> `_In` and `_Out` should be iterators, not `value_type`s. That way, you can check for `__is_cpp17_contiguous_iterator`. What I had locally was:
> 
> 
> ```
> template <class _Iter, class _OutIter, class = void>
> struct __can_optimize_move_to_memmove {
>   static constexpr bool value = false;
> };
> 
> template <class _Iter, class _OutIter>
> struct __can_optimize_move_to_memmove<_Iter, _OutIter, __enable_if_t<
>   __is_cpp17_contiguous_iterator<_Iter>::value && __is_cpp17_contiguous_iterator<_OutIter>::value
> > > {
>   using _InReference = typename iterator_traits<_Iter>::reference;
>   using _OutReference = typename iterator_traits<_OutIter>::reference;
>   static constexpr bool value = is_trivially_assignable<_OutReference, __move_result_whatever_t<_InReference> >::value;
> };
> ```
> 
> Where `__move_result_whatever_t<T>` is basically `decltype(std::move(T))`. At the end of the day, we're trying to do `bool value = __is_trivial(*out = std::move(*in))`. It might be relevant to capture that in a comment.
> 
> 
> Furthermore, I think we can do *almost* the same thing for `std::copy`, but by simply using `is_trivially_assignable<_OutReference, _InReference>::value`, i.e. by dropping the `__move_result_whatever_t` part. I think that's a nice generalization that allows simplifying stuff.
> 
We do need a different trait to implement `std::copy` and `std::move`. We can't check `is_trivially_move_assignable` or `is_trivially_copy_assignable` for both algorithms.


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:32
+    is_trivially_move_assignable<_Out>::value &&
+    !(__libcpp_is_constant_evaluated()
+    // TODO: Remove this once GCC supports __builtin_memmove during constant evaluation
----------------
`__is_constant_evaluated()` will always be `true` now because it is used in a context where a constant expression is required.


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:33-36
+    // TODO: Remove this once GCC supports __builtin_memmove during constant evaluation
+#ifndef _LIBCPP_COMPILER_GCC
+        && !is_trivially_copyable<_In>::value
+#endif
----------------
Instead, I suggest we never try to lower to `memmove` during constant evaluation. That will get rid of this issue as a side effect.


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:40-59
+template <class _AlgPolicy, bool _Backward = false>
+struct __trivial_copy_func {
+
+  template <class _In, class _Out, class =
+      __enable_if_t<__can_memmove<_In, _Out>::value>>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX14 static
+  pair<_In*, _Out*>
----------------
IMO we're trying to jam two functions into one here, and it would be better to instead acknowledge that we have one that operates backwards and the other one, not.

I suggest `__trivial_copy_func` and `__trivial_backward_copy_func` instead, or something along those lines.


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:83
+pair<reverse_iterator<_InIter>, reverse_iterator<_OutIter> >
+__move_impl(reverse_iterator<_InIter> __first,
+            reverse_iterator<_InIter> __last,
----------------
I'm not sure why we need this logic. `__unwrap_iter` seems to work with nested `reverse_iterator`s (though I didn't dig in too deeply and might easily be missing something).

In either case, this seems untested.


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:83
+pair<reverse_iterator<_InIter>, reverse_iterator<_OutIter> >
+__move_impl(reverse_iterator<_InIter> __first,
+            reverse_iterator<_InIter> __last,
----------------
var-const wrote:
> I'm not sure why we need this logic. `__unwrap_iter` seems to work with nested `reverse_iterator`s (though I didn't dig in too deeply and might easily be missing something).
> 
> In either case, this seems untested.
This overload allows optimizing `move(reverse_iterator<CONTIGUOUS>)` to `memmove`. Without this, `unwrap_iter` would not unwrap `reverse_iterator<CONTIGUOUS>`, because that is itself not a contiguous iterator. I am not sure it is worth trying to perform this optimization, however.


================
Comment at: libcxx/include/__algorithm/copy_move_common.h:83
+pair<reverse_iterator<_InIter>, reverse_iterator<_OutIter> >
+__move_impl(reverse_iterator<_InIter> __first,
+            reverse_iterator<_InIter> __last,
----------------
ldionne wrote:
> var-const wrote:
> > I'm not sure why we need this logic. `__unwrap_iter` seems to work with nested `reverse_iterator`s (though I didn't dig in too deeply and might easily be missing something).
> > 
> > In either case, this seems untested.
> This overload allows optimizing `move(reverse_iterator<CONTIGUOUS>)` to `memmove`. Without this, `unwrap_iter` would not unwrap `reverse_iterator<CONTIGUOUS>`, because that is itself not a contiguous iterator. I am not sure it is worth trying to perform this optimization, however.
Now that our implementation no longer relies on reverse iterators, I don't think the extra complexity is justified.


================
Comment at: libcxx/include/__algorithm/move.h:80
-pair<reverse_iterator<_InIter>, reverse_iterator<_OutIter> >
-__move_impl(reverse_iterator<_InIter> __first,
-            reverse_iterator<_InIter> __last,
----------------
I'm not sure if this optimization is still pulling its weight in complexity now that we no longer use reverse iterators in the implementation. I also don't really like that it only applies to the case where both the input and the output iterators are reverse, even though these are orthogonal.



================
Comment at: libcxx/include/__algorithm/move_backward.h:27
 
-template <class _InputIterator, class _OutputIterator>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
-_OutputIterator
-__move_backward(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
-{
-    return _VSTD::__move_backward_constexpr(__first, __last, __result);
-}
+  using __trivial_copy_func<_AlgPolicy, /*_Backward=*/true>::__run;
 
----------------
Using a base class allows making the `memmove` overload of `__run` visible without writing a wrapper (which would be pretty verbose).


================
Comment at: libcxx/include/__algorithm/ranges_move_backward.h:19
 #include <__iterator/next.h>
 #include <__iterator/reverse_iterator.h>
 #include <__ranges/access.h>
----------------
Not needed anymore.


================
Comment at: libcxx/include/__algorithm/unwrap_iter.h:36
   static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iter __unwrap(_Iter __i) _NOEXCEPT { return __i; }
+  static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iter __rewrap(_Iter, _Iter __iter) { return __iter; }
 };
----------------
Not sure whether the reordering is worth doing. Neutral on this.


================
Comment at: libcxx/include/__algorithm/unwrap_iter.h:36
   static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iter __unwrap(_Iter __i) _NOEXCEPT { return __i; }
+  static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Iter __rewrap(_Iter, _Iter __iter) { return __iter; }
 };
----------------
ldionne wrote:
> Not sure whether the reordering is worth doing. Neutral on this.
In that case, I'd keep it -- I think it makes more sense to place _re_wrapping after unwrapping.


================
Comment at: libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp:106-109
+  // FIXME: Only the first iterator gets unwrapped but not the sentinel, resulting in a compilation error.
+  // The same problem exists in `ranges::move_backward` but isn't tested.
+  //test_iterators<InIter, OutIter, sentinel_wrapper<InIter>>();
+  //test_iterators<InIter, OutIter, sized_sentinel<InIter>>();
----------------
I don't think that's still the case?


================
Comment at: libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp:106-109
+  // FIXME: Only the first iterator gets unwrapped but not the sentinel, resulting in a compilation error.
+  // The same problem exists in `ranges::move_backward` but isn't tested.
+  //test_iterators<InIter, OutIter, sentinel_wrapper<InIter>>();
+  //test_iterators<InIter, OutIter, sized_sentinel<InIter>>();
----------------
ldionne wrote:
> I don't think that's still the case?
Yes, this is fixed now!


================
Comment at: libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.pass.cpp:1
 //===----------------------------------------------------------------------===//
 //
----------------
Some notes I took when I wrote my WIP patch to solve this very problem (they may not apply to all algorithms, but should apply to at least some of `std::copy`, `std::copy_backward`, `std::move` and `std::move_backward`):

- I don't think we're testing with a contiguous iterator that is not a pointer.
- Test calling the algorithms with a sentinel that is not the same as the iterator, for a `contiguous_iterator`.
- Test `std::ranges::copy(contiguous_iterator, sentinel_wrapper<contiguous_iterator>, int*)`.



================
Comment at: libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.pass.cpp:1
 //===----------------------------------------------------------------------===//
 //
----------------
ldionne wrote:
> Some notes I took when I wrote my WIP patch to solve this very problem (they may not apply to all algorithms, but should apply to at least some of `std::copy`, `std::copy_backward`, `std::move` and `std::move_backward`):
> 
> - I don't think we're testing with a contiguous iterator that is not a pointer.
> - Test calling the algorithms with a sentinel that is not the same as the iterator, for a `contiguous_iterator`.
> - Test `std::ranges::copy(contiguous_iterator, sentinel_wrapper<contiguous_iterator>, int*)`.
> 
Should all be covered now.


================
Comment at: libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.pass.cpp:189
         std::ranges::move_backward(std::views::all(in), out.data() + out.size());
-    assert(ret.in == in.data());
+    assert(ret.in == in.data() + in.size());
     assert(ret.out == out.data());
----------------
I think this was a bug in `move_backward`.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D130695

Files:
  libcxx/include/CMakeLists.txt
  libcxx/include/__algorithm/copy.h
  libcxx/include/__algorithm/copy_backward.h
  libcxx/include/__algorithm/copy_move_common.h
  libcxx/include/__algorithm/move.h
  libcxx/include/__algorithm/move_backward.h
  libcxx/include/__algorithm/ranges_copy.h
  libcxx/include/__algorithm/ranges_copy_backward.h
  libcxx/include/__algorithm/ranges_copy_n.h
  libcxx/include/__algorithm/ranges_move.h
  libcxx/include/__algorithm/ranges_move_backward.h
  libcxx/include/__algorithm/ranges_set_difference.h
  libcxx/include/__algorithm/ranges_set_symmetric_difference.h
  libcxx/include/__algorithm/ranges_set_union.h
  libcxx/include/__algorithm/rotate.h
  libcxx/include/__algorithm/set_difference.h
  libcxx/include/__algorithm/set_symmetric_difference.h
  libcxx/include/__algorithm/set_union.h
  libcxx/include/__algorithm/unwrap_iter.h
  libcxx/include/__algorithm/unwrap_range.h
  libcxx/include/__iterator/reverse_iterator.h
  libcxx/include/module.modulemap.in
  libcxx/test/libcxx/algorithms/alg.modifying.operations/copy.pass.cpp
  libcxx/test/libcxx/algorithms/alg.modifying.operations/copy_move_nontrivial.pass.cpp
  libcxx/test/libcxx/algorithms/alg.modifying.operations/copy_move_trivial.pass.cpp
  libcxx/test/libcxx/private_headers.verify.cpp
  libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp
  libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
  libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.pass.cpp
  libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.pass.cpp

-------------- next part --------------
A non-text attachment was scrubbed...
Name: D130695.456586.patch
Type: text/x-patch
Size: 71516 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/libcxx-commits/attachments/20220830/9087bf78/attachment-0001.bin>


More information about the libcxx-commits mailing list