[cxx-abi-dev] unresolved-names starting with substitutable namespaces

John McCall rjmccall at apple.com
Mon Oct 19 21:04:47 UTC 2015


A bug was filed against Clang claiming that we mangle a particular symbol differently from GCC:
  https://llvm.org/bugs/show_bug.cgi?id=24794

Basically, it comes down to this:

namespace foo {
  class V {};
}

namespace baz {
  template <class _Ty>
  struct is_enum {
    static const bool value = __is_enum(_Ty);
  };

  template<bool _Test, class _Ty = void>
  struct enable_if {
  };

  template<class _Ty>
  struct enable_if < true, _Ty > {
    typedef _Ty type;
  };

  template <class T>
  typename enable_if< !is_enum< T >::value, void>::type
  Conv(T &x, int *v);
}

int main() {
  int v;
  int i;
  baz::Conv(v, &i);
}

Clang mangles this template specialization as:
  _ZN3baz4ConvIiEENS_9enable_ifIXntsr7is_enumIT_EE5valueEvE4typeERS2_Pi
GCC mangles it as:
  _ZN3baz4ConvIiEENS_9enable_ifIXntsrNS_7is_enumIT_EE5valueEvE4typeERS3_Pi

The key question for this list is the mangling of
  is_enum< T >::value
as either (Clang):
  sr7is_enumIT_EE5valueE
or (GCC, and perhaps EDG):
  srNS_7is_enumIT_EE5valueE

This expression is an unresolved-name.  In the current spec, we have:

  <unresolved-name> ::= [gs] <base-unresolved-name>                     # Production #1: x or (with "gs") ::x
                    ::= sr <unresolved-type> <base-unresolved-name>     # Production #2: T::x / decltype(p)::x
                    ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name>
                                                                        # Production #3: T::N::x /decltype(p)::N::x
                    ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name>  
                                                                        # Production #4: A::x, N::y, A<T>::z; "gs" means leading "::"

  <unresolved-type> ::= <template-param>
                    ::= <decltype>
                    ::= <substitution>

  <unresolved-qualifier-level> ::= <simple-id>
  <base-unresolved-name> ::= <simple-id>
  <simple-id> ::= <source-name> [ <template-args> ]

Clang is looking at this and saying that it’s not rooted in a template-param or a decltype, so it’s not rooted in an unresolved-type, so it has to be mangled using the fourth production of unresolved-name.  GCC appears to be looking at it and saying that it has a substitution for something in the prefix, namely the (implicit) “baz::”, so the unresolved-type should be mangled using one of the second or third productions; since there’s another level of prefix (“is_enum<T>::”) before the final base-unresolved-name, it has to use the third production, srN.

When reasoning about this to myself, it occurred to me that I was arguing based on something that’s not actually spelled out in the spec.  Specifically, it seems right to me that the general intent of <substitution> is that it only ever shortens something that *could* otherwise be mangled by the current production.  For most cases of <substitution>, this is a distinction without a difference, because the production is only used in specific places that limit what entities can appear there, and all those entities can be mangled there; for example, that’s true of all the places where <prefix> is used.  But <unresolved-type> is different, because there are many possible unresolvable prefixes that are substitution candidates but neither a template-parameter or a decltype.  In our example, “baz::” isn’t even a type; and if this conversion function didn’t happen to be written in that namespace, there wouldn’t be an existing substitution for it, and the only legal mangling would use production #4.

Now, if I were designing this mangling from scratch today, I’d probably say that any resolvable entities in the prefix should actually be mangled as normal entities, instead of textually, and of course that would also make them substitution candidates.  That is, “baz", "baz::is_enum”, and “baz::is_enum<T>” would all be legit substitutions here, and if they weren’t substituted they’d at least be mangled as properly-resolved entities, so that (e.g.) a function where is_enum resolved to be an entity in a different namespace would actually be mangled differently.  But that’s not how I read the specification.

Unfortunately, this might be a serious compatibility problem.  What do other compilers do?  What’s the general feeling about this?

John.


More information about the cxx-abi-dev mailing list