1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // Older compilers don't support std::is_constant_evaluated
10 // UNSUPPORTED: clang-4, clang-5, clang-6, clang-7, clang-8
11 // UNSUPPORTED: apple-clang-9, apple-clang-10
12 // UNSUPPORTED: c++03
13 
14 // <algorithm>
15 
16 // We optimize std::copy(_backward) and std::move(_backward) into memmove
17 // when the iterator is trivial and contiguous and the type in question
18 // is also trivially (copyable, movable). This test verifies that the
19 // optimization never eliminates an actually non-trivial copy or move.
20 
21 #include <algorithm>
22 #include <iterator>
23 #include <cassert>
24 
25 #include "test_macros.h"
26 #include "test_iterators.h"
27 
28 struct TMBNTC {
29     int *p;
30     constexpr TMBNTC(int& copies) : p(&copies) {}
31     constexpr TMBNTC(const TMBNTC&) = default;
32     TEST_CONSTEXPR_CXX14 TMBNTC& operator=(TMBNTC&&) = default;
33     TEST_CONSTEXPR_CXX14 TMBNTC& operator=(const TMBNTC&) { ++*p; return *this; }
34 };
35 
36 TEST_CONSTEXPR_CXX20 bool
37 test_trivial_moveassign_but_no_trivial_copyassign()
38 {
39     int copies = 0;
40     TMBNTC ia[] = { copies, copies, copies, copies };
41     TMBNTC ib[] = { copies, copies, copies, copies };
42     std::copy(ia, ia+4, ib);
43     assert(copies == 4);
44     copies = 0;
45     std::copy_backward(ia, ia+4, ib+4);
46     assert(copies == 4);
47 
48     copies = 0;
49     std::copy(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib);
50     assert(copies == 0);
51     std::copy_backward(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib+4);
52     assert(copies == 0);
53 
54     std::move(ia, ia+4, ib);
55     assert(copies == 0);
56     std::move_backward(ia, ia+4, ib+4);
57     assert(copies == 0);
58 
59     return true;
60 }
61 
62 struct TCBNTM {
63     int *p;
64     constexpr TCBNTM(int& moves) : p(&moves) {}
65     constexpr TCBNTM(const TCBNTM&) = default;
66     TEST_CONSTEXPR_CXX14 TCBNTM& operator=(TCBNTM&&) { ++*p; return *this; }
67     TEST_CONSTEXPR_CXX14 TCBNTM& operator=(const TCBNTM&) = default;
68 };
69 
70 TEST_CONSTEXPR_CXX20 bool
71 test_trivial_copyassign_but_no_trivial_moveassign()
72 {
73     int moves = 0;
74     TCBNTM ia[] = { moves, moves, moves, moves };
75     TCBNTM ib[] = { moves, moves, moves, moves };
76     std::move(ia, ia+4, ib);
77     assert(moves == 4);
78     moves = 0;
79     std::move_backward(ia, ia+4, ib+4);
80     assert(moves == 4);
81 
82     moves = 0;
83     std::copy(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib);
84     assert(moves == 4);
85     moves = 0;
86     std::copy_backward(std::make_move_iterator(ia), std::make_move_iterator(ia+4), ib+4);
87     assert(moves == 4);
88 
89     moves = 0;
90     std::copy(ia, ia+4, ib);
91     assert(moves == 0);
92     std::copy_backward(ia, ia+4, ib+4);
93     assert(moves == 0);
94 
95     return true;
96 }
97 
98 int main(int, char**)
99 {
100     test_trivial_moveassign_but_no_trivial_copyassign();
101     test_trivial_copyassign_but_no_trivial_moveassign();
102 
103 #if TEST_STD_VER > 17
104     static_assert(test_trivial_moveassign_but_no_trivial_copyassign());
105     static_assert(test_trivial_copyassign_but_no_trivial_moveassign());
106 #endif
107 
108     return 0;
109 }
110