[libcxx-commits] [libcxx] aeecef0 - [libc++] Remove default definition of std::char_traits

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Wed Nov 23 06:51:25 PST 2022


Author: Louis Dionne
Date: 2022-11-23T09:51:01-05:00
New Revision: aeecef08c385b1e4955155dd649a2d3724463849

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

LOG: [libc++] Remove default definition of std::char_traits

This patch removes the base template implementation for std::char_traits.
If my reading of http://eel.is/c++draft/char.traits is correct, the
Standard mandates that the library provides specializations for several
types like char and wchar_t, but not any implementation in the base
template. Indeed, such an implementation is bound to be incorrect for
most types anyways, since things like `eof()` and `int_type` will definitely
have to be customized.

Since the base template implementation should not have worked for anyone,
this shouldn't be a breaking change (I expect that anyone defining a
custom character type today will already have to provide their own
specialization of char_traits). However, since we're aware of some users
of char_traits for unsigned char and signed char, we're keeping those two
specializations around for two releases to give people some time to migrate.

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

Added: 
    libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.deprecated.verify.cpp
    libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.pass.cpp

Modified: 
    libcxx/docs/ReleaseNotes.rst
    libcxx/include/__string/char_traits.h
    libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp
    libcxx/test/std/strings/basic.string/string.modifiers/string_append/push_back.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index aac5d500e5036..c731eab01dbca 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -100,8 +100,19 @@ Deprecations and Removals
 - The ``_LIBCPP_DEBUG`` macro is not honored anymore, and it is an error to try to use it. Please migrate to
   ``_LIBCPP_ENABLE_DEBUG_MODE`` instead.
 
+- A base template for ``std::char_traits`` is not provided anymore. The Standard mandates that the library
+  provides specializations for several types like ``char`` and ``wchar_t``, which libc++ does. However, libc++
+  used to additionally provide a default implementation for ``std::char_traits<T>`` for arbitrary ``T``. Not
+  only does the Standard not mandate that one is provided, but such an implementation is bound to be incorrect
+  for some types, so it has been removed. As an exception, ``std::char_traits<unsigned char>`` and
+  ``std::char_traits<signed char>`` are kept for a limited period of time and marked as deprecated to let people
+  move off of those, since we know there were some users of those. They will be removed in LLVM 18.
+
 Upcoming Deprecations and Removals
 ----------------------------------
+- The specializations of ``std::char_traits`` for ``unsigned char`` and ``signed char`` are provided until
+  LLVM 18. Those non-standard specializations are provided for a transition period and marked as deprecated
+  but will be removed in the future.
 
 API Changes
 -----------

diff  --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h
index 3d9eb1fc05d13..f6ca48d6033d8 100644
--- a/libcxx/include/__string/char_traits.h
+++ b/libcxx/include/__string/char_traits.h
@@ -39,132 +39,37 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _CharT>
-struct _LIBCPP_TEMPLATE_VIS char_traits
+struct char_traits;
+/*
+The Standard does not define the base template for char_traits because it is impossible to provide
+a correct definition for arbitrary character types. Instead, it requires implementations to provide
+specializations for predefined character types like `char`, `wchar_t` and others. We provide this as
+exposition-only to document what members a char_traits specialization should provide:
 {
     using char_type  = _CharT;
-    using int_type   = int;
-    using off_type   = streamoff;
-    using pos_type   = streampos;
-    using state_type = mbstate_t;
-
-    static inline void _LIBCPP_CONSTEXPR_SINCE_CXX17
-        assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
-    static inline _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT
-        {return __c1 == __c2;}
-    static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
-        {return __c1 < __c2;}
-
-    static _LIBCPP_CONSTEXPR_SINCE_CXX17
-    int compare(const char_type* __s1, const char_type* __s2, size_t __n);
-    _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
-    size_t length(const char_type* __s);
-    _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
-    const char_type* find(const char_type* __s, size_t __n, const char_type& __a);
-    static _LIBCPP_CONSTEXPR_SINCE_CXX20
-    char_type*       move(char_type* __s1, const char_type* __s2, size_t __n);
-    _LIBCPP_INLINE_VISIBILITY
-    static _LIBCPP_CONSTEXPR_SINCE_CXX20
-    char_type*       copy(char_type* __s1, const char_type* __s2, size_t __n);
-    _LIBCPP_INLINE_VISIBILITY
-    static _LIBCPP_CONSTEXPR_SINCE_CXX20
-    char_type*       assign(char_type* __s, size_t __n, char_type __a);
-
-    static inline _LIBCPP_CONSTEXPR int_type  not_eof(int_type __c) _NOEXCEPT
-        {return eq_int_type(__c, eof()) ? ~eof() : __c;}
-    static inline _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT
-        {return char_type(__c);}
-    static inline _LIBCPP_CONSTEXPR int_type  to_int_type(char_type __c) _NOEXCEPT
-        {return int_type(__c);}
-    static inline _LIBCPP_CONSTEXPR bool      eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT
-        {return __c1 == __c2;}
-    static inline _LIBCPP_CONSTEXPR int_type  eof() _NOEXCEPT
-        {return int_type(EOF);}
+    using int_type   = ...;
+    using off_type   = ...;
+    using pos_type   = ...;
+    using state_type = ...;
+
+    static void assign(char_type&, const char_type&);
+    static bool eq(char_type, char_type);
+    static bool lt(char_type, char_type);
+
+    static int              compare(const char_type*, const char_type*, size_t);
+    static size_t           length(const char_type*);
+    static const char_type* find(const char_type*, size_t, const char_type&);
+    static char_type*       move(char_type*, const char_type*, size_t);
+    static char_type*       copy(char_type*, const char_type* __s2, size_t);
+    static char_type*       assign(char_type*, size_t, char_type);
+
+    static int_type  not_eof(int_type);
+    static char_type to_char_type(int_type);
+    static int_type  to_int_type(char_type);
+    static bool      eq_int_type(int_type, int_type);
+    static int_type  eof();
 };
-
-template <class _CharT>
-_LIBCPP_CONSTEXPR_SINCE_CXX17 int
-char_traits<_CharT>::compare(const char_type* __s1, const char_type* __s2, size_t __n)
-{
-    for (; __n; --__n, ++__s1, ++__s2)
-    {
-        if (lt(*__s1, *__s2))
-            return -1;
-        if (lt(*__s2, *__s1))
-            return 1;
-    }
-    return 0;
-}
-
-template <class _CharT>
-inline
-_LIBCPP_CONSTEXPR_SINCE_CXX17 size_t
-char_traits<_CharT>::length(const char_type* __s)
-{
-    size_t __len = 0;
-    for (; !eq(*__s, char_type(0)); ++__s)
-        ++__len;
-    return __len;
-}
-
-template <class _CharT>
-inline
-_LIBCPP_CONSTEXPR_SINCE_CXX17 const _CharT*
-char_traits<_CharT>::find(const char_type* __s, size_t __n, const char_type& __a)
-{
-    for (; __n; --__n)
-    {
-        if (eq(*__s, __a))
-            return __s;
-        ++__s;
-    }
-    return nullptr;
-}
-
-template <class _CharT>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _CharT*
-char_traits<_CharT>::move(char_type* __s1, const char_type* __s2, size_t __n)
-{
-    if (__n == 0) return __s1;
-    char_type* __r = __s1;
-    if (__s1 < __s2)
-    {
-        for (; __n; --__n, ++__s1, ++__s2)
-            assign(*__s1, *__s2);
-    }
-    else if (__s2 < __s1)
-    {
-        __s1 += __n;
-        __s2 += __n;
-        for (; __n; --__n)
-            assign(*--__s1, *--__s2);
-    }
-    return __r;
-}
-
-template <class _CharT>
-inline _LIBCPP_CONSTEXPR_SINCE_CXX20
-_CharT*
-char_traits<_CharT>::copy(char_type* __s1, const char_type* __s2, size_t __n)
-{
-    if (!__libcpp_is_constant_evaluated()) {
-        _LIBCPP_ASSERT(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range");
-    }
-    char_type* __r = __s1;
-    for (; __n; --__n, ++__s1, ++__s2)
-        assign(*__s1, *__s2);
-    return __r;
-}
-
-template <class _CharT>
-inline _LIBCPP_CONSTEXPR_SINCE_CXX20
-_CharT*
-char_traits<_CharT>::assign(char_type* __s, size_t __n, char_type __a)
-{
-    char_type* __r = __s;
-    for (; __n; --__n, ++__s)
-        assign(*__s, __a);
-    return __r;
-}
+*/
 
 template <class _CharT>
 _LIBCPP_HIDE_FROM_ABI static inline _LIBCPP_CONSTEXPR_SINCE_CXX20
@@ -300,7 +205,6 @@ char_traits<char>::find(const char_type* __s, size_t __n, const char_type& __a)
 #endif
 }
 
-
 // char_traits<wchar_t>
 
 #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
@@ -713,6 +617,202 @@ char_traits<char32_t>::find(const char_type* __s, size_t __n, const char_type& _
     return nullptr;
 }
 
+//
+// Temporary extensions for std::char_traits<unsigned char> and std::char_traits<signed char>.
+// TODO: Remove those in LLVM 18.
+//
+template <>
+struct _LIBCPP_TEMPLATE_VIS
+    _LIBCPP_DEPRECATED_("char_traits<unsigned char> is non-standard and is provided for a temporary period. It will be removed in LLVM 18, so please migrate off of it.")
+    char_traits<unsigned char>
+{
+    using char_type  = unsigned char;
+    using int_type   = int;
+    using off_type   = streamoff;
+    using pos_type   = streampos;
+    using state_type = mbstate_t;
+
+    static inline void _LIBCPP_CONSTEXPR_SINCE_CXX17
+        assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
+    static inline _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT
+        {return __c1 == __c2;}
+    static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
+        {return __c1 < __c2;}
+
+    static _LIBCPP_CONSTEXPR_SINCE_CXX17
+    int compare(const char_type* __s1, const char_type* __s2, size_t __n) {
+        for (; __n; --__n, ++__s1, ++__s2)
+        {
+            if (lt(*__s1, *__s2))
+                return -1;
+            if (lt(*__s2, *__s1))
+                return 1;
+        }
+        return 0;
+    }
+    _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
+    size_t length(const char_type* __s) {
+        size_t __len = 0;
+        for (; !eq(*__s, char_type(0)); ++__s)
+            ++__len;
+        return __len;
+    }
+    _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
+    const char_type* find(const char_type* __s, size_t __n, const char_type& __a) {
+        for (; __n; --__n)
+        {
+            if (eq(*__s, __a))
+                return __s;
+            ++__s;
+        }
+        return nullptr;
+    }
+    static _LIBCPP_CONSTEXPR_SINCE_CXX20
+    char_type*       move(char_type* __s1, const char_type* __s2, size_t __n) {
+        if (__n == 0) return __s1;
+        char_type* __r = __s1;
+        if (__s1 < __s2)
+        {
+            for (; __n; --__n, ++__s1, ++__s2)
+                assign(*__s1, *__s2);
+        }
+        else if (__s2 < __s1)
+        {
+            __s1 += __n;
+            __s2 += __n;
+            for (; __n; --__n)
+                assign(*--__s1, *--__s2);
+        }
+        return __r;
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    static _LIBCPP_CONSTEXPR_SINCE_CXX20
+    char_type*       copy(char_type* __s1, const char_type* __s2, size_t __n) {
+        if (!__libcpp_is_constant_evaluated()) {
+            _LIBCPP_ASSERT(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range");
+        }
+        char_type* __r = __s1;
+        for (; __n; --__n, ++__s1, ++__s2)
+            assign(*__s1, *__s2);
+        return __r;
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    static _LIBCPP_CONSTEXPR_SINCE_CXX20
+    char_type*       assign(char_type* __s, size_t __n, char_type __a) {
+        char_type* __r = __s;
+        for (; __n; --__n, ++__s)
+            assign(*__s, __a);
+        return __r;
+    }
+
+    static inline _LIBCPP_CONSTEXPR int_type  not_eof(int_type __c) _NOEXCEPT
+        {return eq_int_type(__c, eof()) ? ~eof() : __c;}
+    static inline _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT
+        {return char_type(__c);}
+    static inline _LIBCPP_CONSTEXPR int_type  to_int_type(char_type __c) _NOEXCEPT
+        {return int_type(__c);}
+    static inline _LIBCPP_CONSTEXPR bool      eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT
+        {return __c1 == __c2;}
+    static inline _LIBCPP_CONSTEXPR int_type  eof() _NOEXCEPT
+        {return int_type(EOF);}
+};
+
+template <>
+struct _LIBCPP_TEMPLATE_VIS
+    _LIBCPP_DEPRECATED_("char_traits<signed char> is non-standard and is provided for a temporary period. It will be removed in LLVM 18, so please migrate off of it.")
+    char_traits<signed char>
+{
+    using char_type  = signed char;
+    using int_type   = int;
+    using off_type   = streamoff;
+    using pos_type   = streampos;
+    using state_type = mbstate_t;
+
+    static inline void _LIBCPP_CONSTEXPR_SINCE_CXX17
+        assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
+    static inline _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT
+        {return __c1 == __c2;}
+    static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
+        {return __c1 < __c2;}
+
+    static _LIBCPP_CONSTEXPR_SINCE_CXX17
+    int compare(const char_type* __s1, const char_type* __s2, size_t __n) {
+        for (; __n; --__n, ++__s1, ++__s2)
+        {
+            if (lt(*__s1, *__s2))
+                return -1;
+            if (lt(*__s2, *__s1))
+                return 1;
+        }
+        return 0;
+    }
+    _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
+    size_t length(const char_type* __s) {
+        size_t __len = 0;
+        for (; !eq(*__s, char_type(0)); ++__s)
+            ++__len;
+        return __len;
+    }
+    _LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
+    const char_type* find(const char_type* __s, size_t __n, const char_type& __a) {
+        for (; __n; --__n)
+        {
+            if (eq(*__s, __a))
+                return __s;
+            ++__s;
+        }
+        return nullptr;
+    }
+    static _LIBCPP_CONSTEXPR_SINCE_CXX20
+    char_type*       move(char_type* __s1, const char_type* __s2, size_t __n) {
+        if (__n == 0) return __s1;
+        char_type* __r = __s1;
+        if (__s1 < __s2)
+        {
+            for (; __n; --__n, ++__s1, ++__s2)
+                assign(*__s1, *__s2);
+        }
+        else if (__s2 < __s1)
+        {
+            __s1 += __n;
+            __s2 += __n;
+            for (; __n; --__n)
+                assign(*--__s1, *--__s2);
+        }
+        return __r;
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    static _LIBCPP_CONSTEXPR_SINCE_CXX20
+    char_type*       copy(char_type* __s1, const char_type* __s2, size_t __n) {
+        if (!__libcpp_is_constant_evaluated()) {
+            _LIBCPP_ASSERT(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range");
+        }
+        char_type* __r = __s1;
+        for (; __n; --__n, ++__s1, ++__s2)
+            assign(*__s1, *__s2);
+        return __r;
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    static _LIBCPP_CONSTEXPR_SINCE_CXX20
+    char_type*       assign(char_type* __s, size_t __n, char_type __a) {
+        char_type* __r = __s;
+        for (; __n; --__n, ++__s)
+            assign(*__s, __a);
+        return __r;
+    }
+
+    static inline _LIBCPP_CONSTEXPR int_type  not_eof(int_type __c) _NOEXCEPT
+        {return eq_int_type(__c, eof()) ? ~eof() : __c;}
+    static inline _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT
+        {return char_type(__c);}
+    static inline _LIBCPP_CONSTEXPR int_type  to_int_type(char_type __c) _NOEXCEPT
+        {return int_type(__c);}
+    static inline _LIBCPP_CONSTEXPR bool      eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT
+        {return __c1 == __c2;}
+    static inline _LIBCPP_CONSTEXPR int_type  eof() _NOEXCEPT
+        {return int_type(EOF);}
+};
+
 // helper fns for basic_string and string_view
 
 // __str_find

diff  --git a/libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.deprecated.verify.cpp b/libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.deprecated.verify.cpp
new file mode 100644
index 0000000000000..a201f6e8cf347
--- /dev/null
+++ b/libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.deprecated.verify.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// template<> struct char_traits<unsigned char>
+// template<> struct char_traits<signed char>
+
+// Make sure we issue deprecation warnings.
+
+#include <string>
+
+void f() {
+    std::char_traits<unsigned char> uc; // expected-warning{{'char_traits<unsigned char>' is deprecated}}
+    std::char_traits<signed char> sc; // expected-warning{{'char_traits<signed char>' is deprecated}}
+
+    (void)uc;
+    (void)sc;
+}

diff  --git a/libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.pass.cpp b/libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.pass.cpp
new file mode 100644
index 0000000000000..d67fb9c175333
--- /dev/null
+++ b/libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.pass.cpp
@@ -0,0 +1,145 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+// template<> struct char_traits<unsigned char>
+// template<> struct char_traits<signed char>
+
+// Non-standard but provided temporarily for users to migrate.
+
+// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated
+
+#include <string>
+#include <cassert>
+#include <type_traits>
+
+#include "test_macros.h"
+
+template <class Char>
+TEST_CONSTEXPR_CXX20 bool test() {
+    static_assert(std::is_same<typename std::char_traits<Char>::char_type, Char>::value, "");
+    static_assert(std::is_same<typename std::char_traits<Char>::int_type, int>::value, "");
+    static_assert(std::is_same<typename std::char_traits<Char>::off_type, std::streamoff>::value, "");
+    static_assert(std::is_same<typename std::char_traits<Char>::pos_type, std::streampos>::value, "");
+    static_assert(std::is_same<typename std::char_traits<Char>::state_type, std::mbstate_t>::value, "");
+
+    assert(std::char_traits<Char>::to_int_type(Char('a')) == Char('a'));
+    assert(std::char_traits<Char>::to_int_type(Char('A')) == Char('A'));
+    assert(std::char_traits<Char>::to_int_type(0) == 0);
+
+    assert(std::char_traits<Char>::to_char_type(Char('a')) == Char('a'));
+    assert(std::char_traits<Char>::to_char_type(Char('A')) == Char('A'));
+    assert(std::char_traits<Char>::to_char_type(0) == 0);
+
+    assert(std::char_traits<Char>::eof() == EOF);
+
+    assert(std::char_traits<Char>::not_eof(Char('a')) == Char('a'));
+    assert(std::char_traits<Char>::not_eof(Char('A')) == Char('A'));
+    assert(std::char_traits<Char>::not_eof(0) == 0);
+    assert(std::char_traits<Char>::not_eof(std::char_traits<Char>::eof()) !=
+           std::char_traits<Char>::eof());
+
+    assert(std::char_traits<Char>::lt(Char('\0'), Char('A')) == (Char('\0') < Char('A')));
+    assert(std::char_traits<Char>::lt(Char('A'), Char('\0')) == (Char('A') < Char('\0')));
+    assert(std::char_traits<Char>::lt(Char('a'), Char('a')) == (Char('a') < Char('a')));
+    assert(std::char_traits<Char>::lt(Char('A'), Char('a')) == (Char('A') < Char('a')));
+    assert(std::char_traits<Char>::lt(Char('a'), Char('A')) == (Char('a') < Char('A')));
+
+    assert( std::char_traits<Char>::eq(Char('a'), Char('a')));
+    assert(!std::char_traits<Char>::eq(Char('a'), Char('A')));
+
+    assert( std::char_traits<Char>::eq_int_type(Char('a'), Char('a')));
+    assert(!std::char_traits<Char>::eq_int_type(Char('a'), Char('A')));
+    assert(!std::char_traits<Char>::eq_int_type(std::char_traits<Char>::eof(), Char('A')));
+    assert( std::char_traits<Char>::eq_int_type(std::char_traits<Char>::eof(), std::char_traits<Char>::eof()));
+
+    {
+        Char s1[] = {1, 2, 3, 0};
+        Char s2[] = {0};
+        assert(std::char_traits<Char>::length(s1) == 3);
+        assert(std::char_traits<Char>::length(s2) == 0);
+    }
+
+    {
+        Char s1[] = {1, 2, 3};
+        assert(std::char_traits<Char>::find(s1, 3, Char(1)) == s1);
+        assert(std::char_traits<Char>::find(s1, 3, Char(2)) == s1+1);
+        assert(std::char_traits<Char>::find(s1, 3, Char(3)) == s1+2);
+        assert(std::char_traits<Char>::find(s1, 3, Char(4)) == 0);
+        assert(std::char_traits<Char>::find(s1, 3, Char(0)) == 0);
+        assert(std::char_traits<Char>::find(NULL, 0, Char(0)) == 0);
+    }
+
+    {
+        Char s1[] = {1, 2, 3};
+        Char s2[3] = {0};
+        assert(std::char_traits<Char>::copy(s2, s1, 3) == s2);
+        assert(s2[0] == Char(1));
+        assert(s2[1] == Char(2));
+        assert(s2[2] == Char(3));
+        assert(std::char_traits<Char>::copy(NULL, s1, 0) == NULL);
+        assert(std::char_traits<Char>::copy(s1, NULL, 0) == s1);
+    }
+
+    {
+        Char s1[] = {1, 2, 3};
+        assert(std::char_traits<Char>::move(s1, s1+1, 2) == s1);
+        assert(s1[0] == Char(2));
+        assert(s1[1] == Char(3));
+        assert(s1[2] == Char(3));
+        s1[2] = Char(0);
+        assert(std::char_traits<Char>::move(s1+1, s1, 2) == s1+1);
+        assert(s1[0] == Char(2));
+        assert(s1[1] == Char(2));
+        assert(s1[2] == Char(3));
+        assert(std::char_traits<Char>::move(NULL, s1, 0) == NULL);
+        assert(std::char_traits<Char>::move(s1, NULL, 0) == s1);
+    }
+
+    {
+        Char s1[] = {0};
+        assert(std::char_traits<Char>::compare(s1, s1, 0) == 0);
+        assert(std::char_traits<Char>::compare(NULL, NULL, 0) == 0);
+
+        Char s2[] = {1, 0};
+        Char s3[] = {2, 0};
+        assert(std::char_traits<Char>::compare(s2, s2, 1) == 0);
+        assert(std::char_traits<Char>::compare(s2, s3, 1) < 0);
+        assert(std::char_traits<Char>::compare(s3, s2, 1) > 0);
+    }
+
+    {
+        Char s2[3] = {0};
+        assert(std::char_traits<Char>::assign(s2, 3, Char(5)) == s2);
+        assert(s2[0] == Char(5));
+        assert(s2[1] == Char(5));
+        assert(s2[2] == Char(5));
+        assert(std::char_traits<Char>::assign(NULL, 0, Char(5)) == NULL);
+    }
+
+    {
+        Char c = Char('\0');
+        std::char_traits<Char>::assign(c, Char('a'));
+        assert(c == Char('a'));
+    }
+
+    return true;
+}
+
+int main(int, char**) {
+    test<unsigned char>();
+    test<signed char>();
+
+#if TEST_STD_VER > 17
+    static_assert(test<unsigned char>());
+    static_assert(test<signed char>());
+#endif
+
+  return 0;
+}

diff  --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp
index 0da59ca4e1fdd..421453245ef8d 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp
@@ -19,27 +19,36 @@
 #include <algorithm>
 #include <array>
 #include <cassert>
+#include <concepts>
 #include <map>
-#include <string>
 #include <string_view>
+#include <string>
 #include <utility>
 #include <vector>
 #include "types.h"
 
-// A constexpr-friendly lightweight string, primarily useful for comparisons.
-// Unlike `std::string_view`, it copies the given string into an
-// internal buffer and can work with non-contiguous inputs.
+// Basic utility to convert a range to a string-like type. This handles ranges
+// that do not contain character types and can work with non-contiguous inputs.
 template <class Char>
 class BasicSmallString {
-  std::basic_string<Char> buffer_{};
+  std::vector<Char> buffer_{};
 
 public:
-  constexpr BasicSmallString(std::basic_string_view<Char> v) : buffer_(v) {}
+  constexpr BasicSmallString(std::basic_string_view<Char> v)
+    requires (std::same_as<Char, char> ||
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+              std::same_as<Char, wchar_t> ||
+#endif
+              std::same_as<Char, char8_t> ||
+              std::same_as<Char, char16_t> ||
+              std::same_as<Char, char32_t>)
+    : buffer_(v.begin(), v.end())
+  {}
 
   template <class I, class S>
   constexpr BasicSmallString(I b, const S& e) {
     for (; b != e; ++b) {
-      buffer_ += *b;
+      buffer_.push_back(*b);
     }
   }
 

diff  --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/push_back.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/push_back.pass.cpp
index 8a8c8ff6049ea..7d9cf9aecd900 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_append/push_back.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_append/push_back.pass.cpp
@@ -11,17 +11,51 @@
 // void push_back(charT c) // constexpr since C++20
 
 #include <string>
+#include <algorithm>
 #include <cassert>
 
 #include "test_macros.h"
 #include "min_allocator.h"
 
-struct veryLarge
-{
+struct VeryLarge {
   long long a;
   char b;
 };
 
+namespace std {
+  template <>
+  struct char_traits<VeryLarge> {
+    using char_type           = VeryLarge;
+    using int_type            = int;
+    using off_type            = streamoff;
+    using pos_type            = streampos;
+    using state_type          = mbstate_t;
+
+    static TEST_CONSTEXPR_CXX20 void assign(char_type& c1, const char_type& c2) { c1 = c2; }
+    static bool eq(char_type c1, char_type c2);
+    static bool lt(char_type c1, char_type c2);
+
+    static int compare(const char_type* s1, const char_type* s2, size_t n);
+    static size_t length(const char_type* s);
+    static const char_type* find(const char_type* s, size_t n, const char_type& a);
+    static char_type* move(char_type* s1, const char_type* s2, size_t n);
+    static TEST_CONSTEXPR_CXX20 char_type* copy(char_type* s1, const char_type* s2, size_t n) {
+      std::copy_n(s2, n, s1);
+      return s1;
+    }
+    static TEST_CONSTEXPR_CXX20 char_type* assign(char_type* s, size_t n, char_type a) {
+      std::fill_n(s, n, a);
+      return s;
+    }
+
+    static int_type not_eof(int_type c);
+    static char_type to_char_type(int_type c);
+    static int_type to_int_type(char_type c);
+    static bool eq_int_type(int_type c1, int_type c2);
+    static int_type eof();
+  };
+} // end namespace std
+
 template <class S>
 TEST_CONSTEXPR_CXX20 void
 test(S s, typename S::value_type c, S expected)
@@ -48,9 +82,9 @@ TEST_CONSTEXPR_CXX20 bool test() {
 #endif
 
   {
-// https://llvm.org/PR31454
-    std::basic_string<veryLarge> s;
-    veryLarge vl = {};
+    // https://llvm.org/PR31454
+    std::basic_string<VeryLarge> s;
+    VeryLarge vl = {};
     s.push_back(vl);
     s.push_back(vl);
     s.push_back(vl);


        


More information about the libcxx-commits mailing list