[libcxx-commits] [libcxx] 6f36ead - [libc++] Fix std::move algorithm with trivial move-only types

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Mon Jul 10 08:50:27 PDT 2023


Author: Louis Dionne
Date: 2023-07-10T11:50:09-04:00
New Revision: 6f36ead577dfa20397549b7034330180da0945f0

URL: https://github.com/llvm/llvm-project/commit/6f36ead577dfa20397549b7034330180da0945f0
DIFF: https://github.com/llvm/llvm-project/commit/6f36ead577dfa20397549b7034330180da0945f0.diff

LOG: [libc++] Fix std::move algorithm with trivial move-only types

As reported in https://reviews.llvm.org/D151953#4472195, the std::move
algorithm (and various other functions that relied on it) stopped working
after starting to use `__constexpr_memmove` in its implementation. This
patch fixes the underlying issue in `__constexpr_memmove` and adds tests
for various related algorithms and functions that were not exercising
trivial move-only types.

Differential Revision: https://reviews.llvm.org/D154613

Added: 
    libcxx/test/libcxx/strings/c.strings/constexpr_memmove.pass.cpp

Modified: 
    libcxx/include/__string/constexpr_c_functions.h
    libcxx/test/libcxx/transitive_includes/cxx03.csv
    libcxx/test/libcxx/transitive_includes/cxx11.csv
    libcxx/test/libcxx/transitive_includes/cxx14.csv
    libcxx/test/libcxx/transitive_includes/cxx17.csv
    libcxx/test/libcxx/transitive_includes/cxx20.csv
    libcxx/test/libcxx/transitive_includes/cxx23.csv
    libcxx/test/libcxx/transitive_includes/cxx26.csv
    libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move.pass.cpp
    libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move_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
    libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter.pass.cpp
    libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter_iter.pass.cpp
    libcxx/test/support/MoveOnly.h

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__string/constexpr_c_functions.h b/libcxx/include/__string/constexpr_c_functions.h
index 86b3394a07d4d2..198f0f5e680914 100644
--- a/libcxx/include/__string/constexpr_c_functions.h
+++ b/libcxx/include/__string/constexpr_c_functions.h
@@ -10,9 +10,13 @@
 #define _LIBCPP___STRING_CONSTEXPR_C_FUNCTIONS_H
 
 #include <__config>
+#include <__memory/addressof.h>
+#include <__memory/construct_at.h>
 #include <__type_traits/datasizeof.h>
 #include <__type_traits/is_always_bitcastable.h>
+#include <__type_traits/is_assignable.h>
 #include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_constructible.h>
 #include <__type_traits/is_equality_comparable.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/is_trivially_copyable.h>
@@ -133,6 +137,59 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __constexpr_memchr(_Tp*
   }
 }
 
+// This function performs an assignment to an existing, already alive TriviallyCopyable object
+// from another TriviallyCopyable object.
+//
+// It basically works around the fact that TriviallyCopyable objects are not required to be
+// syntactically copy/move constructible or copy/move assignable. Technically, only one of the
+// four operations is required to be syntactically valid -- but at least one definitely has to
+// be valid.
+//
+// This is necessary in order to implement __constexpr_memmove below in a way that mirrors as
+// closely as possible what the compiler's __builtin_memmove is able to do.
+template <class _Tp, class _Up, __enable_if_t<is_assignable<_Tp&, _Up const&>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp& __assign_trivially_copyable(_Tp& __dest, _Up const& __src) {
+  __dest = __src;
+  return __dest;
+}
+
+// clang-format off
+template <class _Tp, class _Up, __enable_if_t<!is_assignable<_Tp&, _Up const&>::value &&
+                                               is_assignable<_Tp&, _Up&&>::value, int> = 0>
+// clang-format on
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp& __assign_trivially_copyable(_Tp& __dest, _Up& __src) {
+  __dest =
+      static_cast<_Up&&>(__src); // this is safe, we're not actually moving anything since the assignment is trivial
+  return __dest;
+}
+
+// clang-format off
+template <class _Tp, class _Up, __enable_if_t<!is_assignable<_Tp&, _Up const&>::value &&
+                                              !is_assignable<_Tp&, _Up&&>::value &&
+                                               is_constructible<_Tp, _Up const&>::value, int> = 0>
+// clang-format on
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __assign_trivially_copyable(_Tp& __dest, _Up const& __src) {
+  // _Tp is trivially destructible, so we don't need to call its destructor to end the lifetime of the object
+  // that was there previously
+  std::__construct_at(std::addressof(__dest), __src);
+  return __dest;
+}
+
+// clang-format off
+template <class _Tp, class _Up, __enable_if_t<!is_assignable<_Tp&, _Up const&>::value &&
+                                              !is_assignable<_Tp&, _Up&&>::value &&
+                                              !is_constructible<_Tp, _Up const&>::value &&
+                                               is_constructible<_Tp, _Up&&>::value, int> = 0>
+// clang-format on
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __assign_trivially_copyable(_Tp& __dest, _Up& __src) {
+  // _Tp is trivially destructible, so we don't need to call its destructor to end the lifetime of the object
+  // that was there previously
+  std::__construct_at(
+      std::addressof(__dest),
+      static_cast<_Up&&>(__src)); // this is safe, we're not actually moving anything since the constructor is trivial
+  return __dest;
+}
+
 template <class _Tp, class _Up, __enable_if_t<__is_always_bitcastable<_Up, _Tp>::value, int> = 0>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp*
 __constexpr_memmove(_Tp* __dest, _Up* __src, __element_count __n) {
@@ -146,10 +203,10 @@ __constexpr_memmove(_Tp* __dest, _Up* __src, __element_count __n) {
 #endif
     if (std::__is_pointer_in_range(__src, __src + __count, __dest)) {
       for (; __count > 0; --__count)
-        __dest[__count - 1] = __src[__count - 1];
+        std::__assign_trivially_copyable(__dest[__count - 1], __src[__count - 1]);
     } else {
       for (size_t __i = 0; __i != __count; ++__i)
-        __dest[__i] = __src[__i];
+        std::__assign_trivially_copyable(__dest[__i], __src[__i]);
     }
   } else if (__count > 0) {
     ::__builtin_memmove(__dest, __src, (__count - 1) * sizeof(_Tp) + __libcpp_datasizeof<_Tp>::value);

diff  --git a/libcxx/test/libcxx/strings/c.strings/constexpr_memmove.pass.cpp b/libcxx/test/libcxx/strings/c.strings/constexpr_memmove.pass.cpp
new file mode 100644
index 00000000000000..6bcaf8c57b2b26
--- /dev/null
+++ b/libcxx/test/libcxx/strings/c.strings/constexpr_memmove.pass.cpp
@@ -0,0 +1,157 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// ADDITIONAL_COMPILE_FLAGS: -Wno-private-header
+
+// _Tp* __constexpr_memmove(_Tp* __dest, _Up* __src, __element_count __n);
+//
+// General tests for __constexpr_memmove.
+//
+// In particular, we try to ensure that __constexpr_memmove behaves like
+// __builtin_memmove as closely as possible. This means that it produces the
+// same effect, but also that it has the same type requirements.
+//
+// __builtin_memmove only requires that the types are TriviallyCopyable
+// (which is interestingly 
diff erent from both is_trivially_XXX_constructible
+// and is_trivially_XXX_assignable), so we use some funky types to test these
+// corner cases.
+
+#include <__string/constexpr_c_functions.h>
+#include <cassert>
+#include <cstdint>
+#include <type_traits>
+
+#include "test_macros.h"
+
+// The following types are all TriviallyCopyable, but they are not all
+// trivially_{copy,move}_{constructible,assignable}. TriviallyCopyable
+// guarantees that the type is *at least* one of the four, but no more
+// than that.
+struct CopyConstructible {
+  CopyConstructible() = default;
+  int value = 0;
+
+  CopyConstructible(const CopyConstructible&)            = default;
+  CopyConstructible(CopyConstructible&&)                 = delete;
+  CopyConstructible& operator=(const CopyConstructible&) = delete;
+  CopyConstructible& operator=(CopyConstructible&&)      = delete;
+};
+
+struct MoveConstructible {
+  MoveConstructible() = default;
+  int value = 0;
+
+  MoveConstructible(const MoveConstructible&)            = delete;
+  MoveConstructible(MoveConstructible&&)                 = default;
+  MoveConstructible& operator=(const MoveConstructible&) = delete;
+  MoveConstructible& operator=(MoveConstructible&&)      = delete;
+};
+
+struct CopyAssignable {
+  CopyAssignable() = default;
+  int value = 0;
+
+  CopyAssignable(const CopyAssignable&)            = delete;
+  CopyAssignable(CopyAssignable&&)                 = delete;
+  CopyAssignable& operator=(const CopyAssignable&) = default;
+  CopyAssignable& operator=(CopyAssignable&&)      = delete;
+};
+
+struct MoveAssignable {
+  MoveAssignable() = default;
+  int value = 0;
+
+  MoveAssignable(const MoveAssignable&)            = delete;
+  MoveAssignable(MoveAssignable&&)                 = delete;
+  MoveAssignable& operator=(const MoveAssignable&) = delete;
+  MoveAssignable& operator=(MoveAssignable&&)      = default;
+};
+
+template <class Source, class Dest>
+TEST_CONSTEXPR_CXX14 void test_user_defined_types() {
+  static_assert(std::is_trivially_copyable<Source>::value, "test the test");
+  static_assert(std::is_trivially_copyable<Dest>::value, "test the test");
+
+  // Note that we can't just initialize with an initializer list since some of the types we use here
+  // are not copy-constructible, which is required in pre-C++20 Standards for that syntax to work.
+  Source src[3];
+  src[0].value = 1;
+  src[1].value = 2;
+  src[2].value = 3;
+  Dest dst[3];
+  dst[0].value = 111;
+  dst[1].value = 111;
+  dst[2].value = 111;
+
+  Dest* result = std::__constexpr_memmove(dst, src, std::__element_count(3));
+  assert(result == dst);
+  assert(dst[0].value == 1);
+  assert(dst[1].value == 2);
+  assert(dst[2].value == 3);
+}
+
+template <class Source, class Dest>
+TEST_CONSTEXPR_CXX14 void test_builtin_types() {
+  Source src[3] = {1, 2, 3};
+  Dest dst[3] = {111, 111, 111};
+
+  Dest* result = std::__constexpr_memmove(dst, src, std::__element_count(3));
+  assert(result == dst);
+  assert(dst[0] == 1);
+  assert(dst[1] == 2);
+  assert(dst[2] == 3);
+}
+
+template <class SourcePtr, class DestPtr, class ObjectType>
+TEST_CONSTEXPR_CXX14 void test_pointer_types() {
+  ObjectType objs[3] = {1, 2, 3};
+
+  SourcePtr src[3] = {objs + 0, objs + 1, objs + 2};
+  DestPtr dst[3] = {nullptr, nullptr, nullptr};
+
+  DestPtr* result = std::__constexpr_memmove(dst, src, std::__element_count(3));
+  assert(result == dst);
+  assert(dst[0] == objs + 0);
+  assert(dst[1] == objs + 1);
+  assert(dst[2] == objs + 2);
+}
+
+TEST_CONSTEXPR_CXX14 bool test() {
+  test_user_defined_types<CopyConstructible, CopyConstructible>();
+  test_user_defined_types<MoveConstructible, MoveConstructible>();
+  test_user_defined_types<CopyAssignable, CopyAssignable>();
+  test_user_defined_types<MoveAssignable, MoveAssignable>();
+
+  test_builtin_types<char, char>();
+  test_builtin_types<short, short>();
+  test_builtin_types<int, int>();
+  test_builtin_types<long, long>();
+  test_builtin_types<long long, long long>();
+
+  // Cross-type
+  test_builtin_types<std::int16_t, std::uint16_t>();
+  test_builtin_types<std::int16_t, char16_t>();
+  test_builtin_types<std::int32_t, std::uint32_t>();
+  test_builtin_types<std::int32_t, char32_t>();
+
+  test_pointer_types<char*, char*, char>();
+  test_pointer_types<int*, int*, int>();
+  test_pointer_types<long*, long*, long>();
+  test_pointer_types<void*, void*, int>();
+  test_pointer_types<int* const, int*, int>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 14
+  static_assert(test(), "");
+#endif
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 75df8fdf86462f..63705e7cae5c89 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -42,11 +42,11 @@ array algorithm
 array compare
 array concepts
 array cstddef
-array cstdint
 array cstdlib
 array initializer_list
 array iterator
 array limits
+array new
 array stdexcept
 array type_traits
 array utility
@@ -110,6 +110,7 @@ charconv cstring
 charconv initializer_list
 charconv iosfwd
 charconv limits
+charconv new
 charconv type_traits
 chrono bit
 chrono compare
@@ -307,6 +308,7 @@ format cstdlib
 format initializer_list
 format limits
 format locale
+format new
 format optional
 format queue
 format stack
@@ -508,7 +510,6 @@ locale version
 map compare
 map concepts
 map cstddef
-map cstdint
 map cstdlib
 map functional
 map initializer_list
@@ -647,6 +648,7 @@ queue deque
 queue functional
 queue initializer_list
 queue limits
+queue new
 queue type_traits
 queue vector
 queue version
@@ -735,7 +737,6 @@ semaphore version
 set compare
 set concepts
 set cstddef
-set cstdint
 set cstdlib
 set functional
 set initializer_list
@@ -782,6 +783,7 @@ stack deque
 stack functional
 stack initializer_list
 stack limits
+stack new
 stack type_traits
 stack version
 stdexcept cstdlib
@@ -832,6 +834,7 @@ string_view initializer_list
 string_view iosfwd
 string_view iterator
 string_view limits
+string_view new
 string_view stdexcept
 string_view type_traits
 string_view version

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 4bd5b1bde1fef1..389b8ae03a9ec8 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -42,11 +42,11 @@ array algorithm
 array compare
 array concepts
 array cstddef
-array cstdint
 array cstdlib
 array initializer_list
 array iterator
 array limits
+array new
 array stdexcept
 array type_traits
 array utility
@@ -110,6 +110,7 @@ charconv cstring
 charconv initializer_list
 charconv iosfwd
 charconv limits
+charconv new
 charconv type_traits
 chrono bit
 chrono compare
@@ -307,6 +308,7 @@ format cstdlib
 format initializer_list
 format limits
 format locale
+format new
 format optional
 format queue
 format stack
@@ -508,7 +510,6 @@ locale version
 map compare
 map concepts
 map cstddef
-map cstdint
 map cstdlib
 map functional
 map initializer_list
@@ -648,6 +649,7 @@ queue deque
 queue functional
 queue initializer_list
 queue limits
+queue new
 queue type_traits
 queue vector
 queue version
@@ -736,7 +738,6 @@ semaphore version
 set compare
 set concepts
 set cstddef
-set cstdint
 set cstdlib
 set functional
 set initializer_list
@@ -783,6 +784,7 @@ stack deque
 stack functional
 stack initializer_list
 stack limits
+stack new
 stack type_traits
 stack version
 stdexcept cstdlib
@@ -833,6 +835,7 @@ string_view initializer_list
 string_view iosfwd
 string_view iterator
 string_view limits
+string_view new
 string_view stdexcept
 string_view type_traits
 string_view version

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 010b208f68efa3..4c2a2b2c29929b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -42,11 +42,11 @@ array algorithm
 array compare
 array concepts
 array cstddef
-array cstdint
 array cstdlib
 array initializer_list
 array iterator
 array limits
+array new
 array stdexcept
 array type_traits
 array utility
@@ -110,6 +110,7 @@ charconv cstring
 charconv initializer_list
 charconv iosfwd
 charconv limits
+charconv new
 charconv type_traits
 chrono bit
 chrono compare
@@ -309,6 +310,7 @@ format cstdlib
 format initializer_list
 format limits
 format locale
+format new
 format optional
 format queue
 format stack
@@ -510,7 +512,6 @@ locale version
 map compare
 map concepts
 map cstddef
-map cstdint
 map cstdlib
 map functional
 map initializer_list
@@ -650,6 +651,7 @@ queue deque
 queue functional
 queue initializer_list
 queue limits
+queue new
 queue type_traits
 queue vector
 queue version
@@ -738,7 +740,6 @@ semaphore version
 set compare
 set concepts
 set cstddef
-set cstdint
 set cstdlib
 set functional
 set initializer_list
@@ -785,6 +786,7 @@ stack deque
 stack functional
 stack initializer_list
 stack limits
+stack new
 stack type_traits
 stack version
 stdexcept cstdlib
@@ -835,6 +837,7 @@ string_view initializer_list
 string_view iosfwd
 string_view iterator
 string_view limits
+string_view new
 string_view stdexcept
 string_view type_traits
 string_view version

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 010b208f68efa3..4c2a2b2c29929b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -42,11 +42,11 @@ array algorithm
 array compare
 array concepts
 array cstddef
-array cstdint
 array cstdlib
 array initializer_list
 array iterator
 array limits
+array new
 array stdexcept
 array type_traits
 array utility
@@ -110,6 +110,7 @@ charconv cstring
 charconv initializer_list
 charconv iosfwd
 charconv limits
+charconv new
 charconv type_traits
 chrono bit
 chrono compare
@@ -309,6 +310,7 @@ format cstdlib
 format initializer_list
 format limits
 format locale
+format new
 format optional
 format queue
 format stack
@@ -510,7 +512,6 @@ locale version
 map compare
 map concepts
 map cstddef
-map cstdint
 map cstdlib
 map functional
 map initializer_list
@@ -650,6 +651,7 @@ queue deque
 queue functional
 queue initializer_list
 queue limits
+queue new
 queue type_traits
 queue vector
 queue version
@@ -738,7 +740,6 @@ semaphore version
 set compare
 set concepts
 set cstddef
-set cstdint
 set cstdlib
 set functional
 set initializer_list
@@ -785,6 +786,7 @@ stack deque
 stack functional
 stack initializer_list
 stack limits
+stack new
 stack type_traits
 stack version
 stdexcept cstdlib
@@ -835,6 +837,7 @@ string_view initializer_list
 string_view iosfwd
 string_view iterator
 string_view limits
+string_view new
 string_view stdexcept
 string_view type_traits
 string_view version

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 9ef5cb4548d421..dee590204f0919 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -41,11 +41,11 @@ array algorithm
 array compare
 array concepts
 array cstddef
-array cstdint
 array cstdlib
 array initializer_list
 array iterator
 array limits
+array new
 array stdexcept
 array type_traits
 array utility
@@ -109,6 +109,7 @@ charconv cstring
 charconv initializer_list
 charconv iosfwd
 charconv limits
+charconv new
 charconv type_traits
 chrono array
 chrono bit
@@ -316,6 +317,7 @@ format cstdlib
 format initializer_list
 format limits
 format locale
+format new
 format optional
 format queue
 format stack
@@ -516,7 +518,6 @@ locale version
 map compare
 map concepts
 map cstddef
-map cstdint
 map cstdlib
 map functional
 map initializer_list
@@ -656,6 +657,7 @@ queue deque
 queue functional
 queue initializer_list
 queue limits
+queue new
 queue type_traits
 queue vector
 queue version
@@ -744,7 +746,6 @@ semaphore version
 set compare
 set concepts
 set cstddef
-set cstdint
 set cstdlib
 set functional
 set initializer_list
@@ -791,6 +792,7 @@ stack deque
 stack functional
 stack initializer_list
 stack limits
+stack new
 stack type_traits
 stack version
 stdexcept cstdlib
@@ -841,6 +843,7 @@ string_view initializer_list
 string_view iosfwd
 string_view iterator
 string_view limits
+string_view new
 string_view stdexcept
 string_view type_traits
 string_view version

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 05de55e81f63ee..fd20632ff88986 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -24,6 +24,7 @@ array cstddef
 array cstdint
 array initializer_list
 array limits
+array new
 array stdexcept
 array version
 atomic cstddef
@@ -65,6 +66,7 @@ charconv cstddef
 charconv cstdint
 charconv initializer_list
 charconv limits
+charconv new
 chrono array
 chrono cmath
 chrono compare
@@ -461,6 +463,7 @@ queue cstdint
 queue deque
 queue initializer_list
 queue limits
+queue new
 queue vector
 queue version
 random cmath
@@ -480,7 +483,6 @@ ranges initializer_list
 ranges iosfwd
 ranges iterator
 ranges limits
-ranges new
 ranges optional
 ranges span
 ranges tuple
@@ -558,6 +560,7 @@ stack cstdint
 stack deque
 stack initializer_list
 stack limits
+stack new
 stack version
 stdexcept iosfwd
 stop_token atomic
@@ -594,6 +597,7 @@ string_view cwchar
 string_view initializer_list
 string_view iosfwd
 string_view limits
+string_view new
 string_view stdexcept
 string_view version
 strstream istream

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 05de55e81f63ee..fd20632ff88986 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -24,6 +24,7 @@ array cstddef
 array cstdint
 array initializer_list
 array limits
+array new
 array stdexcept
 array version
 atomic cstddef
@@ -65,6 +66,7 @@ charconv cstddef
 charconv cstdint
 charconv initializer_list
 charconv limits
+charconv new
 chrono array
 chrono cmath
 chrono compare
@@ -461,6 +463,7 @@ queue cstdint
 queue deque
 queue initializer_list
 queue limits
+queue new
 queue vector
 queue version
 random cmath
@@ -480,7 +483,6 @@ ranges initializer_list
 ranges iosfwd
 ranges iterator
 ranges limits
-ranges new
 ranges optional
 ranges span
 ranges tuple
@@ -558,6 +560,7 @@ stack cstdint
 stack deque
 stack initializer_list
 stack limits
+stack new
 stack version
 stdexcept iosfwd
 stop_token atomic
@@ -594,6 +597,7 @@ string_view cwchar
 string_view initializer_list
 string_view iosfwd
 string_view limits
+string_view new
 string_view stdexcept
 string_view version
 strstream istream

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move.pass.cpp
index 3795314a53a2ad..ce5cf0560fc81a 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move.pass.cpp
@@ -18,10 +18,12 @@
 
 #include <algorithm>
 #include <cassert>
+#include <iterator>
 #include <memory>
 
-#include "test_macros.h"
+#include "MoveOnly.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 class PaddedBase {
 public:
@@ -111,11 +113,32 @@ TEST_CONSTEXPR_CXX20 bool test() {
     assert(std::equal(a, a + 10, expected));
   }
 
+  // Make sure that the algorithm works with move-only types
+  {
+    // When non-trivial
+    {
+      MoveOnly from[3] = {1, 2, 3};
+      MoveOnly to[3] = {};
+      std::move(std::begin(from), std::end(from), std::begin(to));
+      assert(to[0] == MoveOnly(1));
+      assert(to[1] == MoveOnly(2));
+      assert(to[2] == MoveOnly(3));
+    }
+    // When trivial
+    {
+      TrivialMoveOnly from[3] = {1, 2, 3};
+      TrivialMoveOnly to[3] = {};
+      std::move(std::begin(from), std::end(from), std::begin(to));
+      assert(to[0] == TrivialMoveOnly(1));
+      assert(to[1] == TrivialMoveOnly(2));
+      assert(to[2] == TrivialMoveOnly(3));
+    }
+  }
+
   return true;
 }
 
-int main(int, char**)
-{
+int main(int, char**) {
   test();
 #if TEST_STD_VER >= 20
   static_assert(test());

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move_backward.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move_backward.pass.cpp
index 04ecd4836a0019..2ed4d37b9dbe64 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move_backward.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move_backward.pass.cpp
@@ -17,10 +17,12 @@
 
 #include <algorithm>
 #include <cassert>
+#include <iterator>
 #include <memory>
 
-#include "test_macros.h"
+#include "MoveOnly.h"
 #include "test_iterators.h"
+#include "test_macros.h"
 
 class PaddedBase {
 public:
@@ -110,6 +112,28 @@ TEST_CONSTEXPR_CXX20 bool test() {
     assert(std::equal(a, a + 10, expected));
   }
 
+  // Make sure that the algorithm works with move-only types
+  {
+    // When non-trivial
+    {
+      MoveOnly from[3] = {1, 2, 3};
+      MoveOnly to[3] = {};
+      std::move_backward(std::begin(from), std::end(from), std::end(to));
+      assert(to[0] == MoveOnly(1));
+      assert(to[1] == MoveOnly(2));
+      assert(to[2] == MoveOnly(3));
+    }
+    // When trivial
+    {
+      TrivialMoveOnly from[3] = {1, 2, 3};
+      TrivialMoveOnly to[3] = {};
+      std::move_backward(std::begin(from), std::end(from), std::end(to));
+      assert(to[0] == TrivialMoveOnly(1));
+      assert(to[1] == TrivialMoveOnly(2));
+      assert(to[2] == TrivialMoveOnly(3));
+    }
+  }
+
   return true;
 }
 

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.pass.cpp
index d98c31929b9fa4..aeff581c46cb51 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move.pass.cpp
@@ -24,6 +24,7 @@
 #include <array>
 #include <cassert>
 #include <deque>
+#include <iterator>
 #include <ranges>
 #include <vector>
 
@@ -203,6 +204,7 @@ constexpr bool test() {
   test_proxy_in_iterators<ProxyIterator>();
 
   { // check that a move-only type works
+    // When non-trivial
     {
       MoveOnly a[] = {1, 2, 3};
       MoveOnly b[3];
@@ -219,6 +221,24 @@ constexpr bool test() {
       assert(b[1].get() == 2);
       assert(b[2].get() == 3);
     }
+
+    // When trivial
+    {
+      TrivialMoveOnly a[] = {1, 2, 3};
+      TrivialMoveOnly b[3];
+      std::ranges::move(a, std::begin(b));
+      assert(b[0].get() == 1);
+      assert(b[1].get() == 2);
+      assert(b[2].get() == 3);
+    }
+    {
+      TrivialMoveOnly a[] = {1, 2, 3};
+      TrivialMoveOnly b[3];
+      std::ranges::move(std::begin(a), std::end(a), std::begin(b));
+      assert(b[0].get() == 1);
+      assert(b[1].get() == 2);
+      assert(b[2].get() == 3);
+    }
   }
 
   { // check that a move-only type works for ProxyIterator

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.pass.cpp
index f6ca42b0adcfcf..9219cbb1dd84d1 100644
--- a/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.pass.cpp
@@ -24,6 +24,7 @@
 #include <array>
 #include <cassert>
 #include <deque>
+#include <iterator>
 #include <ranges>
 #include <vector>
 
@@ -189,6 +190,7 @@ constexpr bool test() {
   test_proxy_in_iterators<ProxyIterator>();
 
   { // check that a move-only type works
+    // When non-trivial
     {
       MoveOnly a[] = {1, 2, 3};
       MoveOnly b[3];
@@ -205,6 +207,24 @@ constexpr bool test() {
       assert(b[1].get() == 2);
       assert(b[2].get() == 3);
     }
+
+    // When trivial
+    {
+      TrivialMoveOnly a[] = {1, 2, 3};
+      TrivialMoveOnly b[3];
+      std::ranges::move_backward(a, std::end(b));
+      assert(b[0].get() == 1);
+      assert(b[1].get() == 2);
+      assert(b[2].get() == 3);
+    }
+    {
+      TrivialMoveOnly a[] = {1, 2, 3};
+      TrivialMoveOnly b[3];
+      std::ranges::move_backward(std::begin(a), std::end(a), std::end(b));
+      assert(b[0].get() == 1);
+      assert(b[1].get() == 2);
+      assert(b[2].get() == 3);
+    }
   }
 
   { // check that a move-only type works for ProxyIterator

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter.pass.cpp
index 43d843e24a11bd..549f29a8f7ba11 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter.pass.cpp
@@ -14,45 +14,57 @@
 #include <iterator>
 #include <cassert>
 
-#include "test_macros.h"
-#include "min_allocator.h"
 #include "asan_testing.h"
+#include "min_allocator.h"
+#include "MoveOnly.h"
+#include "test_macros.h"
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
 struct Throws {
-    Throws() : v_(0) {}
-    Throws(int v) : v_(v) {}
-    Throws(const Throws  &rhs) : v_(rhs.v_) { if (sThrows) throw 1; }
-    Throws(      Throws &&rhs) : v_(rhs.v_) { if (sThrows) throw 1; }
-    Throws& operator=(const Throws  &rhs) { v_ = rhs.v_; return *this; }
-    Throws& operator=(      Throws &&rhs) { v_ = rhs.v_; return *this; }
-    int v_;
-    static bool sThrows;
-    };
+  Throws() : v_(0) {}
+  Throws(int v) : v_(v) {}
+  Throws(const Throws& rhs) : v_(rhs.v_) {
+    if (sThrows)
+      throw 1;
+  }
+  Throws(Throws&& rhs) : v_(rhs.v_) {
+    if (sThrows)
+      throw 1;
+  }
+  Throws& operator=(const Throws& rhs) {
+    v_ = rhs.v_;
+    return *this;
+  }
+  Throws& operator=(Throws&& rhs) {
+    v_ = rhs.v_;
+    return *this;
+  }
+  int v_;
+  static bool sThrows;
+};
 
 bool Throws::sThrows = false;
 #endif
 
-TEST_CONSTEXPR_CXX20 bool tests()
-{
-    {
+TEST_CONSTEXPR_CXX20 bool tests() {
+  {
     int a1[] = {1, 2, 3, 4, 5};
-    std::vector<int> l1(a1, a1+5);
+    std::vector<int> l1(a1, a1 + 5);
     l1.erase(l1.begin());
     assert(is_contiguous_container_asan_correct(l1));
-    assert(l1 == std::vector<int>(a1+1, a1+5));
-    }
-    {
+    assert(l1 == std::vector<int>(a1 + 1, a1 + 5));
+  }
+  {
     int a1[] = {1, 2, 3, 4, 5};
     int e1[] = {1, 3, 4, 5};
-    std::vector<int> l1(a1, a1+5);
+    std::vector<int> l1(a1, a1 + 5);
     l1.erase(l1.begin() + 1);
     assert(is_contiguous_container_asan_correct(l1));
-    assert(l1 == std::vector<int>(e1, e1+4));
-    }
-    {
+    assert(l1 == std::vector<int>(e1, e1 + 4));
+  }
+  {
     int a1[] = {1, 2, 3};
-    std::vector<int> l1(a1, a1+3);
+    std::vector<int> l1(a1, a1 + 3);
     std::vector<int>::const_iterator i = l1.begin();
     assert(is_contiguous_container_asan_correct(l1));
     ++i;
@@ -74,11 +86,36 @@ TEST_CONSTEXPR_CXX20 bool tests()
     assert(l1.size() == 0);
     assert(std::distance(l1.begin(), l1.end()) == 0);
     assert(is_contiguous_container_asan_correct(l1));
-    }
+  }
+
+  // Make sure vector::erase works with move-only types
+  // When non-trivial
+  {
+    std::vector<MoveOnly> v;
+    v.emplace_back(1);
+    v.emplace_back(2);
+    v.emplace_back(3);
+    v.erase(v.begin());
+    assert(v.size() == 2);
+    assert(v[0] == MoveOnly(2));
+    assert(v[1] == MoveOnly(3));
+  }
+  // When trivial
+  {
+    std::vector<TrivialMoveOnly> v;
+    v.emplace_back(1);
+    v.emplace_back(2);
+    v.emplace_back(3);
+    v.erase(v.begin());
+    assert(v.size() == 2);
+    assert(v[0] == TrivialMoveOnly(2));
+    assert(v[1] == TrivialMoveOnly(3));
+  }
+
 #if TEST_STD_VER >= 11
-    {
+  {
     int a1[] = {1, 2, 3};
-    std::vector<int, min_allocator<int>> l1(a1, a1+3);
+    std::vector<int, min_allocator<int>> l1(a1, a1 + 3);
     std::vector<int, min_allocator<int>>::const_iterator i = l1.begin();
     assert(is_contiguous_container_asan_correct(l1));
     ++i;
@@ -100,32 +137,31 @@ TEST_CONSTEXPR_CXX20 bool tests()
     assert(l1.size() == 0);
     assert(std::distance(l1.begin(), l1.end()) == 0);
     assert(is_contiguous_container_asan_correct(l1));
-    }
+  }
 #endif
 
-    return true;
+  return true;
 }
 
-int main(int, char**)
-{
-    tests();
+int main(int, char**) {
+  tests();
 #if TEST_STD_VER > 17
-    static_assert(tests());
+  static_assert(tests());
 #endif
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
-// Test for LWG2853:
-// Throws: Nothing unless an exception is thrown by the assignment operator or move assignment operator of T.
-    {
-        Throws arr[] = {1, 2, 3};
-        std::vector<Throws> v(arr, arr+3);
-        Throws::sThrows = true;
-        v.erase(v.begin());
-        v.erase(--v.end());
-        v.erase(v.begin());
-        assert(v.size() == 0);
-    }
+  // Test for LWG2853:
+  // Throws: Nothing unless an exception is thrown by the assignment operator or move assignment operator of T.
+  {
+    Throws arr[] = {1, 2, 3};
+    std::vector<Throws> v(arr, arr + 3);
+    Throws::sThrows = true;
+    v.erase(v.begin());
+    v.erase(--v.end());
+    v.erase(v.begin());
+    assert(v.size() == 0);
+  }
 #endif
 
-    return 0;
+  return 0;
 }

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter_iter.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter_iter.pass.cpp
index 7857b8868a4c0a..4a5bca0110674c 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/erase_iter_iter.pass.cpp
@@ -14,9 +14,10 @@
 #include <iterator>
 #include <cassert>
 
-#include "test_macros.h"
-#include "min_allocator.h"
 #include "asan_testing.h"
+#include "min_allocator.h"
+#include "MoveOnly.h"
+#include "test_macros.h"
 
 #ifndef TEST_HAS_NO_EXCEPTIONS
 struct Throws {
@@ -87,6 +88,25 @@ TEST_CONSTEXPR_CXX20 bool tests()
         assert(is_contiguous_container_asan_correct(outer[0]));
         assert(is_contiguous_container_asan_correct(outer[1]));
     }
+    // Make sure vector::erase works with move-only types
+    {
+        // When non-trivial
+        {
+            std::vector<MoveOnly> v;
+            v.emplace_back(1); v.emplace_back(2); v.emplace_back(3);
+            v.erase(v.begin(), v.begin() + 2);
+            assert(v.size() == 1);
+            assert(v[0] == MoveOnly(3));
+        }
+        // When trivial
+        {
+            std::vector<TrivialMoveOnly> v;
+            v.emplace_back(1); v.emplace_back(2); v.emplace_back(3);
+            v.erase(v.begin(), v.begin() + 2);
+            assert(v.size() == 1);
+            assert(v[0] == TrivialMoveOnly(3));
+        }
+    }
 #if TEST_STD_VER >= 11
     {
         std::vector<int, min_allocator<int>> l1(a1, a1+3);

diff  --git a/libcxx/test/support/MoveOnly.h b/libcxx/test/support/MoveOnly.h
index 4a1c1fc09cd484..7d867e204f671b 100644
--- a/libcxx/test/support/MoveOnly.h
+++ b/libcxx/test/support/MoveOnly.h
@@ -52,10 +52,12 @@ class MoveOnly
     TEST_CONSTEXPR_CXX14 MoveOnly operator*(const MoveOnly& x) const
         { return MoveOnly(data_ * x.data_); }
 
-    template<class T, class U>
-    friend void operator,(T t, U u) = delete;
-};
+    template<class T>
+    friend void operator,(MoveOnly const&, T) = delete;
 
+    template<class T>
+    friend void operator,(T, MoveOnly const&) = delete;
+};
 
 template <>
 struct std::hash<MoveOnly>
@@ -65,4 +67,62 @@ struct std::hash<MoveOnly>
     TEST_CONSTEXPR std::size_t operator()(const MoveOnly& x) const {return static_cast<size_t>(x.get());}
 };
 
+class TrivialMoveOnly {
+    int data_;
+
+  public:
+    TEST_CONSTEXPR TrivialMoveOnly(int data = 1) : data_(data) {}
+
+    TrivialMoveOnly(const TrivialMoveOnly&)            = delete;
+    TrivialMoveOnly& operator=(const TrivialMoveOnly&) = delete;
+
+    TrivialMoveOnly(TrivialMoveOnly&&)            = default;
+    TrivialMoveOnly& operator=(TrivialMoveOnly&&) = default;
+
+    TEST_CONSTEXPR int get() const { return data_; }
+
+    friend TEST_CONSTEXPR bool operator==(const TrivialMoveOnly& x, const TrivialMoveOnly& y) {
+      return x.data_ == y.data_;
+    }
+    friend TEST_CONSTEXPR bool operator!=(const TrivialMoveOnly& x, const TrivialMoveOnly& y) {
+      return x.data_ != y.data_;
+    }
+    friend TEST_CONSTEXPR bool operator<(const TrivialMoveOnly& x, const TrivialMoveOnly& y) {
+      return x.data_ < y.data_;
+    }
+    friend TEST_CONSTEXPR bool operator<=(const TrivialMoveOnly& x, const TrivialMoveOnly& y) {
+      return x.data_ <= y.data_;
+    }
+    friend TEST_CONSTEXPR bool operator>(const TrivialMoveOnly& x, const TrivialMoveOnly& y) {
+      return x.data_ > y.data_;
+    }
+    friend TEST_CONSTEXPR bool operator>=(const TrivialMoveOnly& x, const TrivialMoveOnly& y) {
+      return x.data_ >= y.data_;
+    }
+
+#if TEST_STD_VER > 17
+    friend constexpr auto operator<=>(const TrivialMoveOnly&, const TrivialMoveOnly&) = default;
+#endif // TEST_STD_VER > 17
+
+    TEST_CONSTEXPR_CXX14 TrivialMoveOnly operator+(const TrivialMoveOnly& x) const {
+      return TrivialMoveOnly(data_ + x.data_);
+    }
+    TEST_CONSTEXPR_CXX14 TrivialMoveOnly operator*(const TrivialMoveOnly& x) const {
+      return TrivialMoveOnly(data_ * x.data_);
+    }
+
+    template<class T>
+    friend void operator,(TrivialMoveOnly const&, T) = delete;
+
+    template<class T>
+    friend void operator,(T, TrivialMoveOnly const&) = delete;
+};
+
+template <>
+struct std::hash<TrivialMoveOnly> {
+    typedef TrivialMoveOnly argument_type;
+    typedef std::size_t result_type;
+    TEST_CONSTEXPR std::size_t operator()(const TrivialMoveOnly& x) const { return static_cast<size_t>(x.get()); }
+};
+
 #endif // MOVEONLY_H


        


More information about the libcxx-commits mailing list