> On Dec 1, 2014, at 2:18 PM, Richard Smith <[email protected]> wrote:
> 
> On 1 December 2014 at 12:02, John McCall <[email protected] 
> <mailto:[email protected]>> wrote:
> > On Nov 25, 2014, at 6:13 PM, Richard Smith <[email protected] 
> > <mailto:[email protected]>> 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.
_______________________________________________
cxx-abi-dev mailing list
[email protected]
http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev

Reply via email to