diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst index c57a7ded2e4f9..1e24ad6b3c550 100644 --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -39,6 +39,7 @@ New Features ------------ - Implemented P0627R6 (Function to mark unreachable code) + - Implemented P1165R1 (Make stateful allocator propagation more consistent for operator+(basic_string)) API Changes ----------- diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv index cf983a4b4829f..438e1f07b896c 100644 --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -72,7 +72,7 @@ "`P1085R2 `__","LWG","Should Span be Regular?","San Diego","|Complete|","8.0" "`P1123R0 `__","LWG","Editorial Guidance for merging P0019r8 and P0528r3","San Diego","* *","" "`P1148R0 `__","LWG","Cleaning up Clause 20","San Diego","* *","" -"`P1165R1 `__","LWG","Make stateful allocator propagation more consistent for ``operator+(basic_string)``\ ","San Diego","* *","" +"`P1165R1 `__","LWG","Make stateful allocator propagation more consistent for ``operator+(basic_string)``\ ","San Diego","|Complete|","15.0" "`P1209R0 `__","LWG","Adopt Consistent Container Erasure from Library Fundamentals 2 for C++20","San Diego","|Complete|","8.0" "`P1210R0 `__","LWG","Completing the Rebase of Library Fundamentals, Version 3, Working Draft","San Diego","* *","" "`P1236R1 `__","CWG","Alternative Wording for P0907R4 Signed Integers are Two's Complement","San Diego","* *","" diff --git a/libcxx/include/string b/libcxx/include/string index 892df770756e2..d33f6edf97e9a 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -4161,9 +4161,10 @@ basic_string<_CharT, _Traits, _Allocator> operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, const basic_string<_CharT, _Traits, _Allocator>& __rhs) { - basic_string<_CharT, _Traits, _Allocator> __r(__lhs.get_allocator()); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __lhs_sz = __lhs.size(); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __rhs_sz = __rhs.size(); + using _String = basic_string<_CharT, _Traits, _Allocator>; + _String __r(_String::__alloc_traits::select_on_container_copy_construction(__lhs.get_allocator())); + typename _String::size_type __lhs_sz = __lhs.size(); + typename _String::size_type __rhs_sz = __rhs.size(); __r.__init(__lhs.data(), __lhs_sz, __lhs_sz + __rhs_sz); __r.append(__rhs.data(), __rhs_sz); return __r; @@ -4173,9 +4174,10 @@ template basic_string<_CharT, _Traits, _Allocator> operator+(const _CharT* __lhs , const basic_string<_CharT,_Traits,_Allocator>& __rhs) { - basic_string<_CharT, _Traits, _Allocator> __r(__rhs.get_allocator()); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __lhs_sz = _Traits::length(__lhs); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __rhs_sz = __rhs.size(); + using _String = basic_string<_CharT, _Traits, _Allocator>; + _String __r(_String::__alloc_traits::select_on_container_copy_construction(__rhs.get_allocator())); + typename _String::size_type __lhs_sz = _Traits::length(__lhs); + typename _String::size_type __rhs_sz = __rhs.size(); __r.__init(__lhs, __lhs_sz, __lhs_sz + __rhs_sz); __r.append(__rhs.data(), __rhs_sz); return __r; @@ -4185,8 +4187,9 @@ template basic_string<_CharT, _Traits, _Allocator> operator+(_CharT __lhs, const basic_string<_CharT,_Traits,_Allocator>& __rhs) { - basic_string<_CharT, _Traits, _Allocator> __r(__rhs.get_allocator()); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __rhs_sz = __rhs.size(); + using _String = basic_string<_CharT, _Traits, _Allocator>; + _String __r(_String::__alloc_traits::select_on_container_copy_construction(__rhs.get_allocator())); + typename _String::size_type __rhs_sz = __rhs.size(); __r.__init(&__lhs, 1, 1 + __rhs_sz); __r.append(__rhs.data(), __rhs_sz); return __r; @@ -4197,9 +4200,10 @@ inline basic_string<_CharT, _Traits, _Allocator> operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, const _CharT* __rhs) { - basic_string<_CharT, _Traits, _Allocator> __r(__lhs.get_allocator()); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __lhs_sz = __lhs.size(); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __rhs_sz = _Traits::length(__rhs); + using _String = basic_string<_CharT, _Traits, _Allocator>; + _String __r(_String::__alloc_traits::select_on_container_copy_construction(__lhs.get_allocator())); + typename _String::size_type __lhs_sz = __lhs.size(); + typename _String::size_type __rhs_sz = _Traits::length(__rhs); __r.__init(__lhs.data(), __lhs_sz, __lhs_sz + __rhs_sz); __r.append(__rhs, __rhs_sz); return __r; @@ -4209,8 +4213,9 @@ template basic_string<_CharT, _Traits, _Allocator> operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, _CharT __rhs) { - basic_string<_CharT, _Traits, _Allocator> __r(__lhs.get_allocator()); - typename basic_string<_CharT, _Traits, _Allocator>::size_type __lhs_sz = __lhs.size(); + using _String = basic_string<_CharT, _Traits, _Allocator>; + _String __r(_String::__alloc_traits::select_on_container_copy_construction(__lhs.get_allocator())); + typename _String::size_type __lhs_sz = __lhs.size(); __r.__init(__lhs.data(), __lhs_sz, __lhs_sz + 1); __r.push_back(__rhs); return __r; diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/allocator_propagation.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/allocator_propagation.pass.cpp new file mode 100644 index 0000000000000..e25bff5fd7ad7 --- /dev/null +++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/allocator_propagation.pass.cpp @@ -0,0 +1,200 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// This test ensures that we properly propagate allocators, per https://wg21.link/p1165r1 + +#include +#include + +#include "test_macros.h" + +template +class soccc_allocator { + int* soccc_count; + int self_soccc_count; + +public: + using value_type = T; + + constexpr explicit soccc_allocator(int* soccc_count_, int self_coccc_count_ = 0) + : soccc_count(soccc_count_), self_soccc_count(self_coccc_count_) {} + + template + constexpr soccc_allocator(const soccc_allocator& a) : soccc_count(a.soccc_count) {} + + constexpr T* allocate(std::size_t n) { return std::allocator().allocate(n); } + constexpr void deallocate(T* p, std::size_t s) { std::allocator().deallocate(p, s); } + + constexpr soccc_allocator select_on_container_copy_construction() const { + *soccc_count += 1; + return soccc_allocator(soccc_count, self_soccc_count + 1); + } + + constexpr auto get_soccc() { return soccc_count; } + constexpr auto get_self_soccc() { return self_soccc_count; } + + typedef std::true_type propagate_on_container_copy_assignment; + typedef std::true_type propagate_on_container_move_assignment; + typedef std::true_type propagate_on_container_swap; +}; + +template +bool test() { + using S = std::basic_string, soccc_allocator>; + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = lhs + rhs; + assert(r.get_allocator().get_soccc() == &soccc_lhs); + assert(r.get_allocator().get_self_soccc() == 1); + assert(soccc_lhs == 1); + assert(soccc_rhs == 0); + } + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = lhs + std::move(rhs); + assert(r.get_allocator().get_soccc() == &soccc_rhs); + assert(r.get_allocator().get_self_soccc() == 0); + assert(soccc_lhs == 0); + assert(soccc_rhs == 0); + } + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = std::move(lhs) + rhs; + assert(r.get_allocator().get_soccc() == &soccc_lhs); + assert(r.get_allocator().get_self_soccc() == 0); + assert(soccc_lhs == 0); + assert(soccc_rhs == 0); + } + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = std::move(lhs) + std::move(rhs); + assert(r.get_allocator().get_soccc() == &soccc_lhs); + assert(r.get_allocator().get_self_soccc() == 0); + assert(soccc_lhs == 0); + assert(soccc_rhs == 0); + } + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = lhs + rhs.data(); + assert(r.get_allocator().get_soccc() == &soccc_lhs); + assert(r.get_allocator().get_self_soccc() == 1); + assert(soccc_lhs == 1); + assert(soccc_rhs == 0); + } + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = lhs + rhs[0]; + assert(r.get_allocator().get_soccc() == &soccc_lhs); + assert(r.get_allocator().get_self_soccc() == 1); + assert(soccc_lhs == 1); + assert(soccc_rhs == 0); + } + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = std::move(lhs) + rhs.data(); + assert(r.get_allocator().get_soccc() == &soccc_lhs); + assert(r.get_allocator().get_self_soccc() == 0); + assert(soccc_lhs == 0); + assert(soccc_rhs == 0); + } + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = std::move(lhs) + rhs[0]; + assert(r.get_allocator().get_soccc() == &soccc_lhs); + assert(r.get_allocator().get_self_soccc() == 0); + assert(soccc_lhs == 0); + assert(soccc_rhs == 0); + } + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = lhs.data() + rhs; + assert(r.get_allocator().get_soccc() == &soccc_rhs); + assert(r.get_allocator().get_self_soccc() == 1); + assert(soccc_lhs == 0); + assert(soccc_rhs == 1); + } + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = lhs[0] + rhs; + assert(r.get_allocator().get_soccc() == &soccc_rhs); + assert(r.get_allocator().get_self_soccc() == 1); + assert(soccc_lhs == 0); + assert(soccc_rhs == 1); + } + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = lhs.data() + std::move(rhs); + assert(r.get_allocator().get_soccc() == &soccc_rhs); + assert(r.get_allocator().get_self_soccc() == 0); + assert(soccc_lhs == 0); + assert(soccc_rhs == 0); + } + { + int soccc_lhs = 0; + int soccc_rhs = 0; + S lhs(soccc_allocator{&soccc_lhs}); + S rhs(soccc_allocator{&soccc_rhs}); + auto r = lhs[0] + std::move(rhs); + assert(r.get_allocator().get_soccc() == &soccc_rhs); + assert(r.get_allocator().get_self_soccc() == 0); + assert(soccc_lhs == 0); + assert(soccc_rhs == 0); + } + + return true; +} + +int main(int, char**) { + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif +#if TEST_STD_VER > 17 + // static_assert(test()); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // static_assert(test()); +#endif +#endif + + return 0; +}