https://gcc.gnu.org/g:198f4df07d6a1db9c8ef39536da56c1b596c57a8

commit r15-7380-g198f4df07d6a1db9c8ef39536da56c1b596c57a8
Author: Simon Martin <si...@nasilyan.com>
Date:   Wed Feb 5 20:35:34 2025 +0100

    c++: Reject default arguments for template class friend functions [PR118319]
    
    We segfault upon the following invalid code
    
    === cut here ===
    template <int> struct S {
      friend void foo (int a = []{}());
    };
    void foo (int a) {}
    int main () {
      S<0> t;
      foo ();
    }
    === cut here ===
    
    The problem is that we end up with a LAMBDA_EXPR callee in
    set_flags_from_callee, and dereference its NULL_TREE
    TREE_TYPE (TREE_TYPE (..)).
    
    This patch sets the default argument to error_mark_node and gives a hard
    error for template class friend functions that do not meet the
    requirement in C++17 11.3.6/4 (the change is restricted to templates per
    discussion with Jason).
    
            PR c++/118319
    
    gcc/cp/ChangeLog:
    
            * decl.cc (grokfndecl): Inspect all friend function parameters.
            If it's not valid for them to have a default value and we're
            processing a template, set the default value to error_mark_node
            and give a hard error.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/parse/defarg18.C: New test.
            * g++.dg/parse/defarg18a.C: New test.

Diff:
---
 gcc/cp/decl.cc                         | 22 +++++++++++++---
 gcc/testsuite/g++.dg/parse/defarg18.C  | 48 ++++++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/parse/defarg18a.C | 33 +++++++++++++++++++++++
 3 files changed, 99 insertions(+), 4 deletions(-)

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index b7af33b32317..4238314558b1 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -11213,14 +11213,28 @@ grokfndecl (tree ctype,
      expression, that declaration shall be a definition..."  */
   if (friendp && !funcdef_flag)
     {
+      bool has_errored = false;
       for (tree t = FUNCTION_FIRST_USER_PARMTYPE (decl);
           t && t != void_list_node; t = TREE_CHAIN (t))
        if (TREE_PURPOSE (t))
          {
-           permerror (DECL_SOURCE_LOCATION (decl),
-                      "friend declaration of %qD specifies default "
-                      "arguments and isn%'t a definition", decl);
-           break;
+           diagnostic_t diag_kind = DK_PERMERROR;
+           /* For templates, mark the default argument as erroneous and give a
+              hard error.  */
+           if (processing_template_decl)
+             {
+               diag_kind = DK_ERROR;
+               TREE_PURPOSE (t) = error_mark_node;
+             }
+           if (!has_errored)
+             {
+               has_errored = true;
+               emit_diagnostic (diag_kind,
+                                DECL_SOURCE_LOCATION (decl),
+                                /*diagnostic_option_id=*/0,
+                                "friend declaration of %qD specifies default "
+                                "arguments and isn%'t a definition", decl);
+             }
          }
     }
 
diff --git a/gcc/testsuite/g++.dg/parse/defarg18.C 
b/gcc/testsuite/g++.dg/parse/defarg18.C
new file mode 100644
index 000000000000..62c8f15f2843
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/defarg18.C
@@ -0,0 +1,48 @@
+// PR c++/118319
+// { dg-do "compile" { target c++11 } }
+
+// Template case, that used to crash.
+
+template <int>
+struct S {
+  friend void foo1 (int a = []{}());  // { dg-error "specifies default|only 
declaration" }
+  friend void foo3 (int a,           // { dg-error "specifies default|only 
declaration" }
+                   int b = []{}(),
+                   int c = []{}());
+};
+
+void foo1 (int a) {}
+void foo3 (int a, int b, int c) {}
+
+void hello (){
+  S<0> t;
+  foo1 ();
+  foo3 (1, 2);
+}
+
+
+// Template case, that already worked.
+
+template <int>
+struct T {
+  friend void bar (int a = []{}()); // { dg-error "specifies default|only 
declaration" }
+};
+
+void hallo (){
+  T<0> t;
+  bar (); // { dg-error "not declared" }
+}
+
+
+// Non template case, that already worked.
+
+struct NoTemplate {
+  friend void baz (int a = []{}()); // { dg-error "specifies default|could not 
convert" }
+};
+
+void baz (int a) {} // { dg-error "only declaration" }
+
+void ola (){
+  NoTemplate t;
+  baz (); // { dg-error "void value not ignored" }
+}
diff --git a/gcc/testsuite/g++.dg/parse/defarg18a.C 
b/gcc/testsuite/g++.dg/parse/defarg18a.C
new file mode 100644
index 000000000000..9157a4d5158f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/defarg18a.C
@@ -0,0 +1,33 @@
+// PR c++/118319 - With -fpermissive
+// { dg-do "compile" { target c++11 } }
+// { dg-additional-options "-fpermissive" }
+
+// Template case, that used to crash.
+// Check that we error-out even with -fpermissive.
+
+template <int>
+struct S { // { dg-error "instantiating erroneous template" }
+  friend void foo1 (int a = []{}());  // { dg-warning "specifies default|only 
declaration" }
+};
+
+void foo1 (int a) {}
+
+void hello (){
+  S<0> t;
+  foo1 ();
+}
+
+
+// Non template case, that already worked.
+// Check that errors are demoted to warnings.
+
+struct NoTemplate {
+  friend void baz (int a = 1); // { dg-warning "specifies default" }
+};
+
+void baz (int a) {} // { dg-warning "only declaration" }
+
+void ola (){
+  NoTemplate t;
+  baz ();
+}

Reply via email to