Message ID | 20211004164057.718456-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 762B63858423 for <patchwork@sourceware.org>; Mon, 4 Oct 2021 16:41:30 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 762B63858423 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1633365690; bh=cwEHIgQAhNo4O7jSC2WpqyYqcyCHMPkoM58jQbZ4Sys=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=nYYPCeUk8EGIhY8WCEDpINfJkFqEoqhu3VLO9eKc8pYPQiT+xg7IEpeFIF5M4A+fb qjpJVi7Kx3OdI7PAx2l6rN+FcQkFXvORL5yqShfJXVVM+/ZMvmOliTPLY2TEC/U+iJ lBJ46tw56d2d8IRAhb0OYiqYO8sVUkRjJ7OlC/7s= 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 [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id 93F9A3858D28 for <gcc-patches@gcc.gnu.org>; Mon, 4 Oct 2021 16:41:01 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 93F9A3858D28 Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-505-sM8HDYHFOYKOP7XjIvTwSg-1; Mon, 04 Oct 2021 12:41:00 -0400 X-MC-Unique: sM8HDYHFOYKOP7XjIvTwSg-1 Received: by mail-qt1-f197.google.com with SMTP id a18-20020aed2792000000b002a6d480aa95so21020783qtd.14 for <gcc-patches@gcc.gnu.org>; Mon, 04 Oct 2021 09:41:00 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=cwEHIgQAhNo4O7jSC2WpqyYqcyCHMPkoM58jQbZ4Sys=; b=5mI3DR0raX2t0/Nq638TmiE9pZ/0o1HGb0moleZvTQ7U9kKK+g4oAvXBOEF5iSbQ9B o8s/KDIuAmVfDIcr3bTvxf/0+SA+DtE0HEokxYP+yKogiQ4+cTJjcc24+kcqgwpeStQ7 o404DuScNbJKTrJFJVHP/hhXgdde6TOx4YdDFW4lLoYt8DwDlgjmpxoLJ3kjSXShcesP J5TDuOY3rWPCGSDA/LwuK3Do1WieljN4+PXYWA08CoGdOawZw/sE9Gi/yCzZzaEeBXjP oEHETEHuYswa7b6fS9kwYP7n/go+lNrUzZES3cIIA7dT2hQh9ugoH7SWnQrWYnjhOwuH W92w== X-Gm-Message-State: AOAM530i+tC/gbjAhesJ0KPYJLgCkk5mbMwL7Ct3NEUuRJGk/O8sbX40 agTIF33b1iQ5ojPSdXcDjZrgyZ2jkiRGezbRVvyWTg1X6lyr21fLjNu0ixBPkDwBxaU8oxNDktK ptt+On3m+xFAIAdJhj/P5tO7WHVP8n1T0e+NDoSbtninYTO/0ldeGvo0vTDvqO3lm3SE= X-Received: by 2002:a37:50f:: with SMTP id 15mr10924352qkf.423.1633365659743; Mon, 04 Oct 2021 09:40:59 -0700 (PDT) X-Google-Smtp-Source: ABdhPJx7DDIvIKSWBP6zA9ouv82Qmhm432uMMiukRaRiNsqMQ6zH/avEyjfX2K1Ezh0JvuJPQuFmBA== X-Received: by 2002:a37:50f:: with SMTP id 15mr10924327qkf.423.1633365659447; Mon, 04 Oct 2021 09:40:59 -0700 (PDT) Received: from localhost.localdomain (ool-457d493a.dyn.optonline.net. [69.125.73.58]) by smtp.gmail.com with ESMTPSA id v23sm7847756qkj.76.2021.10.04.09.40.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 04 Oct 2021 09:40:58 -0700 (PDT) To: gcc-patches@gcc.gnu.org Subject: [PATCH] c++: odr-use argument to a function NTTP [PR53164] Date: Mon, 4 Oct 2021 12:40:57 -0400 Message-Id: <20211004164057.718456-1-ppalka@redhat.com> X-Mailer: git-send-email 2.33.0.610.gcefe983a32 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-Spam-Status: No, score=-15.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_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) 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++: odr-use argument to a function NTTP [PR53164]
|
|
Commit Message
Patrick Palka
Oct. 4, 2021, 4:40 p.m. UTC
When passing a function template as the argument to a function NTTP inside a template, we resolve it to the right specialization ahead of time via resolve_address_of_overloaded_function, though the call to mark_used within defers odr-using it until instantiation time (as usual). But at instantiation time we end up never calling mark_used on the specialization. This patch fixes this by adding a call to mark_used in convert_nontype_argument_function. PR c++/53164 gcc/cp/ChangeLog: * pt.c (convert_nontype_argument_function): Call mark_used. gcc/testsuite/ChangeLog: * g++.dg/template/non-dependent16.C: New test. --- gcc/cp/pt.c | 3 +++ gcc/testsuite/g++.dg/template/non-dependent16.C | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16.C
Comments
On Mon, 4 Oct 2021, Patrick Palka wrote: > When passing a function template as the argument to a function NTTP > inside a template, we resolve it to the right specialization ahead of > time via resolve_address_of_overloaded_function, though the call to > mark_used within defers odr-using it until instantiation time (as usual). > But at instantiation time we end up never calling mark_used on the > specialization. > > This patch fixes this by adding a call to mark_used in > convert_nontype_argument_function. > > PR c++/53164 > > gcc/cp/ChangeLog: > > * pt.c (convert_nontype_argument_function): Call mark_used. > > gcc/testsuite/ChangeLog: > > * g++.dg/template/non-dependent16.C: New test. > --- > gcc/cp/pt.c | 3 +++ > gcc/testsuite/g++.dg/template/non-dependent16.C | 16 ++++++++++++++++ > 2 files changed, 19 insertions(+) > create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16.C > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index f950f4a21b7..5e819c9598c 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -6668,6 +6668,9 @@ convert_nontype_argument_function (tree type, tree expr, > return NULL_TREE; > } > > + if (!mark_used (fn_no_ptr, complain) && !(complain & tf_error)) > + return NULL_TREE; > + > linkage = decl_linkage (fn_no_ptr); > if (cxx_dialect >= cxx11 ? linkage == lk_none : linkage != lk_external) > { > diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C b/gcc/testsuite/g++.dg/template/non-dependent16.C > new file mode 100644 > index 00000000000..b7dca8f6752 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C > @@ -0,0 +1,16 @@ > +// PR c++/53164 > + > +template<class T> > +void f(T) { > + T::fail; // { dg-error "not a member" } > +} > + > +template<void(int)> > +struct A { }; > + > +template<int> > +void g() { > + A<f> a; > +} I should mention that the original testcase in the PR was slightly different than this one in that it also performed a call to the NTTP, e.g. template<void p(int)> struct A { static void h() { p(0); } }; template<int> void g() { A<f>::h(); } templated void g<0>(); and not even the call was enough to odr-use f, apparently because the CALL_EXPR case of tsubst_expr calls mark_used on the callee only when it's a FUNCTION_DECL, but in this case after substitution it's an ADDR_EXPR of a FUNCTION_DECL. Fixing this by looking through the ADDR_EXPR worked, but IIUC the call isn't necessary for f to be odr-used, simply using f as a template argument should be sufficient, so it seems the above is better fix. > + > +template void g<0>(); > -- > 2.33.0.610.gcefe983a32 > >
On 10/5/21 15:17, Patrick Palka wrote: > On Mon, 4 Oct 2021, Patrick Palka wrote: > >> When passing a function template as the argument to a function NTTP >> inside a template, we resolve it to the right specialization ahead of >> time via resolve_address_of_overloaded_function, though the call to >> mark_used within defers odr-using it until instantiation time (as usual). >> But at instantiation time we end up never calling mark_used on the >> specialization. >> >> This patch fixes this by adding a call to mark_used in >> convert_nontype_argument_function. >> >> PR c++/53164 >> >> gcc/cp/ChangeLog: >> >> * pt.c (convert_nontype_argument_function): Call mark_used. >> >> gcc/testsuite/ChangeLog: >> >> * g++.dg/template/non-dependent16.C: New test. >> --- >> gcc/cp/pt.c | 3 +++ >> gcc/testsuite/g++.dg/template/non-dependent16.C | 16 ++++++++++++++++ >> 2 files changed, 19 insertions(+) >> create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16.C >> >> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c >> index f950f4a21b7..5e819c9598c 100644 >> --- a/gcc/cp/pt.c >> +++ b/gcc/cp/pt.c >> @@ -6668,6 +6668,9 @@ convert_nontype_argument_function (tree type, tree expr, >> return NULL_TREE; >> } >> >> + if (!mark_used (fn_no_ptr, complain) && !(complain & tf_error)) >> + return NULL_TREE; >> + >> linkage = decl_linkage (fn_no_ptr); >> if (cxx_dialect >= cxx11 ? linkage == lk_none : linkage != lk_external) >> { >> diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C b/gcc/testsuite/g++.dg/template/non-dependent16.C >> new file mode 100644 >> index 00000000000..b7dca8f6752 >> --- /dev/null >> +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C >> @@ -0,0 +1,16 @@ >> +// PR c++/53164 >> + >> +template<class T> >> +void f(T) { >> + T::fail; // { dg-error "not a member" } >> +} >> + >> +template<void(int)> >> +struct A { }; >> + >> +template<int> >> +void g() { >> + A<f> a; >> +} > > I should mention that the original testcase in the PR was slightly > different than this one in that it also performed a call to the NTTP, > e.g. > > template<void p(int)> > struct A { > static void h() { > p(0); > } > }; > > template<int> > void g() { > A<f>::h(); > } > > templated void g<0>(); > > and not even the call was enough to odr-use f, apparently because the > CALL_EXPR case of tsubst_expr calls mark_used on the callee only when > it's a FUNCTION_DECL, but in this case after substitution it's an > ADDR_EXPR of a FUNCTION_DECL. Fixing this by looking through the ADDR_EXPR > worked, but IIUC the call isn't necessary for f to be odr-used, simply > using f as a template argument should be sufficient, so it seems the > above is better fix. I agree that pedantically the use happens when substituting into the use of A<f>, but convert_nontype_argument_function seems like a weird place to implement that; it's only called again during instantiation of A<f>, when we instantiate the injected-class-name. If A<f> isn't instantiated, e.g. if 'a' is a pointer to A<f>, we again don't instantiate f<int>. I see that clang doesn't reject your testcase, either, but MSVC and icc do (even with 'a' a pointer): https://godbolt.org/z/MGE6TcMch Jason
On Tue, 5 Oct 2021, Jason Merrill wrote: > On 10/5/21 15:17, Patrick Palka wrote: > > On Mon, 4 Oct 2021, Patrick Palka wrote: > > > > > When passing a function template as the argument to a function NTTP > > > inside a template, we resolve it to the right specialization ahead of > > > time via resolve_address_of_overloaded_function, though the call to > > > mark_used within defers odr-using it until instantiation time (as usual). > > > But at instantiation time we end up never calling mark_used on the > > > specialization. > > > > > > This patch fixes this by adding a call to mark_used in > > > convert_nontype_argument_function. > > > > > > PR c++/53164 > > > > > > gcc/cp/ChangeLog: > > > > > > * pt.c (convert_nontype_argument_function): Call mark_used. > > > > > > gcc/testsuite/ChangeLog: > > > > > > * g++.dg/template/non-dependent16.C: New test. > > > --- > > > gcc/cp/pt.c | 3 +++ > > > gcc/testsuite/g++.dg/template/non-dependent16.C | 16 ++++++++++++++++ > > > 2 files changed, 19 insertions(+) > > > create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16.C > > > > > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > > > index f950f4a21b7..5e819c9598c 100644 > > > --- a/gcc/cp/pt.c > > > +++ b/gcc/cp/pt.c > > > @@ -6668,6 +6668,9 @@ convert_nontype_argument_function (tree type, tree > > > expr, > > > return NULL_TREE; > > > } > > > + if (!mark_used (fn_no_ptr, complain) && !(complain & tf_error)) > > > + return NULL_TREE; > > > + > > > linkage = decl_linkage (fn_no_ptr); > > > if (cxx_dialect >= cxx11 ? linkage == lk_none : linkage != > > > lk_external) > > > { > > > diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C > > > b/gcc/testsuite/g++.dg/template/non-dependent16.C > > > new file mode 100644 > > > index 00000000000..b7dca8f6752 > > > --- /dev/null > > > +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C > > > @@ -0,0 +1,16 @@ > > > +// PR c++/53164 > > > + > > > +template<class T> > > > +void f(T) { > > > + T::fail; // { dg-error "not a member" } > > > +} > > > + > > > +template<void(int)> > > > +struct A { }; > > > + > > > +template<int> > > > +void g() { > > > + A<f> a; > > > +} > > > > I should mention that the original testcase in the PR was slightly > > different than this one in that it also performed a call to the NTTP, > > e.g. > > > > template<void p(int)> > > struct A { > > static void h() { > > p(0); > > } > > }; > > > > template<int> > > void g() { > > A<f>::h(); > > } > > > > templated void g<0>(); > > > > and not even the call was enough to odr-use f, apparently because the > > CALL_EXPR case of tsubst_expr calls mark_used on the callee only when > > it's a FUNCTION_DECL, but in this case after substitution it's an > > ADDR_EXPR of a FUNCTION_DECL. Fixing this by looking through the ADDR_EXPR > > worked, but IIUC the call isn't necessary for f to be odr-used, simply > > using f as a template argument should be sufficient, so it seems the > > above is better fix. > > I agree that pedantically the use happens when substituting into the use of > A<f>, but convert_nontype_argument_function seems like a weird place to > implement that; it's only called again during instantiation of A<f>, when we > instantiate the injected-class-name. If A<f> isn't instantiated, e.g. if 'a' > is a pointer to A<f>, we again don't instantiate f<int>. I see, makes sense.. I'm not sure where else we can mark the use, then. Since we resolve the OVERLOAD f to the FUNCTION_DECL f<int> ahead of time (during which mark_used doesn't actually instantiate f<int> because we're inside a template), at instantiation time the type A<f> is already non-dependent so tsubst_aggr_type avoids doing the work that would end up calling convert_nontype_argument_function. > > I see that clang doesn't reject your testcase, either, but MSVC and icc do > (even with 'a' a pointer): https://godbolt.org/z/MGE6TcMch FWIW although Clang doesn't reject 'A<f> a;', it does reject 'using type = A<f>;' weirdly enough: https://godbolt.org/z/T9qEn6bWW Shall we just go with the other more specific approach, that makes sure the CALL_EXPR case of tsubst_expr calls mark_used when the callee is an ADDR_EXPR? Something like (bootstrapped and regtested): -- >8 -- PR c++/53164 gcc/cp/ChangeLog: * pt.c (tsubst_copy_and_build) <case CALL_EXPR>: Look through ADDR_EXPR after substituting into the callee. gcc/testsuite/ChangeLog: * g++.dg/template/fn-ptr3.C: New test. --- gcc/cp/pt.c | 4 ++++ gcc/testsuite/g++.dg/template/fn-ptr3.C | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 gcc/testsuite/g++.dg/template/fn-ptr3.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 19e03369ffa..5af3a6472f8 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -20310,6 +20310,10 @@ tsubst_copy_and_build (tree t, if (BASELINK_P (function)) qualified_p = true; + + if (TREE_CODE (function) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL) + function = TREE_OPERAND (function, 0); } nargs = call_expr_nargs (t); diff --git a/gcc/testsuite/g++.dg/template/fn-ptr3.C b/gcc/testsuite/g++.dg/template/fn-ptr3.C new file mode 100644 index 00000000000..4aa193bd961 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/fn-ptr3.C @@ -0,0 +1,20 @@ +// PR c++/53164 + +template<class T> +void f(T) { + T::fail; // { dg-error "not a member" } +} + +template<void p(int)> +struct A { + static void h() { + p(0); + } +}; + +template<int> +void g() { + A<f>::h(); +} + +template void g<0>();
On Wed, 6 Oct 2021, Patrick Palka wrote: > On Tue, 5 Oct 2021, Jason Merrill wrote: > > > On 10/5/21 15:17, Patrick Palka wrote: > > > On Mon, 4 Oct 2021, Patrick Palka wrote: > > > > > > > When passing a function template as the argument to a function NTTP > > > > inside a template, we resolve it to the right specialization ahead of > > > > time via resolve_address_of_overloaded_function, though the call to > > > > mark_used within defers odr-using it until instantiation time (as usual). > > > > But at instantiation time we end up never calling mark_used on the > > > > specialization. > > > > > > > > This patch fixes this by adding a call to mark_used in > > > > convert_nontype_argument_function. > > > > > > > > PR c++/53164 > > > > > > > > gcc/cp/ChangeLog: > > > > > > > > * pt.c (convert_nontype_argument_function): Call mark_used. > > > > > > > > gcc/testsuite/ChangeLog: > > > > > > > > * g++.dg/template/non-dependent16.C: New test. > > > > --- > > > > gcc/cp/pt.c | 3 +++ > > > > gcc/testsuite/g++.dg/template/non-dependent16.C | 16 ++++++++++++++++ > > > > 2 files changed, 19 insertions(+) > > > > create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16.C > > > > > > > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > > > > index f950f4a21b7..5e819c9598c 100644 > > > > --- a/gcc/cp/pt.c > > > > +++ b/gcc/cp/pt.c > > > > @@ -6668,6 +6668,9 @@ convert_nontype_argument_function (tree type, tree > > > > expr, > > > > return NULL_TREE; > > > > } > > > > + if (!mark_used (fn_no_ptr, complain) && !(complain & tf_error)) > > > > + return NULL_TREE; > > > > + > > > > linkage = decl_linkage (fn_no_ptr); > > > > if (cxx_dialect >= cxx11 ? linkage == lk_none : linkage != > > > > lk_external) > > > > { > > > > diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C > > > > b/gcc/testsuite/g++.dg/template/non-dependent16.C > > > > new file mode 100644 > > > > index 00000000000..b7dca8f6752 > > > > --- /dev/null > > > > +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C > > > > @@ -0,0 +1,16 @@ > > > > +// PR c++/53164 > > > > + > > > > +template<class T> > > > > +void f(T) { > > > > + T::fail; // { dg-error "not a member" } > > > > +} > > > > + > > > > +template<void(int)> > > > > +struct A { }; > > > > + > > > > +template<int> > > > > +void g() { > > > > + A<f> a; > > > > +} > > > > > > I should mention that the original testcase in the PR was slightly > > > different than this one in that it also performed a call to the NTTP, > > > e.g. > > > > > > template<void p(int)> > > > struct A { > > > static void h() { > > > p(0); > > > } > > > }; > > > > > > template<int> > > > void g() { > > > A<f>::h(); > > > } > > > > > > templated void g<0>(); > > > > > > and not even the call was enough to odr-use f, apparently because the > > > CALL_EXPR case of tsubst_expr calls mark_used on the callee only when > > > it's a FUNCTION_DECL, but in this case after substitution it's an > > > ADDR_EXPR of a FUNCTION_DECL. Fixing this by looking through the ADDR_EXPR > > > worked, but IIUC the call isn't necessary for f to be odr-used, simply > > > using f as a template argument should be sufficient, so it seems the > > > above is better fix. > > > > I agree that pedantically the use happens when substituting into the use of > > A<f>, but convert_nontype_argument_function seems like a weird place to > > implement that; it's only called again during instantiation of A<f>, when we > > instantiate the injected-class-name. If A<f> isn't instantiated, e.g. if 'a' > > is a pointer to A<f>, we again don't instantiate f<int>. > > I see, makes sense.. I'm not sure where else we can mark the use, then. > Since we resolve the OVERLOAD f to the FUNCTION_DECL f<int> ahead of > time (during which mark_used doesn't actually instantiate f<int> because > we're inside a template), at instantiation time the type A<f> is already > non-dependent so tsubst_aggr_type avoids doing the work that would end > up calling convert_nontype_argument_function. > > > > > I see that clang doesn't reject your testcase, either, but MSVC and icc do > > (even with 'a' a pointer): https://godbolt.org/z/MGE6TcMch > > FWIW although Clang doesn't reject 'A<f> a;', it does reject > 'using type = A<f>;' weirdly enough: https://godbolt.org/z/T9qEn6bWW > > > Shall we just go with the other more specific approach, that makes sure > the CALL_EXPR case of tsubst_expr calls mark_used when the callee is an > ADDR_EXPR? Something like (bootstrapped and regtested): Err, this approach is wrong because by stripping the ADDR_EXPR here we end up checking access of the unwrapped FUNCTION_DECL again after substituting into the call. So we incorrectly reject e.g. template<void P()> void g() { P(); // error: ‘static void A::h()’ is private within this context } struct A { void f() { g<h>(); } private: static void h(); }; since A::h isn't accessible from g. > > -- >8 -- > > PR c++/53164 > > gcc/cp/ChangeLog: > > * pt.c (tsubst_copy_and_build) <case CALL_EXPR>: Look through > ADDR_EXPR after substituting into the callee. > > gcc/testsuite/ChangeLog: > > * g++.dg/template/fn-ptr3.C: New test. > --- > gcc/cp/pt.c | 4 ++++ > gcc/testsuite/g++.dg/template/fn-ptr3.C | 20 ++++++++++++++++++++ > 2 files changed, 24 insertions(+) > create mode 100644 gcc/testsuite/g++.dg/template/fn-ptr3.C > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index 19e03369ffa..5af3a6472f8 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -20310,6 +20310,10 @@ tsubst_copy_and_build (tree t, > > if (BASELINK_P (function)) > qualified_p = true; > + > + if (TREE_CODE (function) == ADDR_EXPR > + && TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL) > + function = TREE_OPERAND (function, 0); > } > > nargs = call_expr_nargs (t); > diff --git a/gcc/testsuite/g++.dg/template/fn-ptr3.C b/gcc/testsuite/g++.dg/template/fn-ptr3.C > new file mode 100644 > index 00000000000..4aa193bd961 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/fn-ptr3.C > @@ -0,0 +1,20 @@ > +// PR c++/53164 > + > +template<class T> > +void f(T) { > + T::fail; // { dg-error "not a member" } > +} > + > +template<void p(int)> > +struct A { > + static void h() { > + p(0); > + } > +}; > + > +template<int> > +void g() { > + A<f>::h(); > +} > + > +template void g<0>(); > -- > 2.33.0.664.g0785eb7698 > >
On 10/6/21 15:52, Patrick Palka wrote: > On Wed, 6 Oct 2021, Patrick Palka wrote: > >> On Tue, 5 Oct 2021, Jason Merrill wrote: >> >>> On 10/5/21 15:17, Patrick Palka wrote: >>>> On Mon, 4 Oct 2021, Patrick Palka wrote: >>>> >>>>> When passing a function template as the argument to a function NTTP >>>>> inside a template, we resolve it to the right specialization ahead of >>>>> time via resolve_address_of_overloaded_function, though the call to >>>>> mark_used within defers odr-using it until instantiation time (as usual). >>>>> But at instantiation time we end up never calling mark_used on the >>>>> specialization. >>>>> >>>>> This patch fixes this by adding a call to mark_used in >>>>> convert_nontype_argument_function. >>>>> >>>>> PR c++/53164 >>>>> >>>>> gcc/cp/ChangeLog: >>>>> >>>>> * pt.c (convert_nontype_argument_function): Call mark_used. >>>>> >>>>> gcc/testsuite/ChangeLog: >>>>> >>>>> * g++.dg/template/non-dependent16.C: New test. >>>>> --- >>>>> gcc/cp/pt.c | 3 +++ >>>>> gcc/testsuite/g++.dg/template/non-dependent16.C | 16 ++++++++++++++++ >>>>> 2 files changed, 19 insertions(+) >>>>> create mode 100644 gcc/testsuite/g++.dg/template/non-dependent16.C >>>>> >>>>> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c >>>>> index f950f4a21b7..5e819c9598c 100644 >>>>> --- a/gcc/cp/pt.c >>>>> +++ b/gcc/cp/pt.c >>>>> @@ -6668,6 +6668,9 @@ convert_nontype_argument_function (tree type, tree >>>>> expr, >>>>> return NULL_TREE; >>>>> } >>>>> + if (!mark_used (fn_no_ptr, complain) && !(complain & tf_error)) >>>>> + return NULL_TREE; >>>>> + >>>>> linkage = decl_linkage (fn_no_ptr); >>>>> if (cxx_dialect >= cxx11 ? linkage == lk_none : linkage != >>>>> lk_external) >>>>> { >>>>> diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C >>>>> b/gcc/testsuite/g++.dg/template/non-dependent16.C >>>>> new file mode 100644 >>>>> index 00000000000..b7dca8f6752 >>>>> --- /dev/null >>>>> +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C >>>>> @@ -0,0 +1,16 @@ >>>>> +// PR c++/53164 >>>>> + >>>>> +template<class T> >>>>> +void f(T) { >>>>> + T::fail; // { dg-error "not a member" } >>>>> +} >>>>> + >>>>> +template<void(int)> >>>>> +struct A { }; >>>>> + >>>>> +template<int> >>>>> +void g() { >>>>> + A<f> a; >>>>> +} >>>> >>>> I should mention that the original testcase in the PR was slightly >>>> different than this one in that it also performed a call to the NTTP, >>>> e.g. >>>> >>>> template<void p(int)> >>>> struct A { >>>> static void h() { >>>> p(0); >>>> } >>>> }; >>>> >>>> template<int> >>>> void g() { >>>> A<f>::h(); >>>> } >>>> >>>> templated void g<0>(); >>>> >>>> and not even the call was enough to odr-use f, apparently because the >>>> CALL_EXPR case of tsubst_expr calls mark_used on the callee only when >>>> it's a FUNCTION_DECL, but in this case after substitution it's an >>>> ADDR_EXPR of a FUNCTION_DECL. Fixing this by looking through the ADDR_EXPR >>>> worked, but IIUC the call isn't necessary for f to be odr-used, simply >>>> using f as a template argument should be sufficient, so it seems the >>>> above is better fix. >>> >>> I agree that pedantically the use happens when substituting into the use of >>> A<f>, but convert_nontype_argument_function seems like a weird place to >>> implement that; it's only called again during instantiation of A<f>, when we >>> instantiate the injected-class-name. If A<f> isn't instantiated, e.g. if 'a' >>> is a pointer to A<f>, we again don't instantiate f<int>. >> >> I see, makes sense.. I'm not sure where else we can mark the use, then. >> Since we resolve the OVERLOAD f to the FUNCTION_DECL f<int> ahead of >> time (during which mark_used doesn't actually instantiate f<int> because >> we're inside a template), at instantiation time the type A<f> is already >> non-dependent so tsubst_aggr_type avoids doing the work that would end >> up calling convert_nontype_argument_function. >> >>> >>> I see that clang doesn't reject your testcase, either, but MSVC and icc do >>> (even with 'a' a pointer): https://godbolt.org/z/MGE6TcMch >> >> FWIW although Clang doesn't reject 'A<f> a;', it does reject >> 'using type = A<f>;' weirdly enough: https://godbolt.org/z/T9qEn6bWW >> >> >> Shall we just go with the other more specific approach, that makes sure >> the CALL_EXPR case of tsubst_expr calls mark_used when the callee is an >> ADDR_EXPR? Something like (bootstrapped and regtested): > > Err, this approach is wrong because by stripping the ADDR_EXPR here we > end up checking access of the unwrapped FUNCTION_DECL again after > substituting into the call. So we incorrectly reject e.g. > > template<void P()> > void g() { > P(); // error: ‘static void A::h()’ is private within this context > } > > struct A { > void f() { > g<h>(); > } > private: > static void h(); > }; > > since A::h isn't accessible from g. I guess you could call mark_used directly instead of stripping the ADDR_EXPR. Or for the general problem, perhaps we could mark the TEMPLATE_INFO or TI_ARGS to indicate that we still need to mark_used the arguments when we encounter A<f> again during instantiation? >> >> -- >8 -- >> >> PR c++/53164 >> >> gcc/cp/ChangeLog: >> >> * pt.c (tsubst_copy_and_build) <case CALL_EXPR>: Look through >> ADDR_EXPR after substituting into the callee. >> >> gcc/testsuite/ChangeLog: >> >> * g++.dg/template/fn-ptr3.C: New test. >> --- >> gcc/cp/pt.c | 4 ++++ >> gcc/testsuite/g++.dg/template/fn-ptr3.C | 20 ++++++++++++++++++++ >> 2 files changed, 24 insertions(+) >> create mode 100644 gcc/testsuite/g++.dg/template/fn-ptr3.C >> >> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c >> index 19e03369ffa..5af3a6472f8 100644 >> --- a/gcc/cp/pt.c >> +++ b/gcc/cp/pt.c >> @@ -20310,6 +20310,10 @@ tsubst_copy_and_build (tree t, >> >> if (BASELINK_P (function)) >> qualified_p = true; >> + >> + if (TREE_CODE (function) == ADDR_EXPR >> + && TREE_CODE (TREE_OPERAND (function, 0)) == FUNCTION_DECL) >> + function = TREE_OPERAND (function, 0); >> } >> >> nargs = call_expr_nargs (t); >> diff --git a/gcc/testsuite/g++.dg/template/fn-ptr3.C b/gcc/testsuite/g++.dg/template/fn-ptr3.C >> new file mode 100644 >> index 00000000000..4aa193bd961 >> --- /dev/null >> +++ b/gcc/testsuite/g++.dg/template/fn-ptr3.C >> @@ -0,0 +1,20 @@ >> +// PR c++/53164 >> + >> +template<class T> >> +void f(T) { >> + T::fail; // { dg-error "not a member" } >> +} >> + >> +template<void p(int)> >> +struct A { >> + static void h() { >> + p(0); >> + } >> +}; >> + >> +template<int> >> +void g() { >> + A<f>::h(); >> +} >> + >> +template void g<0>(); >> -- >> 2.33.0.664.g0785eb7698 >>
On Wed, 6 Oct 2021, Jason Merrill wrote: > On 10/6/21 15:52, Patrick Palka wrote: > > On Wed, 6 Oct 2021, Patrick Palka wrote: > > > > > On Tue, 5 Oct 2021, Jason Merrill wrote: > > > > > > > On 10/5/21 15:17, Patrick Palka wrote: > > > > > On Mon, 4 Oct 2021, Patrick Palka wrote: > > > > > > > > > > > When passing a function template as the argument to a function NTTP > > > > > > inside a template, we resolve it to the right specialization ahead > > > > > > of > > > > > > time via resolve_address_of_overloaded_function, though the call to > > > > > > mark_used within defers odr-using it until instantiation time (as > > > > > > usual). > > > > > > But at instantiation time we end up never calling mark_used on the > > > > > > specialization. > > > > > > > > > > > > This patch fixes this by adding a call to mark_used in > > > > > > convert_nontype_argument_function. > > > > > > > > > > > > PR c++/53164 > > > > > > > > > > > > gcc/cp/ChangeLog: > > > > > > > > > > > > * pt.c (convert_nontype_argument_function): Call mark_used. > > > > > > > > > > > > gcc/testsuite/ChangeLog: > > > > > > > > > > > > * g++.dg/template/non-dependent16.C: New test. > > > > > > --- > > > > > > gcc/cp/pt.c | 3 +++ > > > > > > gcc/testsuite/g++.dg/template/non-dependent16.C | 16 > > > > > > ++++++++++++++++ > > > > > > 2 files changed, 19 insertions(+) > > > > > > create mode 100644 > > > > > > gcc/testsuite/g++.dg/template/non-dependent16.C > > > > > > > > > > > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > > > > > > index f950f4a21b7..5e819c9598c 100644 > > > > > > --- a/gcc/cp/pt.c > > > > > > +++ b/gcc/cp/pt.c > > > > > > @@ -6668,6 +6668,9 @@ convert_nontype_argument_function (tree type, > > > > > > tree > > > > > > expr, > > > > > > return NULL_TREE; > > > > > > } > > > > > > + if (!mark_used (fn_no_ptr, complain) && !(complain & > > > > > > tf_error)) > > > > > > + return NULL_TREE; > > > > > > + > > > > > > linkage = decl_linkage (fn_no_ptr); > > > > > > if (cxx_dialect >= cxx11 ? linkage == lk_none : linkage != > > > > > > lk_external) > > > > > > { > > > > > > diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C > > > > > > b/gcc/testsuite/g++.dg/template/non-dependent16.C > > > > > > new file mode 100644 > > > > > > index 00000000000..b7dca8f6752 > > > > > > --- /dev/null > > > > > > +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C > > > > > > @@ -0,0 +1,16 @@ > > > > > > +// PR c++/53164 > > > > > > + > > > > > > +template<class T> > > > > > > +void f(T) { > > > > > > + T::fail; // { dg-error "not a member" } > > > > > > +} > > > > > > + > > > > > > +template<void(int)> > > > > > > +struct A { }; > > > > > > + > > > > > > +template<int> > > > > > > +void g() { > > > > > > + A<f> a; > > > > > > +} > > > > > > > > > > I should mention that the original testcase in the PR was slightly > > > > > different than this one in that it also performed a call to the NTTP, > > > > > e.g. > > > > > > > > > > template<void p(int)> > > > > > struct A { > > > > > static void h() { > > > > > p(0); > > > > > } > > > > > }; > > > > > > > > > > template<int> > > > > > void g() { > > > > > A<f>::h(); > > > > > } > > > > > > > > > > templated void g<0>(); > > > > > > > > > > and not even the call was enough to odr-use f, apparently because the > > > > > CALL_EXPR case of tsubst_expr calls mark_used on the callee only when > > > > > it's a FUNCTION_DECL, but in this case after substitution it's an > > > > > ADDR_EXPR of a FUNCTION_DECL. Fixing this by looking through the > > > > > ADDR_EXPR > > > > > worked, but IIUC the call isn't necessary for f to be odr-used, simply > > > > > using f as a template argument should be sufficient, so it seems the > > > > > above is better fix. > > > > > > > > I agree that pedantically the use happens when substituting into the use > > > > of > > > > A<f>, but convert_nontype_argument_function seems like a weird place to > > > > implement that; it's only called again during instantiation of A<f>, > > > > when we > > > > instantiate the injected-class-name. If A<f> isn't instantiated, e.g. > > > > if 'a' > > > > is a pointer to A<f>, we again don't instantiate f<int>. > > > > > > I see, makes sense.. I'm not sure where else we can mark the use, then. > > > Since we resolve the OVERLOAD f to the FUNCTION_DECL f<int> ahead of > > > time (during which mark_used doesn't actually instantiate f<int> because > > > we're inside a template), at instantiation time the type A<f> is already > > > non-dependent so tsubst_aggr_type avoids doing the work that would end > > > up calling convert_nontype_argument_function. > > > > > > > > > > > I see that clang doesn't reject your testcase, either, but MSVC and icc > > > > do > > > > (even with 'a' a pointer): https://godbolt.org/z/MGE6TcMch > > > > > > FWIW although Clang doesn't reject 'A<f> a;', it does reject > > > 'using type = A<f>;' weirdly enough: https://godbolt.org/z/T9qEn6bWW > > > > > > > > > Shall we just go with the other more specific approach, that makes sure > > > the CALL_EXPR case of tsubst_expr calls mark_used when the callee is an > > > ADDR_EXPR? Something like (bootstrapped and regtested): > > > > Err, this approach is wrong because by stripping the ADDR_EXPR here we > > end up checking access of the unwrapped FUNCTION_DECL again after > > substituting into the call. So we incorrectly reject e.g. > > > > template<void P()> > > void g() { > > P(); // error: ‘static void A::h()’ is private within this context > > } > > > > struct A { > > void f() { > > g<h>(); > > } > > private: > > static void h(); > > }; > > > > since A::h isn't accessible from g. > > I guess you could call mark_used directly instead of stripping the ADDR_EXPR. That seems to work nicely, how does the below look? Bootstrapped and regtested on x86_64-pc-linux-gnu. > > Or for the general problem, perhaps we could mark the TEMPLATE_INFO or TI_ARGS > to indicate that we still need to mark_used the arguments when we encounter > A<f> again during instantiation? That sounds plausible, though I suppose it might not be worth it only to handle such a corner case.. -- >8 -- Subject: [PATCH] c++: function NTTP argument considered unused [PR53164] Here at parse time the template argument f (an OVERLOAD) in A<f> gets resolved ahead of time to the FUNCTION_DECL f<int>, and we defer marking f<int> as used until instantiation (of g) as usual. Later when instantiating g the type A<f> (where f has already been resolved) is non-dependent, so tsubst_aggr_type avoids re-processing its template arguments, and we end up never actually marking f<int> as used (which means we never instantiate it) even though A<f>::h() calls it. This patch works around this problem by making us look through ADDR_EXPR when calling mark_used on the callee of a substituted CALL_EXPR. PR c++/53164 gcc/cp/ChangeLog: * pt.c (tsubst_copy_and_build) <case CALL_EXPR>: Look through an ADDR_EXPR callee when calling mark_used. gcc/testsuite/ChangeLog: * g++.dg/template/fn-ptr3.C: New test. --- gcc/cp/pt.c | 12 ++++++++---- gcc/testsuite/g++.dg/template/fn-ptr3.C | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.dg/template/fn-ptr3.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 1e52aa757e1..cd10340ce12 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -20508,10 +20508,14 @@ tsubst_copy_and_build (tree t, } /* Remember that there was a reference to this entity. */ - if (function != NULL_TREE - && DECL_P (function) - && !mark_used (function, complain) && !(complain & tf_error)) - RETURN (error_mark_node); + if (function) + { + tree sub = function; + if (TREE_CODE (sub) == ADDR_EXPR) + sub = TREE_OPERAND (sub, 0); + if (!mark_used (sub, complain) && !(complain & tf_error)) + RETURN (error_mark_node); + } if (!maybe_fold_fn_template_args (function, complain)) return error_mark_node; diff --git a/gcc/testsuite/g++.dg/template/fn-ptr3.C b/gcc/testsuite/g++.dg/template/fn-ptr3.C new file mode 100644 index 00000000000..fd7b31bf775 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/fn-ptr3.C @@ -0,0 +1,20 @@ +// PR c++/53164 + +template<class T> +void f(T) { + T::fail; // { dg-error "'fail' is not a member of 'int'" } +} + +template<void P(int)> +struct A { + static void h() { + P(0); + } +}; + +template<int> +void g() { + A<f>::h(); +} + +template void g<0>();
On 10/7/21 11:17, Patrick Palka wrote: > On Wed, 6 Oct 2021, Jason Merrill wrote: > >> On 10/6/21 15:52, Patrick Palka wrote: >>> On Wed, 6 Oct 2021, Patrick Palka wrote: >>> >>>> On Tue, 5 Oct 2021, Jason Merrill wrote: >>>> >>>>> On 10/5/21 15:17, Patrick Palka wrote: >>>>>> On Mon, 4 Oct 2021, Patrick Palka wrote: >>>>>> >>>>>>> When passing a function template as the argument to a function NTTP >>>>>>> inside a template, we resolve it to the right specialization ahead >>>>>>> of >>>>>>> time via resolve_address_of_overloaded_function, though the call to >>>>>>> mark_used within defers odr-using it until instantiation time (as >>>>>>> usual). >>>>>>> But at instantiation time we end up never calling mark_used on the >>>>>>> specialization. >>>>>>> >>>>>>> This patch fixes this by adding a call to mark_used in >>>>>>> convert_nontype_argument_function. >>>>>>> >>>>>>> PR c++/53164 >>>>>>> >>>>>>> gcc/cp/ChangeLog: >>>>>>> >>>>>>> * pt.c (convert_nontype_argument_function): Call mark_used. >>>>>>> >>>>>>> gcc/testsuite/ChangeLog: >>>>>>> >>>>>>> * g++.dg/template/non-dependent16.C: New test. >>>>>>> --- >>>>>>> gcc/cp/pt.c | 3 +++ >>>>>>> gcc/testsuite/g++.dg/template/non-dependent16.C | 16 >>>>>>> ++++++++++++++++ >>>>>>> 2 files changed, 19 insertions(+) >>>>>>> create mode 100644 >>>>>>> gcc/testsuite/g++.dg/template/non-dependent16.C >>>>>>> >>>>>>> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c >>>>>>> index f950f4a21b7..5e819c9598c 100644 >>>>>>> --- a/gcc/cp/pt.c >>>>>>> +++ b/gcc/cp/pt.c >>>>>>> @@ -6668,6 +6668,9 @@ convert_nontype_argument_function (tree type, >>>>>>> tree >>>>>>> expr, >>>>>>> return NULL_TREE; >>>>>>> } >>>>>>> + if (!mark_used (fn_no_ptr, complain) && !(complain & >>>>>>> tf_error)) >>>>>>> + return NULL_TREE; >>>>>>> + >>>>>>> linkage = decl_linkage (fn_no_ptr); >>>>>>> if (cxx_dialect >= cxx11 ? linkage == lk_none : linkage != >>>>>>> lk_external) >>>>>>> { >>>>>>> diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C >>>>>>> b/gcc/testsuite/g++.dg/template/non-dependent16.C >>>>>>> new file mode 100644 >>>>>>> index 00000000000..b7dca8f6752 >>>>>>> --- /dev/null >>>>>>> +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C >>>>>>> @@ -0,0 +1,16 @@ >>>>>>> +// PR c++/53164 >>>>>>> + >>>>>>> +template<class T> >>>>>>> +void f(T) { >>>>>>> + T::fail; // { dg-error "not a member" } >>>>>>> +} >>>>>>> + >>>>>>> +template<void(int)> >>>>>>> +struct A { }; >>>>>>> + >>>>>>> +template<int> >>>>>>> +void g() { >>>>>>> + A<f> a; >>>>>>> +} >>>>>> >>>>>> I should mention that the original testcase in the PR was slightly >>>>>> different than this one in that it also performed a call to the NTTP, >>>>>> e.g. >>>>>> >>>>>> template<void p(int)> >>>>>> struct A { >>>>>> static void h() { >>>>>> p(0); >>>>>> } >>>>>> }; >>>>>> >>>>>> template<int> >>>>>> void g() { >>>>>> A<f>::h(); >>>>>> } >>>>>> >>>>>> templated void g<0>(); >>>>>> >>>>>> and not even the call was enough to odr-use f, apparently because the >>>>>> CALL_EXPR case of tsubst_expr calls mark_used on the callee only when >>>>>> it's a FUNCTION_DECL, but in this case after substitution it's an >>>>>> ADDR_EXPR of a FUNCTION_DECL. Fixing this by looking through the >>>>>> ADDR_EXPR >>>>>> worked, but IIUC the call isn't necessary for f to be odr-used, simply >>>>>> using f as a template argument should be sufficient, so it seems the >>>>>> above is better fix. >>>>> >>>>> I agree that pedantically the use happens when substituting into the use >>>>> of >>>>> A<f>, but convert_nontype_argument_function seems like a weird place to >>>>> implement that; it's only called again during instantiation of A<f>, >>>>> when we >>>>> instantiate the injected-class-name. If A<f> isn't instantiated, e.g. >>>>> if 'a' >>>>> is a pointer to A<f>, we again don't instantiate f<int>. >>>> >>>> I see, makes sense.. I'm not sure where else we can mark the use, then. >>>> Since we resolve the OVERLOAD f to the FUNCTION_DECL f<int> ahead of >>>> time (during which mark_used doesn't actually instantiate f<int> because >>>> we're inside a template), at instantiation time the type A<f> is already >>>> non-dependent so tsubst_aggr_type avoids doing the work that would end >>>> up calling convert_nontype_argument_function. >>>> >>>>> >>>>> I see that clang doesn't reject your testcase, either, but MSVC and icc >>>>> do >>>>> (even with 'a' a pointer): https://godbolt.org/z/MGE6TcMch >>>> >>>> FWIW although Clang doesn't reject 'A<f> a;', it does reject >>>> 'using type = A<f>;' weirdly enough: https://godbolt.org/z/T9qEn6bWW >>>> >>>> >>>> Shall we just go with the other more specific approach, that makes sure >>>> the CALL_EXPR case of tsubst_expr calls mark_used when the callee is an >>>> ADDR_EXPR? Something like (bootstrapped and regtested): >>> >>> Err, this approach is wrong because by stripping the ADDR_EXPR here we >>> end up checking access of the unwrapped FUNCTION_DECL again after >>> substituting into the call. So we incorrectly reject e.g. >>> >>> template<void P()> >>> void g() { >>> P(); // error: ‘static void A::h()’ is private within this context >>> } >>> >>> struct A { >>> void f() { >>> g<h>(); >>> } >>> private: >>> static void h(); >>> }; >>> >>> since A::h isn't accessible from g. >> >> I guess you could call mark_used directly instead of stripping the ADDR_EXPR. > > That seems to work nicely, how does the below look? Bootstrapped and > regtested on x86_64-pc-linux-gnu. > >> >> Or for the general problem, perhaps we could mark the TEMPLATE_INFO or TI_ARGS >> to indicate that we still need to mark_used the arguments when we encounter >> A<f> again during instantiation? > > That sounds plausible, though I suppose it might not be worth it only to > handle such a corner case.. Indeed. A lower-overhead possibility would be to remember, for a template, decls that we wanted to mark_used but didn't because we were in a template. But I wouldn't worry about it for now. > -- >8 -- > > Subject: [PATCH] c++: function NTTP argument considered unused [PR53164] > > Here at parse time the template argument f (an OVERLOAD) in A<f> gets > resolved ahead of time to the FUNCTION_DECL f<int>, and we defer marking > f<int> as used until instantiation (of g) as usual. > > Later when instantiating g the type A<f> (where f has already been resolved) > is non-dependent, so tsubst_aggr_type avoids re-processing its template > arguments, and we end up never actually marking f<int> as used (which means > we never instantiate it) even though A<f>::h() calls it. > > This patch works around this problem by making us look through ADDR_EXPR > when calling mark_used on the callee of a substituted CALL_EXPR. > > PR c++/53164 > > gcc/cp/ChangeLog: > > * pt.c (tsubst_copy_and_build) <case CALL_EXPR>: Look through an > ADDR_EXPR callee when calling mark_used. > > gcc/testsuite/ChangeLog: > > * g++.dg/template/fn-ptr3.C: New test. > --- > gcc/cp/pt.c | 12 ++++++++---- > gcc/testsuite/g++.dg/template/fn-ptr3.C | 20 ++++++++++++++++++++ > 2 files changed, 28 insertions(+), 4 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/template/fn-ptr3.C > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index 1e52aa757e1..cd10340ce12 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -20508,10 +20508,14 @@ tsubst_copy_and_build (tree t, > } > > /* Remember that there was a reference to this entity. */ > - if (function != NULL_TREE > - && DECL_P (function) > - && !mark_used (function, complain) && !(complain & tf_error)) > - RETURN (error_mark_node); > + if (function) > + { > + tree sub = function; > + if (TREE_CODE (sub) == ADDR_EXPR) > + sub = TREE_OPERAND (sub, 0); Let's add a comment about why this is needed. OK with that change. > + if (!mark_used (sub, complain) && !(complain & tf_error)) > + RETURN (error_mark_node); > + } > > if (!maybe_fold_fn_template_args (function, complain)) > return error_mark_node; > diff --git a/gcc/testsuite/g++.dg/template/fn-ptr3.C b/gcc/testsuite/g++.dg/template/fn-ptr3.C > new file mode 100644 > index 00000000000..fd7b31bf775 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/fn-ptr3.C > @@ -0,0 +1,20 @@ > +// PR c++/53164 > + > +template<class T> > +void f(T) { > + T::fail; // { dg-error "'fail' is not a member of 'int'" } > +} > + > +template<void P(int)> > +struct A { > + static void h() { > + P(0); > + } > +}; > + > +template<int> > +void g() { > + A<f>::h(); > +} > + > +template void g<0>(); >
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index f950f4a21b7..5e819c9598c 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -6668,6 +6668,9 @@ convert_nontype_argument_function (tree type, tree expr, return NULL_TREE; } + if (!mark_used (fn_no_ptr, complain) && !(complain & tf_error)) + return NULL_TREE; + linkage = decl_linkage (fn_no_ptr); if (cxx_dialect >= cxx11 ? linkage == lk_none : linkage != lk_external) { diff --git a/gcc/testsuite/g++.dg/template/non-dependent16.C b/gcc/testsuite/g++.dg/template/non-dependent16.C new file mode 100644 index 00000000000..b7dca8f6752 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/non-dependent16.C @@ -0,0 +1,16 @@ +// PR c++/53164 + +template<class T> +void f(T) { + T::fail; // { dg-error "not a member" } +} + +template<void(int)> +struct A { }; + +template<int> +void g() { + A<f> a; +} + +template void g<0>();