libstdc++: Implement P1328 "Making std::type_info::operator== constexpr"
authorJonathan Wakely <jwakely@redhat.com>
Wed, 5 Jan 2022 14:25:37 +0000 (14:25 +0000)
committerJonathan Wakely <jwakely@redhat.com>
Wed, 5 Jan 2022 14:43:01 +0000 (14:43 +0000)
This feature is present in the C++23 draft.

With Jakub's recent front-end changes we can implement constexpr
equality by comparing the addresses of std::type_info objects. We do not
need string comparisons, because for constant evaluation cases we know
we aren't dealing with std::type_info objects defined in other
translation units.

The ARM EABI requires that the type_info::operator== function can be
defined out-of-line (and suggests that should be the default), but to be
a constexpr function it must be defined inline (at least for C++23
mode). To meet these conflicting requirements we make the inline version
of operator== call a new __equal function when called at runtime. That
is an alias for the non-inline definition of operator== defined in
libsupc++.

libstdc++-v3/ChangeLog:

* config/abi/pre/gnu.ver (GLIBCXX_3.4.30): Export new symbol for
ARM EABI.
* include/bits/c++config (_GLIBCXX23_CONSTEXPR): Define.
* include/std/version (__cpp_lib_constexpr_typeinfo): Define.
* libsupc++/tinfo.cc: Add #error to ensure non-inline definition
is emitted.
(type_info::__equal): Define alias symbol.
* libsupc++/typeinfo (type_info::before): Combine different
implementations into one.
(type_info::operator==): Likewise. Use address equality for
constant evaluation. Call __equal for targets that require the
definition to be non-inline.
* testsuite/18_support/type_info/constexpr.cc: New test.

libstdc++-v3/config/abi/pre/gnu.ver
libstdc++-v3/include/bits/c++config
libstdc++-v3/include/std/version
libstdc++-v3/libsupc++/tinfo.cc
libstdc++-v3/libsupc++/typeinfo
libstdc++-v3/testsuite/18_support/type_info/constexpr.cc [new file with mode: 0644]

index c2f09a9..afd242b 100644 (file)
@@ -2424,6 +2424,9 @@ GLIBCXX_3.4.30 {
     # std::__timepunct<char>::_M_am_pm_format(const char**)
     _ZNKSt11__timepunctI[cw]E15_M_am_pm_formatEPPK[cw];
 
+    # Only defined #if ! __GXX_TYPEINFO_EQUALITY_INLINE
+    _ZNKSt9type_info7__equalERKS_;
+
 } GLIBCXX_3.4.29;
 
 # Symbols in the support library (libsupc++) have their own tag.
index 3609793..c64b61b 100644 (file)
 #endif
 
 #ifndef _GLIBCXX20_CONSTEXPR
-# if __cplusplus > 201703L
+# if __cplusplus >= 202002L
 #  define _GLIBCXX20_CONSTEXPR constexpr
 # else
 #  define _GLIBCXX20_CONSTEXPR
 # endif
 #endif
 
+#ifndef _GLIBCXX23_CONSTEXPR
+# if __cplusplus >= 202100L
+#  define _GLIBCXX23_CONSTEXPR constexpr
+# else
+#  define _GLIBCXX23_CONSTEXPR
+# endif
+#endif
+
 #ifndef _GLIBCXX17_INLINE
 # if __cplusplus >= 201703L
 #  define _GLIBCXX17_INLINE inline
index 58760e6..f421056 100644 (file)
 // c++2b
 #define __cpp_lib_adaptor_iterator_pair_constructor 202106L
 #define __cpp_lib_byteswap 202110L
+#define __cpp_lib_constexpr_typeinfo 202106L
 #define __cpp_lib_invoke_r 202106L
 #define __cpp_lib_ios_noreplace 202200L
 #define __cpp_lib_is_scoped_enum 202011L
index f384720..ef13dd3 100644 (file)
@@ -32,6 +32,10 @@ std::type_info::
 
 #if !__GXX_TYPEINFO_EQUALITY_INLINE
 
+#if __cplusplus > 202002L
+# error "this file must be compiled with C++20 or older to define operator=="
+#endif
+
 // We can't rely on common symbols being shared between shared objects.
 bool std::type_info::
 operator== (const std::type_info& arg) const _GLIBCXX_NOEXCEPT
@@ -47,6 +51,9 @@ operator== (const std::type_info& arg) const _GLIBCXX_NOEXCEPT
 #endif
 }
 
+bool
+std::type_info::__equal (const std::type_info& arg) const _GLIBCXX_NOEXCEPT
+__attribute__((alias("_ZNKSt9type_infoeqERKS_")));
 #endif
 
 namespace std {
index 91c3099..3018a51 100644 (file)
 
 #pragma GCC visibility push(default)
 
+#if __cplusplus >= 202100L
+# define __cpp_lib_constexpr_typeinfo 202106L
+#endif
+
 extern "C++" {
 
 namespace __cxxabiv1
@@ -99,40 +103,12 @@ namespace std
     const char* name() const _GLIBCXX_NOEXCEPT
     { return __name[0] == '*' ? __name + 1 : __name; }
 
-#if !__GXX_TYPEINFO_EQUALITY_INLINE
-    // In old abi, or when weak symbols are not supported, there can
-    // be multiple instances of a type_info object for one
-    // type. Uniqueness must use the _name value, not object address.
-    bool before(const type_info& __arg) const _GLIBCXX_NOEXCEPT;
-    bool operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT;
-#else
-  #if !__GXX_MERGED_TYPEINFO_NAMES
-    /** Returns true if @c *this precedes @c __arg in the implementation's
+    /** Returns true if `*this` precedes `__arg` in the implementation's
      *  collation order.  */
-    // Even with the new abi, on systems that support dlopen
-    // we can run into cases where type_info names aren't merged,
-    // so we still need to do string comparison.
-    bool before(const type_info& __arg) const _GLIBCXX_NOEXCEPT
-    { return (__name[0] == '*' && __arg.__name[0] == '*')
-       ? __name < __arg.__name
-       : __builtin_strcmp (__name, __arg.__name) < 0; }
-
-    bool operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT
-    {
-      return ((__name == __arg.__name)
-             || (__name[0] != '*' &&
-                 __builtin_strcmp (__name, __arg.__name) == 0));
-    }
-  #else
-    // On some targets we can rely on type_info's NTBS being unique,
-    // and therefore address comparisons are sufficient.
-    bool before(const type_info& __arg) const _GLIBCXX_NOEXCEPT
-    { return __name < __arg.__name; }
+    bool before(const type_info& __arg) const _GLIBCXX_NOEXCEPT;
 
-    bool operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT
-    { return __name == __arg.__name; }
-  #endif
-#endif
+    _GLIBCXX23_CONSTEXPR
+    bool operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT;
 
 #if __cpp_impl_three_way_comparison < 201907L
     bool operator!=(const type_info& __arg) const _GLIBCXX_NOEXCEPT
@@ -176,11 +152,65 @@ namespace std
     explicit type_info(const char *__n): __name(__n) { }
 
   private:
-    /// Assigning type_info is not supported.
+    // type_info objects cannot be copied.
+#if __cplusplus >= 201103L
+    type_info& operator=(const type_info&) = delete;
+    type_info(const type_info&) = delete;
+#else
     type_info& operator=(const type_info&);
     type_info(const type_info&);
+#endif
+
+#if ! __GXX_TYPEINFO_EQUALITY_INLINE
+    bool __equal(const type_info&) const _GLIBCXX_NOEXCEPT;
+#endif
   };
 
+#if __GXX_TYPEINFO_EQUALITY_INLINE
+  inline bool
+  type_info::before(const type_info& __arg) const _GLIBCXX_NOEXCEPT
+  {
+#if !__GXX_MERGED_TYPEINFO_NAMES
+    // Even with the new abi, on systems that support dlopen
+    // we can run into cases where type_info names aren't merged,
+    // so we still need to do string comparison.
+    if (__name[0] != '*' || __arg.__name[0] != '*')
+      return __builtin_strcmp (__name, __arg.__name) < 0;
+#else
+    // On some targets we can rely on type_info's NTBS being unique,
+    // and therefore address comparisons are sufficient.
+#endif
+
+    // In old abi, or when weak symbols are not supported, there can
+    // be multiple instances of a type_info object for one
+    // type. Uniqueness must use the __name value, not object address.
+    return __name < __arg.__name;
+  }
+#endif
+
+#if __GXX_TYPEINFO_EQUALITY_INLINE || __cplusplus > 202002L
+  _GLIBCXX23_CONSTEXPR inline bool
+  type_info::operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT
+  {
+    if (std::__is_constant_evaluated())
+      return this == &__arg;
+
+    if (__name == __arg.__name)
+      return true;
+
+#if !__GXX_TYPEINFO_EQUALITY_INLINE
+    // ABI requires comparisons to be non-inline.
+    return __equal(__arg);
+#elif !__GXX_MERGED_TYPEINFO_NAMES
+    // Need to do string comparison.
+    return __name[0] != '*' && __builtin_strcmp (__name, __arg.name()) == 0;
+#else
+    return false;
+#endif
+  }
+# endif
+
+
   /**
    *  @brief  Thrown during incorrect typecasting.
    *  @ingroup exceptions
diff --git a/libstdc++-v3/testsuite/18_support/type_info/constexpr.cc b/libstdc++-v3/testsuite/18_support/type_info/constexpr.cc
new file mode 100644 (file)
index 0000000..07f4fb6
--- /dev/null
@@ -0,0 +1,48 @@
+// { dg-options "-std=gnu++23 -frtti" }
+// { dg-do compile { target c++23 } }
+
+#include <typeinfo>
+
+#ifndef __cpp_lib_constexpr_typeinfo
+# error "Feature-test macro for constexpr typeinfo missing in <typeinfo>"
+#elif __cpp_lib_constexpr_typeinfo != 202106L
+# error "Feature-test macro for constexpr typeinfo has wrong value in <typeinfo>"
+#endif
+
+struct X { };
+
+constexpr bool
+test01()
+{
+  if (typeid(int) == typeid(long))
+    return false;
+
+  if (typeid(int) != typeid(int))
+    return false;
+
+  struct X { virtual ~X() { } };
+
+  if (typeid(X) != typeid(X))
+    return false;
+
+  if (typeid(X) == typeid(::X))
+    return false;
+
+  if (typeid(X) == typeid(int))
+    return false;
+
+  const auto& ti_x = typeid(X);
+  if (ti_x != ti_x)
+    return false;
+
+  if (ti_x != typeid(X))
+    return false;
+
+  struct Y { };
+  if (ti_x == typeid(Y))
+    return false;
+
+  return true;
+}
+
+static_assert( test01() );