2222#include " asan_testing.h"
2323#include " common.h"
2424#include " min_allocator.h"
25+ #include " MoveOnly.h"
2526#include " test_allocator.h"
2627#include " test_macros.h"
2728
28- struct MoveOnly {
29- int value;
30-
31- TEST_CONSTEXPR_CXX14 explicit MoveOnly (int i) : value(i) {}
32-
33- MoveOnly (MoveOnly const &) = delete ;
34- MoveOnly& operator =(MoveOnly const &) = delete ;
35-
36- TEST_CONSTEXPR_CXX14 MoveOnly (MoveOnly&& other) TEST_NOEXCEPT : value(other.value) { other.value = -1 ; }
37-
38- TEST_CONSTEXPR_CXX14 MoveOnly& operator =(MoveOnly&& other) TEST_NOEXCEPT {
39- value = other.value ;
40- other.value = -1 ;
41- return *this ;
42- }
43-
44- TEST_CONSTEXPR_CXX14 friend bool operator ==(MoveOnly const & lhs, MoveOnly const & rhs) {
45- return lhs.value == rhs.value ;
46- }
47- };
48-
4929template <class T >
50- struct has_moved_from_sentinel : std::false_type {};
30+ struct has_moved_from_sentinel_value : std::false_type {};
5131
5232template <>
53- struct has_moved_from_sentinel <MoveOnly> : std::true_type {};
33+ struct has_moved_from_sentinel_value <MoveOnly> : std::true_type {};
5434
5535template <template <class ...> class Allocator , class T >
5636TEST_CONSTEXPR_CXX20 void test () {
@@ -226,48 +206,48 @@ TEST_CONSTEXPR_CXX20 void test() {
226206 // When capacity must increase
227207 {
228208 Vector v;
229- v.emplace_back (0 );
230209 v.emplace_back (1 );
210+ v.emplace_back (2 );
231211
232212 while (v.size () < v.capacity ()) {
233- v.emplace_back (2 );
213+ v.emplace_back (3 );
234214 }
235215 assert (v.size () == v.capacity ());
236- // vector is {0, 1, 2...}
216+ // vector is {1, 2, 3 ...}
237217
238218 std::size_t old_cap = v.capacity ();
239219 v.emplace (v.cbegin (), std::move (v[1 ]));
240220 assert (v.capacity () > old_cap); // test the test
241221
242- // vector is {1, 0, -1, 2 ...}
243- // Note that old v[1] has been set to -1 when it was moved-from
222+ // vector is {2, 1, 0, 3 ...}
223+ // Note that old v[1] has been set to 0 when it was moved-from
244224 assert (v.size () >= 3 );
245- assert (v[0 ] == T (1 ));
246- assert (v[1 ] == T (0 ));
247- if (has_moved_from_sentinel <T>::value)
248- assert (v[2 ] == T (- 1 ));
225+ assert (v[0 ] == T (2 ));
226+ assert (v[1 ] == T (1 ));
227+ if (has_moved_from_sentinel_value <T>::value)
228+ assert (v[2 ] == T (0 ));
249229 assert (is_contiguous_container_asan_correct (v));
250230 }
251231
252232 // When elements shift around
253233 {
254234 Vector v;
255- v.emplace_back (0 );
256235 v.emplace_back (1 );
257- // vector is {0, 1}
236+ v.emplace_back (2 );
237+ // vector is {1, 2}
258238
259239 v.reserve (3 );
260240 std::size_t old_cap = v.capacity ();
261241 v.emplace (v.cbegin (), std::move (v[1 ]));
262242 assert (v.capacity () == old_cap); // test the test
263243
264- // vector is {1, 0, -1 }
265- // Note that old v[1] has been set to -1 when it was moved-from
244+ // vector is {2, 1, 0 }
245+ // Note that old v[1] has been set to 0 when it was moved-from
266246 assert (v.size () == 3 );
267- assert (v[0 ] == T (1 ));
268- assert (v[1 ] == T (0 ));
269- if (has_moved_from_sentinel <T>::value)
270- assert (v[2 ] == T (- 1 ));
247+ assert (v[0 ] == T (2 ));
248+ assert (v[1 ] == T (1 ));
249+ if (has_moved_from_sentinel_value <T>::value)
250+ assert (v[2 ] == T (0 ));
271251 assert (is_contiguous_container_asan_correct (v));
272252 }
273253 }
@@ -301,12 +281,14 @@ TEST_CONSTEXPR_CXX20 void test() {
301281 // on the stack and then moves it into its final location inside the newly allocated vector storage.
302282 //
303283 // If that were the case, and if the element happened to throw upon move construction or move assignment into its
304- // final location, we would have invalidated iterators despite the overall emplace being required to have the
305- // strong exception safety guarantee.
284+ // final location, we would have invalidated iterators, when a different approach would allow us to still provide
285+ // the strong exception safety guarantee.
306286 //
307- // Instead, a conforming implementation needs to emplace the new element into its final location immediately, and
308- // only after this has been done, then start making non-reversible changes to the vector's underlying storage.
309- #ifndef TEST_HAS_NO_EXCEPTIONS
287+ // Instead of the naive approach, libc++ emplaces the new element into its final location immediately, and only
288+ // after this has been done do we start making non-reversible changes to the vector's underlying storage. This
289+ // test pins down that behavior, although that is something that we don't advertise widely and could potentially
290+ // change in the future.
291+ #if defined(_LIBCPP_VERSION) && !defined(TEST_HAS_NO_EXCEPTIONS)
310292 {
311293 std::vector<ThrowingMove, Allocator<ThrowingMove> > v;
312294 v.emplace_back (0 , /* do throw */ false );
@@ -317,15 +299,15 @@ TEST_CONSTEXPR_CXX20 void test() {
317299 }
318300 assert (v.size () == v.capacity ()); // the next emplace will be forced to invalidate iterators
319301
320- v.emplace (v.cend (), 3 , /* do throw */ true ); // this shouldn't throw since we shouldn't move
302+ v.emplace (v.cend (), 3 , /* do throw */ true ); // this shouldn't throw since we shouldn't move this element at all
321303
322304 assert (v.size () >= 3 );
323305 assert (v[0 ] == ThrowingMove (0 ));
324306 assert (v[1 ] == ThrowingMove (1 ));
325307 assert (v.back () == ThrowingMove (3 ));
326308 assert (is_contiguous_container_asan_correct (v));
327309 }
328- #endif // TEST_HAS_NO_EXCEPTIONS
310+ #endif // defined(_LIBCPP_VERSION) && !defined( TEST_HAS_NO_EXCEPTIONS)
329311}
330312
331313TEST_CONSTEXPR_CXX20 bool tests () {
0 commit comments