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++: function NTTP argument considered unused [PR53164, PR105848]
Date: Tue, 7 Jun 2022 09:24:20 -0400 (EDT)	[thread overview]
Message-ID: <50baf432-cc69-45b9-0325-fe1bb7418308@idea> (raw)
In-Reply-To: <41cec505-46f4-5622-ab08-e693fa8b7d90@redhat.com>

On Mon, 6 Jun 2022, Jason Merrill wrote:

> On 6/6/22 14:27, Patrick Palka wrote:
> > On Thu, 7 Oct 2021, Jason Merrill wrote:
> > 
> > > 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.
> > 
> > Thanks.  I ended up neglecting to commit this patch for GCC 12, and
> > turns out it also fixes the recently reported regression PR105848
> > (which is essentially another manifestation of PR53164 exposed by the
> > non-dependent overload set pruning patch r12-6075), so I'm going to
> > commit this now.  I suppose it's also OK to backport to 12?
> 
> What if the use is not as a call, but say assigning the address to a global
> pointer?
> 
> I wonder about calling mark_used when substituting the TEMPLATE_PARM_INDEX,
> probably with tf_none or tf_error to avoid redundant deprecated warnings.

Ah, that seems to work very nicely -- it even handles the case where there the
only use is as a template argument and there is no "use" inside the body
of the function, because we always have to substitute the TEMPLATE_PARM_INDEX
in the generic template arguments.

Like so?  Bootstrapped and tested on x86_64-pc-linux-gnu.  I'm not sure
if it's worth also checking !processing_template_decl && !DECL_ODR_USED
before calling mark_used here, since otherwise the call ought to be
certainly redundant.

-- >8 --

Subject: [PATCH] c++: improve "NTTP argument considered unused" fix [PR53164]

	PR c++/53164
	PR c++/105848

gcc/cp/ChangeLog:

	* pt.cc (tsubst) <case TEMPLATE_PARM_INDEX>: Call mark_used on
	an ADDR_EXPR argument.
	(tsubst_copy_and_build) <case CALL_EXPR>: Revert r13-995 change.

gcc/testsuite/ChangeLog:

	* g++.dg/template/fn-ptr3a.C: New test.

Co-authored-by: Jason Merrill <jason@redhat.com>
---
 gcc/cp/pt.cc                             | 40 +++++++++++++-----------
 gcc/testsuite/g++.dg/template/fn-ptr3a.C | 25 +++++++++++++++
 2 files changed, 46 insertions(+), 19 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/fn-ptr3a.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index dcacba56a1c..5863d83b4fd 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -15858,9 +15858,23 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	      }
 	    else if (code == TEMPLATE_TEMPLATE_PARM)
 	      return arg;
-	    else
-	      /* TEMPLATE_PARM_INDEX.  */
-	      return convert_from_reference (unshare_expr (arg));
+	    else /* if (code == TEMPLATE_PARM_INDEX)  */
+	      {
+		if (TREE_CODE (arg) == ADDR_EXPR
+		    && DECL_P (TREE_OPERAND (arg, 0)))
+		  /* Remember that there was a reference to this entity.
+		     We should already have called mark_used when taking
+		     the address of this entity, but do so again to make
+		     sure: at worst it's redundant, but if we so far only
+		     called mark_used in a template context then we might
+		     not have yet marked it odr-used (53164).  */
+		  /* Mask out tf_warning_or_error to avoid issuing redundant
+		     diagnostics -- we just care about making sure this
+		     entity is odr-used.  */
+		  mark_used (TREE_OPERAND (arg, 0),
+			     complain & ~tf_warning_or_error);
+		return convert_from_reference (unshare_expr (arg));
+	      }
 	  }
 
 	if (level == 1)
@@ -20884,22 +20898,10 @@ tsubst_copy_and_build (tree t,
 	  }
 
 	/* Remember that there was a reference to this entity.  */
-	if (function != NULL_TREE)
-	  {
-	    tree inner = function;
-	    if (TREE_CODE (inner) == ADDR_EXPR
-		&& TREE_CODE (TREE_OPERAND (inner, 0)) == FUNCTION_DECL)
-	      /* We should already have called mark_used when taking the
-		 address of this function, but do so again anyway to make
-		 sure it's odr-used: at worst this is a no-op, but if we
-		 obtained this FUNCTION_DECL as part of ahead-of-time overload
-		 resolution then that call to mark_used wouldn't have marked it
-		 odr-used yet (53164).  */
-	      inner = TREE_OPERAND (inner, 0);
-	    if (DECL_P (inner)
-		&& !mark_used (inner, complain) && !(complain & tf_error))
-	      RETURN (error_mark_node);
-	  }
+	if (function != NULL_TREE
+	    && DECL_P (function)
+	    && !mark_used (function, 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-ptr3a.C b/gcc/testsuite/g++.dg/template/fn-ptr3a.C
new file mode 100644
index 00000000000..c76b5cc32b3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/fn-ptr3a.C
@@ -0,0 +1,25 @@
+// PR c++/53164
+// PR c++/105848
+// A stricter version of fn-ptr3.C that verifies using f as a template
+// argument alone constitutes an odr-use.
+
+template<class T>
+void f(T) { T::fail; } // { dg-error "fail" }
+
+template<void (*P)(int)>
+struct A {
+  static void wrap();
+};
+
+template<void (*P)(char)>
+void wrap();
+
+template<int>
+void g() {
+  A<f>::wrap(); // { dg-message "required from here" }
+  wrap<f>(); // { dg-message "required from here" }
+}
+
+int main() {
+  g<0>();
+}
-- 
2.36.1.299.gab336e8f1c

  reply	other threads:[~2022-06-07 13:24 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-06-06 18:27 Patrick Palka
2022-06-06 19:26 ` Jason Merrill
2022-06-07 13:24   ` Patrick Palka [this message]
2022-06-07 18:05     ` Jason Merrill
2022-06-07 19:18       ` Patrick Palka
2022-06-07 20:09         ` 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=50baf432-cc69-45b9-0325-fe1bb7418308@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).