Commit b51483f4 by Pedro Alves Committed by Pedro Alves

Finish implementing P0426R1 "Constexpr for std::char_traits" for C++17

As discussed in PR c++/80265 ("__builtin_{memcmp,memchr,strlen} are
not usable in constexpr functions"), use __builtin_constant_p to tell
whether we can defer to a constexpr algorithm.

I used __always_inline__ just to be thorough.  It isn't really really
necessary as far as I could determine.

Changes like these:

	 if (__n == 0)
	   return 0;
 -	return wmemcmp(__s1, __s2, __n);
 +	else
 +	  return wmemcmp(__s1, __s2, __n);

are necessary otherwise G++ complains that we're calling a
non-constexpr function, which looks like a a manifestation of PR67026
to me.

libstdc++-v3:
2017-06-12  Pedro Alves  <palves@redhat.com>

	* doc/xml/manual/status_cxx2017.xml: Update C++17 constexpr
	char_traits status.
	* doc/html/*: Regenerate.

	* include/bits/char_traits.h (_GLIBCXX_ALWAYS_INLINE): Define if
	not already defined.
	(__cpp_lib_constexpr_char_traits): Uncomment.
	(__constant_string_p, __constant_char_array_p): New.
	(std::char_traits<char>, std::char_traits<wchar_t>): Add
	_GLIBCXX17_CONSTEXPR on compare, length and find and use
	__constant_string_p, __constant_char_array_p and
	__builtin_constant_p to defer to __gnu_cxx::char_traits at compile
	time.

	* testsuite/21_strings/char_traits/requirements/
	constexpr_functions_c++17.cc: Uncomment
	__cpp_lib_constexpr_char_traits tests.  Uncomment
	test_compare<char>, test_length<char>, test_find<char>,
	test_compare<wchar_t>, test_length<wchar_t> and test_find<wchar_t>
	static_assert tests.

From-SVN: r249137
parent 07cfc2d7
2017-06-12 Pedro Alves <palves@redhat.com>
* doc/xml/manual/status_cxx2017.xml: Update C++17 constexpr
char_traits status.
* doc/html/*: Regenerate.
* include/bits/char_traits.h (_GLIBCXX_ALWAYS_INLINE): Define if
not already defined.
(__cpp_lib_constexpr_char_traits): Uncomment.
(__constant_string_p, __constant_char_array_p): New.
(std::char_traits<char>, std::char_traits<wchar_t>): Add
_GLIBCXX17_CONSTEXPR on compare, length and find and use
__constant_string_p, __constant_char_array_p and
__builtin_constant_p to defer to __gnu_cxx::char_traits at compile
time.
* testsuite/21_strings/char_traits/requirements/
constexpr_functions_c++17.cc: Uncomment
__cpp_lib_constexpr_char_traits tests. Uncomment
test_compare<char>, test_length<char>, test_find<char>,
test_compare<wchar_t>, test_length<wchar_t> and test_find<wchar_t>
static_assert tests.
2017-06-12 François Dumont <fdumont@gcc.gnu.org> 2017-06-12 François Dumont <fdumont@gcc.gnu.org>
* include/bits/stl_tree.h (_Rb_tree_impl()): Restore _Node_allocator * include/bits/stl_tree.h (_Rb_tree_impl()): Restore _Node_allocator
......
...@@ -482,15 +482,14 @@ Feature-testing recommendations for C++</link>. ...@@ -482,15 +482,14 @@ Feature-testing recommendations for C++</link>.
</row> </row>
<row> <row>
<?dbhtml bgcolor="#B0B0B0" ?>
<entry> Constexpr for <code>std::char_traits</code> </entry> <entry> Constexpr for <code>std::char_traits</code> </entry>
<entry> <entry>
<link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0426r1.html"> <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0426r1.html">
P0426R1 P0426R1
</link> </link>
</entry> </entry>
<entry align="center"> 7 (partial) </entry> <entry align="center"> 8 </entry>
<entry><code> ??? </code></entry> <entry><code> __cpp_lib_constexpr_char_traits >= 201611 </code></entry>
</row> </row>
<row> <row>
......
...@@ -40,6 +40,10 @@ ...@@ -40,6 +40,10 @@
#include <bits/postypes.h> // For streampos #include <bits/postypes.h> // For streampos
#include <cwchar> // For WEOF, wmemmove, wmemset, etc. #include <cwchar> // For WEOF, wmemmove, wmemset, etc.
#ifndef _GLIBCXX_ALWAYS_INLINE
#define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__))
#endif
namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
{ {
_GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_BEGIN_NAMESPACE_VERSION
...@@ -139,7 +143,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -139,7 +143,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ return !eq_int_type(__c, eof()) ? __c : to_int_type(char_type()); } { return !eq_int_type(__c, eof()) ? __c : to_int_type(char_type()); }
}; };
// #define __cpp_lib_constexpr_char_traits 201611 #define __cpp_lib_constexpr_char_traits 201611
template<typename _CharT> template<typename _CharT>
_GLIBCXX14_CONSTEXPR int _GLIBCXX14_CONSTEXPR int
...@@ -212,6 +216,42 @@ namespace std _GLIBCXX_VISIBILITY(default) ...@@ -212,6 +216,42 @@ namespace std _GLIBCXX_VISIBILITY(default)
{ {
_GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __cplusplus > 201402
/**
* @brief Determine whether the characters of a NULL-terminated
* string are known at compile time.
* @param __s The string.
*
* Assumes that _CharT is a built-in character type.
*/
template<typename _CharT>
static _GLIBCXX_ALWAYS_INLINE constexpr bool
__constant_string_p(const _CharT* __s)
{
while (__builtin_constant_p(*__s) && *__s)
__s++;
return __builtin_constant_p(*__s);
}
/**
* @brief Determine whether the characters of a character array are
* known at compile time.
* @param __a The character array.
* @param __n Number of characters.
*
* Assumes that _CharT is a built-in character type.
*/
template<typename _CharT>
static _GLIBCXX_ALWAYS_INLINE constexpr bool
__constant_char_array_p(const _CharT* __a, size_t __n)
{
size_t __i = 0;
while (__builtin_constant_p(__a[__i]) && __i < __n)
__i++;
return __i == __n;
}
#endif
// 21.1 // 21.1
/** /**
* @brief Basis for explicit traits specializations. * @brief Basis for explicit traits specializations.
...@@ -256,21 +296,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -256,21 +296,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
< static_cast<unsigned char>(__c2)); < static_cast<unsigned char>(__c2));
} }
static /* _GLIBCXX17_CONSTEXPR */ int static _GLIBCXX17_CONSTEXPR int
compare(const char_type* __s1, const char_type* __s2, size_t __n) compare(const char_type* __s1, const char_type* __s2, size_t __n)
{ {
#if __cplusplus > 201402
if (__builtin_constant_p(__n)
&& __constant_char_array_p(__s1, __n)
&& __constant_char_array_p(__s2, __n))
return __gnu_cxx::char_traits<char_type>::compare(__s1, __s2, __n);
#endif
if (__n == 0) if (__n == 0)
return 0; return 0;
return __builtin_memcmp(__s1, __s2, __n); return __builtin_memcmp(__s1, __s2, __n);
} }
static /* _GLIBCXX17_CONSTEXPR */ size_t static _GLIBCXX17_CONSTEXPR size_t
length(const char_type* __s) length(const char_type* __s)
{ return __builtin_strlen(__s); } {
#if __cplusplus > 201402
if (__constant_string_p(__s))
return __gnu_cxx::char_traits<char_type>::length(__s);
#endif
return __builtin_strlen(__s);
}
static /* _GLIBCXX17_CONSTEXPR */ const char_type* static _GLIBCXX17_CONSTEXPR const char_type*
find(const char_type* __s, size_t __n, const char_type& __a) find(const char_type* __s, size_t __n, const char_type& __a)
{ {
#if __cplusplus > 201402
if (__builtin_constant_p(__n)
&& __builtin_constant_p(__a)
&& __constant_char_array_p(__s, __n))
return __gnu_cxx::char_traits<char_type>::find(__s, __n, __a);
#endif
if (__n == 0) if (__n == 0)
return 0; return 0;
return static_cast<const char_type*>(__builtin_memchr(__s, __a, __n)); return static_cast<const char_type*>(__builtin_memchr(__s, __a, __n));
...@@ -347,24 +405,45 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -347,24 +405,45 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
lt(const char_type& __c1, const char_type& __c2) _GLIBCXX_NOEXCEPT lt(const char_type& __c1, const char_type& __c2) _GLIBCXX_NOEXCEPT
{ return __c1 < __c2; } { return __c1 < __c2; }
static /* _GLIBCXX17_CONSTEXPR */ int static _GLIBCXX17_CONSTEXPR int
compare(const char_type* __s1, const char_type* __s2, size_t __n) compare(const char_type* __s1, const char_type* __s2, size_t __n)
{ {
#if __cplusplus > 201402
if (__builtin_constant_p(__n)
&& __constant_char_array_p(__s1, __n)
&& __constant_char_array_p(__s2, __n))
return __gnu_cxx::char_traits<char_type>::compare(__s1, __s2, __n);
#endif
if (__n == 0) if (__n == 0)
return 0; return 0;
return wmemcmp(__s1, __s2, __n); else
return wmemcmp(__s1, __s2, __n);
} }
static /* _GLIBCXX17_CONSTEXPR */ size_t static _GLIBCXX17_CONSTEXPR size_t
length(const char_type* __s) length(const char_type* __s)
{ return wcslen(__s); } {
#if __cplusplus > 201402
if (__constant_string_p(__s))
return __gnu_cxx::char_traits<char_type>::length(__s);
else
#endif
return wcslen(__s);
}
static /* _GLIBCXX17_CONSTEXPR */ const char_type* static _GLIBCXX17_CONSTEXPR const char_type*
find(const char_type* __s, size_t __n, const char_type& __a) find(const char_type* __s, size_t __n, const char_type& __a)
{ {
#if __cplusplus > 201402
if (__builtin_constant_p(__n)
&& __builtin_constant_p(__a)
&& __constant_char_array_p(__s, __n))
return __gnu_cxx::char_traits<char_type>::find(__s, __n, __a);
#endif
if (__n == 0) if (__n == 0)
return 0; return 0;
return wmemchr(__s, __a, __n); else
return wmemchr(__s, __a, __n);
} }
static char_type* static char_type*
......
...@@ -74,20 +74,20 @@ template<typename CT> ...@@ -74,20 +74,20 @@ template<typename CT>
} }
#ifndef __cpp_lib_constexpr_char_traits #ifndef __cpp_lib_constexpr_char_traits
// #error Feature-test macro for constexpr char_traits is missing # error Feature-test macro for constexpr char_traits is missing
#elif __cpp_lib_constexpr_char_traits != 201611 #elif __cpp_lib_constexpr_char_traits != 201611
// #error Feature-test macro for constexpr char_traits has the wrong value # error Feature-test macro for constexpr char_traits has the wrong value
#endif #endif
static_assert( test_assign<std::char_traits<char>>() ); static_assert( test_assign<std::char_traits<char>>() );
// static_assert( test_compare<std::char_traits<char>>() ); static_assert( test_compare<std::char_traits<char>>() );
// static_assert( test_length<std::char_traits<char>>() ); static_assert( test_length<std::char_traits<char>>() );
// static_assert( test_find<std::char_traits<char>>() ); static_assert( test_find<std::char_traits<char>>() );
#ifdef _GLIBCXX_USE_WCHAR_T #ifdef _GLIBCXX_USE_WCHAR_T
static_assert( test_assign<std::char_traits<wchar_t>>() ); static_assert( test_assign<std::char_traits<wchar_t>>() );
// static_assert( test_compare<std::char_traits<wchar_t>>() ); static_assert( test_compare<std::char_traits<wchar_t>>() );
// static_assert( test_length<std::char_traits<wchar_t>>() ); static_assert( test_length<std::char_traits<wchar_t>>() );
// static_assert( test_find<std::char_traits<wchar_t>>() ); static_assert( test_find<std::char_traits<wchar_t>>() );
#endif #endif
static_assert( test_assign<std::char_traits<char16_t>>() ); static_assert( test_assign<std::char_traits<char16_t>>() );
static_assert( test_compare<std::char_traits<char16_t>>() ); static_assert( test_compare<std::char_traits<char16_t>>() );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment