[v3] Make stateful allocator propagation more consistent for operator+(basic_string) (P1165R1)

Message ID CAAcHv8d3exoTb1AEQZo3CPksgBST+gFX6eZ_21t4Z0uzsNwcpw@mail.gmail.com
State New
Headers show
Series
  • [v3] Make stateful allocator propagation more consistent for operator+(basic_string) (P1165R1)
Related show

Commit Message

Nina Dinka Ranns May 2, 2019, 4:54 p.m.
Tested on Linux x86_64
Make stateful allocator propagation more consistent for
operator+(basic_string) (P1165R1)

2019-05-01  Nina Dinka Ranns  <dinka.ranns@gmail.com>
        Make stateful allocator propagation more consistent for
operator+(basic_string) (P1165R1)
        * include/bits/basic_string.tcc:
         (operator+(const _CharT*, const basic_string&)) : Changed
resulting allocator to be SOCCC on the second parameter's allocator
         (operator+(_CharT, const basic_string&)) : Likewise
         (operator+(basic_string&&,basic_string&&) : Changed resulting
allocator to always be the one from the first parameter
        * testsuite/21_strings/basic_string/allocator/char/operator_plus.cc: New
        * testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc
: New

Comments

Jonathan Wakely May 3, 2019, 10:25 p.m. | #1
On 02/05/19 17:54 +0100, Nina Dinka Ranns wrote:
>Tested on Linux x86_64

>Make stateful allocator propagation more consistent for

>operator+(basic_string) (P1165R1)

>

>2019-05-01  Nina Dinka Ranns  <dinka.ranns@gmail.com>

>        Make stateful allocator propagation more consistent for

>operator+(basic_string) (P1165R1)

>        * include/bits/basic_string.tcc:

>         (operator+(const _CharT*, const basic_string&)) : Changed

>resulting allocator to be SOCCC on the second parameter's allocator

>         (operator+(_CharT, const basic_string&)) : Likewise


The next change is in include/bits/basic_string.h but that file isn't
named here:

>         (operator+(basic_string&&,basic_string&&) : Changed resulting

>allocator to always be the one from the first parameter

>        * testsuite/21_strings/basic_string/allocator/char/operator_plus.cc: New

>        * testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc

>: New


>Index: libstdc++-v3/include/bits/basic_string.h

>===================================================================

>--- libstdc++-v3/include/bits/basic_string.h	(revision 270655)

>+++ libstdc++-v3/include/bits/basic_string.h	(working copy)

>@@ -6096,13 +6096,7 @@

>     inline basic_string<_CharT, _Traits, _Alloc>

>     operator+(basic_string<_CharT, _Traits, _Alloc>&& __lhs,

> 	      basic_string<_CharT, _Traits, _Alloc>&& __rhs)

>-    {

>-      const auto __size = __lhs.size() + __rhs.size();

>-      const bool __cond = (__size > __lhs.capacity()

>-			   && __size <= __rhs.capacity());

>-      return __cond ? std::move(__rhs.insert(0, __lhs))

>-	            : std::move(__lhs.append(__rhs));

>-    }

>+    { return std::move(__lhs.append(__rhs)); }


I know we already discussed this off-list and said just using the LHS
is correct here, even if though it means losing the optimization, but
something occured to me today.

If the allocator traits say is_always_equal is true, or if the two
allocators happen to be equal, then we could still do this
optimization to use the RHS string if doing so will avoid a
reallocation.

So something like this (untested):

    {
      using _Alloc_traits = allocator_traits<_Alloc>;
      bool __use_rhs = false;
      if _GLIBCXX17_CONSTEXPR (_Alloc_traits::is_always_equal())
        __use_rhs = true;
      else if (__lhs.get_allocator() == __rhs.get_allocator())
        __use_rhs = true;
      if (__use_rhs)
        {
          const auto __size = __lhs.size() + __rhs.size();
          if (__size > __lhs.capacity() && __size <= __rhs.capacity())
            return std::move(__rhs.insert(0, __lhs));
        }
      return std::move(__lhs.append(__rhs));
    }

I believe that should pass all your existing tests, because it will
only use the RHS string when the allocators are equal, and so you
can't detect whether the allocator from the LHS or RHS was used.

Otherwise the patch looks good.
Nina Dinka Ranns May 7, 2019, 11:01 a.m. | #2
Ack. I've put the use of _Alloc_traits::is_always_equal within #if
__cplusplus >= 201703L block since it is officially a C++17 feature.
Let me know if you think that's an overkill.
New changelog below. I didn't change the description of
operator+(basic_string&&,basic_string&&) as it's still technically
always resulting in an allocator from the first parameter.


2019-05-01  Nina Dinka Ranns  <dinka.ranns@gmail.com>
        Make stateful allocator propagation more consistent
foroperator+(basic_string) (P1165R1)
        * include/bits/basic_string.h:
        (operator+(basic_string&&,basic_string&&) : Changed resulting
allocator to always be the one from the first parameter
        * include/bits/basic_string.tcc:
        (operator+(const _CharT*, const basic_string&)) : Changed
resulting allocator to be SOCCC on the second parameter's allocator
        (operator+(_CharT, const basic_string&)) : Likewise
        * testsuite/21_strings/basic_string/allocator/char/operator_plus.cc: New
        * testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc:
New
Index: libstdc++-v3/include/bits/basic_string.h
===================================================================
--- libstdc++-v3/include/bits/basic_string.h	(revision 270655)
+++ libstdc++-v3/include/bits/basic_string.h	(working copy)
@@ -6097,11 +6097,22 @@
     operator+(basic_string<_CharT, _Traits, _Alloc>&& __lhs,
 	      basic_string<_CharT, _Traits, _Alloc>&& __rhs)
     {
-      const auto __size = __lhs.size() + __rhs.size();
-      const bool __cond = (__size > __lhs.capacity()
-			   && __size <= __rhs.capacity());
-      return __cond ? std::move(__rhs.insert(0, __lhs))
-	            : std::move(__lhs.append(__rhs));
+	  using _Alloc_traits = allocator_traits<_Alloc>;
+	  bool __use_rhs = false;
+#if __cplusplus >= 201703L
+      if _GLIBCXX17_CONSTEXPR (typename _Alloc_traits::is_always_equal{})
+	    __use_rhs = true;
+	  else
+#endif
+      if (__lhs.get_allocator() == __rhs.get_allocator())
+	    __use_rhs = true;
+	  if (__use_rhs)
+	  {
+		const auto __size = __lhs.size() + __rhs.size();
+		if (__size > __lhs.capacity() && __size <= __rhs.capacity())
+		  return std::move(__rhs.insert(0, __lhs));
+	  }
+	  return std::move(__lhs.append(__rhs));
     }
 
   template<typename _CharT, typename _Traits, typename _Alloc>
Index: libstdc++-v3/include/bits/basic_string.tcc
===================================================================
--- libstdc++-v3/include/bits/basic_string.tcc	(revision 270655)
+++ libstdc++-v3/include/bits/basic_string.tcc	(working copy)
@@ -1161,8 +1161,12 @@
       __glibcxx_requires_string(__lhs);
       typedef basic_string<_CharT, _Traits, _Alloc> __string_type;
       typedef typename __string_type::size_type	  __size_type;
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
+	rebind<_CharT>::other _Char_alloc_type;
+      typedef __gnu_cxx::__alloc_traits<_Char_alloc_type> _Alloc_traits;
       const __size_type __len = _Traits::length(__lhs);
-      __string_type __str;
+      __string_type __str(_Alloc_traits::_S_select_on_copy(
+          __rhs.get_allocator()));
       __str.reserve(__len + __rhs.size());
       __str.append(__lhs, __len);
       __str.append(__rhs);
@@ -1175,7 +1179,11 @@
     {
       typedef basic_string<_CharT, _Traits, _Alloc> __string_type;
       typedef typename __string_type::size_type	  __size_type;
-      __string_type __str;
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
+	rebind<_CharT>::other _Char_alloc_type;
+      typedef __gnu_cxx::__alloc_traits<_Char_alloc_type> _Alloc_traits;
+      __string_type __str(_Alloc_traits::_S_select_on_copy(
+          __rhs.get_allocator()));
       const __size_type __len = __rhs.size();
       __str.reserve(__len + 1);
       __str.append(__size_type(1), __lhs);
Index: libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc
===================================================================
--- libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc	(nonexistent)
+++ libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc	(working copy)
@@ -0,0 +1,151 @@
+// 2019-04-30  Nina Dinka Ranns  <dinka.ranns@gmail.com>
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+ 
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+ 
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++11 } }
+// COW strings don't support C++11 allocators:
+// { dg-require-effective-target cxx11-abi }
+
+#include <string>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
+ 
+using C = char;
+using traits = std::char_traits<C>;
+
+using __gnu_test::propagating_allocator;
+
+void test01()
+{
+  typedef propagating_allocator<C, true> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1("something",alloc_type(1));
+  test_type v2("something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() == 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3("something", alloc_type(3));
+  test_type v4("something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5("something", alloc_type(5));
+  auto r5 = v5 + "str";
+  VERIFY(r5.get_allocator().get_personality() == 5);
+
+  auto r6 = v5 + 'c';
+  VERIFY(r6.get_allocator().get_personality() == 5);
+
+  auto r7 = std::move(v5) + "str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6("something", alloc_type(6));
+  auto r8 = std::move(v6) + 'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7("something", alloc_type(7));
+  auto r9 = "str" + v7;
+  VERIFY(r9.get_allocator().get_personality() == 7);
+
+  auto r10 = 'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() == 7);
+
+  auto r11 = "str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8("something", alloc_type(8));
+  auto r12 = 'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+void test02()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1("something",alloc_type(1));
+  test_type v2("something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() != 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3("something", alloc_type(3));
+  test_type v4("something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5("something", alloc_type(5));
+  auto r5 = v5 + "str";
+  VERIFY(r5.get_allocator().get_personality() != 5);
+
+  auto r6 = v5 + 'c';
+  VERIFY(r6.get_allocator().get_personality() != 5);
+
+  auto r7 = std::move(v5) + "str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6("something", alloc_type(6));
+  auto r8 = std::move(v6) + 'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7("something", alloc_type(7));
+  auto r9 = "str" + v7;
+  VERIFY(r9.get_allocator().get_personality() != 7);
+
+  auto r10 = 'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() != 7);
+
+  auto r11 = "str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8("something", alloc_type(8));
+  auto r12 = 'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+void test03()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1("s",alloc_type(1));
+  v1.resize(10);
+  v1.shrink_to_fit();
+  test_type v2(10000,'x',alloc_type(2));
+  v2.reserve(10010);
+
+  auto r=std::move(v1)+std::move(v2);
+  VERIFY(r.get_allocator().get_personality() == 1);
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  return 0;
+}
Index: libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc
===================================================================
--- libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc	(nonexistent)
+++ libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc	(working copy)
@@ -0,0 +1,152 @@
+// 2019-04-30  Nina Dinka Ranns  <dinka.ranns@gmail.com>
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+ 
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+ 
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++11 } }
+// COW strings don't support C++11 allocators:
+// { dg-require-effective-target cxx11-abi }
+
+#include <string>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
+ 
+using C = wchar_t;
+using traits = std::char_traits<C>;
+
+using __gnu_test::propagating_allocator;
+
+void test01()
+{
+  typedef propagating_allocator<C, true> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1(L"something",alloc_type(1));
+  test_type v2(L"something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() == 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3(L"something", alloc_type(3));
+  test_type v4(L"something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5(L"something", alloc_type(5));
+  auto r5 = v5 + L"str";
+  VERIFY(r5.get_allocator().get_personality() == 5);
+
+  auto r6 = v5 + L'c';
+  VERIFY(r6.get_allocator().get_personality() == 5);
+
+  auto r7 = std::move(v5) + L"str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6(L"something", alloc_type(6));
+  auto r8 = std::move(v6) + L'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7(L"something", alloc_type(7));
+  auto r9 = L"str" + v7;
+  VERIFY(r9.get_allocator().get_personality() == 7);
+
+  auto r10 = L'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() == 7);
+
+  auto r11 = L"str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8(L"something", alloc_type(8));
+  auto r12 = L'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+
+void test02()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1(L"something",alloc_type(1));
+  test_type v2(L"something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() != 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3(L"something", alloc_type(3));
+  test_type v4(L"something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5(L"something", alloc_type(5));
+  auto r5 = v5 + L"str";
+  VERIFY(r5.get_allocator().get_personality() != 5);
+
+  auto r6 = v5 + L'c';
+  VERIFY(r6.get_allocator().get_personality() != 5);
+
+  auto r7 = std::move(v5) + L"str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6(L"something", alloc_type(6));
+  auto r8 = std::move(v6) + L'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7(L"something", alloc_type(7));
+  auto r9 = L"str" + v7;
+  VERIFY(r9.get_allocator().get_personality() != 7);
+
+  auto r10 = L'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() != 7);
+
+  auto r11 = L"str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8(L"something", alloc_type(8));
+  auto r12 = L'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+void test03()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1(L"s",alloc_type(1));
+  v1.resize(10);
+  v1.shrink_to_fit();
+  test_type v2(10000,L'x',alloc_type(2));
+  v2.reserve(10010);
+
+  auto r=std::move(v1)+std::move(v2);
+  VERIFY(r.get_allocator().get_personality() == 1);
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  return 0;
+}
Jonathan Wakely May 7, 2019, 11:22 a.m. | #3
On 07/05/19 12:01 +0100, Nina Dinka Ranns wrote:
>Ack. I've put the use of _Alloc_traits::is_always_equal within #if

>__cplusplus >= 201703L block since it is officially a C++17 feature.

>Let me know if you think that's an overkill.


Yes, that's overkill, we provide is_always_equal unconditionally from
C++11 onwards (to avoid ODR violations in code using different -std
options). Since it's defined fo C++11 we can use it for C++11.

I can remove that #if and test and commit the result for you though,
no need for another revision of the patch.

>New changelog below. I didn't change the description of

>operator+(basic_string&&,basic_string&&) as it's still technically

>always resulting in an allocator from the first parameter.


Yes, that looks fine. Thanks!
Nina Dinka Ranns May 7, 2019, 11:26 a.m. | #4
On Tue, 7 May 2019 at 12:22, Jonathan Wakely <jwakely@redhat.com> wrote:
>

> I can remove that #if and test and commit the result for you though,

> no need for another revision of the patch.

Thanks ! :)
Best,
Nina
Jonathan Wakely May 7, 2019, 11:39 a.m. | #5
On 07/05/19 12:22 +0100, Jonathan Wakely wrote:
>On 07/05/19 12:01 +0100, Nina Dinka Ranns wrote:

>>Ack. I've put the use of _Alloc_traits::is_always_equal within #if

>>__cplusplus >= 201703L block since it is officially a C++17 feature.

>>Let me know if you think that's an overkill.

>

>Yes, that's overkill, we provide is_always_equal unconditionally from

>C++11 onwards (to avoid ODR violations in code using different -std

>options). Since it's defined fo C++11 we can use it for C++11.


For the record, we treat is_always_equal as a DR, resolving LWG 2108
(which I submitted, because the runtime conditionals caused by not
having it were making me cry).

>I can remove that #if and test and commit the result for you though,

>no need for another revision of the patch.

>

>>New changelog below. I didn't change the description of

>>operator+(basic_string&&,basic_string&&) as it's still technically

>>always resulting in an allocator from the first parameter.

>

>Yes, that looks fine. Thanks!

>
Jonathan Wakely May 7, 2019, 3:30 p.m. | #6
On 07/05/19 12:22 +0100, Jonathan Wakely wrote:
>On 07/05/19 12:01 +0100, Nina Dinka Ranns wrote:

>>Ack. I've put the use of _Alloc_traits::is_always_equal within #if

>>__cplusplus >= 201703L block since it is officially a C++17 feature.

>>Let me know if you think that's an overkill.

>

>Yes, that's overkill, we provide is_always_equal unconditionally from

>C++11 onwards (to avoid ODR violations in code using different -std

>options). Since it's defined fo C++11 we can use it for C++11.

>

>I can remove that #if and test and commit the result for you though,

>no need for another revision of the patch.

>

>>New changelog below. I didn't change the description of

>>operator+(basic_string&&,basic_string&&) as it's still technically

>>always resulting in an allocator from the first parameter.

>

>Yes, that looks fine. Thanks!


I realised that for !_GLIBCXX_USE_CXX11_ABI we can always use the
optimization, because we don't support stateful allocators for the old
std::string ABI. So I adjusted the patch slightly for that.

I've attached what I'll commit.

Tested powerpc64le-linux (old and new ABI), committing to trunk.
commit 409ae90af9472e649df14c8d8147225d1cfa20ea
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue May 7 15:21:47 2019 +0100

    Make allocator propagation more consistent for operator+(basic_string) (P1165R1)
    
    2019-05-01  Nina Dinka Ranns  <dinka.ranns@gmail.com>
    
            Make allocator propagation more consistent for
            operator+(basic_string) (P1165R1)
            * include/bits/basic_string.h
            (operator+(basic_string&&, basic_string&&): Changed resulting
            allocator to always be the one from the first parameter.
            * include/bits/basic_string.tcc
            (operator+(const _CharT*, const basic_string&)): Changed
            resulting allocator to be SOCCC on the second parameter's allocator.
            (operator+(_CharT, const basic_string&)): Likewise.
            * testsuite/21_strings/basic_string/allocator/char/operator_plus.cc:
            New.
            * testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc:
            New.

diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h
index 4a6332a8968..5ebe86bad7d 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -6096,11 +6096,21 @@ _GLIBCXX_END_NAMESPACE_CXX11
     operator+(basic_string<_CharT, _Traits, _Alloc>&& __lhs,
 	      basic_string<_CharT, _Traits, _Alloc>&& __rhs)
     {
-      const auto __size = __lhs.size() + __rhs.size();
-      const bool __cond = (__size > __lhs.capacity()
-			   && __size <= __rhs.capacity());
-      return __cond ? std::move(__rhs.insert(0, __lhs))
-	            : std::move(__lhs.append(__rhs));
+#if _GLIBCXX_USE_CXX11_ABI
+      using _Alloc_traits = allocator_traits<_Alloc>;
+      bool __use_rhs = false;
+      if _GLIBCXX17_CONSTEXPR (typename _Alloc_traits::is_always_equal{})
+	__use_rhs = true;
+      else if (__lhs.get_allocator() == __rhs.get_allocator())
+	__use_rhs = true;
+      if (__use_rhs)
+#endif
+	{
+	  const auto __size = __lhs.size() + __rhs.size();
+	  if (__size > __lhs.capacity() && __size <= __rhs.capacity())
+	    return std::move(__rhs.insert(0, __lhs));
+	}
+      return std::move(__lhs.append(__rhs));
     }
 
   template<typename _CharT, typename _Traits, typename _Alloc>
diff --git a/libstdc++-v3/include/bits/basic_string.tcc b/libstdc++-v3/include/bits/basic_string.tcc
index 314b8fe207f..e2315cb1467 100644
--- a/libstdc++-v3/include/bits/basic_string.tcc
+++ b/libstdc++-v3/include/bits/basic_string.tcc
@@ -1161,8 +1161,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       __glibcxx_requires_string(__lhs);
       typedef basic_string<_CharT, _Traits, _Alloc> __string_type;
       typedef typename __string_type::size_type	  __size_type;
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
+	rebind<_CharT>::other _Char_alloc_type;
+      typedef __gnu_cxx::__alloc_traits<_Char_alloc_type> _Alloc_traits;
       const __size_type __len = _Traits::length(__lhs);
-      __string_type __str;
+      __string_type __str(_Alloc_traits::_S_select_on_copy(
+          __rhs.get_allocator()));
       __str.reserve(__len + __rhs.size());
       __str.append(__lhs, __len);
       __str.append(__rhs);
@@ -1175,7 +1179,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     {
       typedef basic_string<_CharT, _Traits, _Alloc> __string_type;
       typedef typename __string_type::size_type	  __size_type;
-      __string_type __str;
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
+	rebind<_CharT>::other _Char_alloc_type;
+      typedef __gnu_cxx::__alloc_traits<_Char_alloc_type> _Alloc_traits;
+      __string_type __str(_Alloc_traits::_S_select_on_copy(
+          __rhs.get_allocator()));
       const __size_type __len = __rhs.size();
       __str.reserve(__len + 1);
       __str.append(__size_type(1), __lhs);
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc
new file mode 100644
index 00000000000..c4693378d0b
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc
@@ -0,0 +1,151 @@
+// 2019-04-30  Nina Dinka Ranns  <dinka.ranns@gmail.com>
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++11 } }
+// COW strings don't support C++11 allocators:
+// { dg-require-effective-target cxx11-abi }
+
+#include <string>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
+
+using C = char;
+using traits = std::char_traits<C>;
+
+using __gnu_test::propagating_allocator;
+
+void test01()
+{
+  typedef propagating_allocator<C, true> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1("something",alloc_type(1));
+  test_type v2("something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() == 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3("something", alloc_type(3));
+  test_type v4("something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5("something", alloc_type(5));
+  auto r5 = v5 + "str";
+  VERIFY(r5.get_allocator().get_personality() == 5);
+
+  auto r6 = v5 + 'c';
+  VERIFY(r6.get_allocator().get_personality() == 5);
+
+  auto r7 = std::move(v5) + "str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6("something", alloc_type(6));
+  auto r8 = std::move(v6) + 'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7("something", alloc_type(7));
+  auto r9 = "str" + v7;
+  VERIFY(r9.get_allocator().get_personality() == 7);
+
+  auto r10 = 'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() == 7);
+
+  auto r11 = "str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8("something", alloc_type(8));
+  auto r12 = 'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+void test02()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1("something",alloc_type(1));
+  test_type v2("something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() != 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3("something", alloc_type(3));
+  test_type v4("something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5("something", alloc_type(5));
+  auto r5 = v5 + "str";
+  VERIFY(r5.get_allocator().get_personality() != 5);
+
+  auto r6 = v5 + 'c';
+  VERIFY(r6.get_allocator().get_personality() != 5);
+
+  auto r7 = std::move(v5) + "str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6("something", alloc_type(6));
+  auto r8 = std::move(v6) + 'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7("something", alloc_type(7));
+  auto r9 = "str" + v7;
+  VERIFY(r9.get_allocator().get_personality() != 7);
+
+  auto r10 = 'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() != 7);
+
+  auto r11 = "str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8("something", alloc_type(8));
+  auto r12 = 'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+void test03()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1("s",alloc_type(1));
+  v1.resize(10);
+  v1.shrink_to_fit();
+  test_type v2(10000,'x',alloc_type(2));
+  v2.reserve(10010);
+
+  auto r=std::move(v1)+std::move(v2);
+  VERIFY(r.get_allocator().get_personality() == 1);
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc
new file mode 100644
index 00000000000..3e1d0c3fbad
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc
@@ -0,0 +1,152 @@
+// 2019-04-30  Nina Dinka Ranns  <dinka.ranns@gmail.com>
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++11 } }
+// COW strings don't support C++11 allocators:
+// { dg-require-effective-target cxx11-abi }
+
+#include <string>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
+
+using C = wchar_t;
+using traits = std::char_traits<C>;
+
+using __gnu_test::propagating_allocator;
+
+void test01()
+{
+  typedef propagating_allocator<C, true> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1(L"something",alloc_type(1));
+  test_type v2(L"something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() == 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3(L"something", alloc_type(3));
+  test_type v4(L"something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5(L"something", alloc_type(5));
+  auto r5 = v5 + L"str";
+  VERIFY(r5.get_allocator().get_personality() == 5);
+
+  auto r6 = v5 + L'c';
+  VERIFY(r6.get_allocator().get_personality() == 5);
+
+  auto r7 = std::move(v5) + L"str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6(L"something", alloc_type(6));
+  auto r8 = std::move(v6) + L'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7(L"something", alloc_type(7));
+  auto r9 = L"str" + v7;
+  VERIFY(r9.get_allocator().get_personality() == 7);
+
+  auto r10 = L'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() == 7);
+
+  auto r11 = L"str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8(L"something", alloc_type(8));
+  auto r12 = L'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+
+void test02()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1(L"something",alloc_type(1));
+  test_type v2(L"something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() != 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3(L"something", alloc_type(3));
+  test_type v4(L"something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5(L"something", alloc_type(5));
+  auto r5 = v5 + L"str";
+  VERIFY(r5.get_allocator().get_personality() != 5);
+
+  auto r6 = v5 + L'c';
+  VERIFY(r6.get_allocator().get_personality() != 5);
+
+  auto r7 = std::move(v5) + L"str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6(L"something", alloc_type(6));
+  auto r8 = std::move(v6) + L'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7(L"something", alloc_type(7));
+  auto r9 = L"str" + v7;
+  VERIFY(r9.get_allocator().get_personality() != 7);
+
+  auto r10 = L'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() != 7);
+
+  auto r11 = L"str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8(L"something", alloc_type(8));
+  auto r12 = L'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+void test03()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1(L"s",alloc_type(1));
+  v1.resize(10);
+  v1.shrink_to_fit();
+  test_type v2(10000,L'x',alloc_type(2));
+  v2.reserve(10010);
+
+  auto r=std::move(v1)+std::move(v2);
+  VERIFY(r.get_allocator().get_personality() == 1);
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  return 0;
+}

Patch

Index: libstdc++-v3/include/bits/basic_string.h
===================================================================
--- libstdc++-v3/include/bits/basic_string.h	(revision 270655)
+++ libstdc++-v3/include/bits/basic_string.h	(working copy)
@@ -6096,13 +6096,7 @@ 
     inline basic_string<_CharT, _Traits, _Alloc>
     operator+(basic_string<_CharT, _Traits, _Alloc>&& __lhs,
 	      basic_string<_CharT, _Traits, _Alloc>&& __rhs)
-    {
-      const auto __size = __lhs.size() + __rhs.size();
-      const bool __cond = (__size > __lhs.capacity()
-			   && __size <= __rhs.capacity());
-      return __cond ? std::move(__rhs.insert(0, __lhs))
-	            : std::move(__lhs.append(__rhs));
-    }
+    { return std::move(__lhs.append(__rhs)); }
 
   template<typename _CharT, typename _Traits, typename _Alloc>
     inline basic_string<_CharT, _Traits, _Alloc>
Index: libstdc++-v3/include/bits/basic_string.tcc
===================================================================
--- libstdc++-v3/include/bits/basic_string.tcc	(revision 270655)
+++ libstdc++-v3/include/bits/basic_string.tcc	(working copy)
@@ -1161,8 +1161,12 @@ 
       __glibcxx_requires_string(__lhs);
       typedef basic_string<_CharT, _Traits, _Alloc> __string_type;
       typedef typename __string_type::size_type	  __size_type;
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
+	rebind<_CharT>::other _Char_alloc_type;
+      typedef __gnu_cxx::__alloc_traits<_Char_alloc_type> _Alloc_traits;
       const __size_type __len = _Traits::length(__lhs);
-      __string_type __str;
+      __string_type __str(_Alloc_traits::_S_select_on_copy(
+          __rhs.get_allocator()));
       __str.reserve(__len + __rhs.size());
       __str.append(__lhs, __len);
       __str.append(__rhs);
@@ -1175,7 +1179,11 @@ 
     {
       typedef basic_string<_CharT, _Traits, _Alloc> __string_type;
       typedef typename __string_type::size_type	  __size_type;
-      __string_type __str;
+      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
+	rebind<_CharT>::other _Char_alloc_type;
+      typedef __gnu_cxx::__alloc_traits<_Char_alloc_type> _Alloc_traits;
+      __string_type __str(_Alloc_traits::_S_select_on_copy(
+          __rhs.get_allocator()));
       const __size_type __len = __rhs.size();
       __str.reserve(__len + 1);
       __str.append(__size_type(1), __lhs);
Index: libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc
===================================================================
--- libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc	(nonexistent)
+++ libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc	(working copy)
@@ -0,0 +1,151 @@ 
+// 2019-04-30  Nina Dinka Ranns  <dinka.ranns@gmail.com>
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+ 
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+ 
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++11 } }
+// COW strings don't support C++11 allocators:
+// { dg-require-effective-target cxx11-abi }
+
+#include <string>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
+ 
+using C = char;
+using traits = std::char_traits<C>;
+
+using __gnu_test::propagating_allocator;
+
+void test01()
+{
+  typedef propagating_allocator<C, true> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1("something",alloc_type(1));
+  test_type v2("something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() == 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3("something", alloc_type(3));
+  test_type v4("something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5("something", alloc_type(5));
+  auto r5 = v5 + "str";
+  VERIFY(r5.get_allocator().get_personality() == 5);
+
+  auto r6 = v5 + 'c';
+  VERIFY(r6.get_allocator().get_personality() == 5);
+
+  auto r7 = std::move(v5) + "str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6("something", alloc_type(6));
+  auto r8 = std::move(v6) + 'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7("something", alloc_type(7));
+  auto r9 = "str" + v7;
+  VERIFY(r9.get_allocator().get_personality() == 7);
+
+  auto r10 = 'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() == 7);
+
+  auto r11 = "str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8("something", alloc_type(8));
+  auto r12 = 'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+void test02()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1("something",alloc_type(1));
+  test_type v2("something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() != 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3("something", alloc_type(3));
+  test_type v4("something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5("something", alloc_type(5));
+  auto r5 = v5 + "str";
+  VERIFY(r5.get_allocator().get_personality() != 5);
+
+  auto r6 = v5 + 'c';
+  VERIFY(r6.get_allocator().get_personality() != 5);
+
+  auto r7 = std::move(v5) + "str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6("something", alloc_type(6));
+  auto r8 = std::move(v6) + 'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7("something", alloc_type(7));
+  auto r9 = "str" + v7;
+  VERIFY(r9.get_allocator().get_personality() != 7);
+
+  auto r10 = 'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() != 7);
+
+  auto r11 = "str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8("something", alloc_type(8));
+  auto r12 = 'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+void test03()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1("s",alloc_type(1));
+  v1.resize(10);
+  v1.shrink_to_fit();
+  test_type v2(10000,'x',alloc_type(2));
+  v2.reserve(10010);
+
+  auto r=std::move(v1)+std::move(v2);
+  VERIFY(r.get_allocator().get_personality() == 1);
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  return 0;
+}
Index: libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc
===================================================================
--- libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc	(nonexistent)
+++ libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc	(working copy)
@@ -0,0 +1,152 @@ 
+// 2019-04-30  Nina Dinka Ranns  <dinka.ranns@gmail.com>
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+ 
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+ 
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++11 } }
+// COW strings don't support C++11 allocators:
+// { dg-require-effective-target cxx11-abi }
+
+#include <string>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+#include <ext/throw_allocator.h>
+ 
+using C = wchar_t;
+using traits = std::char_traits<C>;
+
+using __gnu_test::propagating_allocator;
+
+void test01()
+{
+  typedef propagating_allocator<C, true> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1(L"something",alloc_type(1));
+  test_type v2(L"something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() == 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3(L"something", alloc_type(3));
+  test_type v4(L"something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5(L"something", alloc_type(5));
+  auto r5 = v5 + L"str";
+  VERIFY(r5.get_allocator().get_personality() == 5);
+
+  auto r6 = v5 + L'c';
+  VERIFY(r6.get_allocator().get_personality() == 5);
+
+  auto r7 = std::move(v5) + L"str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6(L"something", alloc_type(6));
+  auto r8 = std::move(v6) + L'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7(L"something", alloc_type(7));
+  auto r9 = L"str" + v7;
+  VERIFY(r9.get_allocator().get_personality() == 7);
+
+  auto r10 = L'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() == 7);
+
+  auto r11 = L"str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8(L"something", alloc_type(8));
+  auto r12 = L'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+
+void test02()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1(L"something",alloc_type(1));
+  test_type v2(L"something",alloc_type(2));
+  auto r1 = v1 + v2;
+  VERIFY(r1.get_allocator().get_personality() != 1);
+
+  auto r2 = v1 + std::move(v2);
+  VERIFY(r2.get_allocator().get_personality() == 2);
+
+  test_type v3(L"something", alloc_type(3));
+  test_type v4(L"something", alloc_type(4));
+  auto r3 = std::move(v3) + v4;
+  VERIFY(r3.get_allocator().get_personality() == 3);
+
+  auto r4 = std::move(v1) +std::move(v4);
+  VERIFY(r4.get_allocator().get_personality() == 1);
+
+  test_type v5(L"something", alloc_type(5));
+  auto r5 = v5 + L"str";
+  VERIFY(r5.get_allocator().get_personality() != 5);
+
+  auto r6 = v5 + L'c';
+  VERIFY(r6.get_allocator().get_personality() != 5);
+
+  auto r7 = std::move(v5) + L"str";
+  VERIFY(r7.get_allocator().get_personality() == 5);
+
+  test_type v6(L"something", alloc_type(6));
+  auto r8 = std::move(v6) + L'c';
+  VERIFY(r8.get_allocator().get_personality() == 6);
+
+  test_type v7(L"something", alloc_type(7));
+  auto r9 = L"str" + v7;
+  VERIFY(r9.get_allocator().get_personality() != 7);
+
+  auto r10 = L'c' + v7;
+  VERIFY(r10.get_allocator().get_personality() != 7);
+
+  auto r11 = L"str" + std::move(v7);
+  VERIFY(r11.get_allocator().get_personality() == 7);
+
+  test_type v8(L"something", alloc_type(8));
+  auto r12 = L'c' + std::move(v8);
+  VERIFY(r12.get_allocator().get_personality() == 8);
+}
+void test03()
+{
+  typedef propagating_allocator<C, false> alloc_type;
+  typedef std::basic_string<C, traits, alloc_type> test_type;
+
+  test_type v1(L"s",alloc_type(1));
+  v1.resize(10);
+  v1.shrink_to_fit();
+  test_type v2(10000,L'x',alloc_type(2));
+  v2.reserve(10010);
+
+  auto r=std::move(v1)+std::move(v2);
+  VERIFY(r.get_allocator().get_personality() == 1);
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  return 0;
+}