diff --git a/libcxx/include/__string/constexpr_c_functions.h b/libcxx/include/__string/constexpr_c_functions.h --- 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,52 @@ } } +// 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 ::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 ::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 ::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) { + std::__construct_at(std::addressof(__dest), __src); + return __dest; +} + +// clang-format off +template ::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) { + 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 ::value, int> = 0> _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __constexpr_memmove(_Tp* __dest, _Up* __src, __element_count __n) { @@ -146,10 +196,10 @@ #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 --- /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 different 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 +#include +#include + +#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 +TEST_CONSTEXPR_CXX14 void test_user_defined_types() { + static_assert(std::is_trivially_copyable::value, "test the test"); + static_assert(std::is_trivially_copyable::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 +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 +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(); + test_user_defined_types(); + test_user_defined_types(); + test_user_defined_types(); + + test_builtin_types(); + test_builtin_types(); + test_builtin_types(); + test_builtin_types(); + test_builtin_types(); + + // Cross-type + test_builtin_types(); + test_builtin_types(); + test_builtin_types(); + test_builtin_types(); + + test_pointer_types(); + test_pointer_types(); + test_pointer_types(); + test_pointer_types(); + test_pointer_types(); + + 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 --- a/libcxx/test/libcxx/transitive_includes/cxx03.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv @@ -47,6 +47,7 @@ array initializer_list array iterator array limits +array new array stdexcept array type_traits array utility @@ -110,6 +111,7 @@ charconv initializer_list charconv iosfwd charconv limits +charconv new charconv type_traits chrono bit chrono compare @@ -307,6 +309,7 @@ format initializer_list format limits format locale +format new format optional format queue format stack @@ -645,6 +648,7 @@ queue functional queue initializer_list queue limits +queue new queue type_traits queue vector queue version @@ -781,6 +785,7 @@ stack functional stack initializer_list stack limits +stack new stack type_traits stack version stdexcept cstdlib @@ -831,6 +836,7 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx11.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv @@ -47,6 +47,7 @@ array initializer_list array iterator array limits +array new array stdexcept array type_traits array utility @@ -110,6 +111,7 @@ charconv initializer_list charconv iosfwd charconv limits +charconv new charconv type_traits chrono bit chrono compare @@ -307,6 +309,7 @@ format initializer_list format limits format locale +format new format optional format queue format stack @@ -646,6 +649,7 @@ queue functional queue initializer_list queue limits +queue new queue type_traits queue vector queue version @@ -782,6 +786,7 @@ stack functional stack initializer_list stack limits +stack new stack type_traits stack version stdexcept cstdlib @@ -832,6 +837,7 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx14.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv @@ -47,6 +47,7 @@ array initializer_list array iterator array limits +array new array stdexcept array type_traits array utility @@ -110,6 +111,7 @@ charconv initializer_list charconv iosfwd charconv limits +charconv new charconv type_traits chrono bit chrono compare @@ -309,6 +311,7 @@ format initializer_list format limits format locale +format new format optional format queue format stack @@ -648,6 +651,7 @@ queue functional queue initializer_list queue limits +queue new queue type_traits queue vector queue version @@ -784,6 +788,7 @@ stack functional stack initializer_list stack limits +stack new stack type_traits stack version stdexcept cstdlib @@ -834,6 +839,7 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx17.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv @@ -47,6 +47,7 @@ array initializer_list array iterator array limits +array new array stdexcept array type_traits array utility @@ -110,6 +111,7 @@ charconv initializer_list charconv iosfwd charconv limits +charconv new charconv type_traits chrono bit chrono compare @@ -309,6 +311,7 @@ format initializer_list format limits format locale +format new format optional format queue format stack @@ -648,6 +651,7 @@ queue functional queue initializer_list queue limits +queue new queue type_traits queue vector queue version @@ -784,6 +788,7 @@ stack functional stack initializer_list stack limits +stack new stack type_traits stack version stdexcept cstdlib @@ -834,6 +839,7 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx20.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv @@ -46,6 +46,7 @@ array initializer_list array iterator array limits +array new array stdexcept array type_traits array utility @@ -109,6 +110,7 @@ charconv initializer_list charconv iosfwd charconv limits +charconv new charconv type_traits chrono array chrono bit @@ -316,6 +318,7 @@ format initializer_list format limits format locale +format new format optional format queue format stack @@ -654,6 +657,7 @@ queue functional queue initializer_list queue limits +queue new queue type_traits queue vector queue version @@ -790,6 +794,7 @@ stack functional stack initializer_list stack limits +stack new stack type_traits stack version stdexcept cstdlib @@ -840,6 +845,7 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx23.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv @@ -24,6 +24,7 @@ array cstdint array initializer_list array limits +array new array stdexcept array version atomic cstddef @@ -65,6 +66,7 @@ charconv cstdint charconv initializer_list charconv limits +charconv new chrono array chrono cmath chrono compare @@ -461,6 +463,7 @@ queue deque queue initializer_list queue limits +queue new queue vector queue version random cmath @@ -480,7 +483,6 @@ ranges iosfwd ranges iterator ranges limits -ranges new ranges optional ranges span ranges tuple @@ -558,6 +560,7 @@ stack deque stack initializer_list stack limits +stack new stack version stdexcept iosfwd stop_token atomic @@ -594,6 +597,7 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx26.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv @@ -24,6 +24,7 @@ array cstdint array initializer_list array limits +array new array stdexcept array version atomic cstddef @@ -65,6 +66,7 @@ charconv cstdint charconv initializer_list charconv limits +charconv new chrono array chrono cmath chrono compare @@ -461,6 +463,7 @@ queue deque queue initializer_list queue limits +queue new queue vector queue version random cmath @@ -480,7 +483,6 @@ ranges iosfwd ranges iterator ranges limits -ranges new ranges optional ranges span ranges tuple @@ -558,6 +560,7 @@ stack deque stack initializer_list stack limits +stack new stack version stdexcept iosfwd stop_token atomic @@ -594,6 +597,7 @@ 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 --- 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 #include +#include #include -#include "test_macros.h" +#include "MoveOnly.h" #include "test_iterators.h" +#include "test_macros.h" class PaddedBase { public: @@ -111,11 +113,32 @@ 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 --- 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 #include +#include #include -#include "test_macros.h" +#include "MoveOnly.h" #include "test_iterators.h" +#include "test_macros.h" class PaddedBase { public: @@ -110,6 +112,28 @@ 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 --- 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 #include #include +#include #include #include @@ -203,6 +204,7 @@ test_proxy_in_iterators(); { // check that a move-only type works + // When non-trivial { MoveOnly a[] = {1, 2, 3}; MoveOnly b[3]; @@ -219,6 +221,24 @@ 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 --- 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 #include #include +#include #include #include @@ -189,6 +190,7 @@ test_proxy_in_iterators(); { // check that a move-only type works + // When non-trivial { MoveOnly a[] = {1, 2, 3}; MoveOnly b[3]; @@ -205,6 +207,24 @@ 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 --- 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 #include -#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 l1(a1, a1+5); + std::vector l1(a1, a1 + 5); l1.erase(l1.begin()); assert(is_contiguous_container_asan_correct(l1)); - assert(l1 == std::vector(a1+1, a1+5)); - } - { + assert(l1 == std::vector(a1 + 1, a1 + 5)); + } + { int a1[] = {1, 2, 3, 4, 5}; int e1[] = {1, 3, 4, 5}; - std::vector l1(a1, a1+5); + std::vector l1(a1, a1 + 5); l1.erase(l1.begin() + 1); assert(is_contiguous_container_asan_correct(l1)); - assert(l1 == std::vector(e1, e1+4)); - } - { + assert(l1 == std::vector(e1, e1 + 4)); + } + { int a1[] = {1, 2, 3}; - std::vector l1(a1, a1+3); + std::vector l1(a1, a1 + 3); std::vector::const_iterator i = l1.begin(); assert(is_contiguous_container_asan_correct(l1)); ++i; @@ -74,11 +86,36 @@ 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 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 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> l1(a1, a1+3); + std::vector> l1(a1, a1 + 3); std::vector>::const_iterator i = l1.begin(); assert(is_contiguous_container_asan_correct(l1)); ++i; @@ -100,32 +137,31 @@ 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 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 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 --- 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 #include -#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 @@ 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 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 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> l1(a1, a1+3); diff --git a/libcxx/test/support/MoveOnly.h b/libcxx/test/support/MoveOnly.h --- a/libcxx/test/support/MoveOnly.h +++ b/libcxx/test/support/MoveOnly.h @@ -52,10 +52,12 @@ TEST_CONSTEXPR_CXX14 MoveOnly operator*(const MoveOnly& x) const { return MoveOnly(data_ * x.data_); } - template - friend void operator,(T t, U u) = delete; -}; + template + friend void operator,(MoveOnly const&, T) = delete; + template + friend void operator,(T, MoveOnly const&) = delete; +}; template <> struct std::hash @@ -65,4 +67,62 @@ TEST_CONSTEXPR std::size_t operator()(const MoveOnly& x) const {return static_cast(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 + friend void operator,(TrivialMoveOnly const&, T) = delete; + + template + friend void operator,(T, TrivialMoveOnly const&) = delete; +}; + +template <> +struct std::hash { + typedef TrivialMoveOnly argument_type; + typedef std::size_t result_type; + TEST_CONSTEXPR std::size_t operator()(const TrivialMoveOnly& x) const { return static_cast(x.get()); } +}; + #endif // MOVEONLY_H