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?

> 
> -- >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