Skip to content

mangling of partially-substituted templates #111

@zygoloid

Description

@zygoloid

The ABI document doesn't appear to give any guidance as to how to mangle partially-substituted portions of templates. For example, in:

template<typename ...T> struct A {
  template<typename ...U> void f(void (*...f)(T, U)) {}
};
void x(int, int);
void g() { A<int>().f(x); } 

how should A<int>::f be mangled?

EDG says: _ZN1AIJiEE1fIJiEEEvDpPFvT_T_E
GCC produces the same (but needs the template argument to be explicitly specified).
Clang gives: _ZN1AIJiEE1fIJiEEEvDpPFv_SUBSTPACK_T_E

Neither mangling preserves enough information to uniquely identify the target function.

The immediate context for this question is issues #47 and #63: after substituting in template arguments from an outer template, how do we mangle the signature of an inner template?

For example:

struct A { int x; };
template<A a> struct B {
  template<typename T> void f(decltype(T(a.x))) {}
};
void g() { B<A{0}>().f<int>(0); }

GCC mangles this as _ZN1BIXtl1AEEE1fIiEEvDTcvT_dtL_Z11_ZTAXtl1AEEE1xE (note the reference to the template parameter object in the middle, not just dttl1AE1x or similar); I think that's the right mangling here.

Another example:

class C {
  char buf[32];
public:
  constexpr const char *data() const { return buf; }
};
C c;
template<const char *p> struct X {};
void f(X<c.data()>) {}
template<const char *p> struct Y { template<typename T> void g(X<T(p)>); };
void h() { Y<c.data()>().g<const char*>({}); }

As I argued in #47, I don't think it's acceptable for us to include the private member name buf in the mangling of f. So similarly it doesn't seem acceptable for us to include the private member name buf in the mangling of Y<c.data()>::g. That suggests to me that the correct mangling to use for a substituted non-type template argument in general is the mangling of the <expression> or <expr-primary> that would appear in the corresponding <template-arg>.

The extension to more general substitution situations seems mostly straightforward: when a type is substituted, you mangle it as you would mangle the <type> in the corresponding <template-arg> (and indeed, presumably this mangling will almost always be a substitution referring back to that <template-arg>). And it would then presumably be appropriate to mangle a substituted pack as J <template-arg>* E.

So:

The first example is mangled as _ZN1AIJiEE1fIJiEEEvDpPFvJiET_E
In the second example, we produce the manglings:

_Z1f1XIXadsoKcL_Z1cEEEE (that is, f(X<&{c subobject of type 'const char' at offset 0}>))
_ZN1YIXadsoKcL_Z1cEEEE1gIPKcEEv1XIXcvT_adsoKcL_Z1cEEEE

Does that seem reasonable? (Note that I'm not suggesting adding any substitution candidates for the new manglings -- and in practice we'll see outer non-type and pack template arguments appearing multiple times in the same mangling. I don't think that we can do much about that, unless we support substitutions of expressions in general (ouch) or ask implementations to keep track of where they substituted into a signature (perhaps also ouch?) -- and in any case changes of that kind would be an ABI break.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions