c++: Ignore default arguments for friend functions that cannot have any [PR118319]

Message ID 010201944b3e44d9-b7ddcbdc-d7aa-4d6f-954b-411c953c4b08-000000@eu-west-1.amazonses.com
State New
Headers
Series c++: Ignore default arguments for friend functions that cannot have any [PR118319] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed

Commit Message

Simon Martin Jan. 9, 2025, 1:25 p.m. UTC
  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 simply sets the default argument to error_mark_node for
friend functions that do not meet the requirement in C++17 11.3.6/4.

Successfully tested on x86_64-pc-linux-gnu.

	PR c++/118319

gcc/cp/ChangeLog:

	* decl.cc (grokfndecl): Inspect all friend function parameters,
	and set them to error_mark_node if invalid.

gcc/testsuite/ChangeLog:

	* g++.dg/parse/defarg18.C: New test.

---
 gcc/cp/decl.cc                        | 13 +++++---
 gcc/testsuite/g++.dg/parse/defarg18.C | 48 +++++++++++++++++++++++++++
 2 files changed, 57 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/parse/defarg18.C
  

Comments

Jason Merrill Jan. 9, 2025, 9:55 p.m. UTC | #1
On 1/9/25 8:25 AM, Simon Martin wrote:
> 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 simply sets the default argument to error_mark_node for
> friend functions that do not meet the requirement in C++17 11.3.6/4.
> 
> Successfully tested on x86_64-pc-linux-gnu.
> 
> 	PR c++/118319
> 
> gcc/cp/ChangeLog:
> 
> 	* decl.cc (grokfndecl): Inspect all friend function parameters,
> 	and set them to error_mark_node if invalid.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/parse/defarg18.C: New test.
> 
> ---
>   gcc/cp/decl.cc                        | 13 +++++---
>   gcc/testsuite/g++.dg/parse/defarg18.C | 48 +++++++++++++++++++++++++++
>   2 files changed, 57 insertions(+), 4 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/parse/defarg18.C
> 
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 503ecd9387e..b2761c23d3e 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -11134,14 +11134,19 @@ grokfndecl (tree ctype,
>        expression, that declaration shall be a definition..."  */
>     if (friendp && !funcdef_flag)
>       {
> +      bool has_permerrored = 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;
> +	    if (!has_permerrored)
> +	      {
> +		has_permerrored = true;
> +		permerror (DECL_SOURCE_LOCATION (decl),
> +			   "friend declaration of %qD specifies default "
> +			   "arguments and isn%'t a definition", decl);
> +	      }
> +	    TREE_PURPOSE (t) = error_mark_node;

If we're going to unconditionally change TREE_PURPOSE, then permerror 
needs to strengthen to error.  But I'd think we could leave the current 
state in a non-template class, only changing the template case.

Jason
  

Patch

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 503ecd9387e..b2761c23d3e 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -11134,14 +11134,19 @@  grokfndecl (tree ctype,
      expression, that declaration shall be a definition..."  */
   if (friendp && !funcdef_flag)
     {
+      bool has_permerrored = 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;
+	    if (!has_permerrored)
+	      {
+		has_permerrored = true;
+		permerror (DECL_SOURCE_LOCATION (decl),
+			   "friend declaration of %qD specifies default "
+			   "arguments and isn%'t a definition", decl);
+	      }
+	    TREE_PURPOSE (t) = error_mark_node;
 	  }
     }
 
diff --git a/gcc/testsuite/g++.dg/parse/defarg18.C b/gcc/testsuite/g++.dg/parse/defarg18.C
new file mode 100644
index 00000000000..a087d45bf34
--- /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" }
+};
+
+void baz (int a) {} // { dg-error "only declaration" }
+
+void ola (){
+  NoTemplate t;
+  baz ();
+}