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 @@ -210,25 +210,22 @@ static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT {return (unsigned char)__c1 < (unsigned char)__c2;} - static _LIBCPP_CONSTEXPR_SINCE_CXX17 - int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT; + static _LIBCPP_CONSTEXPR_SINCE_CXX17 int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT { + if (__n == 0) + return 0; + return std::__constexpr_memcmp(__s1, __s2, __n); + } - static inline size_t _LIBCPP_CONSTEXPR_SINCE_CXX17 length(const char_type* __s) _NOEXCEPT { - // GCC currently does not support __builtin_strlen during constant evaluation. - // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816 -#ifdef _LIBCPP_COMPILER_GCC - if (__libcpp_is_constant_evaluated()) { - size_t __i = 0; - for (; __s[__i] != char_type('\0'); ++__i) - ; - return __i; - } -#endif - return __builtin_strlen(__s); - } + static inline size_t _LIBCPP_CONSTEXPR_SINCE_CXX17 length(const char_type* __s) _NOEXCEPT { + return std::__constexpr_strlen(__s); + } - static _LIBCPP_CONSTEXPR_SINCE_CXX17 - const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT; + static _LIBCPP_CONSTEXPR_SINCE_CXX17 + const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT { + if (__n == 0) + return nullptr; + return std::__constexpr_char_memchr(__s, static_cast(__a), __n); + } static inline _LIBCPP_CONSTEXPR_SINCE_CXX20 char_type* move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT { @@ -261,49 +258,6 @@ {return int_type(EOF);} }; -inline _LIBCPP_CONSTEXPR_SINCE_CXX17 -int -char_traits::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT -{ - if (__n == 0) - return 0; -#if __has_feature(cxx_constexpr_string_builtins) - return __builtin_memcmp(__s1, __s2, __n); -#elif _LIBCPP_STD_VER <= 14 - return _VSTD::memcmp(__s1, __s2, __n); -#else - for (; __n; --__n, ++__s1, ++__s2) - { - if (lt(*__s1, *__s2)) - return -1; - if (lt(*__s2, *__s1)) - return 1; - } - return 0; -#endif -} - -inline _LIBCPP_CONSTEXPR_SINCE_CXX17 -const char* -char_traits::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT -{ - if (__n == 0) - return nullptr; -#if __has_feature(cxx_constexpr_string_builtins) - return __builtin_char_memchr(__s, to_int_type(__a), __n); -#elif _LIBCPP_STD_VER <= 14 - return (const char_type*) _VSTD::memchr(__s, to_int_type(__a), __n); -#else - for (; __n; --__n) - { - if (eq(*__s, __a)) - return __s; - ++__s; - } - return nullptr; -#endif -} - // char_traits #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS @@ -326,12 +280,22 @@ 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) _NOEXCEPT; - static _LIBCPP_CONSTEXPR_SINCE_CXX17 - size_t length(const char_type* __s) _NOEXCEPT; - static _LIBCPP_CONSTEXPR_SINCE_CXX17 - const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT; + static _LIBCPP_CONSTEXPR_SINCE_CXX17 int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT { + if (__n == 0) + return 0; + return std::__constexpr_wmemcmp(__s1, __s2, __n); + } + + static _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t length(const char_type* __s) _NOEXCEPT { + return std::__constexpr_wcslen(__s); + } + + static _LIBCPP_CONSTEXPR_SINCE_CXX17 + const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT { + if (__n == 0) + return nullptr; + return std::__constexpr_wmemchr(__s, __a, __n); + } static inline _LIBCPP_CONSTEXPR_SINCE_CXX20 char_type* move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT { @@ -363,65 +327,6 @@ static inline _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT {return int_type(WEOF);} }; - -inline _LIBCPP_CONSTEXPR_SINCE_CXX17 -int -char_traits::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT -{ - if (__n == 0) - return 0; -#if __has_feature(cxx_constexpr_string_builtins) - return __builtin_wmemcmp(__s1, __s2, __n); -#elif _LIBCPP_STD_VER <= 14 - return _VSTD::wmemcmp(__s1, __s2, __n); -#else - for (; __n; --__n, ++__s1, ++__s2) - { - if (lt(*__s1, *__s2)) - return -1; - if (lt(*__s2, *__s1)) - return 1; - } - return 0; -#endif -} - -inline _LIBCPP_CONSTEXPR_SINCE_CXX17 -size_t -char_traits::length(const char_type* __s) _NOEXCEPT -{ -#if __has_feature(cxx_constexpr_string_builtins) - return __builtin_wcslen(__s); -#elif _LIBCPP_STD_VER <= 14 - return _VSTD::wcslen(__s); -#else - size_t __len = 0; - for (; !eq(*__s, char_type(0)); ++__s) - ++__len; - return __len; -#endif -} - -inline _LIBCPP_CONSTEXPR_SINCE_CXX17 -const wchar_t* -char_traits::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT -{ - if (__n == 0) - return nullptr; -#if __has_feature(cxx_constexpr_string_builtins) - return __builtin_wmemchr(__s, __a, __n); -#elif _LIBCPP_STD_VER <= 14 - return _VSTD::wmemchr(__s, __a, __n); -#else - for (; __n; --__n) - { - if (eq(*__s, __a)) - return __s; - ++__s; - } - return nullptr; -#endif -} #endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS #ifndef _LIBCPP_HAS_NO_CHAR8_T @@ -445,8 +350,10 @@ static inline constexpr bool lt(char_type __c1, char_type __c2) noexcept {return __c1 < __c2;} - static constexpr - int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT; + static _LIBCPP_HIDE_FROM_ABI constexpr int + compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT { + return std::__constexpr_memcmp(__s1, __s2, __n); + } static constexpr size_t length(const char_type* __s) _NOEXCEPT; @@ -496,24 +403,6 @@ return __len; } -inline constexpr -int -char_traits::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT -{ -#if __has_feature(cxx_constexpr_string_builtins) - return __builtin_memcmp(__s1, __s2, __n); -#else - for (; __n; --__n, ++__s1, ++__s2) - { - if (lt(*__s1, *__s2)) - return -1; - if (lt(*__s2, *__s1)) - return 1; - } - return 0; -#endif -} - // TODO use '__builtin_char_memchr' if it ever supports char8_t ?? inline constexpr const char8_t* diff --git a/libcxx/include/cstring b/libcxx/include/cstring --- a/libcxx/include/cstring +++ b/libcxx/include/cstring @@ -58,6 +58,7 @@ #include <__assert> // all public C++ headers provide the assertion handler #include <__config> +#include <__type_traits/is_constant_evaluated.h> #include @@ -99,6 +100,52 @@ using ::strerror _LIBCPP_USING_IF_EXISTS; using ::strlen _LIBCPP_USING_IF_EXISTS; +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR size_t __constexpr_strlen(const char* __str) { + // GCC currently doesn't support __builtin_strlen for heap-allocated memory during constant evaluation. + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816 +#ifdef _LIBCPP_COMPILER_GCC + if (__libcpp_is_constant_evaluated()) { + size_t __i = 0; + for (; __str[__i] != '\0'; ++__i) + ; + return __i; + } +#endif + return __builtin_strlen(__str); +} + +template +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __constexpr_memcmp(const _Tp* __lhs, const _Tp* __rhs, size_t __count) { +#ifdef _LIBCPP_COMPILER_GCC + if (__libcpp_is_constant_evaluated()) { + for (; __count; --__count, ++__lhs, ++__rhs) { + if (*__lhs < *__rhs) + return -1; + if (*__rhs < *__lhs) + return 1; + } + return 0; + } +#endif + return __builtin_memcmp(__lhs, __rhs, __count); +} + +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const char* +__constexpr_char_memchr(const char* __str, int __char, size_t __count) { +#if __has_builtin(__builtin_char_memchr) + return __builtin_char_memchr(__str, __char, __count); +#else + if (!__libcpp_is_constant_evaluated()) + return static_cast(std::memchr(__str, __char, __count)); + for (; __count; --__count) { + if (*__str == __char) + return __str; + ++__str; + } + return nullptr; +#endif +} + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP_CSTRING diff --git a/libcxx/include/cwchar b/libcxx/include/cwchar --- a/libcxx/include/cwchar +++ b/libcxx/include/cwchar @@ -104,6 +104,7 @@ #include <__assert> // all public C++ headers provide the assertion handler #include <__config> +#include <__type_traits/is_constant_evaluated.h> #include #include @@ -189,6 +190,55 @@ using ::vwprintf _LIBCPP_USING_IF_EXISTS; using ::wprintf _LIBCPP_USING_IF_EXISTS; +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_wcslen(const wchar_t* __str) { +#if __has_builtin(__builtin_wcslen) + return __builtin_wcslen(__str); +#else + if (!__libcpp_is_constant_evaluated()) + return std::wcslen(__str); + + size_t __len = 0; + for (; *__str != L'\0'; ++__str) + ++__len; + return __len; +#endif +} + +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int +__constexpr_wmemcmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __count) { +#if __has_builtin(__builtin_wmemcmp) + return __builtin_wmemcmp(__lhs, __rhs, __count); +#else + if (!__libcpp_is_constant_evaluated()) + return std::wmemcmp(__lhs, __rhs, __count); + + for (; __count; --__count, ++__lhs, ++__rhs) { + if (*__lhs < *__rhs) + return -1; + if (*__rhs < *__lhs) + return 1; + } + return 0; +#endif +} + +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const wchar_t* +__constexpr_wmemchr(const wchar_t* __str, wchar_t __char, size_t __count) { +#if __has_feature(cxx_constexpr_string_builtins) + return __builtin_wmemchr(__str, __char, __count); +#else + if (!__libcpp_is_constant_evaluated()) + return std::wmemchr(__str, __char, __count); + + for (; __count; --__count) { + if (*__str == __char) + return __str; + ++__str; + } + return nullptr; +#endif +} + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP_CWCHAR diff --git a/libcxx/test/libcxx/strings/c.strings/constexpr.cstring.compile.pass.cpp b/libcxx/test/libcxx/strings/c.strings/constexpr.cstring.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/strings/c.strings/constexpr.cstring.compile.pass.cpp @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// 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, c++11 + +// Check that __constexpr_* cstring functions are actually constexpr + +#include + +static_assert(std::__constexpr_strlen("Banane") == 6); +static_assert(std::__constexpr_memcmp("Banane", "Banand", 6) == 1); +static_assert(std::__constexpr_memcmp("Banane", "Banane", 6) == 0); +static_assert(std::__constexpr_memcmp("Banane", "Bananf", 6) == -1); +static_assert([] { + const char str[] = "Banane"; + return std::__constexpr_char_memchr(str, 'n', 6) == str + 2; +}()); diff --git a/libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp b/libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11 + +// UNSUPPORTED: no-wide-characters + +// Check that __constexpr_* cwchar functions are actually constexpr + +#include + +static_assert(std::__constexpr_wcslen(L"Banane") == 6); +static_assert(std::__constexpr_wmemcmp(L"Banane", L"Banand", 6) == 1); +static_assert(std::__constexpr_wmemcmp(L"Banane", L"Banane", 6) == 0); +static_assert(std::__constexpr_wmemcmp(L"Banane", L"Bananf", 6) == -1); +static_assert([] { + const wchar_t str[] = L"Banane"; + return std::__constexpr_wmemchr(str, 'n', 6) == str + 2; +}());