diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -100,19 +100,14 @@ - 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`` 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`` and - ``std::char_traits`` 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. +- The base template for ``std::char_traits`` has been marked as deprecated and will be removed in LLVM 18. If + you are using ``std::char_traits`` with types other than ``char``, ``wchar_t``, ``char8_t``, ``char16_t``, + ``char32_t`` or a custom character type for which you specialized ``std::char_traits``, your code will stop + working when we remove the base template. The Standard does not mandate that a base template is provided, + and such a base template is bound to be incorrect for some types, which could currently cause unexpected + behavior while going undetected. API Changes ----------- diff --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h --- a/libcxx/include/__string/char_traits.h +++ b/libcxx/include/__string/char_traits.h @@ -60,7 +60,7 @@ 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* copy(char_type*, const char_type*, size_t); static char_type* assign(char_type*, size_t, char_type); static int_type not_eof(int_type); @@ -71,6 +71,105 @@ }; */ +// +// Temporary extension to provide a base template for std::char_traits. +// TODO: Remove in LLVM 18. +// +template +struct _LIBCPP_DEPRECATED_("char_traits for T not equal to char, wchar_t, char8_t, char16_t or char32_t 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 +{ + 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) { + 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 _LIBCPP_HIDE_FROM_ABI static inline _LIBCPP_CONSTEXPR_SINCE_CXX20 _CharT* __char_traits_move(_CharT* __dest, const _CharT* __source, size_t __n) _NOEXCEPT @@ -617,202 +716,6 @@ return nullptr; } -// -// Temporary extensions for std::char_traits and std::char_traits. -// TODO: Remove those in LLVM 18. -// -template <> -struct _LIBCPP_TEMPLATE_VIS - _LIBCPP_DEPRECATED_("char_traits 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 -{ - 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 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 -{ - 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/arbitrary_char_type.deprecated.verify.cpp rename from libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.deprecated.verify.cpp rename to libcxx/test/libcxx/strings/char.traits/char.traits.specializations/arbitrary_char_type.deprecated.verify.cpp --- 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/arbitrary_char_type.deprecated.verify.cpp @@ -8,17 +8,14 @@ // -// template<> struct char_traits -// template<> struct char_traits +// template<> struct char_traits for arbitrary T // Make sure we issue deprecation warnings. #include void f() { - std::char_traits uc; // expected-warning{{'char_traits' is deprecated}} - std::char_traits sc; // expected-warning{{'char_traits' is deprecated}} - - (void)uc; - (void)sc; + std::char_traits t1; (void)t1; // expected-warning{{'char_traits' is deprecated}} + std::char_traits t2; (void)t2; // expected-warning{{'char_traits' is deprecated}} + std::char_traits t3; (void)t3; // expected-warning{{'char_traits' is deprecated}} } 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/arbitrary_char_type.pass.cpp rename from libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.pass.cpp rename to libcxx/test/libcxx/strings/char.traits/char.traits.specializations/arbitrary_char_type.pass.cpp --- 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/arbitrary_char_type.pass.cpp @@ -8,8 +8,7 @@ // -// template<> struct char_traits -// template<> struct char_traits +// template<> struct char_traits for arbitrary T // Non-standard but provided temporarily for users to migrate. @@ -135,10 +134,12 @@ int main(int, char**) { test(); test(); + test(); #if TEST_STD_VER > 17 static_assert(test()); static_assert(test()); + static_assert(test()); #endif return 0; diff --git a/libcxx/test/std/input.output/iostream.forward/iosfwd.pass.cpp b/libcxx/test/std/input.output/iostream.forward/iosfwd.pass.cpp --- a/libcxx/test/std/input.output/iostream.forward/iosfwd.pass.cpp +++ b/libcxx/test/std/input.output/iostream.forward/iosfwd.pass.cpp @@ -28,7 +28,6 @@ #ifndef TEST_HAS_NO_WIDE_CHARACTERS test* >(); #endif - test*>(); test* >(); #ifndef TEST_HAS_NO_WIDE_CHARACTERS diff --git a/libcxx/test/std/strings/basic.string.hash/enabled_hashes.pass.cpp b/libcxx/test/std/strings/basic.string.hash/enabled_hashes.pass.cpp --- a/libcxx/test/std/strings/basic.string.hash/enabled_hashes.pass.cpp +++ b/libcxx/test/std/strings/basic.string.hash/enabled_hashes.pass.cpp @@ -24,6 +24,32 @@ char c; }; +template <> +struct std::char_traits { + using char_type = MyChar; + using int_type = std::char_traits::int_type; + using off_type = std::char_traits::off_type; + using pos_type = std::char_traits::pos_type; + using state_type = std::char_traits::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*, 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(); +}; + int main(int, char**) { test_library_hash_specializations_available(); { diff --git a/libcxx/test/std/strings/string.view/string.view.hash/enabled_hashes.pass.cpp b/libcxx/test/std/strings/string.view/string.view.hash/enabled_hashes.pass.cpp --- a/libcxx/test/std/strings/string.view/string.view.hash/enabled_hashes.pass.cpp +++ b/libcxx/test/std/strings/string.view/string.view.hash/enabled_hashes.pass.cpp @@ -24,6 +24,32 @@ char c; }; +template <> +struct std::char_traits { + using char_type = MyChar; + using int_type = std::char_traits::int_type; + using off_type = std::char_traits::off_type; + using pos_type = std::char_traits::pos_type; + using state_type = std::char_traits::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*, 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(); +}; + int main(int, char**) { test_library_hash_specializations_available(); { diff --git a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp --- a/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp +++ b/libcxx/test/std/utilities/format/format.formattable/concept.formattable.compile.pass.cpp @@ -81,8 +81,10 @@ assert_is_formattable(); assert_is_formattable(); assert_is_formattable(); - assert_is_formattable, CharT>(); - assert_is_formattable, CharT>(); + if constexpr (!std::same_as) { // string and string_view only work with proper character types + assert_is_formattable, CharT>(); + assert_is_formattable, CharT>(); + } assert_is_formattable();