[libcxx-commits] [libcxx] [libc++][strings] P2591R5: Concatenation of strings and string views (PR #88389)

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Mon Apr 15 23:41:58 PDT 2024


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/88389

>From 2d606f402e34d446322c44598ada051c14fa6057 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 11 Apr 2024 10:28:35 +0300
Subject: [PATCH 1/9] [libc++][strings] P2591R5: Concatenation of strings and
 string views

Implemented: https://wg21.link/P2591R5
---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/ReleaseNotes/19.rst               |   1 +
 libcxx/docs/Status/Cxx2cPapers.csv            |   2 +-
 libcxx/include/string                         | 104 ++++++++++++++----
 libcxx/include/version                        |   5 +-
 .../string.version.compile.pass.cpp           |   5 +-
 .../string_view.version.compile.pass.cpp      |   5 +-
 .../version.version.compile.pass.cpp          |   5 +-
 .../string_op+/string.string_view.pass.cpp    |  76 +++++++++++++
 .../generate_feature_test_macro_components.py |   2 +-
 10 files changed, 176 insertions(+), 31 deletions(-)
 create mode 100644 libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 3197d2cd1b271c..0b8abe38e8bd77 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -456,6 +456,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_sstream_from_string_view``                     ``202306L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_string_view``                                  ``202403L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_submdspan``                                    *unimplemented*
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_text_encoding``                                *unimplemented*
diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index 81c05b9112bd26..6fbac70593cc0b 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -45,6 +45,7 @@ Implemented Papers
 - P2867R2 - Remove Deprecated ``strstream``\s From C++26
 - P2872R3 - Remove ``wstring_convert`` From C++26
 - P3142R0 - Printing Blank Lines with ``println`` (as DR against C++23)
+- P2591R5 - Concatenation of strings and string views
 - P2302R4 - ``std::ranges::contains``
 - P1659R3 - ``std::ranges::starts_with`` and ``std::ranges::ends_with``
 
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index fa11da62bc080e..8793d9385b79ae 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -55,7 +55,7 @@
 "`P2845R8 <https://wg21.link/P2845R8>`__","LWG","Formatting of ``std::filesystem::path``","Tokyo March 2024","","","|format|"
 "`P0493R5 <https://wg21.link/P0493R5>`__","LWG","Atomic minimum/maximum","Tokyo March 2024","","",""
 "`P2542R8 <https://wg21.link/P2542R8>`__","LWG","``views::concat``","Tokyo March 2024","","","|ranges|"
-"`P2591R5 <https://wg21.link/P2591R5>`__","LWG","Concatenation of strings and string views","Tokyo March 2024","","",""
+"`P2591R5 <https://wg21.link/P2591R5>`__","LWG","Concatenation of strings and string views","Tokyo March 2024","|Complete|","19.0",""
 "`P2248R8 <https://wg21.link/P2248R8>`__","LWG","Enabling list-initialization for algorithms","Tokyo March 2024","","",""
 "`P2810R4 <https://wg21.link/P2810R4>`__","LWG","``is_debugger_present`` ``is_replaceable``","Tokyo March 2024","","",""
 "`P1068R11 <https://wg21.link/P1068R11>`__","LWG","Vector API for random number generation","Tokyo March 2024","","",""
diff --git a/libcxx/include/string b/libcxx/include/string
index a456f8cb80ee35..7758a4ae42e046 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -407,6 +407,24 @@ template<class charT, class traits, class Allocator>
 basic_string<charT, traits, Allocator>
 operator+(const basic_string<charT, traits, Allocator>& lhs, charT rhs);                        // constexpr since C++20
 
+template<class charT, class traits, class Allocator>
+  constexpr basic_string<charT, traits, Allocator>
+    operator+(const basic_string<charT, traits, Allocator>& lhs,
+              type_identity_t<basic_string_view<charT, traits>> rhs);                           // Since C++26
+template<class charT, class traits, class Allocator>
+  constexpr basic_string<charT, traits, Allocator>
+    operator+(basic_string<charT, traits, Allocator>&& lhs,
+              type_identity_t<basic_string_view<charT, traits>> rhs);                           // Since C++26
+template<class charT, class traits, class Allocator>
+  constexpr basic_string<charT, traits, Allocator>
+    operator+(type_identity_t<basic_string_view<charT, traits>> lhs,
+              const basic_string<charT, traits, Allocator>& rhs);                               // Since C++26
+template<class charT, class traits, class Allocator>
+  constexpr basic_string<charT, traits, Allocator>
+    operator+(type_identity_t<basic_string_view<charT, traits>> lhs,
+              basic_string<charT, traits, Allocator>&& rhs);                                    // Since C++26
+
+
 template<class charT, class traits, class Allocator>
 bool operator==(const basic_string<charT, traits, Allocator>& lhs,
                 const basic_string<charT, traits, Allocator>& rhs) noexcept;                    // constexpr since C++20
@@ -1074,8 +1092,8 @@ public:
             __enable_if_t<__can_be_converted_to_string_view<_CharT, _Traits, _Tp>::value &&
                               !__is_same_uncvref<_Tp, basic_string>::value,
                           int> = 0>
-  _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit basic_string(
-      const _Tp& __t, const allocator_type& __a)
+  _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit basic_string(const _Tp& __t, const allocator_type& __a)
       : __r_(__default_init_tag(), __a) {
     __self_view __sv = __t;
     __init(__sv.data(), __sv.size());
@@ -1307,8 +1325,8 @@ public:
                           int> = 0>
   _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _LIBCPP_CONSTEXPR_SINCE_CXX20
 
-      basic_string&
-      append(const _Tp& __t, size_type __pos, size_type __n = npos);
+  basic_string&
+  append(const _Tp& __t, size_type __pos, size_type __n = npos);
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s, size_type __n);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s);
@@ -1997,15 +2015,15 @@ private:
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20
 #if _LIBCPP_ABI_VERSION >= 2 //  We want to use the function in the dylib in ABIv1
-      _LIBCPP_HIDE_FROM_ABI
+  _LIBCPP_HIDE_FROM_ABI
 #endif
-          _LIBCPP_DEPRECATED_("use __grow_by_without_replace") void __grow_by(
-              size_type __old_cap,
-              size_type __delta_cap,
-              size_type __old_sz,
-              size_type __n_copy,
-              size_type __n_del,
-              size_type __n_add = 0);
+  _LIBCPP_DEPRECATED_("use __grow_by_without_replace") void __grow_by(
+      size_type __old_cap,
+      size_type __delta_cap,
+      size_type __old_sz,
+      size_type __n_copy,
+      size_type __n_del,
+      size_type __n_add = 0);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __grow_by_without_replace(
       size_type __old_cap,
       size_type __delta_cap,
@@ -2171,8 +2189,8 @@ template <class _CharT,
           class _Traits,
           class _Allocator = allocator<_CharT>,
           class            = enable_if_t<__is_allocator<_Allocator>::value> >
-explicit basic_string(basic_string_view<_CharT, _Traits>, const _Allocator& = _Allocator())
-    -> basic_string<_CharT, _Traits, _Allocator>;
+explicit basic_string(basic_string_view<_CharT, _Traits>,
+                      const _Allocator& = _Allocator()) -> basic_string<_CharT, _Traits, _Allocator>;
 
 template <class _CharT,
           class _Traits,
@@ -2407,15 +2425,15 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
 template <class _CharT, class _Traits, class _Allocator>
 void _LIBCPP_CONSTEXPR_SINCE_CXX20
 #if _LIBCPP_ABI_VERSION >= 2 // We want to use the function in the dylib in ABIv1
-    _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_HIDE_FROM_ABI
 #endif
-    _LIBCPP_DEPRECATED_("use __grow_by_without_replace") basic_string<_CharT, _Traits, _Allocator>::__grow_by(
-        size_type __old_cap,
-        size_type __delta_cap,
-        size_type __old_sz,
-        size_type __n_copy,
-        size_type __n_del,
-        size_type __n_add) {
+_LIBCPP_DEPRECATED_("use __grow_by_without_replace") basic_string<_CharT, _Traits, _Allocator>::__grow_by(
+    size_type __old_cap,
+    size_type __delta_cap,
+    size_type __old_sz,
+    size_type __n_copy,
+    size_type __n_del,
+    size_type __n_add) {
   size_type __ms = max_size();
   if (__delta_cap > __ms - __old_cap)
     __throw_length_error();
@@ -4005,6 +4023,48 @@ operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, _CharT __rhs) {
 
 #endif // _LIBCPP_CXX03_LANG
 
+#if _LIBCPP_STD_VER >= 26
+
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
+operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
+          type_identity_t<basic_string_view<_CharT, _Traits>> __rhs) {
+  using _String = basic_string<_CharT, _Traits, _Allocator>;
+
+  _String __r = __lhs;
+  __r.append(__rhs);
+  return __r;
+}
+
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
+operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs,
+          type_identity_t<basic_string_view<_CharT, _Traits>> __rhs) {
+  __lhs.append(__rhs);
+  return std::move(__lhs);
+}
+
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
+operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
+          const basic_string<_CharT, _Traits, _Allocator>& __rhs) {
+  using _String = basic_string<_CharT, _Traits, _Allocator>;
+
+  _String __r = __rhs;
+  __r.insert(0, __lhs);
+  return __r;
+}
+
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
+operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
+          basic_string<_CharT, _Traits, _Allocator>&& __rhs) {
+  __rhs.insert(0, __lhs);
+  return std::move(__rhs);
+}
+
+#endif // _LIBCPP_STD_VER >= 26
+
 // swap
 
 template <class _CharT, class _Traits, class _Allocator>
diff --git a/libcxx/include/version b/libcxx/include/version
index 0ed77345baa71d..d7e98fdb47208d 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -222,7 +222,8 @@ __cpp_lib_stdatomic_h                                   202011L <stdatomic.h>
 __cpp_lib_string_contains                               202011L <string> <string_view>
 __cpp_lib_string_resize_and_overwrite                   202110L <string>
 __cpp_lib_string_udls                                   201304L <string>
-__cpp_lib_string_view                                   201803L <string> <string_view>
+__cpp_lib_string_view                                   202403L <string> <string_view>
+                                                        201803L // C++20
                                                         201606L // C++17
 __cpp_lib_submdspan                                     202306L <mdspan>
 __cpp_lib_syncbuf                                       201803L <syncstream>
@@ -530,6 +531,8 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 # define __cpp_lib_span_at                              202311L
 # define __cpp_lib_span_initializer_list                202311L
 # define __cpp_lib_sstream_from_string_view             202306L
+# undef  __cpp_lib_string_view
+# define __cpp_lib_string_view                          202403L
 // # define __cpp_lib_submdspan                            202306L
 // # define __cpp_lib_text_encoding                        202306L
 # undef  __cpp_lib_to_chars
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
index 8d944a194faf42..4fb733716b99e2 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
@@ -29,6 +29,7 @@
     __cpp_lib_string_udls                                   201304L [C++14]
     __cpp_lib_string_view                                   201606L [C++17]
                                                             201803L [C++20]
+                                                            202403L [C++26]
     __cpp_lib_to_string                                     202306L [C++23]
 */
 
@@ -492,8 +493,8 @@
 # ifndef __cpp_lib_string_view
 #   error "__cpp_lib_string_view should be defined in c++26"
 # endif
-# if __cpp_lib_string_view != 201803L
-#   error "__cpp_lib_string_view should have the value 201803L in c++26"
+# if __cpp_lib_string_view != 202403L
+#   error "__cpp_lib_string_view should have the value 202403L in c++26"
 # endif
 
 # if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp
index a86ab2adff6a93..f3c70cf9779737 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/string_view.version.compile.pass.cpp
@@ -23,6 +23,7 @@
     __cpp_lib_string_contains             202011L [C++23]
     __cpp_lib_string_view                 201606L [C++17]
                                           201803L [C++20]
+                                          202403L [C++26]
 */
 
 #include <string_view>
@@ -252,8 +253,8 @@
 # ifndef __cpp_lib_string_view
 #   error "__cpp_lib_string_view should be defined in c++26"
 # endif
-# if __cpp_lib_string_view != 201803L
-#   error "__cpp_lib_string_view should have the value 201803L in c++26"
+# if __cpp_lib_string_view != 202403L
+#   error "__cpp_lib_string_view should have the value 202403L in c++26"
 # endif
 
 #endif // TEST_STD_VER > 23
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 3ec548f56cea1d..35bbfd916ac80a 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -206,6 +206,7 @@
     __cpp_lib_string_udls                                   201304L [C++14]
     __cpp_lib_string_view                                   201606L [C++17]
                                                             201803L [C++20]
+                                                            202403L [C++26]
     __cpp_lib_submdspan                                     202306L [C++26]
     __cpp_lib_syncbuf                                       201803L [C++20]
     __cpp_lib_text_encoding                                 202306L [C++26]
@@ -7686,8 +7687,8 @@
 # ifndef __cpp_lib_string_view
 #   error "__cpp_lib_string_view should be defined in c++26"
 # endif
-# if __cpp_lib_string_view != 201803L
-#   error "__cpp_lib_string_view should have the value 201803L in c++26"
+# if __cpp_lib_string_view != 202403L
+#   error "__cpp_lib_string_view should have the value 202403L in c++26"
 # endif
 
 # if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
new file mode 100644
index 00000000000000..efa57442997e7f
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20, c++23
+
+// <string>
+
+// [string.op.plus]
+//
+// template<class charT, class traits, class Allocator>
+//   constexpr basic_string<charT, traits, Allocator>
+//     operator+(const basic_string<charT, traits, Allocator>& lhs,
+//               type_identity_t<basic_string_view<charT, traits>> rhs);                           // Since C++26
+// template<class charT, class traits, class Allocator>
+//   constexpr basic_string<charT, traits, Allocator>
+//     operator+(basic_string<charT, traits, Allocator>&& lhs,
+//               type_identity_t<basic_string_view<charT, traits>> rhs);                           // Since C++26
+// template<class charT, class traits, class Allocator>
+//   constexpr basic_string<charT, traits, Allocator>
+//     operator+(type_identity_t<basic_string_view<charT, traits>> lhs,
+//               const basic_string<charT, traits, Allocator>& rhs);                               // Since C++26
+// template<class charT, class traits, class Allocator>
+//   constexpr basic_string<charT, traits, Allocator>
+//     operator+(type_identity_t<basic_string_view<charT, traits>> lhs,
+//               basic_string<charT, traits, Allocator>&& rhs);                                    // Since C++26
+
+#include <cassert>
+#include <string>
+#include <utility>
+
+#include "asan_testing.h"
+#include "constexpr_char_traits.h"
+#include "make_string.h"
+#include "min_allocator.h"
+#include "test_macros.h"
+
+#define CS(S) MAKE_CSTRING(CharT, S)
+#define ST(S, a) std::basic_string<CharT, TraitsT, AllocT>(MAKE_CSTRING(CharT, S), MKSTR_LEN(CharT, S), a)
+#define SV(S) std::basic_string_view<CharT, TraitsT>(MAKE_CSTRING(CharT, S), MKSTR_LEN(CharT, S))
+
+template <typename CharT, typename TraitsT, typename AllocT>
+constexpr void test() {
+  AllocT allocator;
+  std::basic_string<CharT, TraitsT, AllocT> st{ST("Hello", allocator)};
+  std::basic_string_view<CharT, TraitsT> sv{SV("World")};
+
+  assert(st + sv == ST("HelloWorld", allocator));
+  assert(st + sv != ST("Hello World", allocator));
+}
+
+constexpr bool test() {
+  test<char, std::char_traits<char>, min_allocator<char>>();
+  test<char, constexpr_char_traits<char>, min_allocator<char>>();
+  test<char, std::char_traits<char>, safe_allocator<char>>();
+  test<char, constexpr_char_traits<char>, safe_allocator<char>>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t, std::char_traits<wchar_t>, min_allocator<wchar_t>>();
+  test<wchar_t, constexpr_char_traits<wchar_t>, min_allocator<wchar_t>>();
+  test<wchar_t, std::char_traits<wchar_t>, safe_allocator<wchar_t>>();
+  test<wchar_t, constexpr_char_traits<wchar_t>, safe_allocator<wchar_t>>();
+#endif
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f2b8d55c0e11b0..1afdb6475b1877 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1202,7 +1202,7 @@ def add_version_header(tc):
             "values": {
                 "c++17": 201606,
                 "c++20": 201803,
-                # "c++26": 202403, # P2591R5: Concatenation of strings and string views
+                "c++26": 202403, # P2591R5: Concatenation of strings and string views
             },
             "headers": ["string", "string_view"],
         },

>From af56ab902866a6971fad6034b483ad55e02ccdef Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 11 Apr 2024 21:01:34 +0300
Subject: [PATCH 2/9] Implemented tests

---
 libcxx/include/string                         |  11 +-
 .../string_op+/string.string_view.pass.cpp    | 107 ++++++++++++++++--
 .../generate_feature_test_macro_components.py |   2 +-
 3 files changed, 107 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index 7758a4ae42e046..eb5f2b78af082b 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -4050,8 +4050,8 @@ operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
           const basic_string<_CharT, _Traits, _Allocator>& __rhs) {
   using _String = basic_string<_CharT, _Traits, _Allocator>;
 
-  _String __r = __rhs;
-  __r.insert(0, __lhs);
+  _String __r{__lhs};
+  __r.append(__rhs);
   return __r;
 }
 
@@ -4059,8 +4059,11 @@ template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
 operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
           basic_string<_CharT, _Traits, _Allocator>&& __rhs) {
-  __rhs.insert(0, __lhs);
-  return std::move(__rhs);
+  using _String = basic_string<_CharT, _Traits, _Allocator>;
+
+  _String __r{__lhs};
+  __r.append(std::move(__rhs));
+  return __r;
 }
 
 #endif // _LIBCPP_STD_VER >= 26
diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
index efa57442997e7f..21936d76d06320 100644
--- a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
@@ -30,6 +30,7 @@
 //               basic_string<charT, traits, Allocator>&& rhs);                                    // Since C++26
 
 #include <cassert>
+#include <concepts>
 #include <string>
 #include <utility>
 
@@ -37,6 +38,7 @@
 #include "constexpr_char_traits.h"
 #include "make_string.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 #include "test_macros.h"
 
 #define CS(S) MAKE_CSTRING(CharT, S)
@@ -44,25 +46,114 @@
 #define SV(S) std::basic_string_view<CharT, TraitsT>(MAKE_CSTRING(CharT, S), MKSTR_LEN(CharT, S))
 
 template <typename CharT, typename TraitsT, typename AllocT>
-constexpr void test() {
+constexpr void test(const CharT* x, const CharT* y, const CharT* expected) {
   AllocT allocator;
-  std::basic_string<CharT, TraitsT, AllocT> st{ST("Hello", allocator)};
-  std::basic_string_view<CharT, TraitsT> sv{SV("World")};
 
-  assert(st + sv == ST("HelloWorld", allocator));
-  assert(st + sv != ST("Hello World", allocator));
+  // string& + string_view
+  {
+    std::basic_string<CharT, TraitsT, AllocT> st{x, allocator};
+    std::basic_string_view<CharT, TraitsT> sv{y};
+
+    std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = st + sv;
+    assert(result == expected);
+    assert(result.get_allocator() == allocator);
+    LIBCPP_ASSERT(is_string_asan_correct(st + sv));
+  }
+  // const string& + string_view
+  {
+    const std::basic_string<CharT, TraitsT, AllocT> st{x, allocator};
+    std::basic_string_view<CharT, TraitsT> sv{y};
+
+    std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = st + sv;
+    assert(result == expected);
+    assert(result.get_allocator() == allocator);
+    LIBCPP_ASSERT(is_string_asan_correct(st + sv));
+  }
+  // string&& + string_view
+  {
+    std::basic_string<CharT, TraitsT, AllocT> st{x, allocator};
+    std::basic_string_view<CharT, TraitsT> sv{y};
+
+    std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = std::move(st) + sv;
+    assert(result == expected);
+    assert(result.get_allocator() == allocator);
+    LIBCPP_ASSERT(is_string_asan_correct(st + sv));
+  }
+  // string_view + string&
+  {
+    std::basic_string_view<CharT, TraitsT> sv{x};
+    std::basic_string<CharT, TraitsT, AllocT> st{y, allocator};
+
+    std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = sv + st;
+    assert(result == expected);
+    assert(result.get_allocator() == allocator);
+    LIBCPP_ASSERT(is_string_asan_correct(sv + st));
+  }
+  // string_view + const string&
+  {
+    std::basic_string_view<CharT, TraitsT> sv{x};
+    const std::basic_string<CharT, TraitsT, AllocT> st{y, allocator};
+
+    std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = sv + st;
+    assert(result == expected);
+    assert(result.get_allocator() == allocator);
+    LIBCPP_ASSERT(is_string_asan_correct(sv + st));
+  }
+  // string_view + string&&
+  {
+    std::basic_string_view<CharT, TraitsT> sv{x};
+    std::basic_string<CharT, TraitsT, AllocT> st{y, allocator};
+
+    std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = sv + std::move(st);
+    assert(result == expected);
+    assert(result.get_allocator() == allocator);
+    LIBCPP_ASSERT(is_string_asan_correct(sv + st));
+  }
+}
+
+template <typename CharT, typename TraitsT = std::char_traits<CharT>, typename AllocT = std::allocator<CharT>>
+constexpr void test() {
+  // Concatinate with an empty string
+  test<CharT, TraitsT, AllocT>(CS(""), CS(""), CS(""));
+  test<CharT, TraitsT, AllocT>(CS(""), CS("short"), CS("short"));
+  test<CharT, TraitsT, AllocT>(CS(""), CS("not so short"), CS("not so short"));
+  test<CharT, TraitsT, AllocT>(CS(""), CS("this is a much longer string"), CS("this is a much longer string"));
+
+  test<CharT, TraitsT, AllocT>(CS(""), CS(""), CS(""));
+  test<CharT, TraitsT, AllocT>(CS("short"), CS(""), CS("short"));
+  test<CharT, TraitsT, AllocT>(CS("not so short"), CS(""), CS("not so short"));
+  test<CharT, TraitsT, AllocT>(CS("this is a much longer string"), CS(""), CS("this is a much longer string"));
+
+  // Non empty
+  test<CharT, TraitsT, AllocT>(CS("B"), CS("D"), CS("BD"));
+  test<CharT, TraitsT, AllocT>(CS("zmt94"), CS("+hkt82"), CS("zmt94+hkt82"));
+  test<CharT, TraitsT, AllocT>(CS("not so short"), CS("+is not bad"), CS("not so short+is not bad"));
+  test<CharT, TraitsT, AllocT>(
+      CS("this is a much longer string"),
+      CS("+which is so much better"),
+      CS("this is a much longer string+which is so much better"));
 }
 
 constexpr bool test() {
+  test<char, std::char_traits<char>>();
   test<char, std::char_traits<char>, min_allocator<char>>();
-  test<char, constexpr_char_traits<char>, min_allocator<char>>();
   test<char, std::char_traits<char>, safe_allocator<char>>();
+  // test<char, std::char_traits<char>, test_allocator<char>>();
+
+  test<char, constexpr_char_traits<char>>();
+  test<char, constexpr_char_traits<char>, min_allocator<char>>();
   test<char, constexpr_char_traits<char>, safe_allocator<char>>();
+  // test<char, constexpr_char_traits<char>, test_allocator<char>>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t, std::char_traits<wchar_t>, min_allocator<wchar_t>>();
-  test<wchar_t, constexpr_char_traits<wchar_t>, min_allocator<wchar_t>>();
+  test<wchar_t, std::char_traits<wchar_t>>();
+  // test<wchar_t, std::char_traits<wchar_t>, min_allocator<wchar_t>>();
   test<wchar_t, std::char_traits<wchar_t>, safe_allocator<wchar_t>>();
+  // test<wchar_t, std::char_traits<wchar_t>, test_allocator<wchar_t>>();
+
+  test<wchar_t, constexpr_char_traits<wchar_t>>();
+  // test<wchar_t, constexpr_char_traits<wchar_t>, min_allocator<wchar_t>>();
   test<wchar_t, constexpr_char_traits<wchar_t>, safe_allocator<wchar_t>>();
+  // test<wchar_t, constexpr_char_traits<wchar_t>, test_allocator<wchar_t>>();
 #endif
 
   return true;
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 1afdb6475b1877..1b938fa30c59d2 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1202,7 +1202,7 @@ def add_version_header(tc):
             "values": {
                 "c++17": 201606,
                 "c++20": 201803,
-                "c++26": 202403, # P2591R5: Concatenation of strings and string views
+                "c++26": 202403,  # P2591R5: Concatenation of strings and string views
             },
             "headers": ["string", "string_view"],
         },

>From 474d5b9d8a15921369614cf82df46502fad13f7f Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 11 Apr 2024 21:03:43 +0300
Subject: [PATCH 3/9] Cleanup

---
 .../string_op+/string.string_view.pass.cpp               | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
index 21936d76d06320..f2fb1dbe7195de 100644
--- a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
@@ -38,7 +38,6 @@
 #include "constexpr_char_traits.h"
 #include "make_string.h"
 #include "min_allocator.h"
-#include "test_allocator.h"
 #include "test_macros.h"
 
 #define CS(S) MAKE_CSTRING(CharT, S)
@@ -113,7 +112,7 @@ constexpr void test(const CharT* x, const CharT* y, const CharT* expected) {
 
 template <typename CharT, typename TraitsT = std::char_traits<CharT>, typename AllocT = std::allocator<CharT>>
 constexpr void test() {
-  // Concatinate with an empty string
+  // Concatinate with an empty `string`/`string_view`
   test<CharT, TraitsT, AllocT>(CS(""), CS(""), CS(""));
   test<CharT, TraitsT, AllocT>(CS(""), CS("short"), CS("short"));
   test<CharT, TraitsT, AllocT>(CS(""), CS("not so short"), CS("not so short"));
@@ -138,22 +137,16 @@ constexpr bool test() {
   test<char, std::char_traits<char>>();
   test<char, std::char_traits<char>, min_allocator<char>>();
   test<char, std::char_traits<char>, safe_allocator<char>>();
-  // test<char, std::char_traits<char>, test_allocator<char>>();
 
   test<char, constexpr_char_traits<char>>();
   test<char, constexpr_char_traits<char>, min_allocator<char>>();
   test<char, constexpr_char_traits<char>, safe_allocator<char>>();
-  // test<char, constexpr_char_traits<char>, test_allocator<char>>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<wchar_t, std::char_traits<wchar_t>>();
-  // test<wchar_t, std::char_traits<wchar_t>, min_allocator<wchar_t>>();
   test<wchar_t, std::char_traits<wchar_t>, safe_allocator<wchar_t>>();
-  // test<wchar_t, std::char_traits<wchar_t>, test_allocator<wchar_t>>();
 
   test<wchar_t, constexpr_char_traits<wchar_t>>();
-  // test<wchar_t, constexpr_char_traits<wchar_t>, min_allocator<wchar_t>>();
   test<wchar_t, constexpr_char_traits<wchar_t>, safe_allocator<wchar_t>>();
-  // test<wchar_t, constexpr_char_traits<wchar_t>, test_allocator<wchar_t>>();
 #endif
 
   return true;

>From 38f6e853deb3dbc6b9020de509f30cfeb981b12f Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 11 Apr 2024 21:06:16 +0300
Subject: [PATCH 4/9] Restored formatting

---
 libcxx/include/string | 44 +++++++++++++++++++++----------------------
 1 file changed, 22 insertions(+), 22 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index eb5f2b78af082b..5af81486fa30d7 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1092,8 +1092,8 @@ public:
             __enable_if_t<__can_be_converted_to_string_view<_CharT, _Traits, _Tp>::value &&
                               !__is_same_uncvref<_Tp, basic_string>::value,
                           int> = 0>
-  _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit basic_string(const _Tp& __t, const allocator_type& __a)
+  _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _LIBCPP_CONSTEXPR_SINCE_CXX20 explicit basic_string(
+      const _Tp& __t, const allocator_type& __a)
       : __r_(__default_init_tag(), __a) {
     __self_view __sv = __t;
     __init(__sv.data(), __sv.size());
@@ -1325,8 +1325,8 @@ public:
                           int> = 0>
   _LIBCPP_METHOD_TEMPLATE_IMPLICIT_INSTANTIATION_VIS _LIBCPP_CONSTEXPR_SINCE_CXX20
 
-  basic_string&
-  append(const _Tp& __t, size_type __pos, size_type __n = npos);
+      basic_string&
+      append(const _Tp& __t, size_type __pos, size_type __n = npos);
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s, size_type __n);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s);
@@ -2015,15 +2015,15 @@ private:
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20
 #if _LIBCPP_ABI_VERSION >= 2 //  We want to use the function in the dylib in ABIv1
-  _LIBCPP_HIDE_FROM_ABI
+      _LIBCPP_HIDE_FROM_ABI
 #endif
-  _LIBCPP_DEPRECATED_("use __grow_by_without_replace") void __grow_by(
-      size_type __old_cap,
-      size_type __delta_cap,
-      size_type __old_sz,
-      size_type __n_copy,
-      size_type __n_del,
-      size_type __n_add = 0);
+          _LIBCPP_DEPRECATED_("use __grow_by_without_replace") void __grow_by(
+              size_type __old_cap,
+              size_type __delta_cap,
+              size_type __old_sz,
+              size_type __n_copy,
+              size_type __n_del,
+              size_type __n_add = 0);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __grow_by_without_replace(
       size_type __old_cap,
       size_type __delta_cap,
@@ -2189,8 +2189,8 @@ template <class _CharT,
           class _Traits,
           class _Allocator = allocator<_CharT>,
           class            = enable_if_t<__is_allocator<_Allocator>::value> >
-explicit basic_string(basic_string_view<_CharT, _Traits>,
-                      const _Allocator& = _Allocator()) -> basic_string<_CharT, _Traits, _Allocator>;
+explicit basic_string(basic_string_view<_CharT, _Traits>, const _Allocator& = _Allocator())
+    -> basic_string<_CharT, _Traits, _Allocator>;
 
 template <class _CharT,
           class _Traits,
@@ -2425,15 +2425,15 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocator>::__
 template <class _CharT, class _Traits, class _Allocator>
 void _LIBCPP_CONSTEXPR_SINCE_CXX20
 #if _LIBCPP_ABI_VERSION >= 2 // We want to use the function in the dylib in ABIv1
-_LIBCPP_HIDE_FROM_ABI
+    _LIBCPP_HIDE_FROM_ABI
 #endif
-_LIBCPP_DEPRECATED_("use __grow_by_without_replace") basic_string<_CharT, _Traits, _Allocator>::__grow_by(
-    size_type __old_cap,
-    size_type __delta_cap,
-    size_type __old_sz,
-    size_type __n_copy,
-    size_type __n_del,
-    size_type __n_add) {
+    _LIBCPP_DEPRECATED_("use __grow_by_without_replace") basic_string<_CharT, _Traits, _Allocator>::__grow_by(
+        size_type __old_cap,
+        size_type __delta_cap,
+        size_type __old_sz,
+        size_type __n_copy,
+        size_type __n_del,
+        size_type __n_add) {
   size_type __ms = max_size();
   if (__delta_cap > __ms - __old_cap)
     __throw_length_error();

>From ea270b6f3aff22da6cf8513fe2867453740b491c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 13 Apr 2024 07:12:20 +0300
Subject: [PATCH 5/9] Addressed review comments

---
 libcxx/include/string                           | 17 +++++------------
 .../string_op+/string.string_view.pass.cpp      |  2 ++
 2 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index 5af81486fa30d7..bd648a33b5cdd9 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -4029,9 +4029,7 @@ template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
 operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
           type_identity_t<basic_string_view<_CharT, _Traits>> __rhs) {
-  using _String = basic_string<_CharT, _Traits, _Allocator>;
-
-  _String __r = __lhs;
+  basic_string<_CharT, _Traits, _Allocator> __r = __lhs;
   __r.append(__rhs);
   return __r;
 }
@@ -4048,10 +4046,8 @@ template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
 operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
           const basic_string<_CharT, _Traits, _Allocator>& __rhs) {
-  using _String = basic_string<_CharT, _Traits, _Allocator>;
-
-  _String __r{__lhs};
-  __r.append(__rhs);
+  basic_string<_CharT, _Traits, _Allocator> __r = __rhs;
+  __r.insert(0, __lhs);
   return __r;
 }
 
@@ -4059,11 +4055,8 @@ template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
 operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
           basic_string<_CharT, _Traits, _Allocator>&& __rhs) {
-  using _String = basic_string<_CharT, _Traits, _Allocator>;
-
-  _String __r{__lhs};
-  __r.append(std::move(__rhs));
-  return __r;
+  __rhs.insert(0, __lhs);
+  return std::move(__rhs);
 }
 
 #endif // _LIBCPP_STD_VER >= 26
diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
index f2fb1dbe7195de..2d0981ed450be1 100644
--- a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
@@ -143,9 +143,11 @@ constexpr bool test() {
   test<char, constexpr_char_traits<char>, safe_allocator<char>>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<wchar_t, std::char_traits<wchar_t>>();
+  test<wchar_t, std::char_traits<wchar_t>, min_allocator<wchar_t>>();
   test<wchar_t, std::char_traits<wchar_t>, safe_allocator<wchar_t>>();
 
   test<wchar_t, constexpr_char_traits<wchar_t>>();
+  test<wchar_t, constexpr_char_traits<wchar_t>, min_allocator<wchar_t>>();
   test<wchar_t, constexpr_char_traits<wchar_t>, safe_allocator<wchar_t>>();
 #endif
 

>From 292cd8f7a485d8ccdf9635c50bba1c4a88fc7532 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sat, 13 Apr 2024 07:13:20 +0300
Subject: [PATCH 6/9] Removed `constexpr_char_traits` tests

---
 .../string_op+/string.string_view.pass.cpp                 | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
index 2d0981ed450be1..7f08d460a6fb12 100644
--- a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
@@ -138,17 +138,10 @@ constexpr bool test() {
   test<char, std::char_traits<char>, min_allocator<char>>();
   test<char, std::char_traits<char>, safe_allocator<char>>();
 
-  test<char, constexpr_char_traits<char>>();
-  test<char, constexpr_char_traits<char>, min_allocator<char>>();
-  test<char, constexpr_char_traits<char>, safe_allocator<char>>();
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
   test<wchar_t, std::char_traits<wchar_t>>();
   test<wchar_t, std::char_traits<wchar_t>, min_allocator<wchar_t>>();
   test<wchar_t, std::char_traits<wchar_t>, safe_allocator<wchar_t>>();
-
-  test<wchar_t, constexpr_char_traits<wchar_t>>();
-  test<wchar_t, constexpr_char_traits<wchar_t>, min_allocator<wchar_t>>();
-  test<wchar_t, constexpr_char_traits<wchar_t>, safe_allocator<wchar_t>>();
 #endif
 
   return true;

>From 9a86ea1a9c1c0eefca8bec6223b14bec108ee445 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 15 Apr 2024 12:23:24 +0300
Subject: [PATCH 7/9] Use Will Hawkins implementation

---
 libcxx/include/string                         | 88 +++++++++++++++++--
 .../string_op+/string.string_view.pass.cpp    | 52 ++++++++---
 2 files changed, 121 insertions(+), 19 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index bd648a33b5cdd9..2a8cbb0b49072e 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -725,6 +725,26 @@ struct __can_be_converted_to_string_view
 struct __uninitialized_size_tag {};
 struct __init_with_sentinel_tag {};
 
+#if _LIBCPP_STD_VER >= 26
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
+operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
+          type_identity_t<basic_string_view<_CharT, _Traits>> __rhs);
+
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
+operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, type_identity_t<basic_string_view<_CharT, _Traits>> __rhs);
+
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
+operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
+          const basic_string<_CharT, _Traits, _Allocator>& __rhs);
+
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
+operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs, basic_string<_CharT, _Traits, _Allocator>&& __rhs);
+#endif
+
 template <class _CharT, class _Traits, class _Allocator>
 class basic_string {
 private:
@@ -2158,6 +2178,30 @@ private:
   friend _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string operator+ <>(value_type, const basic_string&);
   friend _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string operator+ <>(const basic_string&, const value_type*);
   friend _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string operator+ <>(const basic_string&, value_type);
+#if _LIBCPP_STD_VER >= 26
+  // friend constexpr basic_string operator+ <>(const basic_string&, const __self_view&);
+
+  // friend constexpr basic_string operator+ <>(const __self_view&, const basic_string&);
+
+  // friend constexpr operator+ <>(type_identity_t<__self_view> __lhs,
+  //                               const basic_string& __rhs);
+
+  // friend constexpr basic_string operator+
+  //     <>(type_identity_t<basic_string_view<_CharT2, _Traits2>> __lhs,
+  //        const basic_string<_CharT2, _Traits2, _Allocator2>& __rhs);
+
+  // template <class _CharT2, class _Traits2, class _Allocator2>
+  // friend constexpr basic_string<_CharT2, _Traits2, _Allocator2>
+  // operator+(type_identity_t<basic_string_view<_CharT2, _Traits2>>, const basic_string<_CharT2, _Traits2, _Allocator2>&);
+
+
+  // friend constexpr basic_string operator+ <>(type_identity_t<basic_string_view>, const basic_string&);
+
+  friend constexpr basic_string operator+ <>(const basic_string&, type_identity_t<__self_view>);
+  friend constexpr basic_string operator+ <>(basic_string&&, type_identity_t<__self_view>);
+  friend constexpr basic_string operator+ <>(type_identity_t<__self_view>, const basic_string&);
+  friend constexpr basic_string operator+ <>(type_identity_t<__self_view>, basic_string&&);
+#endif
 };
 
 // These declarations must appear before any functions are implicitly used
@@ -4029,9 +4073,17 @@ template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
 operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
           type_identity_t<basic_string_view<_CharT, _Traits>> __rhs) {
-  basic_string<_CharT, _Traits, _Allocator> __r = __lhs;
-  __r.append(__rhs);
-  return __r;
+    using _String = basic_string<_CharT, _Traits, _Allocator>;
+    typename _String::size_type __lhs_sz = __lhs.size();
+    typename _String::size_type __rhs_sz = __rhs.size();
+    _String __r(__uninitialized_size_tag(),
+                __lhs_sz + __rhs_sz,
+                _String::__alloc_traits::select_on_container_copy_construction(__lhs.get_allocator()));
+    auto __ptr = std::__to_address(__r.__get_pointer());
+    _Traits::copy(__ptr, __lhs.data(), __lhs_sz);
+    _Traits::copy(__ptr + __lhs_sz, __rhs.data(), __rhs_sz);
+    _Traits::assign(__ptr + __lhs_sz + __rhs_sz, 1, _CharT());
+    return __r;
 }
 
 template <class _CharT, class _Traits, class _Allocator>
@@ -4046,8 +4098,19 @@ template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
 operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
           const basic_string<_CharT, _Traits, _Allocator>& __rhs) {
-  basic_string<_CharT, _Traits, _Allocator> __r = __rhs;
-  __r.insert(0, __lhs);
+  // basic_string<_CharT, _Traits, _Allocator> __r = __rhs;
+  // __r.insert(0, __lhs);
+  // return __r;
+  using _String                        = basic_string<_CharT, _Traits, _Allocator>;
+  typename _String::size_type __lhs_sz = __lhs.size();
+  typename _String::size_type __rhs_sz = __rhs.size();
+  _String __r(__uninitialized_size_tag(),
+              __lhs_sz + __rhs_sz,
+              _String::__alloc_traits::select_on_container_copy_construction(__rhs.get_allocator()));
+  auto __ptr = std::__to_address(__r.__get_pointer());
+  _Traits::copy(__ptr, __lhs.data(), __lhs_sz);
+  _Traits::copy(__ptr + __lhs_sz, __rhs.data(), __rhs_sz);
+  _Traits::assign(__ptr + __lhs_sz + __rhs_sz, 1, _CharT());
   return __r;
 }
 
@@ -4055,8 +4118,19 @@ template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
 operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
           basic_string<_CharT, _Traits, _Allocator>&& __rhs) {
-  __rhs.insert(0, __lhs);
-  return std::move(__rhs);
+  // __rhs.insert(0, __lhs);
+  // return std::move(__rhs);
+  using _String                        = basic_string<_CharT, _Traits, _Allocator>;
+  typename _String::size_type __lhs_sz = __lhs.size();
+  typename _String::size_type __rhs_sz = __rhs.size();
+  _String __r(__uninitialized_size_tag(),
+              __lhs_sz + __rhs_sz,
+              _String::__alloc_traits::select_on_container_copy_construction(__rhs.get_allocator()));
+  auto __ptr = std::__to_address(__r.__get_pointer());
+  _Traits::copy(__ptr, __lhs.data(), __lhs_sz);
+  _Traits::copy(__ptr + __lhs_sz, __rhs.data(), __rhs_sz);
+  _Traits::assign(__ptr + __lhs_sz + __rhs_sz, 1, _CharT());
+  return __r;
 }
 
 #endif // _LIBCPP_STD_VER >= 26
diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
index 7f08d460a6fb12..28a14950eef0aa 100644
--- a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
@@ -110,9 +110,9 @@ constexpr void test(const CharT* x, const CharT* y, const CharT* expected) {
   }
 }
 
-template <typename CharT, typename TraitsT = std::char_traits<CharT>, typename AllocT = std::allocator<CharT>>
+template <typename CharT, typename TraitsT, typename AllocT = std::allocator<CharT>>
 constexpr void test() {
-  // Concatinate with an empty `string`/`string_view`
+  // Concatenate with an empty `string`/`string_view`
   test<CharT, TraitsT, AllocT>(CS(""), CS(""), CS(""));
   test<CharT, TraitsT, AllocT>(CS(""), CS("short"), CS("short"));
   test<CharT, TraitsT, AllocT>(CS(""), CS("not so short"), CS("not so short"));
@@ -133,23 +133,51 @@ constexpr void test() {
       CS("this is a much longer string+which is so much better"));
 }
 
+template <typename CharT>
 constexpr bool test() {
-  test<char, std::char_traits<char>>();
-  test<char, std::char_traits<char>, min_allocator<char>>();
-  test<char, std::char_traits<char>, safe_allocator<char>>();
+  test<CharT, std::char_traits<CharT>>();
+  test<CharT, std::char_traits<CharT>, min_allocator<CharT>>();
+  test<CharT, std::char_traits<CharT>, safe_allocator<CharT>>();
 
-#ifndef TEST_HAS_NO_WIDE_CHARACTERS
-  test<wchar_t, std::char_traits<wchar_t>>();
-  test<wchar_t, std::char_traits<wchar_t>, min_allocator<wchar_t>>();
-  test<wchar_t, std::char_traits<wchar_t>, safe_allocator<wchar_t>>();
-#endif
+  test<CharT, constexpr_char_traits<CharT>>();
+  test<CharT, constexpr_char_traits<CharT>, min_allocator<CharT>>();
+  test<CharT, constexpr_char_traits<CharT>, safe_allocator<CharT>>();
 
   return true;
 }
 
+// constexpr bool test() {
+//   test<char>();
+// #ifndef TEST_HAS_NO_WIDE_CHARACTERS
+//   test<wchar_t>();
+// #endif
+// #ifndef TEST_HAS_NO_CHAR8_T
+//   test<char8_t>();
+// #endif
+//   test<char16_t>();
+//   test<char32_t>();
+
+//   return true;
+// }
+
 int main(int, char**) {
-  test();
-  static_assert(test());
+  // test();
+  // static_assert(test());
+
+  test<char>();
+  static_assert(test<char>());
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+  static_assert(test<wchar_t>());
+#endif
+#ifndef TEST_HAS_NO_CHAR8_T
+  test<char8_t>();
+  static_assert(test<char8_t>());
+#endif
+  test<char16_t>();
+  static_assert(test<char16_t>());
+  test<char32_t>();
+  static_assert(test<char32_t>());
 
   return 0;
 }

>From 695b4b61acaf072c75d573aeef3545276b04b99d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Mon, 15 Apr 2024 12:28:33 +0300
Subject: [PATCH 8/9] Cleanup

---
 libcxx/include/string                         | 28 -------------------
 .../string_op+/string.string_view.pass.cpp    | 20 ++-----------
 2 files changed, 3 insertions(+), 45 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index 2a8cbb0b49072e..a2606b73d5e578 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -731,10 +731,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
 operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
           type_identity_t<basic_string_view<_CharT, _Traits>> __rhs);
 
-template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
-operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, type_identity_t<basic_string_view<_CharT, _Traits>> __rhs);
-
 template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
 operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
@@ -2179,26 +2175,7 @@ private:
   friend _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string operator+ <>(const basic_string&, const value_type*);
   friend _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string operator+ <>(const basic_string&, value_type);
 #if _LIBCPP_STD_VER >= 26
-  // friend constexpr basic_string operator+ <>(const basic_string&, const __self_view&);
-
-  // friend constexpr basic_string operator+ <>(const __self_view&, const basic_string&);
-
-  // friend constexpr operator+ <>(type_identity_t<__self_view> __lhs,
-  //                               const basic_string& __rhs);
-
-  // friend constexpr basic_string operator+
-  //     <>(type_identity_t<basic_string_view<_CharT2, _Traits2>> __lhs,
-  //        const basic_string<_CharT2, _Traits2, _Allocator2>& __rhs);
-
-  // template <class _CharT2, class _Traits2, class _Allocator2>
-  // friend constexpr basic_string<_CharT2, _Traits2, _Allocator2>
-  // operator+(type_identity_t<basic_string_view<_CharT2, _Traits2>>, const basic_string<_CharT2, _Traits2, _Allocator2>&);
-
-
-  // friend constexpr basic_string operator+ <>(type_identity_t<basic_string_view>, const basic_string&);
-
   friend constexpr basic_string operator+ <>(const basic_string&, type_identity_t<__self_view>);
-  friend constexpr basic_string operator+ <>(basic_string&&, type_identity_t<__self_view>);
   friend constexpr basic_string operator+ <>(type_identity_t<__self_view>, const basic_string&);
   friend constexpr basic_string operator+ <>(type_identity_t<__self_view>, basic_string&&);
 #endif
@@ -4098,9 +4075,6 @@ template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
 operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
           const basic_string<_CharT, _Traits, _Allocator>& __rhs) {
-  // basic_string<_CharT, _Traits, _Allocator> __r = __rhs;
-  // __r.insert(0, __lhs);
-  // return __r;
   using _String                        = basic_string<_CharT, _Traits, _Allocator>;
   typename _String::size_type __lhs_sz = __lhs.size();
   typename _String::size_type __rhs_sz = __rhs.size();
@@ -4118,8 +4092,6 @@ template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
 operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
           basic_string<_CharT, _Traits, _Allocator>&& __rhs) {
-  // __rhs.insert(0, __lhs);
-  // return std::move(__rhs);
   using _String                        = basic_string<_CharT, _Traits, _Allocator>;
   typename _String::size_type __lhs_sz = __lhs.size();
   typename _String::size_type __rhs_sz = __rhs.size();
diff --git a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
index 28a14950eef0aa..7efaa1125efb01 100644
--- a/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.nonmembers/string_op+/string.string_view.pass.cpp
@@ -38,6 +38,7 @@
 #include "constexpr_char_traits.h"
 #include "make_string.h"
 #include "min_allocator.h"
+#include "test_allocator.h"
 #include "test_macros.h"
 
 #define CS(S) MAKE_CSTRING(CharT, S)
@@ -138,32 +139,17 @@ constexpr bool test() {
   test<CharT, std::char_traits<CharT>>();
   test<CharT, std::char_traits<CharT>, min_allocator<CharT>>();
   test<CharT, std::char_traits<CharT>, safe_allocator<CharT>>();
+  test<CharT, std::char_traits<CharT>, test_allocator<CharT>>();
 
   test<CharT, constexpr_char_traits<CharT>>();
   test<CharT, constexpr_char_traits<CharT>, min_allocator<CharT>>();
   test<CharT, constexpr_char_traits<CharT>, safe_allocator<CharT>>();
+  test<CharT, constexpr_char_traits<CharT>, test_allocator<CharT>>();
 
   return true;
 }
 
-// constexpr bool test() {
-//   test<char>();
-// #ifndef TEST_HAS_NO_WIDE_CHARACTERS
-//   test<wchar_t>();
-// #endif
-// #ifndef TEST_HAS_NO_CHAR8_T
-//   test<char8_t>();
-// #endif
-//   test<char16_t>();
-//   test<char32_t>();
-
-//   return true;
-// }
-
 int main(int, char**) {
-  // test();
-  // static_assert(test());
-
   test<char>();
   static_assert(test<char>());
 #ifndef TEST_HAS_NO_WIDE_CHARACTERS

>From 6b77e5a8428bffcd124f5dffb157ca9732c809b9 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Tue, 16 Apr 2024 09:41:32 +0300
Subject: [PATCH 9/9] Reordered forward declared functions

---
 libcxx/include/string | 34 ++++++++++++++++++----------------
 1 file changed, 18 insertions(+), 16 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index fd6d894d0f63ec..65968d78163a2d 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -706,6 +706,24 @@ template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string<_CharT, _Traits, _Allocator>
 operator+(const basic_string<_CharT, _Traits, _Allocator>& __x, _CharT __y);
 
+#if _LIBCPP_STD_VER >= 26
+
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
+operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
+          type_identity_t<basic_string_view<_CharT, _Traits>> __rhs);
+
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
+operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
+          const basic_string<_CharT, _Traits, _Allocator>& __rhs);
+
+template <class _CharT, class _Traits, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
+operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs, basic_string<_CharT, _Traits, _Allocator>&& __rhs);
+
+#endif
+
 extern template _LIBCPP_EXPORTED_FROM_ABI string operator+
     <char, char_traits<char>, allocator<char> >(char const*, string const&);
 
@@ -726,22 +744,6 @@ struct __can_be_converted_to_string_view
 struct __uninitialized_size_tag {};
 struct __init_with_sentinel_tag {};
 
-#if _LIBCPP_STD_VER >= 26
-template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
-operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
-          type_identity_t<basic_string_view<_CharT, _Traits>> __rhs);
-
-template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
-operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs,
-          const basic_string<_CharT, _Traits, _Allocator>& __rhs);
-
-template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT, _Traits, _Allocator>
-operator+(type_identity_t<basic_string_view<_CharT, _Traits>> __lhs, basic_string<_CharT, _Traits, _Allocator>&& __rhs);
-#endif
-
 template <class _CharT, class _Traits, class _Allocator>
 class basic_string {
 private:



More information about the libcxx-commits mailing list