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
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
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
@@ -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;
}
}
new file mode 100644
@@ -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 ();
+}