Skip to content

Commit

Permalink
[libc++] Implement P1165R1 (Make stateful allocator propagation more …
Browse files Browse the repository at this point in the history
…consistent)

Reviewed By: Quuxplusone, ldionne, #libc

Spies: libcxx-commits

Differential Revision: https://reviews.llvm.org/D119112
  • Loading branch information
philnik777 committed Feb 17, 2022
1 parent a7b9af7 commit 1cfa485
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 14 deletions.
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes.rst
Expand Up @@ -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
-----------
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Papers.csv
Expand Up @@ -72,7 +72,7 @@
"`P1085R2 <https://wg21.link/P1085R2>`__","LWG","Should Span be Regular?","San Diego","|Complete|","8.0"
"`P1123R0 <https://wg21.link/P1123R0>`__","LWG","Editorial Guidance for merging P0019r8 and P0528r3","San Diego","* *",""
"`P1148R0 <https://wg21.link/P1148R0>`__","LWG","Cleaning up Clause 20","San Diego","* *",""
"`P1165R1 <https://wg21.link/P1165R1>`__","LWG","Make stateful allocator propagation more consistent for ``operator+(basic_string)``\ ","San Diego","* *",""
"`P1165R1 <https://wg21.link/P1165R1>`__","LWG","Make stateful allocator propagation more consistent for ``operator+(basic_string)``\ ","San Diego","|Complete|","15.0"
"`P1209R0 <https://wg21.link/P1209R0>`__","LWG","Adopt Consistent Container Erasure from Library Fundamentals 2 for C++20","San Diego","|Complete|","8.0"
"`P1210R0 <https://wg21.link/P1210R0>`__","LWG","Completing the Rebase of Library Fundamentals, Version 3, Working Draft","San Diego","* *",""
"`P1236R1 <https://wg21.link/P1236R1>`__","CWG","Alternative Wording for P0907R4 Signed Integers are Two's Complement","San Diego","* *",""
Expand Down
31 changes: 18 additions & 13 deletions libcxx/include/string
Expand Up @@ -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;
Expand All @@ -4173,9 +4174,10 @@ template<class _CharT, class _Traits, class _Allocator>
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;
Expand All @@ -4185,8 +4187,9 @@ template<class _CharT, class _Traits, class _Allocator>
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;
Expand All @@ -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;
Expand All @@ -4209,8 +4213,9 @@ template<class _CharT, class _Traits, class _Allocator>
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;
Expand Down
@@ -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 <cassert>
#include <string>

#include "test_macros.h"

template <class T>
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 <class U>
constexpr soccc_allocator(const soccc_allocator<U>& a) : soccc_count(a.soccc_count) {}

constexpr T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
constexpr void deallocate(T* p, std::size_t s) { std::allocator<T>().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 <class CharT>
bool test() {
using S = std::basic_string<CharT, std::char_traits<CharT>, soccc_allocator<CharT>>;
{
int soccc_lhs = 0;
int soccc_rhs = 0;
S lhs(soccc_allocator<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<CharT>{&soccc_lhs});
S rhs(soccc_allocator<CharT>{&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<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif
#if TEST_STD_VER > 17
// static_assert(test<char>());
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
// static_assert(test<wchar_t>());
#endif
#endif

return 0;
}

0 comments on commit 1cfa485

Please sign in to comment.