Skip to content

Commit 08a0faf

Browse files
committed
[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
1 parent e647b4f commit 08a0faf

File tree

8 files changed

+169
-220
lines changed

8 files changed

+169
-220
lines changed

Diff for: libcxx/docs/ReleaseNotes.rst

+6-11
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,14 @@ Deprecations and Removals
100100
- The ``_LIBCPP_DEBUG`` macro is not honored anymore, and it is an error to try to use it. Please migrate to
101101
``_LIBCPP_ENABLE_DEBUG_MODE`` instead.
102102

103-
- A base template for ``std::char_traits`` is not provided anymore. The Standard mandates that the library
104-
provides specializations for several types like ``char`` and ``wchar_t``, which libc++ does. However, libc++
105-
used to additionally provide a default implementation for ``std::char_traits<T>`` for arbitrary ``T``. Not
106-
only does the Standard not mandate that one is provided, but such an implementation is bound to be incorrect
107-
for some types, so it has been removed. As an exception, ``std::char_traits<unsigned char>`` and
108-
``std::char_traits<signed char>`` are kept for a limited period of time and marked as deprecated to let people
109-
move off of those, since we know there were some users of those. They will be removed in LLVM 18.
110-
111103
Upcoming Deprecations and Removals
112104
----------------------------------
113-
- The specializations of ``std::char_traits`` for ``unsigned char`` and ``signed char`` are provided until
114-
LLVM 18. Those non-standard specializations are provided for a transition period and marked as deprecated
115-
but will be removed in the future.
105+
- The base template for ``std::char_traits`` has been marked as deprecated and will be removed in LLVM 18. If
106+
you are using ``std::char_traits`` with types other than ``char``, ``wchar_t``, ``char8_t``, ``char16_t``,
107+
``char32_t`` or a custom character type for which you specialized ``std::char_traits``, your code will stop
108+
working when we remove the base template. The Standard does not mandate that a base template is provided,
109+
and such a base template is bound to be incorrect for some types, which could currently cause unexpected
110+
behavior while going undetected.
116111

117112
API Changes
118113
-----------

Diff for: libcxx/include/__string/char_traits.h

+100-197
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ exposition-only to document what members a char_traits specialization should pro
6060
static size_t length(const char_type*);
6161
static const char_type* find(const char_type*, size_t, const char_type&);
6262
static char_type* move(char_type*, const char_type*, size_t);
63-
static char_type* copy(char_type*, const char_type* __s2, size_t);
63+
static char_type* copy(char_type*, const char_type*, size_t);
6464
static char_type* assign(char_type*, size_t, char_type);
6565
6666
static int_type not_eof(int_type);
@@ -71,6 +71,105 @@ exposition-only to document what members a char_traits specialization should pro
7171
};
7272
*/
7373

74+
//
75+
// Temporary extension to provide a base template for std::char_traits.
76+
// TODO: Remove in LLVM 18.
77+
//
78+
template <class _CharT>
79+
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.")
80+
char_traits
81+
{
82+
using char_type = _CharT;
83+
using int_type = int;
84+
using off_type = streamoff;
85+
using pos_type = streampos;
86+
using state_type = mbstate_t;
87+
88+
static inline void _LIBCPP_CONSTEXPR_SINCE_CXX17
89+
assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
90+
static inline _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT
91+
{return __c1 == __c2;}
92+
static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
93+
{return __c1 < __c2;}
94+
95+
static _LIBCPP_CONSTEXPR_SINCE_CXX17
96+
int compare(const char_type* __s1, const char_type* __s2, size_t __n) {
97+
for (; __n; --__n, ++__s1, ++__s2)
98+
{
99+
if (lt(*__s1, *__s2))
100+
return -1;
101+
if (lt(*__s2, *__s1))
102+
return 1;
103+
}
104+
return 0;
105+
}
106+
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
107+
size_t length(const char_type* __s) {
108+
size_t __len = 0;
109+
for (; !eq(*__s, char_type(0)); ++__s)
110+
++__len;
111+
return __len;
112+
}
113+
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
114+
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) {
115+
for (; __n; --__n)
116+
{
117+
if (eq(*__s, __a))
118+
return __s;
119+
++__s;
120+
}
121+
return nullptr;
122+
}
123+
static _LIBCPP_CONSTEXPR_SINCE_CXX20
124+
char_type* move(char_type* __s1, const char_type* __s2, size_t __n) {
125+
if (__n == 0) return __s1;
126+
char_type* __r = __s1;
127+
if (__s1 < __s2)
128+
{
129+
for (; __n; --__n, ++__s1, ++__s2)
130+
assign(*__s1, *__s2);
131+
}
132+
else if (__s2 < __s1)
133+
{
134+
__s1 += __n;
135+
__s2 += __n;
136+
for (; __n; --__n)
137+
assign(*--__s1, *--__s2);
138+
}
139+
return __r;
140+
}
141+
_LIBCPP_INLINE_VISIBILITY
142+
static _LIBCPP_CONSTEXPR_SINCE_CXX20
143+
char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) {
144+
if (!__libcpp_is_constant_evaluated()) {
145+
_LIBCPP_ASSERT(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range");
146+
}
147+
char_type* __r = __s1;
148+
for (; __n; --__n, ++__s1, ++__s2)
149+
assign(*__s1, *__s2);
150+
return __r;
151+
}
152+
_LIBCPP_INLINE_VISIBILITY
153+
static _LIBCPP_CONSTEXPR_SINCE_CXX20
154+
char_type* assign(char_type* __s, size_t __n, char_type __a) {
155+
char_type* __r = __s;
156+
for (; __n; --__n, ++__s)
157+
assign(*__s, __a);
158+
return __r;
159+
}
160+
161+
static inline _LIBCPP_CONSTEXPR int_type not_eof(int_type __c) _NOEXCEPT
162+
{return eq_int_type(__c, eof()) ? ~eof() : __c;}
163+
static inline _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT
164+
{return char_type(__c);}
165+
static inline _LIBCPP_CONSTEXPR int_type to_int_type(char_type __c) _NOEXCEPT
166+
{return int_type(__c);}
167+
static inline _LIBCPP_CONSTEXPR bool eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT
168+
{return __c1 == __c2;}
169+
static inline _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT
170+
{return int_type(EOF);}
171+
};
172+
74173
template <class _CharT>
75174
_LIBCPP_HIDE_FROM_ABI static inline _LIBCPP_CONSTEXPR_SINCE_CXX20
76175
_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& _
617716
return nullptr;
618717
}
619718

620-
//
621-
// Temporary extensions for std::char_traits<unsigned char> and std::char_traits<signed char>.
622-
// TODO: Remove those in LLVM 18.
623-
//
624-
template <>
625-
struct _LIBCPP_TEMPLATE_VIS
626-
_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.")
627-
char_traits<unsigned char>
628-
{
629-
using char_type = unsigned char;
630-
using int_type = int;
631-
using off_type = streamoff;
632-
using pos_type = streampos;
633-
using state_type = mbstate_t;
634-
635-
static inline void _LIBCPP_CONSTEXPR_SINCE_CXX17
636-
assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
637-
static inline _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT
638-
{return __c1 == __c2;}
639-
static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
640-
{return __c1 < __c2;}
641-
642-
static _LIBCPP_CONSTEXPR_SINCE_CXX17
643-
int compare(const char_type* __s1, const char_type* __s2, size_t __n) {
644-
for (; __n; --__n, ++__s1, ++__s2)
645-
{
646-
if (lt(*__s1, *__s2))
647-
return -1;
648-
if (lt(*__s2, *__s1))
649-
return 1;
650-
}
651-
return 0;
652-
}
653-
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
654-
size_t length(const char_type* __s) {
655-
size_t __len = 0;
656-
for (; !eq(*__s, char_type(0)); ++__s)
657-
++__len;
658-
return __len;
659-
}
660-
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
661-
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) {
662-
for (; __n; --__n)
663-
{
664-
if (eq(*__s, __a))
665-
return __s;
666-
++__s;
667-
}
668-
return nullptr;
669-
}
670-
static _LIBCPP_CONSTEXPR_SINCE_CXX20
671-
char_type* move(char_type* __s1, const char_type* __s2, size_t __n) {
672-
if (__n == 0) return __s1;
673-
char_type* __r = __s1;
674-
if (__s1 < __s2)
675-
{
676-
for (; __n; --__n, ++__s1, ++__s2)
677-
assign(*__s1, *__s2);
678-
}
679-
else if (__s2 < __s1)
680-
{
681-
__s1 += __n;
682-
__s2 += __n;
683-
for (; __n; --__n)
684-
assign(*--__s1, *--__s2);
685-
}
686-
return __r;
687-
}
688-
_LIBCPP_INLINE_VISIBILITY
689-
static _LIBCPP_CONSTEXPR_SINCE_CXX20
690-
char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) {
691-
if (!__libcpp_is_constant_evaluated()) {
692-
_LIBCPP_ASSERT(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range");
693-
}
694-
char_type* __r = __s1;
695-
for (; __n; --__n, ++__s1, ++__s2)
696-
assign(*__s1, *__s2);
697-
return __r;
698-
}
699-
_LIBCPP_INLINE_VISIBILITY
700-
static _LIBCPP_CONSTEXPR_SINCE_CXX20
701-
char_type* assign(char_type* __s, size_t __n, char_type __a) {
702-
char_type* __r = __s;
703-
for (; __n; --__n, ++__s)
704-
assign(*__s, __a);
705-
return __r;
706-
}
707-
708-
static inline _LIBCPP_CONSTEXPR int_type not_eof(int_type __c) _NOEXCEPT
709-
{return eq_int_type(__c, eof()) ? ~eof() : __c;}
710-
static inline _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT
711-
{return char_type(__c);}
712-
static inline _LIBCPP_CONSTEXPR int_type to_int_type(char_type __c) _NOEXCEPT
713-
{return int_type(__c);}
714-
static inline _LIBCPP_CONSTEXPR bool eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT
715-
{return __c1 == __c2;}
716-
static inline _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT
717-
{return int_type(EOF);}
718-
};
719-
720-
template <>
721-
struct _LIBCPP_TEMPLATE_VIS
722-
_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.")
723-
char_traits<signed char>
724-
{
725-
using char_type = signed char;
726-
using int_type = int;
727-
using off_type = streamoff;
728-
using pos_type = streampos;
729-
using state_type = mbstate_t;
730-
731-
static inline void _LIBCPP_CONSTEXPR_SINCE_CXX17
732-
assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;}
733-
static inline _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT
734-
{return __c1 == __c2;}
735-
static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
736-
{return __c1 < __c2;}
737-
738-
static _LIBCPP_CONSTEXPR_SINCE_CXX17
739-
int compare(const char_type* __s1, const char_type* __s2, size_t __n) {
740-
for (; __n; --__n, ++__s1, ++__s2)
741-
{
742-
if (lt(*__s1, *__s2))
743-
return -1;
744-
if (lt(*__s2, *__s1))
745-
return 1;
746-
}
747-
return 0;
748-
}
749-
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
750-
size_t length(const char_type* __s) {
751-
size_t __len = 0;
752-
for (; !eq(*__s, char_type(0)); ++__s)
753-
++__len;
754-
return __len;
755-
}
756-
_LIBCPP_INLINE_VISIBILITY static _LIBCPP_CONSTEXPR_SINCE_CXX17
757-
const char_type* find(const char_type* __s, size_t __n, const char_type& __a) {
758-
for (; __n; --__n)
759-
{
760-
if (eq(*__s, __a))
761-
return __s;
762-
++__s;
763-
}
764-
return nullptr;
765-
}
766-
static _LIBCPP_CONSTEXPR_SINCE_CXX20
767-
char_type* move(char_type* __s1, const char_type* __s2, size_t __n) {
768-
if (__n == 0) return __s1;
769-
char_type* __r = __s1;
770-
if (__s1 < __s2)
771-
{
772-
for (; __n; --__n, ++__s1, ++__s2)
773-
assign(*__s1, *__s2);
774-
}
775-
else if (__s2 < __s1)
776-
{
777-
__s1 += __n;
778-
__s2 += __n;
779-
for (; __n; --__n)
780-
assign(*--__s1, *--__s2);
781-
}
782-
return __r;
783-
}
784-
_LIBCPP_INLINE_VISIBILITY
785-
static _LIBCPP_CONSTEXPR_SINCE_CXX20
786-
char_type* copy(char_type* __s1, const char_type* __s2, size_t __n) {
787-
if (!__libcpp_is_constant_evaluated()) {
788-
_LIBCPP_ASSERT(__s2 < __s1 || __s2 >= __s1+__n, "char_traits::copy overlapped range");
789-
}
790-
char_type* __r = __s1;
791-
for (; __n; --__n, ++__s1, ++__s2)
792-
assign(*__s1, *__s2);
793-
return __r;
794-
}
795-
_LIBCPP_INLINE_VISIBILITY
796-
static _LIBCPP_CONSTEXPR_SINCE_CXX20
797-
char_type* assign(char_type* __s, size_t __n, char_type __a) {
798-
char_type* __r = __s;
799-
for (; __n; --__n, ++__s)
800-
assign(*__s, __a);
801-
return __r;
802-
}
803-
804-
static inline _LIBCPP_CONSTEXPR int_type not_eof(int_type __c) _NOEXCEPT
805-
{return eq_int_type(__c, eof()) ? ~eof() : __c;}
806-
static inline _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT
807-
{return char_type(__c);}
808-
static inline _LIBCPP_CONSTEXPR int_type to_int_type(char_type __c) _NOEXCEPT
809-
{return int_type(__c);}
810-
static inline _LIBCPP_CONSTEXPR bool eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT
811-
{return __c1 == __c2;}
812-
static inline _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT
813-
{return int_type(EOF);}
814-
};
815-
816719
// helper fns for basic_string and string_view
817720

818721
// __str_find

Diff for: libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.deprecated.verify.cpp renamed to libcxx/test/libcxx/strings/char.traits/char.traits.specializations/arbitrary_char_type.deprecated.verify.cpp

+4-7
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,14 @@
88

99
// <string>
1010

11-
// template<> struct char_traits<unsigned char>
12-
// template<> struct char_traits<signed char>
11+
// template<> struct char_traits<T> for arbitrary T
1312

1413
// Make sure we issue deprecation warnings.
1514

1615
#include <string>
1716

1817
void f() {
19-
std::char_traits<unsigned char> uc; // expected-warning{{'char_traits<unsigned char>' is deprecated}}
20-
std::char_traits<signed char> sc; // expected-warning{{'char_traits<signed char>' is deprecated}}
21-
22-
(void)uc;
23-
(void)sc;
18+
std::char_traits<unsigned char> t1; (void)t1; // expected-warning{{'char_traits<unsigned char>' is deprecated}}
19+
std::char_traits<signed char> t2; (void)t2; // expected-warning{{'char_traits<signed char>' is deprecated}}
20+
std::char_traits<unsigned long> t3; (void)t3; // expected-warning{{'char_traits<unsigned long>' is deprecated}}
2421
}

Diff for: libcxx/test/libcxx/strings/char.traits/char.traits.specializations/signed_unsigned_char.pass.cpp renamed to libcxx/test/libcxx/strings/char.traits/char.traits.specializations/arbitrary_char_type.pass.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88

99
// <string>
1010

11-
// template<> struct char_traits<unsigned char>
12-
// template<> struct char_traits<signed char>
11+
// template<> struct char_traits<T> for arbitrary T
1312

1413
// Non-standard but provided temporarily for users to migrate.
1514

@@ -135,10 +134,12 @@ TEST_CONSTEXPR_CXX20 bool test() {
135134
int main(int, char**) {
136135
test<unsigned char>();
137136
test<signed char>();
137+
test<unsigned long>();
138138

139139
#if TEST_STD_VER > 17
140140
static_assert(test<unsigned char>());
141141
static_assert(test<signed char>());
142+
static_assert(test<unsigned long>());
142143
#endif
143144

144145
return 0;

Diff for: libcxx/test/std/input.output/iostream.forward/iosfwd.pass.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ int main(int, char**)
2828
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
2929
test<std::char_traits<wchar_t>* >();
3030
#endif
31-
test<std::char_traits<unsigned short>*>();
3231

3332
test<std::basic_ios<char>* >();
3433
#ifndef TEST_HAS_NO_WIDE_CHARACTERS

0 commit comments

Comments
 (0)