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" }
> > > > > > > > +}
> > > > > > > 
> > > > > > > 
> > > > > > 
> > > > > 
> > > > > 
> > > > 
> > > 
> > > 
> > 
> 

Reply via email to