On Tue, 1 Oct 2024, Patrick Palka wrote: > On Thu, 12 Sep 2024, Patrick Palka wrote: > > > (Sorry to resurrect this thread so late, I lost track of this patch...) > > > > On Fri, 2 Dec 2022, Jason Merrill wrote: > > > > > On 12/2/22 09:30, Patrick Palka wrote: > > > > On Thu, 1 Dec 2022, Jason Merrill wrote: > > > > > > > > > On 12/1/22 14:51, Patrick Palka wrote: > > > > > > On Thu, 1 Dec 2022, Jason Merrill wrote: > > > > > > > > > > > > > On 12/1/22 11:37, Patrick Palka wrote: > > > > > > > > When defining a explicit specialization of a constrained member > > > > > > > > template > > > > > > > > (of a class template) such as f and g in the below testcase, the > > > > > > > > DECL_TEMPLATE_PARMS of the corresponding TEMPLATE_DECL are > > > > > > > > partially > > > > > > > > instantiated, whereas its associated constraints are carried > > > > > > > > over > > > > > > > > from the original template and thus are in terms of the original > > > > > > > > DECL_TEMPLATE_PARMS. > > > > > > > > > > > > > > But why are they carried over? We wrote a specification of the > > > > > > > constraints in > > > > > > > terms of the template parameters of the specialization, why are we > > > > > > > throwing > > > > > > > that away? > > > > > > > > > > > > Using the partially instantiated constraints would require adding a > > > > > > special case to satisfaction since during satisfaction we currently > > > > > > always use the full set of template arguments (relative to the most > > > > > > general template). > > > > > > > > > > But not for partial specializations, right? It seems natural to > > > > > handle > > > > > this > > > > > explicit instantiation the way we handle partial specializations, as > > > > > both > > > > > have > > > > > their constraints written in terms of their template parameters. > > > > > > > > True, but what about the general rule that we don't partially > > > > instantiate > > > > constraints outside of declaration matching? Checking satisfaction of > > > > partially instantiated constraints here can introduce hard errors during > > > > normalization, e.g. > > > > > > > > template<class T> > > > > concept C1 = __same_as(T, void); > > > > > > > > template<class T> > > > > concept C2 = C1<typename T::type>; > > > > > > > > template<int N> > > > > concept D = (N == 42); > > > > > > > > template<class T> > > > > struct A { > > > > template<int N> > > > > static void f() requires C2<T> || D<N>; > > > > }; > > > > > > > > template<> > > > > template<int N> > > > > void A<int>::f() requires C2<int> || D<N> { } > > > > > > > > int main() { > > > > A<int>::f<42>(); > > > > } > > > > > > > > Normalization of the the partially instantiated constraints will give a > > > > hard error due to 'int::type' being ill-formed, whereas the > > > > uninstantiated > > > > constraints are fine. > > > > > > Hmm, interesting point, but in this example that happens because the > > > specialization is nonsensical: we wouldn't be normalizing the > > > partially-instantiated constraints so much as the ones that the user > > > explicitly wrote, so a hard error seems justified. > > > > While the written partially-instantiated constraints are nonsensical, > > aren't they only needed for sake of declaration matching? It doesn't > > seem to necessarily imply that that form of constraints is what should > > prevail. This is where the analogy with partial specializations breaks > > down IMHO: partial specializations own their constraints. > > > > Implementing your desired approach isn't so bad either however. We > > mainly just need to correct for TI_ARGS being relative to the primary > > template rather than the partially instantiated template. Something > > like the following? > > Ping. Not sure if the original approach or this one is preferable?
Ping. > > > > > -- >8 -- > > > > Subject: [PATCH] c++: explicit spec of constrained member tmpl [PR107522] > > > > PR c++/107522 > > > > gcc/cp/ChangeLog: > > > > * constraint.cc (satisfy_declaration_constraints): Remove > > extraneous outer arguments for a partial or explicit > > specialization. > > * pt.cc (determine_specialization): For an explicit > > specialization of a member template, make the partially > > instantiated constraints prevail. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp2a/concepts-explicit-spec7.C: New test. > > --- > > gcc/cp/constraint.cc | 14 ++++++++- > > gcc/cp/pt.cc | 7 ++++- > > .../g++.dg/cpp2a/concepts-explicit-spec7.C | 30 +++++++++++++++++++ > > 3 files changed, 49 insertions(+), 2 deletions(-) > > create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec7.C > > > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > > index ebfcdefd284..4dc4fedc659 100644 > > --- a/gcc/cp/constraint.cc > > +++ b/gcc/cp/constraint.cc > > @@ -2728,6 +2728,11 @@ satisfy_declaration_constraints (tree t, sat_info > > info) > > args = TI_ARGS (ti); > > if (inh_ctor_targs) > > args = add_outermost_template_args (args, inh_ctor_targs); > > + if (DECL_TEMPLATE_SPECIALIZATION (TI_TEMPLATE (ti))) > > + { > > + tree parms = DECL_TEMPLATE_PARMS (TI_TEMPLATE (ti)); > > + args = get_innermost_template_args (args, TMPL_PARMS_DEPTH (parms)); > > + } > > } > > > > if (regenerated_lambda_fn_p (t)) > > @@ -2811,7 +2816,14 @@ satisfy_declaration_constraints (tree t, tree args, > > sat_info info) > > args = add_to_template_args (outer_args, args); > > } > > else > > - args = add_outermost_template_args (t, args); > > + { > > + args = add_outermost_template_args (t, args); > > + if (DECL_TEMPLATE_SPECIALIZATION (t)) > > + { > > + tree parms = DECL_TEMPLATE_PARMS (t); > > + args = get_innermost_template_args (args, TMPL_PARMS_DEPTH (parms)); > > + } > > + } > > > > /* If the innermost arguments are dependent, or if the outer arguments > > are dependent and are needed by the constraints, we can't check > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > > index 310e5dfff03..04987f66746 100644 > > --- a/gcc/cp/pt.cc > > +++ b/gcc/cp/pt.cc > > @@ -2502,7 +2502,12 @@ determine_specialization (tree template_id, > > *targs_out = copy_node (DECL_TI_ARGS (fn)); > > > > /* Propagate the candidate's constraints to the declaration. */ > > - if (tsk != tsk_template) > > + if (tsk == tsk_template) > > + { > > + remove_constraints (fn); > > + set_constraints (fn, get_constraints (decl)); > > + } > > + else > > set_constraints (decl, get_constraints (fn)); > > > > /* DECL is a re-declaration or partial instantiation of a template > > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec7.C > > b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec7.C > > new file mode 100644 > > index 00000000000..9452159faf7 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec7.C > > @@ -0,0 +1,30 @@ > > +// PR c++/107522 > > +// { dg-do compile { target c++20 } } > > + > > +template<class T> > > +struct A { > > + template<int N> > > + static void f() requires (N == 42); > > + > > + template<class U> > > + struct B { > > + template<int N> > > + static void g() requires (T(N) == 42); > > + }; > > +}; > > + > > +template<> > > +template<int N> > > +void A<int>::f() requires (N == 42) { } > > + > > +template<> > > +template<> > > +template<int N> > > +void A<int>::B<int>::g() requires (int(N) == 42) { } > > + > > +int main() { > > + A<int>::f<42>(); > > + A<int>::f<43>(); // { dg-error "no match" } > > + A<int>::B<int>::g<42>(); > > + A<int>::B<int>::g<43>(); // { dg-error "no match" } > > +} > > -- > > 2.46.0.551.gc5ee8f2d1c > > > > > > > > > > > > > For satisfaction of the partially instantiated > > > > > > constraints, we'd instead have to use the template arguments > > > > > > relative to > > > > > > the explicit specialization, e.g. {42} instead of {{int},{42}} for > > > > > > A<int>::f<42>. Not sure if that would be preferable, but it seems > > > > > > doable. > > > > > > > > > > > > > > > > > > > > > So during normalization for such an explicit > > > > > > > > specialization we need to consider the (parameters of) the most > > > > > > > > general > > > > > > > > template, since that's what the constraints are in terms of and > > > > > > > > since we > > > > > > > > always use the full set of template arguments during > > > > > > > > satisfaction. > > > > > > > > > > > > > > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this > > > > > > > > look OK > > > > > > > > for > > > > > > > > trunk and perhaps 12? > > > > > > > > > > > > > > > > PR c++/107522 > > > > > > > > > > > > > > > > gcc/cp/ChangeLog: > > > > > > > > > > > > > > > > * constraint.cc (get_normalized_constraints_from_decl): > > > > > > > > Use > > > > > > > > the > > > > > > > > most general template for an explicit specialization of > > > > > > > > a > > > > > > > > member template. > > > > > > > > > > > > > > > > gcc/testsuite/ChangeLog: > > > > > > > > > > > > > > > > * g++.dg/cpp2a/concepts-explicit-spec7.C: New test. > > > > > > > > --- > > > > > > > > gcc/cp/constraint.cc | 18 > > > > > > > > ++++++++--- > > > > > > > > .../g++.dg/cpp2a/concepts-explicit-spec7.C | 31 > > > > > > > > +++++++++++++++++++ > > > > > > > > 2 files changed, 44 insertions(+), 5 deletions(-) > > > > > > > > create mode 100644 > > > > > > > > gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec7.C > > > > > > > > > > > > > > > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > > > > > > > > index ab0f66b3d7e..f1df84c2a1c 100644 > > > > > > > > --- a/gcc/cp/constraint.cc > > > > > > > > +++ b/gcc/cp/constraint.cc > > > > > > > > @@ -973,11 +973,19 @@ get_normalized_constraints_from_decl > > > > > > > > (tree d, > > > > > > > > bool > > > > > > > > diag = false) > > > > > > > > accepting the latter causes the template parameter > > > > > > > > level of > > > > > > > > U > > > > > > > > to be reduced in a way that makes it overly difficult > > > > > > > > substitute > > > > > > > > concrete arguments (i.e., eventually {int, int} during > > > > > > > > satisfaction. > > > > > > > > */ > > > > > > > > - if (tmpl) > > > > > > > > - { > > > > > > > > - if (DECL_LANG_SPECIFIC(tmpl) && > > > > > > > > !DECL_TEMPLATE_SPECIALIZATION > > > > > > > > (tmpl)) > > > > > > > > - tmpl = most_general_template (tmpl); > > > > > > > > - } > > > > > > > > + if (tmpl && DECL_LANG_SPECIFIC (tmpl) > > > > > > > > + && (!DECL_TEMPLATE_SPECIALIZATION (tmpl) > > > > > > > > + /* DECL_TEMPLATE_SPECIALIZATION means we're dealing > > > > > > > > with > > > > > > > > either a > > > > > > > > + partial specialization or an explicit > > > > > > > > specialization of a > > > > > > > > member > > > > > > > > + template. In the former case all is well: the > > > > > > > > constraints are in > > > > > > > > + terms in TMPL's parameters. But in the latter case > > > > > > > > TMPL's > > > > > > > > + parameters are partially instantiated whereas its > > > > > > > > constraints > > > > > > > > + aren't, so we need to consider (the parameters of) > > > > > > > > the > > > > > > > > most > > > > > > > > + general template. The following test distinguishes > > > > > > > > between a > > > > > > > > + partial specialization and such an explicit > > > > > > > > specialization. */ > > > > > > > > + || (TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl)) > > > > > > > > + < TMPL_ARGS_DEPTH (DECL_TI_ARGS (tmpl))))) > > > > > > > > + tmpl = most_general_template (tmpl); > > > > > > > > d = tmpl ? tmpl : decl; > > > > > > > > diff --git > > > > > > > > a/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec7.C > > > > > > > > b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec7.C > > > > > > > > new file mode 100644 > > > > > > > > index 00000000000..5b5a6df20ff > > > > > > > > --- /dev/null > > > > > > > > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-explicit-spec7.C > > > > > > > > @@ -0,0 +1,31 @@ > > > > > > > > +// PR c++/107522 > > > > > > > > +// { dg-do compile { target c++20 } } > > > > > > > > + > > > > > > > > +template<class T> > > > > > > > > +struct A > > > > > > > > +{ > > > > > > > > + template<int N> > > > > > > > > + static void f() requires (N == 42); > > > > > > > > + > > > > > > > > + template<class U> > > > > > > > > + struct B { > > > > > > > > + template<int N> > > > > > > > > + static void g() requires (T(N) == 42); > > > > > > > > + }; > > > > > > > > +}; > > > > > > > > + > > > > > > > > +template<> > > > > > > > > +template<int N> > > > > > > > > +void A<int>::f() requires (N == 42) { } > > > > > > > > + > > > > > > > > +template<> > > > > > > > > +template<> > > > > > > > > +template<int N> > > > > > > > > +void A<int>::B<int>::g() requires (int(N) == 42) { } > > > > > > > > + > > > > > > > > +int main() { > > > > > > > > + A<int>::f<42>(); > > > > > > > > + A<int>::f<43>(); // { dg-error "no match" } > > > > > > > > + A<int>::B<int>::g<42>(); > > > > > > > > + A<int>::B<int>::g<43>(); // { dg-error "no match" } > > > > > > > > +} > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > >