c++: odr-use argument to a function NTTP [PR53164]

Message ID 20211004164057.718456-1-ppalka@redhat.com
State New
Headers
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

Patrick Palka Oct. 5, 2021, 7:17 p.m. UTC | #1
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
> 
>
  
Jason Merrill Oct. 6, 2021, 12:38 a.m. UTC | #2
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
  
Patrick Palka Oct. 6, 2021, 4:25 p.m. UTC | #3
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>();
  
Patrick Palka Oct. 6, 2021, 7:52 p.m. UTC | #4
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
> 
>
  
Jason Merrill Oct. 6, 2021, 10:19 p.m. UTC | #5
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
>>
  
Patrick Palka Oct. 7, 2021, 3:17 p.m. UTC | #6
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>();
  
Jason Merrill Oct. 7, 2021, 5:03 p.m. UTC | #7
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>();
>
  

Patch

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