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::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_backward(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; } 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,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 { @@ -28,7 +29,7 @@ Throws& operator=( Throws &&rhs) { v_ = rhs.v_; return *this; } int v_; static bool sThrows; - }; +}; bool Throws::sThrows = false; #endif @@ -36,70 +37,92 @@ TEST_CONSTEXPR_CXX20 bool tests() { { - int a1[] = {1, 2, 3, 4, 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)); + int a1[] = {1, 2, 3, 4, 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)); } { - int a1[] = {1, 2, 3, 4, 5}; - int e1[] = {1, 3, 4, 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)); + int a1[] = {1, 2, 3, 4, 5}; + int e1[] = {1, 3, 4, 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)); } { - int a1[] = {1, 2, 3}; - std::vector l1(a1, a1+3); - std::vector::const_iterator i = l1.begin(); - assert(is_contiguous_container_asan_correct(l1)); - ++i; - std::vector::iterator j = l1.erase(i); - assert(l1.size() == 2); - assert(std::distance(l1.begin(), l1.end()) == 2); - assert(*j == 3); - assert(*l1.begin() == 1); - assert(*std::next(l1.begin()) == 3); - assert(is_contiguous_container_asan_correct(l1)); - j = l1.erase(j); - assert(j == l1.end()); - assert(l1.size() == 1); - assert(std::distance(l1.begin(), l1.end()) == 1); - assert(*l1.begin() == 1); - assert(is_contiguous_container_asan_correct(l1)); - j = l1.erase(l1.begin()); - assert(j == l1.end()); - assert(l1.size() == 0); - assert(std::distance(l1.begin(), l1.end()) == 0); - assert(is_contiguous_container_asan_correct(l1)); + int a1[] = {1, 2, 3}; + std::vector l1(a1, a1+3); + std::vector::const_iterator i = l1.begin(); + assert(is_contiguous_container_asan_correct(l1)); + ++i; + std::vector::iterator j = l1.erase(i); + assert(l1.size() == 2); + assert(std::distance(l1.begin(), l1.end()) == 2); + assert(*j == 3); + assert(*l1.begin() == 1); + assert(*std::next(l1.begin()) == 3); + assert(is_contiguous_container_asan_correct(l1)); + j = l1.erase(j); + assert(j == l1.end()); + assert(l1.size() == 1); + assert(std::distance(l1.begin(), l1.end()) == 1); + assert(*l1.begin() == 1); + assert(is_contiguous_container_asan_correct(l1)); + j = l1.erase(l1.begin()); + assert(j == l1.end()); + 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>::const_iterator i = l1.begin(); - assert(is_contiguous_container_asan_correct(l1)); - ++i; - std::vector>::iterator j = l1.erase(i); - assert(l1.size() == 2); - assert(std::distance(l1.begin(), l1.end()) == 2); - assert(*j == 3); - assert(*l1.begin() == 1); - assert(*std::next(l1.begin()) == 3); - assert(is_contiguous_container_asan_correct(l1)); - j = l1.erase(j); - assert(j == l1.end()); - assert(l1.size() == 1); - assert(std::distance(l1.begin(), l1.end()) == 1); - assert(*l1.begin() == 1); - assert(is_contiguous_container_asan_correct(l1)); - j = l1.erase(l1.begin()); - assert(j == l1.end()); - assert(l1.size() == 0); - assert(std::distance(l1.begin(), l1.end()) == 0); - assert(is_contiguous_container_asan_correct(l1)); + int a1[] = {1, 2, 3}; + std::vector> l1(a1, a1+3); + std::vector>::const_iterator i = l1.begin(); + assert(is_contiguous_container_asan_correct(l1)); + ++i; + std::vector>::iterator j = l1.erase(i); + assert(l1.size() == 2); + assert(std::distance(l1.begin(), l1.end()) == 2); + assert(*j == 3); + assert(*l1.begin() == 1); + assert(*std::next(l1.begin()) == 3); + assert(is_contiguous_container_asan_correct(l1)); + j = l1.erase(j); + assert(j == l1.end()); + assert(l1.size() == 1); + assert(std::distance(l1.begin(), l1.end()) == 1); + assert(*l1.begin() == 1); + assert(is_contiguous_container_asan_correct(l1)); + j = l1.erase(l1.begin()); + assert(j == l1.end()); + assert(l1.size() == 0); + assert(std::distance(l1.begin(), l1.end()) == 0); + assert(is_contiguous_container_asan_correct(l1)); } #endif 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() + 1); + 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() + 1); + 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 @@ -56,7 +56,6 @@ friend void operator,(T t, U u) = delete; }; - template <> struct std::hash { @@ -65,4 +64,59 @@ 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,(T t, U u) = 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