[libcxx-commits] [libcxx] 29378ab - [libc++] Implement P2438R2 (std::string::substr() &&)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Wed Nov 2 12:28:53 PDT 2022


Author: Nikolas Klauser
Date: 2022-11-02T20:28:47+01:00
New Revision: 29378ab24b98137b4959034a0882c3bbd97c46e4

URL: https://github.com/llvm/llvm-project/commit/29378ab24b98137b4959034a0882c3bbd97c46e4
DIFF: https://github.com/llvm/llvm-project/commit/29378ab24b98137b4959034a0882c3bbd97c46e4.diff

LOG: [libc++] Implement P2438R2 (std::string::substr() &&)

This doesn't affect our ABI because `std::string::substr()` isn't in the dylib and the mangling of `substr() const` and `substr() const&` are different.

Reviewed By: ldionne, Mordante, var-const, avogelsgesang, #libc

Spies: arphaman, huixie90, libcxx-commits

Differential Revision: https://reviews.llvm.org/D131668

Added: 
    libcxx/test/libcxx/strings/basic.string/string.cons/debug.iterator.substr.pass.cpp
    libcxx/test/std/strings/basic.string/string.cons/substr_rvalue.pass.cpp
    libcxx/test/std/strings/basic.string/string.ops/string_substr/substr_rvalue.pass.cpp

Modified: 
    libcxx/docs/ReleaseNotes.rst
    libcxx/docs/Status/Cxx2bPapers.csv
    libcxx/include/string
    libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.index.pass.cpp
    libcxx/test/std/strings/basic.string/string.ops/string_substr/substr.pass.cpp
    libcxx/test/support/count_new.h
    libcxx/test/support/make_string.h

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index d10fdde8d719f..abcebeb01ebee 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -46,6 +46,7 @@ Implemented Papers
   ``from_chars`` for Integral Types in ``<charconv>`` Header
 - P0220R1 - Adopt Library Fundamentals V1 TS Components for C++17
 - P0482R6 - char8_t: A type for UTF-8 characters and strings
+- P2438R2 - ``std::string::substr() &&``
 
 Improvements and New Features
 -----------------------------

diff  --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv
index 7017c31a92c41..f40cca9c7ac60 100644
--- a/libcxx/docs/Status/Cxx2bPapers.csv
+++ b/libcxx/docs/Status/Cxx2bPapers.csv
@@ -71,7 +71,7 @@
 "`P2408R5 <https://wg21.link/P2408R5>`__","LWG","Ranges iterators as inputs to non-Ranges algorithms","July 2022","",""
 "`P2417R2 <https://wg21.link/P2417R2>`__","LWG","A more ``constexpr`` ``bitset``","July 2022","|Complete|","16.0"
 "`P2419R2 <https://wg21.link/P2419R2>`__","LWG","Clarify handling of encodings in localized formatting of chrono types","July 2022","",""
-"`P2438R2 <https://wg21.link/P2438R2>`__","LWG","``std::string::substr() &&``","July 2022","",""
+"`P2438R2 <https://wg21.link/P2438R2>`__","LWG","``std::string::substr() &&``","July 2022","|Complete|","16.0"
 "`P2445R1 <https://wg21.link/P2445R1>`__","LWG","``forward_like``","July 2022","|Complete|","16.0"
 "`P2446R2 <https://wg21.link/P2446R2>`__","LWG","``views::as_rvalue``","July 2022","",""
 "`P2460R2 <https://wg21.link/P2460R2>`__","LWG","Relax requirements on ``wchar_t`` to match existing practices","July 2022","",""

diff  --git a/libcxx/include/string b/libcxx/include/string
index 8eb1d30970421..726bba3156f6e 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -109,6 +109,10 @@ public:
                  const allocator_type& a = allocator_type());                                   // constexpr since C++20
     basic_string(const basic_string& str, size_type pos, size_type n,
                  const Allocator& a = Allocator());                                             // constexpr since C++20
+    constexpr basic_string(
+        basic_string&& str, size_type pos, const Allocator& a = Allocator());                   // since C++23
+    constexpr basic_string(
+        basic_string&& str, size_type pos, size_type n, const Allocator& a = Allocator());      // since C++23
     template<class T>
         basic_string(const T& t, size_type pos, size_type n, const Allocator& a = Allocator()); // C++17, constexpr since C++20
     template <class T>
@@ -261,8 +265,9 @@ public:
     basic_string& replace(const_iterator i1, const_iterator i2, initializer_list<value_type>);  // constexpr since C++20
 
     size_type copy(value_type* s, size_type n, size_type pos = 0) const;                        // constexpr since C++20
-    basic_string substr(size_type pos = 0, size_type n = npos) const;                           // constexpr since C++20
-
+    basic_string substr(size_type pos = 0, size_type n = npos) const;                           // constexpr in C++20, removed in C++23
+    basic_string substr(size_type pos = 0, size_type n = npos) const&;                          // since C++23
+    constexpr basic_string substr(size_type pos = 0, size_type n = npos) &&;                    // since C++23
     void swap(basic_string& str)
         noexcept(allocator_traits<allocator_type>::propagate_on_container_swap::value ||
                  allocator_traits<allocator_type>::is_always_equal::value);                     // C++17, constexpr since C++20
@@ -897,6 +902,36 @@ public:
     std::__debug_db_insert_c(this);
   }
 
+#if _LIBCPP_STD_VER > 20
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  basic_string(basic_string&& __str, size_type __pos, const _Allocator& __alloc = _Allocator())
+      : basic_string(std::move(__str), __pos, npos, __alloc) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  basic_string(basic_string&& __str, size_type __pos, size_type __n, const _Allocator& __alloc = _Allocator())
+      : __r_(__default_init_tag(), __alloc) {
+    if (__pos > __str.size())
+      __throw_out_of_range();
+
+    auto __len = std::min<size_type>(__n, __str.size() - __pos);
+    if (__alloc_traits::is_always_equal::value || __alloc == __str.__alloc()) {
+      __r_.first() = __str.__r_.first();
+      __str.__default_init();
+
+      _Traits::move(data(), data() + __pos, __len);
+      __set_size(__len);
+      _Traits::assign(data()[__len], value_type());
+    } else {
+      // Perform a copy because the allocators are not compatible.
+      __init(__str.data() + __pos, __len);
+    }
+
+    std::__debug_db_insert_c(this);
+    if (__is_long())
+      std::__debug_db_swap(this, &__str);
+  }
+#endif
+
   template <class = __enable_if_t<__is_allocator<_Allocator>::value, nullptr_t> >
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(size_type __n, _CharT __c, const _Allocator& __a);
 
@@ -1324,8 +1359,24 @@ public:
 #endif // _LIBCPP_CXX03_LANG
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type copy(value_type* __s, size_type __n, size_type __pos = 0) const;
+
+    // TODO: Maybe don't pass in the allocator. See https://llvm.org/PR57190
+#if _LIBCPP_STD_VER <= 20
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
-    basic_string substr(size_type __pos = 0, size_type __n = npos) const;
+    basic_string substr(size_type __pos = 0, size_type __n = npos) const {
+      return basic_string(*this, __pos, __n, __alloc());
+    }
+#else
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    basic_string substr(size_type __pos = 0, size_type __n = npos) const& {
+      return basic_string(*this, __pos, __n, __alloc());
+    }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    basic_string substr(size_type __pos = 0, size_type __n = npos) && {
+      return basic_string(std::move(*this), __pos, __n, __alloc());
+    }
+#endif
 
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
     void swap(basic_string& __str)
@@ -3472,14 +3523,6 @@ basic_string<_CharT, _Traits, _Allocator>::copy(value_type* __s, size_type __n,
     return __rlen;
 }
 
-template <class _CharT, class _Traits, class _Allocator>
-inline _LIBCPP_CONSTEXPR_SINCE_CXX20
-basic_string<_CharT, _Traits, _Allocator>
-basic_string<_CharT, _Traits, _Allocator>::substr(size_type __pos, size_type __n) const
-{
-    return basic_string(*this, __pos, __n, __alloc());
-}
-
 template <class _CharT, class _Traits, class _Allocator>
 inline _LIBCPP_CONSTEXPR_SINCE_CXX20
 void

diff  --git a/libcxx/test/libcxx/strings/basic.string/string.cons/debug.iterator.substr.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.cons/debug.iterator.substr.pass.cpp
new file mode 100644
index 0000000000000..8eeb26233acc7
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/string.cons/debug.iterator.substr.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// Check that basic_string(basic_string&&, size_type, Allocator) and
+// basic_string(basic_string&&, size_type, size_type, Allocator) inserts the container into the debug database
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: !libcpp-has-debug-mode, c++03
+
+#include <cassert>
+#include <string>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+  using namespace std::string_literals;
+
+  {
+    std::string s = {"Banane"s, 1};
+    auto i        = s.begin();
+    assert(i[0] == 'a');
+    TEST_LIBCPP_ASSERT_FAILURE(i[5], "Attempted to subscript an iterator outside its valid range");
+  }
+  {
+    std::string s = {"Banane"s, 0, 5};
+    auto i        = s.begin();
+    assert(i[0] == 'B');
+    TEST_LIBCPP_ASSERT_FAILURE(i[5], "Attempted to subscript an iterator outside its valid range");
+  }
+  {
+    std::string s = {"long long string so no SSO"s, 21};
+    auto i        = s.begin();
+    assert(i[0] == 'o');
+    TEST_LIBCPP_ASSERT_FAILURE(i[5], "Attempted to subscript an iterator outside its valid range");
+  }
+  {
+    std::string s = {"long long string so no SSO"s, 0, 5};
+    auto i        = s.begin();
+    assert(i[0] == 'l');
+    TEST_LIBCPP_ASSERT_FAILURE(i[5], "Attempted to subscript an iterator outside its valid range");
+  }
+}

diff  --git a/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.index.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.index.pass.cpp
index f1b4e5666569b..13a2301937309 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.index.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.index.pass.cpp
@@ -20,6 +20,7 @@
 #include "min_allocator.h"
 
 int main(int, char**) {
+  using T = decltype(uint8_t() - uint8_t());
   {
     typedef std::string C;
     C c(1, '\0');

diff  --git a/libcxx/test/std/strings/basic.string/string.cons/substr_rvalue.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/substr_rvalue.pass.cpp
new file mode 100644
index 0000000000000..6a431be5cf851
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.cons/substr_rvalue.pass.cpp
@@ -0,0 +1,233 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <string>
+
+// constexpr basic_string(basic_string&& str, size_type pos, const Allocator& a = Allocator());
+// constexpr basic_string(basic_string&& str, size_type pos, size_type n, const Allocator& a = Allocator());
+
+#include <cassert>
+#include <string>
+
+#include "constexpr_char_traits.h"
+#include "count_new.h"
+#include "make_string.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+#include "test_macros.h"
+
+#define STR(string) MAKE_CSTRING(typename S::value_type, string)
+
+constexpr struct should_throw_exception_t {
+} should_throw_exception;
+
+template <class S>
+constexpr void test_string_pos(S orig, typename S::size_type pos, S expected) {
+#ifdef _LIBCPP_VERSION
+  ConstexprDisableAllocationGuard g;
+#endif
+  S substr(std::move(orig), pos);
+  LIBCPP_ASSERT(orig.__invariants());
+  LIBCPP_ASSERT(orig.empty());
+  LIBCPP_ASSERT(substr.__invariants());
+  assert(substr == expected);
+}
+
+template <class S>
+constexpr void test_string_pos(S orig, typename S::size_type pos, should_throw_exception_t) {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  if (!std::is_constant_evaluated()) {
+    try {
+      [[maybe_unused]] S substr = S(std::move(orig), pos);
+      assert(false);
+    } catch (const std::out_of_range&) {
+    }
+  }
+#else
+  (void)orig;
+  (void)pos;
+#endif
+}
+
+template <class S>
+constexpr void
+test_string_pos_alloc(S orig, typename S::size_type pos, const typename S::allocator_type& alloc, S expected) {
+  S substr(std::move(orig), pos, alloc);
+  LIBCPP_ASSERT(orig.__invariants());
+  LIBCPP_ASSERT(substr.__invariants());
+  assert(substr == expected);
+  assert(substr.get_allocator() == alloc);
+}
+
+template <class S>
+constexpr void test_string_pos_alloc(
+    S orig, typename S::size_type pos, const typename S::allocator_type& alloc, should_throw_exception_t) {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  if (!std::is_constant_evaluated()) {
+    try {
+      [[maybe_unused]] S substr = S(std::move(orig), pos, alloc);
+      assert(false);
+    } catch (const std::out_of_range&) {
+    }
+  }
+#else
+  (void)orig;
+  (void)pos;
+  (void)alloc;
+#endif
+}
+
+template <class S>
+constexpr void test_string_pos_n(S orig, typename S::size_type pos, typename S::size_type n, S expected) {
+#ifdef _LIBCPP_VERSION
+  ConstexprDisableAllocationGuard g;
+#endif
+  S substr(std::move(orig), pos, n);
+  LIBCPP_ASSERT(orig.__invariants());
+  LIBCPP_ASSERT(orig.empty());
+  LIBCPP_ASSERT(substr.__invariants());
+  assert(substr == expected);
+}
+
+template <class S>
+constexpr void test_string_pos_n(S orig, typename S::size_type pos, typename S::size_type n, should_throw_exception_t) {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  if (!std::is_constant_evaluated()) {
+    try {
+      [[maybe_unused]] S substr = S(std::move(orig), pos, n);
+      assert(false);
+    } catch (const std::out_of_range&) {
+    }
+  }
+#else
+  (void)orig;
+  (void)pos;
+  (void)n;
+#endif
+}
+
+template <class S>
+constexpr void test_string_pos_n_alloc(
+    S orig, typename S::size_type pos, typename S::size_type n, const typename S::allocator_type& alloc, S expected) {
+  S substr(std::move(orig), pos, n, alloc);
+  LIBCPP_ASSERT(orig.__invariants());
+  LIBCPP_ASSERT(substr.__invariants());
+  assert(substr == expected);
+  assert(substr.get_allocator() == alloc);
+}
+
+template <class S>
+constexpr void test_string_pos_n_alloc(
+    S orig,
+    typename S::size_type pos,
+    typename S::size_type n,
+    const typename S::allocator_type& alloc,
+    should_throw_exception_t) {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  if (!std::is_constant_evaluated()) {
+    try {
+      [[maybe_unused]] S substr = S(std::move(orig), pos, n, alloc);
+      assert(false);
+    } catch (const std::out_of_range&) {
+    }
+  }
+#else
+  (void)orig;
+  (void)pos;
+  (void)n;
+  (void)alloc;
+#endif
+}
+
+template <class S>
+constexpr void test_string(const typename S::allocator_type& alloc) {
+  test_string_pos<S>(STR(""), 0, STR(""));
+  test_string_pos<S>(STR(""), 1, should_throw_exception);
+  test_string_pos<S>(STR("Banane"), 1, STR("anane"));
+  test_string_pos<S>(STR("Banane"), 6, STR(""));
+  test_string_pos<S>(STR("Banane"), 7, should_throw_exception);
+  test_string_pos<S>(STR("long long string so no SSO"), 0, STR("long long string so no SSO"));
+  test_string_pos<S>(STR("long long string so no SSO"), 10, STR("string so no SSO"));
+  test_string_pos<S>(STR("long long string so no SSO"), 26, STR(""));
+  test_string_pos<S>(STR("long long string so no SSO"), 27, should_throw_exception);
+
+  test_string_pos_alloc<S>(STR(""), 0, alloc, STR(""));
+  test_string_pos_alloc<S>(STR(""), 1, alloc, should_throw_exception);
+  test_string_pos_alloc<S>(STR("Banane"), 1, alloc, STR("anane"));
+  test_string_pos_alloc<S>(STR("Banane"), 6, alloc, STR(""));
+  test_string_pos_alloc<S>(STR("Banane"), 7, alloc, should_throw_exception);
+  test_string_pos_alloc<S>(STR("long long string so no SSO"), 0, alloc, STR("long long string so no SSO"));
+  test_string_pos_alloc<S>(STR("long long string so no SSO"), 10, alloc, STR("string so no SSO"));
+  test_string_pos_alloc<S>(STR("long long string so no SSO"), 26, alloc, STR(""));
+  test_string_pos_alloc<S>(STR("long long string so no SSO"), 27, alloc, should_throw_exception);
+
+  test_string_pos_n<S>(STR(""), 0, 0, STR(""));
+  test_string_pos_n<S>(STR(""), 0, 1, STR(""));
+  test_string_pos_n<S>(STR(""), 1, 0, should_throw_exception);
+  test_string_pos_n<S>(STR(""), 1, 1, should_throw_exception);
+  test_string_pos_n<S>(STR("Banane"), 1, 10, STR("anane"));
+  test_string_pos_n<S>(STR("Banane"), 6, 0, STR(""));
+  test_string_pos_n<S>(STR("Banane"), 6, 5, STR(""));
+  test_string_pos_n<S>(STR("Banane"), 7, 10, should_throw_exception);
+  test_string_pos_n<S>(STR("long long string so no SSO"), 0, 10, STR("long long "));
+  test_string_pos_n<S>(STR("long long string so no SSO"), 10, 8, STR("string s"));
+  test_string_pos_n<S>(STR("long long string so no SSO"), 20, 10, STR("no SSO"));
+  test_string_pos_n<S>(STR("long long string so no SSO"), 26, 10, STR(""));
+  test_string_pos_n<S>(STR("long long string so no SSO"), 27, 10, should_throw_exception);
+
+  test_string_pos_n_alloc<S>(STR(""), 0, 0, alloc, STR(""));
+  test_string_pos_n_alloc<S>(STR(""), 0, 1, alloc, STR(""));
+  test_string_pos_n_alloc<S>(STR(""), 1, 0, alloc, should_throw_exception);
+  test_string_pos_n_alloc<S>(STR(""), 1, 1, alloc, should_throw_exception);
+  test_string_pos_n_alloc<S>(STR("Banane"), 1, 10, alloc, STR("anane"));
+  test_string_pos_n_alloc<S>(STR("Banane"), 6, 0, alloc, STR(""));
+  test_string_pos_n_alloc<S>(STR("Banane"), 6, 5, alloc, STR(""));
+  test_string_pos_n_alloc<S>(STR("Banane"), 7, 10, alloc, should_throw_exception);
+  test_string_pos_n_alloc<S>(STR("long long string so no SSO"), 0, 10, alloc, STR("long long "));
+  test_string_pos_n_alloc<S>(STR("long long string so no SSO"), 10, 8, alloc, STR("string s"));
+  test_string_pos_n_alloc<S>(STR("long long string so no SSO"), 20, 10, alloc, STR("no SSO"));
+  test_string_pos_n_alloc<S>(STR("long long string so no SSO"), 26, 10, alloc, STR(""));
+  test_string_pos_n_alloc<S>(STR("long long string so no SSO"), 27, 10, alloc, should_throw_exception);
+}
+
+template <class CharT, class CharTraits>
+constexpr void test_allocators() {
+  test_string<std::basic_string<CharT, CharTraits, std::allocator<CharT>>>(std::allocator<CharT>{});
+  test_string<std::basic_string<CharT, CharTraits, min_allocator<CharT>>>(min_allocator<CharT>{});
+  test_string<std::basic_string<CharT, CharTraits, test_allocator<CharT>>>(test_allocator<CharT>{42});
+}
+
+template <class CharT>
+constexpr bool test_char_traits() {
+  test_allocators<CharT, std::char_traits<CharT>>();
+  test_allocators<CharT, constexpr_char_traits<CharT>>();
+
+  return true;
+}
+
+int main(int, char**) {
+  // TODO: put these into a single function when we increase the constexpr step limit
+  test_char_traits<char>();
+  static_assert(test_char_traits<char>());
+  test_char_traits<char16_t>();
+  static_assert(test_char_traits<char16_t>());
+  test_char_traits<char32_t>();
+  static_assert(test_char_traits<char32_t>());
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_char_traits<wchar_t>();
+  static_assert(test_char_traits<wchar_t>());
+#endif
+#ifndef TEST_HAS_NO_CHAR8_T
+  test_char_traits<char8_t>();
+  static_assert(test_char_traits<char8_t>());
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr.pass.cpp b/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr.pass.cpp
index 4ae469d597a0f..7f6404abd8261 100644
--- a/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr.pass.cpp
@@ -8,7 +8,8 @@
 
 // <string>
 
-// basic_string substr(size_type pos = 0, size_type n = npos) const; // constexpr since C++20
+// basic_string substr(size_type pos = 0, size_type n = npos) const; // constexpr since C++20, removed in C++23
+// basic_string substr(size_type pos = 0, size_type n = npos) const&; // since in C++23
 
 #include <string>
 #include <stdexcept>
@@ -47,130 +48,72 @@ test(const S& s, typename S::size_type pos, typename S::size_type n)
 #endif
 }
 
+template <class S>
+TEST_CONSTEXPR_CXX20 void test_string() {
+  test(S(""), 0, 0);
+  test(S(""), 1, 0);
+  test(S("pniot"), 0, 0);
+  test(S("htaob"), 0, 1);
+  test(S("fodgq"), 0, 2);
+  test(S("hpqia"), 0, 4);
+  test(S("qanej"), 0, 5);
+  test(S("dfkap"), 1, 0);
+  test(S("clbao"), 1, 1);
+  test(S("ihqrf"), 1, 2);
+  test(S("mekdn"), 1, 3);
+  test(S("ngtjf"), 1, 4);
+  test(S("srdfq"), 2, 0);
+  test(S("qkdrs"), 2, 1);
+  test(S("ikcrq"), 2, 2);
+  test(S("cdaih"), 2, 3);
+  test(S("dmajb"), 4, 0);
+  test(S("karth"), 4, 1);
+  test(S("lhcdo"), 5, 0);
+  test(S("acbsj"), 6, 0);
+  test(S("pbsjikaole"), 0, 0);
+  test(S("pcbahntsje"), 0, 1);
+  test(S("mprdjbeiak"), 0, 5);
+  test(S("fhepcrntko"), 0, 9);
+  test(S("eqmpaidtls"), 0, 10);
+  test(S("joidhalcmq"), 1, 0);
+  test(S("omigsphflj"), 1, 1);
+  test(S("kocgbphfji"), 1, 4);
+  test(S("onmjekafbi"), 1, 8);
+  test(S("fbslrjiqkm"), 1, 9);
+  test(S("oqmrjahnkg"), 5, 0);
+  test(S("jeidpcmalh"), 5, 1);
+  test(S("schfalibje"), 5, 2);
+  test(S("crliponbqe"), 5, 4);
+  test(S("igdscopqtm"), 5, 5);
+  test(S("qngpdkimlc"), 9, 0);
+  test(S("thdjgafrlb"), 9, 1);
+  test(S("hcjitbfapl"), 10, 0);
+  test(S("mgojkldsqh"), 11, 0);
+  test(S("gfshlcmdjreqipbontak"), 0, 0);
+  test(S("nadkhpfemgclosibtjrq"), 0, 1);
+  test(S("nkodajteqplrbifhmcgs"), 0, 10);
+  test(S("ofdrqmkeblthacpgijsn"), 0, 19);
+  test(S("gbmetiprqdoasckjfhln"), 0, 20);
+  test(S("bdfjqgatlksriohemnpc"), 1, 0);
+  test(S("crnklpmegdqfiashtojb"), 1, 1);
+  test(S("ejqcnahdrkfsmptilgbo"), 1, 9);
+  test(S("jsbtafedocnirgpmkhql"), 1, 18);
+  test(S("prqgnlbaejsmkhdctoif"), 1, 19);
+  test(S("qnmodrtkebhpasifgcjl"), 10, 0);
+  test(S("pejafmnokrqhtisbcdgl"), 10, 1);
+  test(S("cpebqsfmnjdolhkratgi"), 10, 5);
+  test(S("odnqkgijrhabfmcestlp"), 10, 9);
+  test(S("lmofqdhpkibagnrcjste"), 10, 10);
+  test(S("lgjqketopbfahrmnsicd"), 19, 0);
+  test(S("ktsrmnqagdecfhijpobl"), 19, 1);
+  test(S("lsaijeqhtrbgcdmpfkno"), 20, 0);
+  test(S("dplqartnfgejichmoskb"), 21, 0);
+}
+
 TEST_CONSTEXPR_CXX20 bool test() {
-    {
-    typedef std::string S;
-    test(S(""), 0, 0);
-    test(S(""), 1, 0);
-    test(S("pniot"), 0, 0);
-    test(S("htaob"), 0, 1);
-    test(S("fodgq"), 0, 2);
-    test(S("hpqia"), 0, 4);
-    test(S("qanej"), 0, 5);
-    test(S("dfkap"), 1, 0);
-    test(S("clbao"), 1, 1);
-    test(S("ihqrf"), 1, 2);
-    test(S("mekdn"), 1, 3);
-    test(S("ngtjf"), 1, 4);
-    test(S("srdfq"), 2, 0);
-    test(S("qkdrs"), 2, 1);
-    test(S("ikcrq"), 2, 2);
-    test(S("cdaih"), 2, 3);
-    test(S("dmajb"), 4, 0);
-    test(S("karth"), 4, 1);
-    test(S("lhcdo"), 5, 0);
-    test(S("acbsj"), 6, 0);
-    test(S("pbsjikaole"), 0, 0);
-    test(S("pcbahntsje"), 0, 1);
-    test(S("mprdjbeiak"), 0, 5);
-    test(S("fhepcrntko"), 0, 9);
-    test(S("eqmpaidtls"), 0, 10);
-    test(S("joidhalcmq"), 1, 0);
-    test(S("omigsphflj"), 1, 1);
-    test(S("kocgbphfji"), 1, 4);
-    test(S("onmjekafbi"), 1, 8);
-    test(S("fbslrjiqkm"), 1, 9);
-    test(S("oqmrjahnkg"), 5, 0);
-    test(S("jeidpcmalh"), 5, 1);
-    test(S("schfalibje"), 5, 2);
-    test(S("crliponbqe"), 5, 4);
-    test(S("igdscopqtm"), 5, 5);
-    test(S("qngpdkimlc"), 9, 0);
-    test(S("thdjgafrlb"), 9, 1);
-    test(S("hcjitbfapl"), 10, 0);
-    test(S("mgojkldsqh"), 11, 0);
-    test(S("gfshlcmdjreqipbontak"), 0, 0);
-    test(S("nadkhpfemgclosibtjrq"), 0, 1);
-    test(S("nkodajteqplrbifhmcgs"), 0, 10);
-    test(S("ofdrqmkeblthacpgijsn"), 0, 19);
-    test(S("gbmetiprqdoasckjfhln"), 0, 20);
-    test(S("bdfjqgatlksriohemnpc"), 1, 0);
-    test(S("crnklpmegdqfiashtojb"), 1, 1);
-    test(S("ejqcnahdrkfsmptilgbo"), 1, 9);
-    test(S("jsbtafedocnirgpmkhql"), 1, 18);
-    test(S("prqgnlbaejsmkhdctoif"), 1, 19);
-    test(S("qnmodrtkebhpasifgcjl"), 10, 0);
-    test(S("pejafmnokrqhtisbcdgl"), 10, 1);
-    test(S("cpebqsfmnjdolhkratgi"), 10, 5);
-    test(S("odnqkgijrhabfmcestlp"), 10, 9);
-    test(S("lmofqdhpkibagnrcjste"), 10, 10);
-    test(S("lgjqketopbfahrmnsicd"), 19, 0);
-    test(S("ktsrmnqagdecfhijpobl"), 19, 1);
-    test(S("lsaijeqhtrbgcdmpfkno"), 20, 0);
-    test(S("dplqartnfgejichmoskb"), 21, 0);
-    }
+  test_string<std::string>();
 #if TEST_STD_VER >= 11
-    {
-    typedef std::basic_string<char, std::char_traits<char>, min_allocator<char>> S;
-    test(S(""), 0, 0);
-    test(S(""), 1, 0);
-    test(S("pniot"), 0, 0);
-    test(S("htaob"), 0, 1);
-    test(S("fodgq"), 0, 2);
-    test(S("hpqia"), 0, 4);
-    test(S("qanej"), 0, 5);
-    test(S("dfkap"), 1, 0);
-    test(S("clbao"), 1, 1);
-    test(S("ihqrf"), 1, 2);
-    test(S("mekdn"), 1, 3);
-    test(S("ngtjf"), 1, 4);
-    test(S("srdfq"), 2, 0);
-    test(S("qkdrs"), 2, 1);
-    test(S("ikcrq"), 2, 2);
-    test(S("cdaih"), 2, 3);
-    test(S("dmajb"), 4, 0);
-    test(S("karth"), 4, 1);
-    test(S("lhcdo"), 5, 0);
-    test(S("acbsj"), 6, 0);
-    test(S("pbsjikaole"), 0, 0);
-    test(S("pcbahntsje"), 0, 1);
-    test(S("mprdjbeiak"), 0, 5);
-    test(S("fhepcrntko"), 0, 9);
-    test(S("eqmpaidtls"), 0, 10);
-    test(S("joidhalcmq"), 1, 0);
-    test(S("omigsphflj"), 1, 1);
-    test(S("kocgbphfji"), 1, 4);
-    test(S("onmjekafbi"), 1, 8);
-    test(S("fbslrjiqkm"), 1, 9);
-    test(S("oqmrjahnkg"), 5, 0);
-    test(S("jeidpcmalh"), 5, 1);
-    test(S("schfalibje"), 5, 2);
-    test(S("crliponbqe"), 5, 4);
-    test(S("igdscopqtm"), 5, 5);
-    test(S("qngpdkimlc"), 9, 0);
-    test(S("thdjgafrlb"), 9, 1);
-    test(S("hcjitbfapl"), 10, 0);
-    test(S("mgojkldsqh"), 11, 0);
-    test(S("gfshlcmdjreqipbontak"), 0, 0);
-    test(S("nadkhpfemgclosibtjrq"), 0, 1);
-    test(S("nkodajteqplrbifhmcgs"), 0, 10);
-    test(S("ofdrqmkeblthacpgijsn"), 0, 19);
-    test(S("gbmetiprqdoasckjfhln"), 0, 20);
-    test(S("bdfjqgatlksriohemnpc"), 1, 0);
-    test(S("crnklpmegdqfiashtojb"), 1, 1);
-    test(S("ejqcnahdrkfsmptilgbo"), 1, 9);
-    test(S("jsbtafedocnirgpmkhql"), 1, 18);
-    test(S("prqgnlbaejsmkhdctoif"), 1, 19);
-    test(S("qnmodrtkebhpasifgcjl"), 10, 0);
-    test(S("pejafmnokrqhtisbcdgl"), 10, 1);
-    test(S("cpebqsfmnjdolhkratgi"), 10, 5);
-    test(S("odnqkgijrhabfmcestlp"), 10, 9);
-    test(S("lmofqdhpkibagnrcjste"), 10, 10);
-    test(S("lgjqketopbfahrmnsicd"), 19, 0);
-    test(S("ktsrmnqagdecfhijpobl"), 19, 1);
-    test(S("lsaijeqhtrbgcdmpfkno"), 20, 0);
-    test(S("dplqartnfgejichmoskb"), 21, 0);
-    }
+  test_string<std::basic_string<char, std::char_traits<char>, min_allocator<char>>>();
 #endif
 
   return true;

diff  --git a/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr_rvalue.pass.cpp b/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr_rvalue.pass.cpp
new file mode 100644
index 0000000000000..13019ae351077
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/string.ops/string_substr/substr_rvalue.pass.cpp
@@ -0,0 +1,103 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <string>
+
+// constexpr basic_string substr(size_type pos = 0, size_type n = npos) &&;
+
+#include <algorithm>
+#include <string>
+
+#include "constexpr_char_traits.h"
+#include "make_string.h"
+#include "min_allocator.h"
+#include "test_allocator.h"
+
+#define STR(string) MAKE_CSTRING(typename S::value_type, string)
+
+constexpr struct should_throw_exception_t {
+} should_throw_exception;
+
+template <class S>
+constexpr void test(S orig, size_t pos, ptr
diff _t n, S expected) {
+  S str = std::move(orig).substr(pos, n);
+  LIBCPP_ASSERT(orig.__invariants());
+  LIBCPP_ASSERT(str.__invariants());
+  assert(str == expected);
+}
+
+template <class S>
+constexpr void test(S orig, size_t pos, ptr
diff _t n, should_throw_exception_t) {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+  if (!std::is_constant_evaluated()) {
+    try {
+      S str = std::move(orig).substr(pos, n);
+      assert(false);
+    } catch (const std::out_of_range&) {
+    }
+  }
+#else
+  (void)orig;
+  (void)pos;
+  (void)n;
+#endif
+}
+
+template <class S>
+constexpr void test_string() {
+  test<S>(STR(""), 0, 0, STR(""));
+  test<S>(STR(""), 0, 1, STR(""));
+  test<S>(STR(""), 1, 0, should_throw_exception);
+  test<S>(STR(""), 1, 1, should_throw_exception);
+  test<S>(STR("short string"), 0, 1, STR("s"));
+  test<S>(STR("short string"), 5, 5, STR(" stri"));
+  test<S>(STR("short string"), 12, 5, STR(""));
+  test<S>(STR("short string"), 13, 5, should_throw_exception);
+  test<S>(STR("long long string so no SSO"), 0, 0, STR(""));
+  test<S>(STR("long long string so no SSO"), 0, 10, STR("long long "));
+  test<S>(STR("long long string so no SSO"), 10, 10, STR("string so "));
+  test<S>(STR("long long string so no SSO"), 20, 10, STR("no SSO"));
+  test<S>(STR("long long string so no SSO"), 26, 10, STR(""));
+  test<S>(STR("long long string so no SSO"), 27, 0, should_throw_exception);
+}
+
+template <class CharT, class CharTraits>
+constexpr void test_allocators() {
+  test_string<std::basic_string<CharT, CharTraits, std::allocator<CharT>>>();
+  test_string<std::basic_string<CharT, CharTraits, min_allocator<CharT>>>();
+  test_string<std::basic_string<CharT, CharTraits, test_allocator<CharT>>>();
+}
+
+template <class CharT>
+constexpr void test_char_traits() {
+  test_allocators<CharT, std::char_traits<CharT>>();
+  test_allocators<CharT, constexpr_char_traits<CharT>>();
+}
+
+constexpr bool test() {
+  test_char_traits<char>();
+  test_char_traits<char16_t>();
+  test_char_traits<char32_t>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test_char_traits<wchar_t>();
+#endif
+#ifndef TEST_HAS_NO_CHAR8_T
+  test_char_traits<char8_t>();
+#endif
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index aadebe444708f..645062a01446d 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -472,6 +472,40 @@ struct DisableAllocationGuard {
     DisableAllocationGuard& operator=(DisableAllocationGuard const&);
 };
 
+#if TEST_STD_VER >= 20
+
+struct ConstexprDisableAllocationGuard {
+    TEST_CONSTEXPR_CXX14 explicit ConstexprDisableAllocationGuard(bool disable = true) : m_disabled(disable)
+    {
+        if (!TEST_IS_CONSTANT_EVALUATED) {
+            // Don't re-disable if already disabled.
+            if (globalMemCounter.disable_allocations == true) m_disabled = false;
+            if (m_disabled) globalMemCounter.disableAllocations();
+        } else {
+            m_disabled = false;
+        }
+    }
+
+    TEST_CONSTEXPR_CXX14 void release() {
+        if (!TEST_IS_CONSTANT_EVALUATED) {
+            if (m_disabled) globalMemCounter.enableAllocations();
+            m_disabled = false;
+        }
+    }
+
+    TEST_CONSTEXPR_CXX20 ~ConstexprDisableAllocationGuard() {
+        release();
+    }
+
+private:
+    bool m_disabled;
+
+    ConstexprDisableAllocationGuard(ConstexprDisableAllocationGuard const&);
+    ConstexprDisableAllocationGuard& operator=(ConstexprDisableAllocationGuard const&);
+};
+
+#endif
+
 struct RequireAllocationGuard {
     explicit RequireAllocationGuard(std::size_t RequireAtLeast = 1)
             : m_req_alloc(RequireAtLeast),

diff  --git a/libcxx/test/support/make_string.h b/libcxx/test/support/make_string.h
index 00c2a48e3d004..728b6540abe07 100644
--- a/libcxx/test/support/make_string.h
+++ b/libcxx/test/support/make_string.h
@@ -89,7 +89,7 @@ struct MultiStringType {
 // This helper is used in unit tests to make them generic. The input should be
 // valid ASCII which means the input is also valid UTF-8.
 #define MAKE_CSTRING(CharT, Str)                                               \
-  MKSTR(Str).as_ptr((const CharT*)0)
+  MKSTR(Str).as_ptr(static_cast<const CharT*>(nullptr))
 
 // Like MAKE_CSTRING but makes a basic_string<CharT>. Embedded nulls are OK.
 #define MAKE_STRING(CharT, Str)                                                \


        


More information about the libcxx-commits mailing list