<html>
<head>
<base href="https://bugs.llvm.org/">
</head>
<body><table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Bug ID</th>
<td><a class="bz_bug_link
bz_status_NEW "
title="NEW - IA64 ABI: RTTI name for class in anonymous namespace lacks '*', breaks dynamic_cast and type_info::operator== on GNU/Linux"
href="https://bugs.llvm.org/show_bug.cgi?id=34907">34907</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>IA64 ABI: RTTI name for class in anonymous namespace lacks '*', breaks dynamic_cast and type_info::operator== on GNU/Linux
</td>
</tr>
<tr>
<th>Product</th>
<td>clang
</td>
</tr>
<tr>
<th>Version</th>
<td>5.0
</td>
</tr>
<tr>
<th>Hardware</th>
<td>PC
</td>
</tr>
<tr>
<th>OS</th>
<td>Linux
</td>
</tr>
<tr>
<th>Status</th>
<td>NEW
</td>
</tr>
<tr>
<th>Severity</th>
<td>enhancement
</td>
</tr>
<tr>
<th>Priority</th>
<td>P
</td>
</tr>
<tr>
<th>Component</th>
<td>LLVM Codegen
</td>
</tr>
<tr>
<th>Assignee</th>
<td>unassignedclangbugs@nondot.org
</td>
</tr>
<tr>
<th>Reporter</th>
<td>ryan.prichard@gmail.com
</td>
</tr>
<tr>
<th>CC</th>
<td>llvm-bugs@lists.llvm.org
</td>
</tr></table>
<p>
<div>
<pre>libstdc++ apparently has a convention where the typeinfo name for a class
declared in an anonymous namespace begins with an asterisk ('*'), which tells
std::type_info::operator== to consider two type_info objects unequal even if
their names are equal. Clang is not outputting this asterisk on GNU/Linux.
Because it's omitted, if I declare two classes with the same name, in two
different anonymous namespaces, the two class types are considered equal
according to std::type_info::operator==, and I can cast from one type to
another with dynamic_cast. G++ outputs the asterisk, so the types are treated
as unequal.
The asterisk is stripped off in GNU's std::type_info::name(), so it's not user
visible.
AFAICT, libc++ doesn't have this convention, but for ARM64 iOS, there is a
different convention of setting the highest(?) bit of the type_info's
__type_name pointer to indicate that string comparison *should* be performed.
(Look for the _LIBCPP_HAS_NONUNIQUE_TYPEINFO and _LIBCPP_NONUNIQUE_RTTI_BIT
flags in libc++. I wonder if ARM64 iOS also sets _LIBCXX_DYNAMIC_FALLBACK for
libc++abi?)
I'm wondering whether there's a compatibility concern here w.r.t. previous
versions of Clang. My first guess is that compatibility with
G++/libstdc++/libsupc++ (and correctness) is sufficient to motivate changing
Clang. I guess Clang would have to generate different code for
-stdlib=libstdc++ and -stdlib=libc++?
Test case:
test.h
#include <typeinfo>
#include <stddef.h>
#include <stdio.h>
struct Base {
virtual ~Base() {}
};
namespace def {
Base *alloc();
const std::type_info &type();
}
test-def.cc
#include "test.h"
namespace {
struct A : Base {};
}
namespace def {
Base *alloc() {
return new A;
}
const std::type_info &type() {
return typeid(A);
}
}
test-run.cc
#include "test.h"
namespace {
struct A : Base {
void func() {
printf("ERROR: run func called, field=%d\n", field);
}
private:
int field = 42;
};
}
__attribute__((noinline))
static A *do_cast(Base *b) {
return dynamic_cast<A*>(b);
}
__attribute__((noinline))
static bool types_eq(const std::type_info &x, const std::type_info &y) {
return x == y;
}
int main() {
printf("def A == run A: %d\n", types_eq(def::type(),
typeid(A)));
printf("&def A == &run A: %d\n", &def::type() == &typeid(A));
printf("name of def A: %s\n", def::type().name());
printf("name of run A: %s\n", typeid(A).name());
printf("def A name == run A name: %d\n", def::type().name() ==
typeid(A).name());
Base *b = def::alloc();
auto *p = do_cast(b);
if (p == nullptr) {
printf("SUCCESS: dynamic_cast returned nullptr\n");
} else {
p->func();
}
#ifdef __GXX_TYPEINFO_EQUALITY_INLINE
printf("__GXX_TYPEINFO_EQUALITY_INLINE = %d\n",
__GXX_TYPEINFO_EQUALITY_INLINE);
#endif
#ifdef __GXX_MERGED_TYPEINFO_NAMES
printf("__GXX_MERGED_TYPEINFO_NAMES = %d\n",
__GXX_MERGED_TYPEINFO_NAMES);
#endif
}
$ cat /etc/issue
Ubuntu 14.04.5 LTS \n \l
$ uname -m
x86_64
$ g++ test-def.cc test-run.cc -std=c++11 && ./a.out
def A == run A: 0
&def A == &run A: 0
name of def A: N12_GLOBAL__N_11AE
name of run A: N12_GLOBAL__N_11AE
def A name == run A name: 0
SUCCESS: dynamic_cast returned nullptr
__GXX_TYPEINFO_EQUALITY_INLINE = 1
__GXX_MERGED_TYPEINFO_NAMES = 0
$ ~/clang+llvm-5.0.0-linux-x86_64-ubuntu14.04/bin/clang++ test-def.cc
test-run.cc -std=c++11 && ./a.out
def A == run A: 1
&def A == &run A: 0
name of def A: N12_GLOBAL__N_11AE
name of run A: N12_GLOBAL__N_11AE
def A name == run A name: 0
ERROR: run func called, field=0
__GXX_TYPEINFO_EQUALITY_INLINE = 1
__GXX_MERGED_TYPEINFO_NAMES = 0
$ g++ test-def.cc -S && cat test-def.s
...
_ZTSN12_GLOBAL__N_11AE:
.string "*N12_GLOBAL__N_11AE"
...
$ ~/clang+llvm-5.0.0-linux-x86_64-ubuntu14.04/bin/clang++ test-def.cc -S && cat
test-def.s
...
_ZTSN12_GLOBAL__N_11AE:
.asciz "N12_GLOBAL__N_11AE"
...</pre>
</div>
</p>
<hr>
<span>You are receiving this mail because:</span>
<ul>
<li>You are on the CC list for the bug.</li>
</ul>
</body>
</html>