Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mangling for placeholders for deduced class template specializations (CTAD) #109

Open
zygoloid opened this issue Oct 18, 2020 · 7 comments

Comments

@zygoloid
Copy link
Contributor

zygoloid commented Oct 18, 2020

Testcase:

namespace N { template<typename T> struct A { A(T); }; }
template<typename T> void f(T t, decltype(N::A(t)) b, N::A<int> c);
void g() { f(0, 0, N::A(0)); }

GCC mangles this as: _Z1fIiEvT_DTcvDafL0p_EN1N1AIiEE
That is: f(T t, decltype(auto(t)), N::A<int>)

Clang trunk asserts; older Clangs mangle this as: _Z1fIiEvT_DTcv1AfL0p_ENS1_IiEE
That is: f(T t, decltype(A(t)), {subst for A}<int>)

ICC mangles this as: _Z1fIiEvT_DTcvT18446744073709551614_fL0p_EN1N1AIiEE
That is: f(T t, decltype(<template parameter 18446744073709551615>(t)), N::A<int>)

These are all different from each other and none of them is reasonable.

The natural way to mange such a placeholder type would seem to be as a <name>, where the final <unqualified-name> in a <nested-name> would be the template name. So the mangling for the above example would be: _Z1fIiEvT_DTcvN1N1AEfL0p_ENS1_1AIiEE. (Note that the <type> in the first parameter is not a substitution candidate for the <template-prefix> in the second.)

(I'm not sure if this needs actual ABI updates or if this is just an implementation bug shared by every implementation; there don't seem to be any other natural choices for how to mangle this.)

@rjmccall
Copy link
Collaborator

It would be better if it were a substitution candidate, right? And that would only change mangling for this case, where we already have implementation disagreement?

@zygoloid
Copy link
Contributor Author

Well, the complete type will be a substitution candidate if the same deduced placeholder is used twice, so we will get some substitutions at least. But we won't treat the type N::A as a substitution for the template name in N::A<int> (because one of them is a type and the other is a template name). At least in Clang, this seemed more in line with the "substitutions correspond to symbol table entries" principle.

If we choose to treat it as a substitution candidate, would we introduce two N::A substitutions (one for N::A as a <template-prefix> and one for N::A as a <type>) or only one substitution with two meanings? Either way seems a little unpleasant in some ways.

@rjmccall
Copy link
Collaborator

I think you're drawing a distinction that doesn't exist; template names are usually substitution candidates when they appear in later types, e.g.

template <class> class A;
template <template <class> class T, class U> void foo() {}
template void foo<A, A<int>>();  // _Z3fooI1AS0_IiEEvv

@zygoloid
Copy link
Contributor Author

zygoloid commented Nov 6, 2020

Your example certainly addresses half of my concern: it demonstrates that we'll use <substitution>s in one production (A is mangled as a <type>) when mangling a different production (the A in A<int> is mangled as a <template-prefix>, which doesn't even use the same mangling scheme). However, in your example, both occurrences of A refer to the template A, not to a type with the same spelling, so I don't think it's exactly the same situation.

Here's a situation that I think is a little more directly analogous, where we use the same name as both a type and a template:

template<typename> struct U {};
struct T : U<int> {};
template<typename, template<typename> typename> struct X {};
template<typename T> void g(X<typename T::U, T::template U>) {}
void h() { g<T>(X<U<int>, U>()); }

While Clang and GCC mangle this slightly differently, both agree that typename T::U is not a substitution candidate for T::template U, (presumably) because the latter is a template and the former is a type. (If you were to allow such substitutions, it's not too hard to formulate a testcase that would have a mangling collision.)

Nonetheless, I don't think there are any practical problems with allowing a substitution to be used in this specific case -- I don't think we can ever encounter a mangling collision due to this, even though (as noted above) the template/type duality of template names can lead to mangling collisions in other contexts. But I don't think it falls out from the "substitution represents a particular entity" model, because A as a class template and A as a placeholder type for class template argument deduction aren't the same entity -- they're not even the same kind of entity. So I think if we want this, we should call it out explicitly in the mangling rule (eg, "The entity associated with a substitution for a placeholder type for class template deduction is the class template.").

@rjmccall
Copy link
Collaborator

rjmccall commented Nov 6, 2020

However, in your example, both occurrences of A refer to the template A, not to a type with the same spelling, so I don't think it's exactly the same situation.

I don't think it's ever the rule that substitutions are production-specific. The rule is focused squarely on whether you're talking about the same entity. And the similarity of the two N::As in your example is not about spelling: they're actually concretely the same entity, and it wouldn't matter if you named that entity in wildly different ways (e.g. with a using declaration or directive), it would still be the same entity. That is, we wouldn't normally mangle the spelling here at all, we'd mangle the reference to a concrete (if unspecialized) declaration.

Am I missing something where placeholder types can be more complicated, e.g. where you infer arguments from multiple levels of type at once?

My concrete suggestion is that we should just allow cv to take a <template-name>. If that would be ambiguous — which it might be, since I suppose you need to distinguish T::typename X(foo) from T::template X(foo)? — then maybe we shouldn't use cv for this.

@zygoloid
Copy link
Contributor Author

My concrete suggestion is that we should just allow cv to take a <template-name>.

I don't think we should special-case cv: such a placeholder type can appear in other places (such as in a tl or nw mangling or in the type of a template parameter), and I'd expect that in the not-too-distant future we'll allow such types as a function parameter or return type; I think we should support it as a general <type> mangling. So concretely I suppose I'd propose this:

<type> ::= <template name>  # placeholder for deduced class type

... with an explicit mention in 5.1.10 that such a placeholder is a substitution candidate for its deduced template and vice versa.

I suppose you need to distinguish T::typename X(foo) from T::template X(foo)?

I don't think we do, because I don't think there's any context in which we can encounter T::X as a placeholder for a deduced class type and also encounter it as a template-name or as an injected-class-name type. But I don't have a proof it's impossible, I've just been unable to find an example :)

@rjmccall
Copy link
Collaborator

Ah, sure. That works for me.

zygoloid added a commit to llvm/llvm-project that referenced this issue Nov 25, 2020
arichardson pushed a commit to arichardson/llvm-project that referenced this issue Mar 26, 2021
nstester pushed a commit to nstester/gcc that referenced this issue Dec 1, 2023
Per itanium-cxx-abi/cxx-abi#109 mangle a C++17
CTAD placeholder as its template.

gcc/cp/ChangeLog:

	* mangle.cc (write_type): Mangle placeholder as its template.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/nontype-class4.C: Specify ABI v18.
	* g++.dg/cpp2a/nontype-class4a.C: New test.
Blackhex pushed a commit to Windows-on-ARM-Experiments/gcc-woarm64 that referenced this issue Dec 18, 2023
Per itanium-cxx-abi/cxx-abi#109 mangle a C++17
CTAD placeholder as its template.

gcc/cp/ChangeLog:

	* mangle.cc (write_type): Mangle placeholder as its template.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/nontype-class4.C: Specify ABI v18.
	* g++.dg/cpp2a/nontype-class4a.C: New test.
Liaoshihua pushed a commit to Liaoshihua/ruyi-gcc that referenced this issue Mar 11, 2024
Per itanium-cxx-abi/cxx-abi#109 mangle a C++17
CTAD placeholder as its template.

gcc/cp/ChangeLog:

	* mangle.cc (write_type): Mangle placeholder as its template.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/nontype-class4.C: Specify ABI v18.
	* g++.dg/cpp2a/nontype-class4a.C: New test.
Liaoshihua pushed a commit to Liaoshihua/ruyi-gcc that referenced this issue Mar 11, 2024
Per itanium-cxx-abi/cxx-abi#109 mangle a C++17
CTAD placeholder as its template.

gcc/cp/ChangeLog:

	* mangle.cc (write_type): Mangle placeholder as its template.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/nontype-class4.C: Specify ABI v18.
	* g++.dg/cpp2a/nontype-class4a.C: New test.
Liaoshihua pushed a commit to Liaoshihua/gcc that referenced this issue Mar 19, 2024
Per itanium-cxx-abi/cxx-abi#109 mangle a C++17
CTAD placeholder as its template.

gcc/cp/ChangeLog:

	* mangle.cc (write_type): Mangle placeholder as its template.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/nontype-class4.C: Specify ABI v18.
	* g++.dg/cpp2a/nontype-class4a.C: New test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants