public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Patrick Palka <ppalka@redhat.com>
To: Jason Merrill <jason@redhat.com>
Cc: Patrick Palka <ppalka@redhat.com>, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++: explicit spec of constrained member tmpl [PR107522]
Date: Fri, 2 Dec 2022 09:30:38 -0500 (EST)	[thread overview]
Message-ID: <fee2c84f-1609-3cae-9f4e-2d1d7d2e872d@idea> (raw)
In-Reply-To: <967940e9-3ed4-bcfe-20f4-73eaf38d41a4@redhat.com>

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.

> 
> > 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	other threads:[~2022-12-02 14:30 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-01 16:37 Patrick Palka
2022-12-01 19:15 ` Jason Merrill
2022-12-01 19:51   ` Patrick Palka
2022-12-01 21:17     ` Jason Merrill
2022-12-02 14:30       ` Patrick Palka [this message]
2022-12-02 16:16         ` Jason Merrill

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=fee2c84f-1609-3cae-9f4e-2d1d7d2e872d@idea \
    --to=ppalka@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).