This should compile: #include <typeinfo> #if __cpp_lib_constexpr_typeinfo constexpr bool b = typeid(int) == typeid(long); #else constexpr bool b = &typeid(int) == &typeid(long); #endif But GCC trunk (12.0.0 20211125) says: ti.C:5:33: error: '(((const std::type_info*)(& _ZTIi)) == ((const std::type_info*)(& _ZTIl)))' is not a constant expression 5 | constexpr bool b = &typeid(int) == &typeid(long); | ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~ This blocks http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1328r1.html (Making std::type_info::operator== constexpr) With the library changes in place compiling with -std=gnu++23 gives: ti.C:3:32: in 'constexpr' expansion of '((const std::type_info*)(& _ZTIi))->std::type_info::operator==(_ZTIl)' ti.C:3:46: error: accessing value of '_ZTIi' through a 'const std::type_info' glvalue in a constant expression 3 | constexpr bool b = typeid(int) == typeid(long); | ^ Using clang++ -std=c++2b the same patched libstdc++ code gives: ti.C:3:16: error: constexpr variable 'b' must be initialized by a constant expression constexpr bool b = typeid(int) == typeid(long); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ /home/jwakely/gcc/latest/lib/gcc/x86_64-pc-linux-gnu/12.0.0/../../../../include/c++/12.0.0/typeinfo:195:9: note: read of object 'typeid(int).__name' whose value is not known if (__name == __arg.__name) ^ ti.C:3:32: note: in call to '&typeid(int)->operator==(typeid(long))' constexpr bool b = typeid(int) == typeid(long); ^ Which presumably means that the RTTI for built-in types (defined in libsupc++/fundamental_type_info.cc) needs to be made visible during constant evaluation.
I think this is a dup of bug 102551.
Or at least the constexpr bool b = &typeid(int) == &typeid(long); example.
Ah yes thanks. The requirement to make the RTTI visible during constant evaluation is new info.
We handle e.g. constexpr auto x = &typeid(int) == &typeid(int); or int a, b; constexpr auto y = &a == &b; The former by the (cmp @0 @0) folding in match.pd, the latter by the address_compare match.pd patterns. But, we already punt on e.g. template <int N> inline int a = N; constexpr auto c = &a<0> == &a<1>; because address_compare -> equal_address_to punts on those, decl_binds_to_current_def_p is false (they are comdat and can end up being defined from some other TU etc.). And similarly it punts in the &typeid(int) == &typeid(long) case, neither _ZTIi nor _ZTIl are defined in the current TU (both are defined in libstdc++) and the code attempts to play safe, say if the actual definitions would be aliases of each other etc. I guess at least for constexpr evaluation we want to have some C++ rules, both for typeid vars - dunno if we can rely on the get_tinfo_decl_direct created vars to be always different if they aren't the same VAR_DECL, or if we need to e.g. compute the name and compare those, or for stuff like inline vars or variable templates. And the question is if cxx_eval_binary_expression should for EQ_EXPR/NE_EXPR repeat what the match.pd address_compare simplification does with its own address_compare, or if it should temporarily set some langhook and let address_compare use that langhook to handle the special cases, or if a langhook should handle some of those cases always for C++. Another case is the typeid(int) == typeid(long) comparison, operator== is I think bool operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT { return ((__name == __arg.__name) || (__name[0] != '*' && __builtin_strcmp (__name, __arg.__name) == 0)); } so we'd need to be able to constant fold during constexpr evaluation typeid(int).__name to &_ZTSi when the typeinfo isn't defined locally and handle &_ZTSwhatever similarly to &_ZTIwhatever, but further we even need to constant fold reading from those strings even when they aren't local..
The new definition of operator== will be something like: #if __GXX_TYPEINFO_EQUALITY_INLINE || __cplusplus > 202002L _GLIBCXX23_CONSTEXPR inline bool type_info::operator==(const type_info& __arg) const _GLIBCXX_NOEXCEPT { if (__name == __arg.__name) return true; if (!std::__is_constant_evaluated()) return false; #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 i.e. no strcmp for the constant evaluation case. During constant evaluation I think we only need to handle types that are completely defined in the current TU, so we can ignore aliasing of _ZTi symbols, and we can ignore the problem of non-unique std::type_info objects. Within the TU they will be unique.
Created attachment 51945 [details] gcc12-pr103600.patch Untested patch that should make typeid(x) == typeid(y) and &typeid(x) == &typeid(y) work, but don't want to duplicate <typeinfo>, so for a testcase I'd need to wait (at least for the former case) until the libstdc++ change lands.
Created attachment 51946 [details] libstdc++: Implement P1328 "Making std::type_info::operator== constexpr" Jakub, that patch works for me. Here's the libstdc++ patch to make operator== constexpr, which passes with your patch applied.
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:134442b2178a164ed4580255a0de007dda19b855 commit r12-6183-g134442b2178a164ed4580255a0de007dda19b855 Author: Jakub Jelinek <jakub@redhat.com> Date: Mon Jan 3 11:21:00 2022 +0100 c++: Support &typeid(x) == &typeid(y) and typeid(x) == typeid(y) in constant evaluation [PR103600] If the tinfo vars are emitted in the current TU, they are emitted at the end of the compilation, and for some types they are exported from libstdc++/libsupc++ and not emitted in the current TU at all. The following patch allows constant folding of comparisons of typeid addresses and makes it possible to implement P1328R1 - making type_info operator== constexpr (Jonathan has a patch for that). As mentioned in the PR, the varpool/middle-end code is trying to be conservative with address comparisons of different vars if those vars don't bind locally, because of possible aliases in other TUs etc. and so while match.pd folds &typeid(int) == &typeid(int) because it is equality comparison with the same operands, for different typeids it doesn't fold it. On Wed, Dec 08, 2021 at 08:53:03AM -0500, Jason Merrill wrote: > Would it make sense to assume that DECL_ARTIFICIAL variables can't be > aliases? If not, could we have some way of marking a variable as > non-aliasing, perhaps an attribute? I think DECL_ARTIFICIAL vars generally can overlap. The following patch adds a GCC internal attribute "non overlapping" and uses it in symtab_node::equal_address_to. Not sure what plans has Honza in that area and whether it would be useful to make the attribute public and let users assert that some variable will never overlap with other variables, won't have aliases etc. > During constant evaluation, the operator== could compare the type_info > address instead of the __name address, reducing this to the previous > problem. Ah, indeed, good idea. FYI, clang++ seems to constant fold &typeid(x) != &typeid(y) already, so Jonathan could use it even for clang++ in the constexpr operator==. But it folds even extern int &a, &b; constexpr bool c = &a != &b; regardless of whether some other TU has int a; int b __attribute__((alias (a)); or not. 2022-01-03 Jakub Jelinek <jakub@redhat.com> PR c++/103600 gcc/ * symtab.c (symtab_node::equal_address_to): Return 0 if one of VAR_DECLs has "non overlapping" attribute and rs1 != rs2. gcc/c-family/ * c-attribs.c (handle_non_overlapping_attribute): New function. (c_common_attribute_table): Add "non overlapping" attribute. gcc/cp/ * rtti.c (get_tinfo_decl_direct): Add "non overlapping" attribute to DECL_TINFO_P VAR_DECLs. gcc/testsuite/ * g++.dg/cpp0x/constexpr-typeid2.C: New test.
Sorry for the delay, I've missed Honza's https://gcc.gnu.org/pipermail/gcc-patches/2021-December/586501.html mail (didn't reach my mailbox but is in the archives).
*** Bug 102551 has been marked as a duplicate of this bug. ***
Thus fixed since GCC 12