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

CWG2980 [temp.names] How transient are template template parameter constraints? #658

Open
hubert-reinterpretcast opened this issue Dec 23, 2024 · 5 comments

Comments

@hubert-reinterpretcast
Copy link

Full name of submitter (unless configured in github; will be published with the issue): Hubert Tong

Reference (section label): temp.names

Link to reflector thread (if any): N/A

Issue description

https://wg21.link/temp.names#8 specifies that when

all template-arguments in the simple-template-id are non-dependent ([temp.dep.temp]), the associated constraints ([temp.constr.decl]) of the constrained template shall be satisfied

where the "constrained template" may be a template template parameter.

Note: GCC and Clang fail to implement this even for straightforward cases.

It is unclear how this interacts with instantiation or substitution. In particular, https://wg21.link/temp.arg.template#3 allows for the template template parameter to be more constrained than the argument provided; however, once the template template parameter is replaced by the corresponding argument, the constraints of the template template parameter seemingly disappear into the void.

Is the following ill-formed because of non-satisfaction of C<int>?

template <typename T>
concept C = false;

template <typename> struct Q;

template <template <C> class TT>
struct A {
  template <typename T> void f(TT<T> *) {}
};

template void A<Q>::f(Q<int> *);

Implementations appear to accept the above: https://godbolt.org/z/68hE87TdY

In the opposite direction, given the special case in https://wg21.link/temp.arg.template#3 to allow P to be unconstrained, also consider:

template <typename T>
concept C = false;

template <typename> struct Q;

template <template <typename> class TT, typename T>
using Alias = TT<T>;

template <template <C> class TT>
struct A {
  Alias<TT, int> *p;
};

A<Q> a;

With GCC and Clang being known to be defective, the other implementations appear to use the constraints of TT from A despite the emanation of the simple-template-id from Alias.

Suggested resolution

The transient nature of the constraints on template template parameters are unavoidable if we allow constrained templates as arguments for unconstrained template template parameters: for example, the constraints of a template template parameter is not transmitted when it is used as an argument for an unconstrained template template parameter of a class template.

The question is whether the constraints from the template template parameter should be checked for "usages" (perhaps via substitution into an alias template) of the template template parameter within its scope once the arguments are known.

@jensmaurer
Copy link
Member

CWG2980

@jensmaurer jensmaurer changed the title [temp.names] How transient are template template parameter constraints? CWG2980 [temp.names] How transient are template template parameter constraints? Jan 2, 2025
@hubert-reinterpretcast
Copy link
Author

Thanks Jens. I have some comments regarding the write-up.

Typos:

  • tempalte
  • contraints

Hopefully clearer description regarding alias template expansions:

Maybe the constraints from the template template parameters should be checked where those they appear (including within an alias template) or are substituted into an alias template to form a simple-template-id once the arguments are known.
[ins]
That is, the associated constraints of both

  • template template parameters of alias templates and
  • template template parameters used as arguments to alias templates

are checked for simple-template-id‌s appearing in an alias template.
[/ins]

Additional case to consider:

template <typename T>
concept C = true;

template <typename T>
concept CandMore = C<T> && true;

template <typename> struct Q;

template <template <C> class P, typename T>
using Alias = P<T>;

template <template <CandMore> class TT>
struct A {
  Alias<TT, int> *p;
};

A<Q> a;

The P for Alias fails to be at least as specialized as the argument (TT) if we take the constraints on TT at face value; however, the actual argument to TT may be such that P is at least as specialized as said argument.
It is unclear whether the constraints declared for TT are meant to be checked (as Clang and GCC does) against those declared for P (for example, when checking the template definition).
If the check is allowed, then it should follow that the instantiation is invalid (although EDG and MSVC accept).

@jensmaurer
Copy link
Member

Thanks, updated: CWG2980

@hubert-reinterpretcast
Copy link
Author

Thanks again. Follow-on comments below.

Suggest to add dashes:

they appear (including within an alias template)—or are substituted into an alias template—to form

Missing "and" in the newly-added bulleted list.

@jensmaurer
Copy link
Member

Thanks, fixed: CWG2980

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