diff --git a/stl/inc/xstring b/stl/inc/xstring index ae566f24af..46991c2aef 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -3052,8 +3052,9 @@ private: if (_Right_data._Large_mode_engaged()) { // steal buffer _Construct_in_place(_My_data._Bx._Ptr, _Right_data._Bx._Ptr); - _Right_data._Bx._Ptr = nullptr; _Swap_proxy_and_iterators(_Right); + + _Destroy_in_place(_Right_data._Bx._Ptr); } else { // copy small string buffer _My_data._Activate_SSO_buffer(); _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1); @@ -3166,12 +3167,13 @@ public: size_type _New_capacity = _Calculate_growth(_Right_size, _Small_string_capacity, _Right.max_size()); auto _Right_al_non_const = _Right_al; const pointer _New_ptr = _Allocate_for_capacity(_Right_al_non_const, _New_capacity); // throws - _Traits::copy(_Unfancy(_New_ptr), _Right_ptr, _Right_size + 1); + _Tidy_deallocate(); - _Mypair._Myval2._Bx._Ptr = _New_ptr; - _Mypair._Myval2._Mysize = _Right_size; - _Mypair._Myval2._Myres = _New_capacity; + _Construct_in_place(_Mypair._Myval2._Bx._Ptr, _New_ptr); + _Mypair._Myval2._Mysize = _Right_size; + _Mypair._Myval2._Myres = _New_capacity; + _ASAN_STRING_CREATE(*this); } else { _Tidy_deallocate(); _Traits::copy(_Mypair._Myval2._Bx._Buf, _Right_ptr, _Right_size + 1); diff --git a/tests/std/tests/VSO_0000000_allocator_propagation/test.cpp b/tests/std/tests/VSO_0000000_allocator_propagation/test.cpp index d697fc595a..9585dca3ea 100644 --- a/tests/std/tests/VSO_0000000_allocator_propagation/test.cpp +++ b/tests/std/tests/VSO_0000000_allocator_propagation/test.cpp @@ -34,6 +34,160 @@ _CONSTEXPR20 void assert_is_permutation(const Container& cont, initializer_list< assert(is_permutation(cont.begin(), cont.end(), il.begin(), il.end())); } +class test_leak { +private: + char* res; + +public: + test_leak(const test_leak&) = delete; + test_leak& operator=(const test_leak&) = delete; + + _CONSTEXPR20 test_leak() noexcept /* terminates */ : res(allocator{}.allocate(1)) {} + _CONSTEXPR20 ~test_leak() { + allocator{}.deallocate(res, 1); + } +}; + +template +class nontrivial_pointer : private test_leak { +public: + T* ptr; + _CONSTEXPR20 nontrivial_pointer() noexcept : ptr(nullptr) {} + _CONSTEXPR20 explicit nontrivial_pointer(T* ptr_) noexcept : ptr(ptr_) {} + _CONSTEXPR20 /*implicit*/ nontrivial_pointer(nullptr_t) noexcept : ptr(nullptr) {} + + _CONSTEXPR20 nontrivial_pointer(const nontrivial_pointer& other) noexcept : ptr(other.ptr) {} + template , int> = 0> + _CONSTEXPR20 nontrivial_pointer(const nontrivial_pointer& other) noexcept : ptr(other.ptr) {} + _CONSTEXPR20 nontrivial_pointer& operator=(const nontrivial_pointer& other) noexcept { + ptr = other.ptr; + return *this; + } + + _CONSTEXPR20 ~nontrivial_pointer() = default; + + _CONSTEXPR20 explicit operator bool() const noexcept { +#ifdef __EDG__ // TRANSITION, VSO-1888157 + return static_cast(ptr); +#else // ^^^ workaround / no workaround vvv + return ptr != nullptr; +#endif // ^^^ no workaround ^^^ + } + _CONSTEXPR20 add_lvalue_reference_t operator*() const noexcept { + return *ptr; + } + _CONSTEXPR20 T* operator->() const noexcept { + return ptr; + } + template , int> = 0> + _CONSTEXPR20 add_lvalue_reference_t operator[](I off) const noexcept { + return ptr[off]; + } + + _CONSTEXPR20 nontrivial_pointer& operator++() noexcept { + ++ptr; + return *this; + } + _CONSTEXPR20 nontrivial_pointer operator++(int) noexcept { + return nontrivial_pointer(ptr++); + } + _CONSTEXPR20 nontrivial_pointer& operator--() noexcept { + --ptr; + return *this; + } + _CONSTEXPR20 nontrivial_pointer operator--(int) noexcept { + return nontrivial_pointer(ptr--); + } + template , int> = 0> + _CONSTEXPR20 nontrivial_pointer& operator+=(I diff) noexcept { + ptr += diff; + return *this; + } + template , int> = 0> + _CONSTEXPR20 nontrivial_pointer& operator-=(I diff) noexcept { + ptr -= diff; + return *this; + } + + template , int> = 0> + friend _CONSTEXPR20 nontrivial_pointer operator+(nontrivial_pointer ptr, I diff) noexcept { + return ptr += diff; + } + template , int> = 0> + friend _CONSTEXPR20 nontrivial_pointer operator+(I diff, nontrivial_pointer ptr) noexcept { + return ptr += diff; + } + friend _CONSTEXPR20 ptrdiff_t operator-(nontrivial_pointer lhs, nontrivial_pointer rhs) noexcept { + return lhs.ptr - rhs.ptr; + } + template , int> = 0> + friend _CONSTEXPR20 nontrivial_pointer operator-(nontrivial_pointer ptr, I diff) noexcept { + return ptr -= diff; + } + + friend _CONSTEXPR20 bool operator==(nontrivial_pointer lhs, nontrivial_pointer rhs) noexcept { + return lhs.ptr == rhs.ptr; + } + friend _CONSTEXPR20 bool operator==(nontrivial_pointer ptr, nullptr_t) noexcept { + return !ptr; + } + friend _CONSTEXPR20 bool operator==(nullptr_t, nontrivial_pointer ptr) noexcept { + return !ptr; + } + friend _CONSTEXPR20 bool operator!=(nontrivial_pointer lhs, nontrivial_pointer rhs) noexcept { + return lhs.ptr != rhs.ptr; + } + friend _CONSTEXPR20 bool operator!=(nontrivial_pointer ptr, nullptr_t) noexcept { + return static_cast(ptr); + } + friend _CONSTEXPR20 bool operator!=(nullptr_t, nontrivial_pointer ptr) noexcept { + return static_cast(ptr); + } + friend _CONSTEXPR20 bool operator<(nontrivial_pointer lhs, nontrivial_pointer rhs) noexcept { + return lhs.ptr < rhs.ptr; + } + friend _CONSTEXPR20 bool operator<=(nontrivial_pointer lhs, nontrivial_pointer rhs) noexcept { + return lhs.ptr <= rhs.ptr; + } + friend _CONSTEXPR20 bool operator>(nontrivial_pointer lhs, nontrivial_pointer rhs) noexcept { + return lhs.ptr > rhs.ptr; + } + friend _CONSTEXPR20 bool operator>=(nontrivial_pointer lhs, nontrivial_pointer rhs) noexcept { + return lhs.ptr >= rhs.ptr; + } +}; + +template > +struct impl_pointer_to { + // no pointer_to for void +}; + +template +struct impl_pointer_to { + static constexpr nontrivial_pointer pointer_to(T& r) noexcept { + return nontrivial_pointer(addressof(r)); + } +}; + +template +struct std::pointer_traits> : impl_pointer_to { + using pointer = nontrivial_pointer; + using element_type = T; + using difference_type = ptrdiff_t; + + template + using rebind = nontrivial_pointer; +}; + +template +struct std::iterator_traits> { + using iterator_category = random_access_iterator_tag; + using difference_type = ptrdiff_t; + using value_type = remove_const_t; + using reference = add_lvalue_reference_t; + using pointer = nontrivial_pointer; +}; + template class MyAlloc { private: @@ -49,6 +203,7 @@ class MyAlloc { } using value_type = T; + using pointer = nontrivial_pointer; using propagate_on_container_copy_assignment = POCCA; using propagate_on_container_move_assignment = POCMA; @@ -70,12 +225,12 @@ class MyAlloc { return equal_id() != other.equal_id(); } - [[nodiscard]] constexpr T* allocate(const size_t numElements) { - return allocator{}.allocate(numElements + equal_id()) + equal_id(); + [[nodiscard]] constexpr pointer allocate(const size_t numElements) { + return pointer(allocator{}.allocate(numElements + equal_id()) + equal_id()); } - constexpr void deallocate(T* const first, const size_t numElements) noexcept { - allocator{}.deallocate(first - equal_id(), numElements + equal_id()); + constexpr void deallocate(pointer const first, const size_t numElements) noexcept { + allocator{}.deallocate(_Unfancy(first - equal_id()), numElements + equal_id()); } }; @@ -307,7 +462,12 @@ _CONSTEXPR20 bool test_sequence() { test_sequence_copy_assign>(11, 22, 11); // POCCA, non-equal allocators test_sequence_copy_assign>(11, 22, 11); // POCCA, always-equal allocators - test_sequence_move_ctor(); +#if _HAS_CXX20 && !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-1888462 + if (!is_constant_evaluated()) +#endif // ^^^ workaround ^^^ + { + test_sequence_move_ctor(); + } test_sequence_move_alloc_ctor(11, 11); // equal allocators test_sequence_move_alloc_ctor(11, 22); // non-equal allocators @@ -559,7 +719,7 @@ void test_flist() { // NOTE: Having 4 elements of type char32_t bypasses the Small String Optimization. -void test_string_copy_ctor() { +_CONSTEXPR20 void test_string_copy_ctor() { basic_string, StationaryAlloc> src( {5, 10, 20, 30}, StationaryAlloc(11)); auto src_it = src.begin(); @@ -583,7 +743,7 @@ void test_string_copy_ctor() { assert(dst.get_allocator().id() == 11); } -void test_string_copy_alloc_ctor(const size_t id1, const size_t id2) { +_CONSTEXPR20 void test_string_copy_alloc_ctor(const size_t id1, const size_t id2) { basic_string, StationaryAlloc> src( {5, 10, 20, 30}, StationaryAlloc(id1)); auto src_it = src.begin(); @@ -608,7 +768,7 @@ void test_string_copy_alloc_ctor(const size_t id1, const size_t id2) { } template -void test_string_copy_assign(const size_t id1, const size_t id2, const size_t id3) { +_CONSTEXPR20 void test_string_copy_assign(const size_t id1, const size_t id2, const size_t id3) { basic_string, Alloc> src({5, 10, 20, 30}, Alloc(id1)); basic_string, Alloc> dst({0, 0, 0, 0}, Alloc(id2)); @@ -636,7 +796,7 @@ void test_string_copy_assign(const size_t id1, const size_t id2, const size_t id assert(dst.get_allocator().id() == id3); } -void test_string_copy_assign_pocca_sso() { +_CONSTEXPR20 void test_string_copy_assign_pocca_sso() { // GH-3862 fixed a bug where the POCCA codepath in basic_string's copy assignment operator mishandled // the scenario where the string on the right hand side has a large capacity but a small size - so while // the RHS has dynamically allocated memory, the LHS should activate the Small String Optimization. @@ -661,7 +821,7 @@ void test_string_copy_assign_pocca_sso() { assert(right == "yyyyyyy"); } -void test_string_move_ctor() { +_CONSTEXPR20 void test_string_move_ctor() { basic_string, StationaryAlloc> src( {5, 10, 20, 30}, StationaryAlloc(11)); auto it1 = src.begin(); @@ -690,7 +850,7 @@ void test_string_move_ctor() { assert(dst.get_allocator().id() == 11); } -void test_string_move_alloc_ctor(const size_t id1, const size_t id2) { +_CONSTEXPR20 void test_string_move_alloc_ctor(const size_t id1, const size_t id2) { basic_string, StationaryAlloc> src( {5, 10, 20, 30}, StationaryAlloc(id1)); auto it1 = src.begin(); @@ -721,7 +881,7 @@ void test_string_move_alloc_ctor(const size_t id1, const size_t id2) { } template -void test_string_move_assign(const size_t id1, const size_t id2, const size_t id3) { +_CONSTEXPR20 void test_string_move_assign(const size_t id1, const size_t id2, const size_t id3) { basic_string, Alloc> src({5, 10, 20, 30}, Alloc(id1)); basic_string, Alloc> dst({0, 0, 0, 0}, Alloc(id2)); @@ -755,7 +915,7 @@ void test_string_move_assign(const size_t id1, const size_t id2, const size_t id } template -void test_string_swap(const size_t id1, const size_t id2) { +_CONSTEXPR20 void test_string_swap(const size_t id1, const size_t id2) { basic_string, Alloc> src({5, 10, 20, 30}, Alloc(id1)); basic_string, Alloc> dst({6, 40, 50, 60}, Alloc(id2)); @@ -784,7 +944,7 @@ void test_string_swap(const size_t id1, const size_t id2) { assert(dst.get_allocator().id() == id1); } -void test_string() { +_CONSTEXPR20 bool test_string() { test_string_copy_ctor(); test_string_copy_alloc_ctor(11, 11); // equal allocators @@ -814,6 +974,8 @@ void test_string() { test_string_swap>(11, 11); // POCS, equal allocators test_string_swap>(11, 22); // POCS, non-equal allocators test_string_swap>(11, 22); // POCS, always-equal allocators + + return true; } @@ -1680,8 +1842,11 @@ int main() { test_sequence(); test_sequence(); test_sequence(); + #if _HAS_CXX20 static_assert(test_sequence()); + + static_assert(test_string()); #endif // _HAS_CXX20 test_flist();