public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jason Merrill <jason@redhat.com>
To: Patrick Palka <ppalka@redhat.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++: function NTTP argument considered unused [PR53164, PR105848]
Date: Tue, 7 Jun 2022 14:05:03 -0400	[thread overview]
Message-ID: <ceeba2a4-7cf6-4628-e572-cd074928905a@redhat.com> (raw)
In-Reply-To: <50baf432-cc69-45b9-0325-fe1bb7418308@idea>

On 6/7/22 09:24, Patrick Palka wrote:
> 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.

Wouldn't hurt to benchmark that, but I wouldn't expect it to make a 
measurable difference.

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

If the parameter is a reference to function, rather than a pointer, do 
we need to look through a NOP_EXPR?

> +		  /* 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>();
> +}


  reply	other threads:[~2022-06-07 18:05 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
2022-06-07 18:05     ` Jason Merrill [this message]
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=ceeba2a4-7cf6-4628-e572-cd074928905a@redhat.com \
    --to=jason@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=ppalka@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).