cplusplus / CWG

Core Working Group
23 stars 7 forks source link

[temp.spec.partial.match] It is not explained how to deduce template arguments during partial specialization matching #408

Open t3nsor opened 1 year ago

t3nsor commented 1 year ago

Full name of submitter: Brian Bi

Reference (section label): [temp.spec.partial.match]

Issue description: [temp.spec.partial.match]/2 requires deduction to be done in order to determine whether a partial specialization declaration matches a template argument list, but doesn't explain which set of rules apply to such deduction.

Suggested resolution: Edit [temp.spec.partial.match]/2:

A partial specialization matches a given actual template argument list if the template arguments of the partial specialization can be deduced from the actual template argument list, and the deduced template arguments satisfy the associated constraints of the partial specialization, if any. For a class template partial specialization, deduction is performed according to [temp.deduct.type], taking the type of the partial specialization as P, and the actual specialization as A. For a variable template partial specialization v, deduction is performed as if P were a partial specialization of an invented class template C whose template parameters and template argument list are the same as that of v, and A were the specialization of C with the same template arguments as the actual variable template specialization.

dfrib commented 1 year ago

Example of current implementation divergence:

struct S { void f() const noexcept; };
void g() noexcept;

template <auto> struct Mem;
template <void (S::*mem_fn)() const> struct Mem<mem_fn> {};

template <auto> struct NonMem;
template <void (*fn)()> struct NonMem<fn> {};

Mem<&S::f> m{};
NonMem<&g> nm{};

GCC rejects both cases, whereas Clang and MSVC both accepts them (applying [conv.fctptr]/1)).

RealLitb commented 1 year ago

May be a dupe of https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#697 .

RealLitb commented 1 year ago

Another example of implementation divergence:

template<typename T, int[T::size]>
struct A;

int x;
template<typename T>
struct A<T, &x> { };

A<int, &x> a;

Clang and EDG(ICC) reject, GCC accepts.

I don't know with absolute certainty what should happen. The explicit argument list is "<T, &x>", I think? And substituting gives "<T, int[T::size]>" which makes Clang complain, according to "The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the partial specialization." (according to [temp.spec.partial]p9.1. But that doesn't bother GCC.

Maybe related to https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1444 ? I have no idea.

RealLitb commented 1 year ago

Another case of implementation behavioral divergence removes the (supposed) violation of [temp.spec.partial]p9.1:

template<int N, int[N], typename T>
struct A;

int x;
template<typename T>
struct A<0, &x, T> { };

A<0, &x, bool> a;

Now GCC and EDG(ICC) accept, and Clang still rejects because of the zero-length array. I think it uses [temp.deduct]p3: "After this substitution is performed, the function parameter type adjustments described in [dcl.fct] are performed.". So the adjustments happen afterwards, not before.

t3nsor commented 10 months ago

May be a dupe of https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#697 .

Yeah, I think you're right. That issue doesn't have proposed wording, so maybe it's time to resurrect it—unless it's more complicated than I thought.

t3nsor commented 6 months ago

This underspecification is particularly problematic whenever decltype(auto) template parameters are used. At the very least, we need to make it clear how to handle an example like this:

template <decltype(auto) R>
struct S;  // undefined

template <int X>
struct S<X> { };

constexpr int x = 0;
constexpr const int& r = x;
S<r> s;

If the primary template were template <int X> struct S; then S<r> would just mean S<0>. But in this case we have a partial specialization and it should be "subordinate" to the primary template and never allowed to have its arguments deduced directly from the template-id S<r>. We need to first determine the identity of the specialization by looking at the primary template declaration (i.e. S<R> where R is of type const int& and refers to x) and then try to make S<X> in the partial specialization identical to that S<R>, which of course is not possible, so the partial specialization should not be used in this case. GCC wrongly accepts this snippet.

hubert-reinterpretcast commented 2 months ago

This underspecification is particularly problematic whenever decltype(auto) template parameters are used.

@t3nsor, https://github.com/cplusplus/CWG/issues/546 would clarify that the partial specialization is not a match.