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

Richard Smith richardsmith at google.com
Wed Dec 3 01:42:43 UTC 2014


On 2 December 2014 at 16:34, John McCall <rjmccall at apple.com> wrote:

> On Dec 2, 2014, at 3:51 PM, Richard Smith <richardsmith at google.com> wrote:
> On 2 December 2014 at 13:58, John McCall <rjmccall at apple.com> wrote:
>>
>> Hmm.  I’d prefer not to hard-code an order dependence, because I think
>> it’s likely that the committee will eventually weaken the rules about where
>> packs can appear in parameter lists (e.g. to allow templates to pull
>> arguments off the end instead of the beginning).
>>
> So I think we either need to do what EDG does and heroically match later
>> arguments which have to be part of the pack — which may or may not work
>> equally well with future pack-placement rules — or pull back and say we’re
>> completely blocked by the existence of dependent pack expansions.
>>
>
> EDG's approach is impossible to follow in some cases, such as:
>
>   template<int A, short B, int C = 0> struct X {};
>   template<int ...N> void f(X<N..., 5>) {}
>   template void f<0>(X<0, 5>);
>   template void f<0, 1>(X<0, 1, 5>);
>
> (EDG rejects this, which I suspect is related to their eager analysis of
> template arguments.)
>
>
> Okay.  So are we comfortable with a simple rule that dependent pack
> expansions always make a template argument “dependent” in the sense I
> described?
>

Is the suggestion that the entire template argument list becomes dependent
if it contains a dependent pack expansion[1]? Or that template arguments at
or after a dependent pack expansion become dependent? The latter is what
everyone is currently doing; the former would be more future-proof but is
an ABI break for all implementations I surveyed.

 [1] How do we define "dependent pack expansion"? Current implementation
practice is that it's expanding a pack of unknown size[2] into a non-pack
parameter. If the pack is being expanded into a pack parameter, we know
that every expansion of that pack, and every subsequent template argument,
matches that parameter, so we can do the relevant conversions.

 [2] What is a pack of unknown size? Sometimes we cannot yet expand a pack
expansion, but we already know its size because we have already one of its
constituent parameter packs. Consider:

template<int, int, int, short...> struct X {};
template<int...I> struct Y {
  template<int...J> void f(X<I+J..., 5>);
};
void g() { Y<1, 2, 3>().f<4, 5, 6>({}); }

The ABI doesn't specify how to mangle this; GCC
uses _ZN1YIJLi1ELi2ELi3EEE1fIJLi4ELi5ELi6EEEEv1XIXspplT_T_ELi5EE which is
wrong and collides with other manglings, Clang uses
_ZN1YIJLi1ELi2ELi3EEE1fIJLi4ELi5ELi6EEEEv1XIXsppl_SUBSTPACK_T_EXLi5EEE
which is outside the ABI but at least doesn't collide, EDG rejects.

This at least suggests that "pack of unknown size" means that the pack
expansion contains any dependent parameter pack, even if the pack expansion
also contains a non-dependent parameter pack (so its size is knowable). And
also that we're missing a mangling rule for the above case...

> I think the ABI rule we’re looking at is something like this:  there are
>> two kinds of template argument, dependent and non-dependent.  A template
>> argument is dependent if:
>>   - it is itself instantiation-dependent,
>>   - the template name is dependent, or
>>   - <some rule about dependent pack expansions?>.
>>
>
> ... or its corresponding template parameter is a non-type template
> parameter with a dependent type (or perhaps is an instantiation-dependent
> template template parameter).
>
>
> Ah, right.
>
> A dependent template argument should be mangled using its original
>> value/type/template-name expression.  A non-dependent non-type template
>> argument can always be matched with a corresponding template parameter type
>> and should be mangled as a (possibly coerced) literal value.
>>
>> Right now, coercion only applies to non-type template arguments, but it’s
>> also possible for it to apply to template template arguments in the future
>> — I was just thinking of passing
>>   template <class T, class U=int> class A;
>> to a parameter typed as:
>>   template<class> class
>> but Doug points out that you could also pass variadic templates, e.g.
>>   template <class… T> class A;
>>
>
> The template template argument issue is not an "in the future" issue; we
> get this wrong today:
>
>
> Oh, I didn’t realize this was something you could currently do; I assumed
> that template template arguments had to be well-kinded.  Silly me.
>
> I guess we need a contorting mangling here when the kind of a template
> doesn’t match the kind of a template template parameter, but… it’s probably
> not the most urgent thing we could be doing. :)  Especially given that you
> can contort the parameters of a template template parameter in a recursive
> position...
>
>   template<typename> struct X {};
>   template<template<typename> class> void f();
>   template<template<typename...> class> void f();
>
> Both f<X>s have the same mangling.
>
> This might be a somewhat unhelpful observation, but the root cause of the
> problem seems to be that our mangling of a function template misses out
> part of the signature (the template parameter list); consequently, if the
> name of a function template specialization is not dependent, we must put
> sufficient information in the template arguments to allow us to recover the
> template parameter list (at least to the point that we can distinguish
> between overloads).
>
>
> I agree that the original sin here is not directly mangling anything about
> template parameter lists.  That’s not fixable, though, without completely
> breaking function-template compatibility.  The approach of mangling the
> final type/kind of template arguments works well enough, especially given
> that type/kind mismatches are uncommon and, in many cases, were previously
> forbidden.
>
> It looks like the template template parameter / template type parameter
> duality for injected-class-names can also theoretically cause problems, but
> you need to be a terrible person to observe it:
>
>   template<template<typename T> class> int f() {}
>   template<class> int g() {}
>   template<typename> struct A {
>     template<typename T> friend void h(decltype(T() + f<A>()) *,
> decltype(T() + g<A>()) *, T) {}
>     void x() { h(0, 0, this); }
>   };
>   void j() { A<int>().x(); }
>
> Here, GCC mangles f<A> and g<A> the same, even though one of them refers
> to A as a class template, and the other refers to A as a non-template
> injected-class-name. That might just be a GCC bug, though; its diagnostics
> suggest that it thinks the template argument for f is A<int> rather than A.
>
>
> Definitely seems like a GCC bug.  I hope Clang mangles the first as a
> reference to A and the second as the type A<int>?
>

Clang does not implement injected-class-name template-argument duality, so
it rejects.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://sourcerytools.com/pipermail/cxx-abi-dev/attachments/20141202/b2cf61ca/attachment-0001.html>


More information about the cxx-abi-dev mailing list