diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -1219,6 +1219,21 @@ # define _LIBCPP_PACKED # endif +// c8rtomb() and mbrtoc8() were added in C++20 and C23. Support for these +// functions is gradually being added to existing C libraries. The conditions +// below check for known C library versions and conditions under which these +// functions are declared by the C library. +# define _LIBCPP_HAS_NO_C8RTOMB_MBRTOC8 +// GNU libc 2.36 and newer declare c8rtomb() and mbrtoc8() in C++ modes if +// __cpp_char8_t is defined or if C2X extensions are enabled. Unfortunately, +// determining the latter depends on internal GNU libc details. If the +// __cpp_char8_t feature test macro is not defined, then a char8_t typedef +// will be declared as well. +# if defined(_LIBCPP_GLIBC_PREREQ) && _LIBCPP_GLIBC_PREREQ(2, 36) && \ + (defined(__cpp_char8_t) || __GLIBC_USE(ISOC2X)) +# undef _LIBCPP_HAS_NO_C8RTOMB_MBRTOC8 +# endif + #endif // __cplusplus #endif // _LIBCPP___CONFIG diff --git a/libcxx/include/cuchar b/libcxx/include/cuchar --- a/libcxx/include/cuchar +++ b/libcxx/include/cuchar @@ -25,6 +25,8 @@ mbstate_t size_t +size_t mbrtoc8(char8_t* pc8, const char* s, size_t n, mbstate_t* ps); // C++20 +size_t c8rtomb(char* s, char8_t c8, mbstate_t* ps); // C++20 size_t mbrtoc16(char16_t* pc16, const char* s, size_t n, mbstate_t* ps); size_t c16rtomb(char* s, char16_t c16, mbstate_t* ps); size_t mbrtoc32(char32_t* pc32, const char* s, size_t n, mbstate_t* ps); @@ -49,6 +51,10 @@ using ::mbstate_t _LIBCPP_USING_IF_EXISTS; using ::size_t _LIBCPP_USING_IF_EXISTS; +#if !defined(_LIBCPP_HAS_NO_C8RTOMB_MBRTOC8) +using ::mbrtoc8 _LIBCPP_USING_IF_EXISTS; +using ::c8rtomb _LIBCPP_USING_IF_EXISTS; +#endif using ::mbrtoc16 _LIBCPP_USING_IF_EXISTS; using ::c16rtomb _LIBCPP_USING_IF_EXISTS; using ::mbrtoc32 _LIBCPP_USING_IF_EXISTS; diff --git a/libcxx/include/uchar.h b/libcxx/include/uchar.h --- a/libcxx/include/uchar.h +++ b/libcxx/include/uchar.h @@ -11,7 +11,7 @@ #define _LIBCPP_UCHAR_H /* - uchar.h synopsis // since C++11 + uchar.h synopsis // since C11 Macros: @@ -22,7 +22,12 @@ mbstate_t size_t + char8_t // C23, Builtin type in C++20 + char16_t // Builtin type in C++11 + char32_t // Builtin type in C++11 +size_t mbrtoc8(char8_t* pc8, const char* s, size_t n, mbstate_t* ps); // C23 +size_t c8rtomb(char* s, char8_t c8, mbstate_t* ps); // C23 size_t mbrtoc16(char16_t* pc16, const char* s, size_t n, mbstate_t* ps); size_t c16rtomb(char* s, char16_t c16, mbstate_t* ps); size_t mbrtoc32(char32_t* pc32, const char* s, size_t n, mbstate_t* ps); diff --git a/libcxx/test/std/depr/depr.c.headers/uchar_h.compile.pass.cpp b/libcxx/test/std/depr/depr.c.headers/uchar_h.compile.pass.cpp --- a/libcxx/test/std/depr/depr.c.headers/uchar_h.compile.pass.cpp +++ b/libcxx/test/std/depr/depr.c.headers/uchar_h.compile.pass.cpp @@ -23,6 +23,11 @@ // __STDC_UTF_16__ may or may not be defined by the C standard library // __STDC_UTF_32__ may or may not be defined by the C standard library +#if !defined(TEST_HAS_NO_C8RTOMB_MBRTOC8) +ASSERT_SAME_TYPE(size_t, decltype(mbrtoc8((char8_t*)0, (const char*)0, (size_t)0, (mbstate_t*)0))); +ASSERT_SAME_TYPE(size_t, decltype(c8rtomb((char*)0, (char8_t)0, (mbstate_t*)0))); +#endif + ASSERT_SAME_TYPE(size_t, decltype(mbrtoc16((char16_t*)0, (const char*)0, (size_t)0, (mbstate_t*)0))); ASSERT_SAME_TYPE(size_t, decltype(c16rtomb((char*)0, (char16_t)0, (mbstate_t*)0))); diff --git a/libcxx/test/std/strings/c.strings/cuchar.compile.pass.cpp b/libcxx/test/std/strings/c.strings/cuchar.compile.pass.cpp --- a/libcxx/test/std/strings/c.strings/cuchar.compile.pass.cpp +++ b/libcxx/test/std/strings/c.strings/cuchar.compile.pass.cpp @@ -20,11 +20,14 @@ #include "test_macros.h" -// TODO: Implement mbrtoc8 and c8rtomb, and add tests for those - // __STDC_UTF_16__ may or may not be defined by the C standard library // __STDC_UTF_32__ may or may not be defined by the C standard library +#if !defined(TEST_HAS_NO_C8RTOMB_MBRTOC8) +ASSERT_SAME_TYPE(size_t, decltype(std::mbrtoc8((char8_t*)0, (const char*)0, (size_t)0, (mbstate_t*)0))); +ASSERT_SAME_TYPE(size_t, decltype(std::c8rtomb((char*)0, (char8_t)0, (mbstate_t*)0))); +#endif + ASSERT_SAME_TYPE(size_t, decltype(std::mbrtoc16((char16_t*)0, (const char*)0, (size_t)0, (mbstate_t*)0))); ASSERT_SAME_TYPE(size_t, decltype(std::c16rtomb((char*)0, (char16_t)0, (mbstate_t*)0))); diff --git a/libcxx/test/std/strings/c.strings/no_c8rtomb_mbrtoc8.verify.cpp b/libcxx/test/std/strings/c.strings/no_c8rtomb_mbrtoc8.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/c.strings/no_c8rtomb_mbrtoc8.verify.cpp @@ -0,0 +1,20 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include + +using U = decltype(::c8rtomb); +using V = decltype(::mbrtoc8); +#if defined(_LIBCPP_HAS_NO_C8RTOMB_MBRTOC8) +// expected-error@-3 {{no member named 'c8rtomb' in the global namespace}} +// expected-error@-3 {{no member named 'mbrtoc8' in the global namespace}} +#else +// expected-no-diagnostics +#endif diff --git a/libcxx/test/support/test_macros.h b/libcxx/test/support/test_macros.h --- a/libcxx/test/support/test_macros.h +++ b/libcxx/test/support/test_macros.h @@ -383,6 +383,10 @@ # define TEST_HAS_NO_FGETPOS_FSETPOS #endif +#if defined(_LIBCPP_HAS_NO_C8RTOMB_MBRTOC8) +# define TEST_HAS_NO_C8RTOMB_MBRTOC8 +#endif + #if defined(TEST_COMPILER_CLANG) # define TEST_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") # define TEST_DIAGNOSTIC_POP _Pragma("clang diagnostic pop")