<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>