[cxx-abi-dev] N4198 and mangling for member pointer template arguments

John McCall rjmccall at apple.com
Tue Dec 2 01:41:15 UTC 2014


> On Dec 1, 2014, at 2:18 PM, Richard Smith <richardsmith at google.com> wrote:
> 
> On 1 December 2014 at 12:02, John McCall <rjmccall at apple.com <mailto:rjmccall at apple.com>> wrote:
> > On Nov 25, 2014, at 6:13 PM, Richard Smith <richardsmith at google.com <mailto:richardsmith at google.com>> wrote:
> >
> > N4198 (accepted at Urbana) makes it possible for a template parameter of type T U::* to have a template argument of type T V::*, where V is a base class of U or vice versa. A naive attempt to apply the existing ABI rules leads to mangling collisions in cases like this:
> >
> > struct A { int n; };
> > struct B : A {};
> > template<int A::*> void f() {}
> > template<int B::*> void f() {}
> > void g() {
> >   constexpr int A::*p = &A::n;
> >   constexpr int B::*q = p;
> >   f<p>();
> >   f<q>();
> > }
> >
> > (Here, a naive approach would use XadL_ZN1A1nEEE as the template argument value in both calls.)
> >
> > In order to resolve this, I suggest we introduce a new mangling for the case of a member pointer template argument where the class containing the member is different from the class in the template parameter. The minimal information we'll need to include is the class in the template parameter and a designator if the base class is a repeated base class.
> >
> > One approach would be to use
> >
> >   sc <type> ad L<member>E
> >
> > and to explicitly include the final type plus those intermediate types that introduce multiple inheritance from the base class (that is, just enough to uniquely identify the path).
> >
> > Another would be to introduce a new mangling that incorporates the final type and an offset or discriminator.
> 
> Do we have the same problem for references and pointers to base subobjects?  Okay, I see that the answer is “no”, but only because you kept that restriction in N4198.  I think we can assume that that’s not permanent.
> 
> I agree; I expect we'll eventually pare back the restrictions to something like "no pointers/references to union members, and no one-past-the-end pointers", or even remove all restrictions altogether if no-one gets upset that different template arguments can compare equal. (We've actually already crossed this bridge by specifying that pointers to members of a union compare equal even if they point to different members, but no-one has got upset about it yet...)
> 
> I like the idea of using (possibly invented) static_casts; it’s not optimally compact, but it at least theoretically works with existing demanglers.  Have you checked to see if it actually works?
> 
> For _Z1fIXscM1BiadL_ZN1A1nEEEEvv (from my example above):
> 
> GCC's c++filt gives void f<static_cast<int B::*>(&A::n)>()
> libc++abi's demangler gives void f<static_cast<int B::*>(&(A::n))>() ... which is wrong, but it's equally wrong without the static_cast.

Awesome.

> I agree with only including those intermediate steps necessary to uniquely determine the path.
> 
> We’d have to specify in what dependent situations we include the path.  “Never” is the easiest answer, so that in
>   template <class T, int T::*member> void foo(decltype(T() + temp<&A::baz>());
> we’d mangle &A::baz without a path clarification even if we could type-check "temp<&A::baz>()” at template definition time.
> 
> That seems reasonable to me, but I'm not exactly sure what classifies as a "dependent situation"; do you mean that we should mangle the path only if the <template-arg> is not nested within an instantiation-dependent <expression>?

Good question.  We get this same issue with integer template arguments: the expression 1 has type int, but <1> (sometimes) gets mangled with the template parameter type to which it’s been coerced.  I don’t think the ABI completely specifies when to use one or the other — it’s an example of one of the few places where “mangle the token stream” isn’t really enough information — but I feel like the same rule should clearly apply here.

The simplest rule is probably “only mangle using the coerced type when identifying a concrete specialization, as in the <name> of an <encoding>”.  However, I suspect that Clang, at least, probably aggressively uses the coerced type whenever it's already type-checked the template arguments, meaning probably whenever the reference isn’t (some kind of) dependent.

> There's another issue that we should probably fix at the same time: qualification conversions are permitted in template arguments, and we currently mangle a signature that performs a qualification conversion the same way as we mangle a signature that does not. We could either fold the qualification conversion into the last (synthetic) static_cast, or add an explicit synthetic const_cast to model it. I'm inclined to favour the latter, even though it will give longer manglings in the (hopefully rare) case where both conversions occur (because it also works if the user has cast away constness, and because it's simpler). Example:
> 
> // tu1
> extern int n;
> template<int*> void f() {}
> void g() { f<&n>(); }
> 
> // tu2
> extern int n;
> template<const int*> void f() {}
> void h() { f<&n>(); }
> 
> Here:
> g calls _Z1fIXadL_Z1nEEEvv
> h calls _Z1fIXccPKiadL_Z1nEEEvv

Is this a compatibility issue?  As in, aren’t qualification conversions already allowed in template arguments?  There might be a significant number of existing template arguments that, say, bind a non-const global to a const reference.

John.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://sourcerytools.com/pipermail/cxx-abi-dev/attachments/20141201/0943620e/attachment-0001.html>


More information about the cxx-abi-dev mailing list