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 <si...@nasilyan.com> 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