Bug 83258 - Rejecting function pointer non-type template parameter without linkage
Summary: Rejecting function pointer non-type template parameter without linkage
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 8.0
: P3 normal
Target Milestone: 13.2
Assignee: Patrick Palka
URL:
Keywords: rejects-valid
: 84981 92320 (view as bug list)
Depends on:
Blocks:
 
Reported: 2017-12-02 22:22 UTC by Barry Revzin
Modified: 2023-05-12 15:25 UTC (History)
10 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2021-12-03 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Barry Revzin 2017-12-02 22:22:47 UTC
Example from StackOverflow (https://stackoverflow.com/q/47606810/2069064):

template<void(*)()> struct A{};

int main()
{
    constexpr auto fp = +[]{};
    A<fp>{};
}

As of N4268, this should be accepted (linkage is no longer a requirement), but gcc rejects with:

prog.cc: In function 'int main()':
prog.cc:6:9: error: 'main()::<lambda()>::_FUN' is not a valid template argument for type 'void (*)()' because 'static constexpr void main()::<lambda()>::_FUN()' has no linkage
     A<fp>{};
         ^
Comment 1 Jonathan Wakely 2018-03-20 12:22:44 UTC
*** Bug 84981 has been marked as a duplicate of this bug. ***
Comment 2 Jonathan Wakely 2018-03-20 14:54:40 UTC
With this patch:

--- a/gcc/cp/pt.c                                                                                                                                                                                                                                                                                                            
+++ b/gcc/cp/pt.c                                                                                                                                                                                                                                                                                                            
@@ -6259,7 +6259,8 @@ convert_nontype_argument_function (tree type, tree expr,                                                                                                                                                                                                                                               
     }                                                                                                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                                                                                                             
   linkage = decl_linkage (fn_no_ptr);                                                                                                                                                                                                                                                                                       
-  if (cxx_dialect >= cxx11 ? linkage == lk_none : linkage != lk_external)                                                                                                                                                                                                                                                   
+  if ((cxx_dialect < cxx11 && linkage != lk_external)
+      || (cxx_dialect < cxx17 && linkage == lk_none))
     {
       if (complain & tf_error)
        {

We accept Barry's testcase in C++17 mode.

I think we also need this for some non-type arguments without linkage:


@@ -6979,7 +6980,8 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 
          /* DR 1155 allows internal linkage in C++11 and up.  */
          linkage_kind linkage = decl_linkage (expr);
-         if (linkage < (cxx_dialect >= cxx11 ? lk_internal : lk_external))
+         if ((cxx_dialect < cxx11 && linkage < lk_external)
+             || (cxx_dialect < cxx17 && linkage < lk_internal))
            {
              if (complain & tf_error)
                error ("%qE is not a valid template argument for type %qT "


but that second change causes a new ice-on-invalid for this in C++17 mode (but seems to correctly compile it when VALID is defined):


template <typename T, const T &val> struct TestClass
{
  TestClass() : _val(val) { }
  T _val;
};

extern constexpr float e = 2.72;    // external linkage
static constexpr float pi = 3.14;   // internal linkage

int main()
{
#ifdef VALID
  static
#endif
  constexpr float one = 1; // no linkage

  TestClass<float, e> test1;

  TestClass<float, pi> test2;

  TestClass<float, one> test3;
}


linkage.cc: In constructor 'TestClass<T, val>::TestClass() [with T = float; const T& val = one]':
linkage.cc:3:3: error: Local declaration from a different function
   TestClass() : _val(val) { }
   ^~~~~~~~~
one
linkage.cc:3:25: note: in statement
   TestClass() : _val(val) { }
                         ^
one.2_1 = one;
during GIMPLE pass: cfg
linkage.cc:3:3: internal compiler error: verify_gimple failed
   TestClass() : _val(val) { }
   ^~~~~~~~~
0xdfad0d verify_gimple_in_cfg(function*, bool)
        /home/jwakely/src/gcc/gcc/gcc/tree-cfg.c:5579
0xcc6956 execute_function_todo
        /home/jwakely/src/gcc/gcc/gcc/passes.c:1994
0xcc79d2 execute_todo
        /home/jwakely/src/gcc/gcc/gcc/passes.c:2048
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
See <https://gcc.gnu.org/bugs/> for instructions.
Comment 3 Jonathan Wakely 2018-05-01 17:14:58 UTC
*** Bug 85589 has been marked as a duplicate of this bug. ***
Comment 4 Jonathan Wakely 2018-05-01 17:15:43 UTC
Testcase from Bug 85589:

template<auto& v> struct foo {};

    int main() {
        static auto v = "str";
        (void) foo<v> {};
    }
Comment 5 S. Davis Herring 2019-11-18 23:58:27 UTC
As an anti-example, making the client a template works:

template<void(*)()> struct A{};

template<int=0>
void f()
{
    constexpr auto fp = +[]{};
    A<fp>{};
}

int main()
{
    f();
}
Comment 6 Eric Gallager 2019-11-19 23:46:16 UTC
*** Bug 92320 has been marked as a duplicate of this bug. ***
Comment 7 S. Davis Herring 2020-03-17 18:17:48 UTC
My example in Comment #5 works in 9.2 but not in (Compiler Explorer's) trunk.
Comment 8 Matthias Noack 2020-08-26 15:51:15 UTC
Still failing in 10.2 (original reproducer).
Comment 9 Johel Ernesto Guerrero Peña 2021-03-13 19:44:02 UTC
I think Bug 97700 is also a duplicate of this.
Comment 10 Johel Ernesto Guerrero Peña 2021-03-13 21:51:22 UTC
Another workaround, extended from Bug 92320's example. See https://godbolt.org/z/69onWf.
You can wrap the closure object in a template function which itself invokes the closure object and converts to a function pointer that GCC accepts.
```C++
template<auto V>
void templ() {}

void dummy(){}

template<auto F>
void fn(){F();}

void foo() {
    constexpr int a = 7 + 3;
    templ<a>();

    templ<dummy>();

    typedef void(FPtr)();
    constexpr FPtr * b = &dummy;
    templ<b>();

    constexpr auto l = []{};
    constexpr void (*d)()=fn<l>;
    templ<d>();

    constexpr FPtr * c = [](){};
    templ<c>();
}

```
Comment 11 Andrew Pinski 2021-12-04 01:01:06 UTC
(In reply to Jonathan Wakely from comment #4)
> Testcase from Bug 85589:
> 
> template<auto& v> struct foo {};
> 
>     int main() {
>         static auto v = "str";
>         (void) foo<v> {};
>     }

Note comment #4 is already fixed (I reopened PR 85589 to close it was fixed [or as a dup if I can find one]).

The rest of the testcases still fail on the trunk.
Comment 12 Eric Gallager 2021-12-20 04:10:53 UTC
(In reply to Johel Ernesto Guerrero Peña from comment #9)
> I think Bug 97700 is also a duplicate of this.

Well, it seems related enough, at least...
Comment 13 GCC Commits 2023-05-11 14:05:03 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:c3afdb8ba8f1839544c414f57e41a58c8fda5349

commit r14-708-gc3afdb8ba8f1839544c414f57e41a58c8fda5349
Author: Patrick Palka <ppalka@redhat.com>
Date:   Thu May 11 10:04:25 2023 -0400

    c++: converted lambda as template argument [PR83258, ...]
    
    r8-1253-g3d2e25a240c711 removed the template argument linkage requirement
    in convert_nontype_argument for C++17 (which r9-3836-g4be5c72cf3ea3e later
    factored out into invalid_tparm_referent_p), but we need to also remove
    the one in convert_nontype_argument_function for benefit of the first and
    third testcase which we currently reject even in C++17/20 mode.
    
    And in invalid_tparm_referent_p we're inadvertendly returning false for
    the address of a lambda's static op() since it's DECL_ARTIFICIAL, which
    currently causes us to reject the second (C++20) testcase.  But this
    DECL_ARTIFICIAL check seems to be relevant only for VAR_DECL, and in fact
    this code path was originally reachable only for VAR_DECL until recently
    (r13-6970-gb5e38b1c166357).  So this patch restricts the check to VAR_DECL.
    
    Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
    
            PR c++/83258
            PR c++/80488
            PR c++/97700
    
    gcc/cp/ChangeLog:
    
            * pt.cc (convert_nontype_argument_function): Remove linkage
            requirement for C++17 and later.
            (invalid_tparm_referent_p) <case ADDR_EXPR>: Restrict
            DECL_ARTIFICIAL rejection test to VAR_DECL.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/ext/visibility/anon8.C: Don't expect a "no linkage"
            error for the template argument &B2:fn in C++17 mode.
            * g++.dg/cpp0x/lambda/lambda-conv15.C: New test.
            * g++.dg/cpp2a/nontype-class56.C: New test.
            * g++.dg/template/function2.C: New test.
Comment 14 Patrick Palka 2023-05-11 14:25:22 UTC
Fixed on trunk so far.
Comment 15 GCC Commits 2023-05-12 12:38:49 UTC
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:10098788ff61467f5a499f01c5443fb53ff564dd

commit r14-787-g10098788ff61467f5a499f01c5443fb53ff564dd
Author: Patrick Palka <ppalka@redhat.com>
Date:   Fri May 12 08:37:54 2023 -0400

    c++: remove redundant testcase [PR83258]
    
    I noticed only after the fact that the new testcase template/function2.C
    (from r14-708-gc3afdb8ba8f183) is just a subset of ext/visibility/anon8.C,
    so let's get rid of it.
    
            PR c++/83258
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/ext/visibility/anon8.C: Mention PR83258.
            * g++.dg/template/function2.C: Removed.
Comment 16 GCC Commits 2023-05-12 15:08:01 UTC
The releases/gcc-13 branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:42f9b481be3527b336b800128247d053fd18d121

commit r13-7324-g42f9b481be3527b336b800128247d053fd18d121
Author: Patrick Palka <ppalka@redhat.com>
Date:   Thu May 11 10:04:25 2023 -0400

    c++: converted lambda as template argument [PR83258, ...]
    
    r8-1253-g3d2e25a240c711 removed the template argument linkage requirement
    in convert_nontype_argument for C++17 (which r9-3836-g4be5c72cf3ea3e later
    factored out into invalid_tparm_referent_p), but we need to also remove
    the one in convert_nontype_argument_function for benefit of the first and
    third testcase which we currently reject even in C++17/20 mode.
    
    And in invalid_tparm_referent_p we're inadvertendly returning false for
    the address of a lambda's static op() since it's DECL_ARTIFICIAL, which
    currently causes us to reject the second (C++20) testcase.  But this
    DECL_ARTIFICIAL check seems to be relevant only for VAR_DECL, and in fact
    this code path was originally reachable only for VAR_DECL until recently
    (r13-6970-gb5e38b1c166357).  So this patch restricts the check to VAR_DECL.
    
    Co-authored-by: Jonathan Wakely <jwakely@redhat.com>
    
            PR c++/83258
            PR c++/80488
            PR c++/97700
    
    gcc/cp/ChangeLog:
    
            * pt.cc (convert_nontype_argument_function): Remove linkage
            requirement for C++17 and later.
            (invalid_tparm_referent_p) <case ADDR_EXPR>: Restrict
            DECL_ARTIFICIAL rejection test to VAR_DECL.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/ext/visibility/anon8.C: Don't expect a "no linkage"
            error for the template argument &B2:fn in C++17 mode.
            * g++.dg/cpp0x/lambda/lambda-conv15.C: New test.
            * g++.dg/cpp2a/nontype-class56.C: New test.
            * g++.dg/template/function2.C: New test.
    
    (cherry picked from commit c3afdb8ba8f1839544c414f57e41a58c8fda5349)
Comment 17 GCC Commits 2023-05-12 15:08:17 UTC
The releases/gcc-13 branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:20b1e1d8a79356e03f50f53e2fa02b376d56d7d7

commit r13-7327-g20b1e1d8a79356e03f50f53e2fa02b376d56d7d7
Author: Patrick Palka <ppalka@redhat.com>
Date:   Fri May 12 08:37:54 2023 -0400

    c++: remove redundant testcase [PR83258]
    
    I noticed only after the fact that the new testcase template/function2.C
    (from r14-708-gc3afdb8ba8f183) is just a subset of ext/visibility/anon8.C,
    so let's get rid of it.
    
            PR c++/83258
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/ext/visibility/anon8.C: Mention PR83258.
            * g++.dg/template/function2.C: Removed.
    
    (cherry picked from commit 10098788ff61467f5a499f01c5443fb53ff564dd)
Comment 18 Patrick Palka 2023-05-12 15:25:48 UTC
Fixed for GCC 13.2