mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-12-12 05:13:50 +08:00
libstdc++: Implement P1328 "Making std::type_info::operator== constexpr"
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.
This commit is contained in:
parent
096228d84e
commit
3633cc5428
@ -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.
|
||||
|
@ -175,13 +175,21 @@
|
||||
#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
|
||||
|
@ -295,6 +295,7 @@
|
||||
// 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
|
||||
|
@ -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 {
|
||||
|
@ -38,6 +38,10 @@
|
||||
|
||||
#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 before(const type_info& __arg) const _GLIBCXX_NOEXCEPT;
|
||||
|
||||
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 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
|
||||
|
48
libstdc++-v3/testsuite/18_support/type_info/constexpr.cc
Normal file
48
libstdc++-v3/testsuite/18_support/type_info/constexpr.cc
Normal file
@ -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() );
|
Loading…
Reference in New Issue
Block a user