Hi Jason,
On 4 Feb 2025, at 21:06, Jason Merrill wrote:
> On 2/4/25 11:25 AM, Simon Martin wrote:
>> Hi Jason,
>>
>> On 4 Feb 2025, at 1:11, Jason Merrill wrote:
>>
>>> On 1/31/25 11:12 AM, Simon Martin wrote:
>>>> Hi Jason,
>>>>
>>>> On 31 Jan 2025, at 16:29, Jason Merrill wrote:
>>>>
>>>>> On 1/31/25 9:52 AM, Simon Martin wrote:
>>>>>> Hi Jason,
>>>>>>
>>>>>> On 9 Jan 2025, at 22:55, Jason Merrill wrote:
>>>>>>
>>>>>>> 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.
>>>>>> Thanks. It’s true that setting the argument to error_mark_node
>>>>>> is
>>>>>> contradictory with the fact that we accept the code with
>>>>>> -fpermissive,
>>>>>> even if only under processing_template_decl, so I checked if
>>>>>> there’s
>>>>>> not a better way of approaching this PR.
>>>>>>
>>>>>> After a bit of investigation, I think that the real problem is
>>>>>> that
>>
>>>>>> duplicate_decls tries to merge the two declarations, even though
>>
>>>>>> they
>>>>>> don’t meet the constraint about friend functions and default
>>>>>> arguments.
>>>>>
>>>>> I disagree; in this testcase the friend is the (lexically) first
>>>>> declaration, the problem is that it's a non-defining friend (in a
>>>>> template) that specifies default args, as addressed by your first
>>>>> patch.
>>>> Fair.
>>>>
>>>>> I still think my earlier comments are the way forward here: leave
>>
>>>>> the
>>>>> non-template case alone (permerror, don't change TREE_PURPOSE), in
>>>>> a
>>
>>>>> template give a hard error and change to error_mark_node.
>>>> Thanks, understood. The reason I looked for another “solution”
>>>> is
>>>> that it felt strange to be permissive in non-templates and stricter
>>>> in
>>>> templates. For example, if we do so, we’ll regress the case I
>>>> added
>>>> in
>>>> defarg19.C in -fpermissive (also available at
>>>> https://godbolt.org/z/YT3dexGjM).
>>>>
>>>> I’m probably splitting hair, and I’m happy to go ahead with
>>>> your
>>>> suggestion if you think it’s fine. Otherwise I’ll see if I find
>>>> some
>>>> better fix.
>>>
>>> That's fine, it's common to be stricter in templates.
>> Ok, cool. The attached updated patch does this, and has been
>> successfully tested on x86_64-pc-linux-gnu. OK for trunk?
>
>> + if (!has_permerrored)
>> + {
>> + has_permerrored = true;
>> + permerror (DECL_SOURCE_LOCATION (decl),
>
> This still needs to be a hard error in a template; you probably want
> to use emit_diagnostic to allow the diagnostic category to be
> different.
I really need to learn how to read... sorry :-(
The updated patch promotes the permerror to error for templates, and
adds an extra test to check that we keep erroring out *for templates
only* under -fpermissive.
Successfully tested on x86_64-pc-linux-gnu. OK for trunk?
Simon
From 17490ed526f04826f664df0f1f53872707fb9cab Mon Sep 17 00:00:00 2001
From: Simon Martin <[email protected]>
Date: Wed, 5 Feb 2025 11:26:52 +0100
Subject: [PATCH] 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).
Successfully tested on x86_64-pc-linux-gnu.
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.
---
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(-)
create mode 100644 gcc/testsuite/g++.dg/parse/defarg18.C
create mode 100644 gcc/testsuite/g++.dg/parse/defarg18a.C
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index b7af33b3231..4238314558b 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 00000000000..62c8f15f284
--- /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 00000000000..9157a4d5158
--- /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 ();
+}
--
2.44.0