Finish implementing P0426R1 "Constexpr for std::char_traits" for C++17
authorPedro Alves <palves@redhat.com>
Mon, 12 Jun 2017 22:22:39 +0000 (22:22 +0000)
committerPedro Alves <palves@gcc.gnu.org>
Mon, 12 Jun 2017 22:22:39 +0000 (22:22 +0000)
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

libstdc++-v3/ChangeLog
libstdc++-v3/doc/xml/manual/status_cxx2017.xml
libstdc++-v3/include/bits/char_traits.h
libstdc++-v3/testsuite/21_strings/char_traits/requirements/constexpr_functions_c++17.cc

index 5ee9e68af684f40a835921c8abacad1529ec37ca..05efaa06824a8457f358fabbb1752c62324d739a 100644 (file)
@@ -1,3 +1,26 @@
+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>
 
        * include/bits/stl_tree.h (_Rb_tree_impl()): Restore _Node_allocator
index a20823800433366e2846c8dac373a5d033886792..85e193daffa1a5a67ee7254541ad438e0f5b089a 100644 (file)
@@ -482,15 +482,14 @@ Feature-testing recommendations for C++</link>.
     </row>
 
     <row>
-      <?dbhtml bgcolor="#B0B0B0" ?>
       <entry> Constexpr for <code>std::char_traits</code> </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">
        P0426R1
        </link>
       </entry>
-      <entry align="center"> 7 (partial) </entry>
-      <entry><code> ??? </code></entry>
+      <entry align="center"> 8 </entry>
+      <entry><code> __cpp_lib_constexpr_char_traits >= 201611 </code></entry>
     </row>
 
     <row>
index f19120bd16a4bf15bd90fe00848ba1e6ceb4239c..5ccb4396734a97c32c26db4da9860a05f08f320d 100644 (file)
 #include <bits/postypes.h>      // For streampos
 #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)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -139,7 +143,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { 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>
     _GLIBCXX14_CONSTEXPR int
@@ -212,6 +216,42 @@ namespace std _GLIBCXX_VISIBILITY(default)
 {
 _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
   /**
    *  @brief  Basis for explicit traits specializations.
@@ -256,21 +296,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                < 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)
       {
+#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)
          return 0;
        return __builtin_memcmp(__s1, __s2, __n);
       }
 
-      static /* _GLIBCXX17_CONSTEXPR */ size_t
+      static _GLIBCXX17_CONSTEXPR size_t
       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)
       {
+#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)
          return 0;
        return static_cast<const char_type*>(__builtin_memchr(__s, __a, __n));
@@ -347,24 +405,45 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       lt(const char_type& __c1, const char_type& __c2) _GLIBCXX_NOEXCEPT
       { return __c1 < __c2; }
 
-      static /* _GLIBCXX17_CONSTEXPR */ int
+      static _GLIBCXX17_CONSTEXPR int
       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)
          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)
-      { 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)
       {
+#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)
          return 0;
-       return wmemchr(__s, __a, __n);
+       else
+         return wmemchr(__s, __a, __n);
       }
 
       static char_type*
index 014caa0231344f025cd869fc486936d36dcb4d3d..efd280fea231494c0c97774c3db28e6056190226 100644 (file)
@@ -74,20 +74,20 @@ template<typename CT>
   }
 
 #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
-// #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
 
 static_assert( test_assign<std::char_traits<char>>() );
-// static_assert( test_compare<std::char_traits<char>>() );
-// static_assert( test_length<std::char_traits<char>>() );
-// static_assert( test_find<std::char_traits<char>>() );
+static_assert( test_compare<std::char_traits<char>>() );
+static_assert( test_length<std::char_traits<char>>() );
+static_assert( test_find<std::char_traits<char>>() );
 #ifdef _GLIBCXX_USE_WCHAR_T
 static_assert( test_assign<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_find<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_find<std::char_traits<wchar_t>>() );
 #endif
 static_assert( test_assign<std::char_traits<char16_t>>() );
 static_assert( test_compare<std::char_traits<char16_t>>() );