Message ID | 20221201163752.2176490-1-ppalka@redhat.com |
---|---|
State | New |
Headers |
Return-Path: <gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org> X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 62ACC3858408 for <patchwork@sourceware.org>; Thu, 1 Dec 2022 16:38:28 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 62ACC3858408 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1669912708; bh=it14fpxQjcR23b4X0DB8NvmvqI8vzOAIExG3ekvEKT4=; h=To:Cc:Subject:Date:List-Id:List-Unsubscribe:List-Archive: List-Post:List-Help:List-Subscribe:From:Reply-To:From; b=xsq8sVsHgHkQaViTqLeAyk50/9ZvjpE6lyXbtXL3x92xpAgA1nic6trswmaUhZoI4 Hi5GsHD9k8BzN8yyAPgT5xUmWlE2sOGtYuE3STSjzQB0ce2W3NCXDXMkoz7lMN8MUB I5A/fwxgBp7I3G5KIAaXjpv9GUbR6nGW9grugWhA= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id E42A53858D32 for <gcc-patches@gcc.gnu.org>; Thu, 1 Dec 2022 16:37:58 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org E42A53858D32 Received: from mail-qv1-f70.google.com (mail-qv1-f70.google.com [209.85.219.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-433-bVcXYSFCNWaOAQ4AovXuow-1; Thu, 01 Dec 2022 11:37:57 -0500 X-MC-Unique: bVcXYSFCNWaOAQ4AovXuow-1 Received: by mail-qv1-f70.google.com with SMTP id q17-20020a056214019100b004b1d3c9f3acso5941973qvr.0 for <gcc-patches@gcc.gnu.org>; Thu, 01 Dec 2022 08:37:57 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=it14fpxQjcR23b4X0DB8NvmvqI8vzOAIExG3ekvEKT4=; b=4QzL+KVxmLG2c9Endqn7h/R8+PZjU+ewRykRga9YIS8vG8pU2l0zrgBbcyZOdthNLu VjEsY/CklpxUNmL9WHAgsehgvEHc7pCua59YGKX34e4ylKd+Pb94wODNriMEneNawOQj gVU7P7U0c9KJq4/wsrbZVoXQ+w93mxNfXfAg5pxTHl5AcOR5azFdk3DG1cHkIvbI3wYA 7S5RadKHmFz9lS+nTi8p+dYvjfVhsGsmKv1d8jb+2+frK6+Q6WXU72k/EiszOLolg9lE cJP0Rjd8SWaED8LHvia8Afj29gJ3T5qjSlEGaEWtV6Psf48IhPTr0/uBNDx0OBqNDTNB 9EIQ== X-Gm-Message-State: ANoB5plcc4wMmi+TQxxlGNoxD6jusemB/+zrYqhf6WPcZISQjVFcAQVs aHCg/aDURLmCFgQSC7Zh23XLByQ4+VlXh2oXhBWqhRHpPsgBCMxvPilS36yFyqVx+xkfnYcYh0a iZgz7TIhGpp8cIDz4+xKbsT+kJodfJEg9OXtiplPCxXhNXZJAjzhvCST5WDFyeVs6bZk= X-Received: by 2002:a05:620a:128f:b0:6fc:2d7a:c3ed with SMTP id w15-20020a05620a128f00b006fc2d7ac3edmr35763578qki.649.1669912676387; Thu, 01 Dec 2022 08:37:56 -0800 (PST) X-Google-Smtp-Source: AA0mqf7eKTnwSYT34wDW/AZzZI+wW+9paqek0uCI/3o5EmdTyagIHSrGH/eAjIrf2+1glSsHHb8v0Q== X-Received: by 2002:a05:620a:128f:b0:6fc:2d7a:c3ed with SMTP id w15-20020a05620a128f00b006fc2d7ac3edmr35763545qki.649.1669912675987; Thu, 01 Dec 2022 08:37:55 -0800 (PST) Received: from localhost.localdomain (ool-457670bb.dyn.optonline.net. [69.118.112.187]) by smtp.gmail.com with ESMTPSA id h12-20020ac8714c000000b0039a372fbaa5sm2742365qtp.69.2022.12.01.08.37.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 01 Dec 2022 08:37:55 -0800 (PST) To: gcc-patches@gcc.gnu.org Cc: jason@redhat.com, Patrick Palka <ppalka@redhat.com> Subject: [PATCH] c++: explicit spec of constrained member tmpl [PR107522] Date: Thu, 1 Dec 2022 11:37:52 -0500 Message-Id: <20221201163752.2176490-1-ppalka@redhat.com> X-Mailer: git-send-email 2.39.0.rc0.49.g083e01275b MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-13.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list <gcc-patches.gcc.gnu.org> List-Unsubscribe: <https://gcc.gnu.org/mailman/options/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe> List-Archive: <https://gcc.gnu.org/pipermail/gcc-patches/> List-Post: <mailto:gcc-patches@gcc.gnu.org> List-Help: <mailto:gcc-patches-request@gcc.gnu.org?subject=help> List-Subscribe: <https://gcc.gnu.org/mailman/listinfo/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe> From: Patrick Palka via Gcc-patches <gcc-patches@gcc.gnu.org> Reply-To: Patrick Palka <ppalka@redhat.com> Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" <gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org> |
Series |
c++: explicit spec of constrained member tmpl [PR107522]
|
|
Commit Message
Patrick Palka
Dec. 1, 2022, 4:37 p.m. UTC
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. 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
Comments
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 temprate parameters of the specialization, why are we throwing that away? > 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" } > +}
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 temprate 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). For satisfaction of the partiall 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" } > > +} > >
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. > 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" } >>> +} >> >> >
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" } > > > > +} > > > > > > > > > >
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. >>> 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" } >>>>> +} >>>> >>>> >>> >> >> >
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" } +}