[libc++] Keep char_traits<T> for arbitrary T around until LLVM 18

This is in response to failures seen after landing D138307.

Differential Revision: https://reviews.llvm.org/D138596
This commit is contained in:
Louis Dionne 2022-11-23 14:06:14 -05:00
parent e647b4f519
commit 08a0faf4cd
8 changed files with 169 additions and 220 deletions

View File

@ -100,19 +100,14 @@ 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.
- 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
-----------

View File

@ -60,7 +60,7 @@ exposition-only to document what members a char_traits specialization should pro
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 @@ exposition-only to document what members a char_traits specialization should pro
};
*/
//
// Temporary extension to provide a base template for std::char_traits.
// TODO: Remove in LLVM 18.
//
template <class _CharT>
struct _LIBCPP_DEPRECATED_("char_traits<T> 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 <class _CharT>
_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 @@ 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

View File

@ -8,17 +8,14 @@
// <string>
// template<> struct char_traits<unsigned char>
// template<> struct char_traits<signed char>
// template<> struct char_traits<T> for arbitrary T
// 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;
std::char_traits<unsigned char> t1; (void)t1; // expected-warning{{'char_traits<unsigned char>' is deprecated}}
std::char_traits<signed char> t2; (void)t2; // expected-warning{{'char_traits<signed char>' is deprecated}}
std::char_traits<unsigned long> t3; (void)t3; // expected-warning{{'char_traits<unsigned long>' is deprecated}}
}

View File

@ -8,8 +8,7 @@
// <string>
// template<> struct char_traits<unsigned char>
// template<> struct char_traits<signed char>
// template<> struct char_traits<T> for arbitrary T
// Non-standard but provided temporarily for users to migrate.
@ -135,10 +134,12 @@ TEST_CONSTEXPR_CXX20 bool test() {
int main(int, char**) {
test<unsigned char>();
test<signed char>();
test<unsigned long>();
#if TEST_STD_VER > 17
static_assert(test<unsigned char>());
static_assert(test<signed char>());
static_assert(test<unsigned long>());
#endif
return 0;

View File

@ -28,7 +28,6 @@ int main(int, char**)
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<std::char_traits<wchar_t>* >();
#endif
test<std::char_traits<unsigned short>*>();
test<std::basic_ios<char>* >();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS

View File

@ -24,6 +24,32 @@ struct MyChar {
char c;
};
template <>
struct std::char_traits<MyChar> {
using char_type = MyChar;
using int_type = std::char_traits<char>::int_type;
using off_type = std::char_traits<char>::off_type;
using pos_type = std::char_traits<char>::pos_type;
using state_type = std::char_traits<char>::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();
{

View File

@ -24,6 +24,32 @@ struct MyChar {
char c;
};
template <>
struct std::char_traits<MyChar> {
using char_type = MyChar;
using int_type = std::char_traits<char>::int_type;
using off_type = std::char_traits<char>::off_type;
using pos_type = std::char_traits<char>::pos_type;
using state_type = std::char_traits<char>::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();
{

View File

@ -81,8 +81,10 @@ void test_P0645() {
assert_is_formattable<CharT*, CharT>();
assert_is_formattable<const CharT*, CharT>();
assert_is_formattable<CharT[42], CharT>();
assert_is_formattable<std::basic_string<CharT>, CharT>();
assert_is_formattable<std::basic_string_view<CharT>, CharT>();
if constexpr (!std::same_as<CharT, int>) { // string and string_view only work with proper character types
assert_is_formattable<std::basic_string<CharT>, CharT>();
assert_is_formattable<std::basic_string_view<CharT>, CharT>();
}
assert_is_formattable<bool, CharT>();