Hi Jason, On 7 Feb 2025, at 23:10, Jason Merrill wrote:
> On 2/7/25 4:04 PM, Simon Martin wrote: >> Hi Jason, >> >> On 7 Feb 2025, at 14:21, Jason Merrill wrote: >> >>> On 2/6/25 3:05 PM, Simon Martin wrote: >>>> Hi Jason, >>>> >>>> On 6 Feb 2025, at 16:48, Jason Merrill wrote: >>>> >>>>> On 2/5/25 2:21 PM, Simon Martin wrote: >>>>>> Hi Jason, >>>>>> >>>>>> On 4 Feb 2025, at 21:23, Jason Merrill wrote: >>>>>> >>>>>>> On 2/4/25 3:03 PM, Jason Merrill wrote: >>>>>>>> On 2/4/25 11:45 AM, Simon Martin wrote: >>>>>>>>> On 4 Feb 2025, at 17:17, Jason Merrill wrote: >>>>>>>>> >>>>>>>>>> On 2/4/25 10:56 AM, Simon Martin wrote: >>>>>>>>>>> Hi Jason, >>>>>>>>>>> >>>>>>>>>>> On 4 Feb 2025, at 16:39, Jason Merrill wrote: >>>>>>>>>>> >>>>>>>>>>>> On 1/15/25 9:56 AM, Jason Merrill wrote: >>>>>>>>>>>>> On 1/15/25 7:24 AM, Simon Martin wrote: >>>>>>>>>>>>>> Hi, >>>>>>>>>>>>>> >>>>>>>>>>>>>> On 14 Jan 2025, at 23:31, Jason Merrill wrote: >>>>>>>>>>>>>> >>>>>>>>>>>>>>> On 1/14/25 2:13 PM, Simon Martin wrote: >>>>>>>>>>>>>>>> On 10 Jan 2025, at 19:10, Andrew Pinski wrote: >>>>>>>>>>>>>>>>> On Fri, Jan 10, 2025 at 3:18 AM Simon Martin >>>>>>>>>>>>>>>>> <si...@nasilyan.com> >>>>>>>>>>>>>>>>> wrote: >>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>>> We currently accept the following invalid code (EDG >>>>>>>>>>>>>>>>>> and >> >>>>>>>>>>>>>>>>>> MSVC >>>>>>>>>>>>>>>>>> do >>>>>>>>>>>>>>>>>> as >>>>>>>>>>>>>>>>>> well) >>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>> clang does too: >>>>>>>>>>>>>>>>> https://github.com/llvm/llvm-project/issues/121706 . >>>>>>>>>>>>>> >>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>> Note it might be useful if a testcase with multiply >>>>>>>>>>>>>>>>> `*` >>>>>>>>>>>>>>>>> is >>>>>>>>>>>>>>>>> included >>>>>>>>>>>>>>>> >>>>>>>>>>>>>>>>> too: >>>>>>>>>>>>>>>>> ``` >>>>>>>>>>>>>>>>> struct A { >>>>>>>>>>>>>>>>> ****A (); >>>>>>>>>>>>>>>>> }; >>>>>>>>>>>>>>>>> ``` >>>>>>>>>>>>>>>> Thanks, makes sense to add those. Done in the attached >>>>>>>>>>>>>>>> updated >>>>>>>>>>>>>>>> revision, >>>>>>>>>>>>>>>> successfully tested on x86_64-pc-linux-gnu. >>>>>>>>>>>>>>> >>>>>>>>>>>>>>>> +/* Check that it's OK to declare a function at ID_LOC >> >>>>>>>>>>>>>>>> with >>>>>>>>>>>>>>>> the >>>>>>>>>>>>>>>> indicated TYPE, >>>>>>>>>>>>>>>> + TYPE_QUALS and DECLARATOR. SFK indicates the >>>>>>>>>>>>>>>> kind >>>>>>>>>>>>>>>> of >>>>>>>>>>>>>>>> special >>>>>>>>>>>>>>>> function (if >>>>>>>>>>>>>>>> + any) that this function is. OPTYPE is the type >>>>>>>>>>>>>>>> given >>>>>>>>>>>>>>>> in >>>>>>>>>>>>>>>> a >>>>>>>>>>>>>>>> conversion >>>>>>>>>>>>>>>> operator declaration, or the class type >>>>>>>>>>>>>>>> for >>>>>>>>>>>>>>>> a >>>>>>>>>>>>>>>> constructor/destructor. >>>>>>>>>>>>>>>> Returns the actual return type of the >>>>>>>>>>>>>>>> function; >>>>>>>>>>>>>>>> that >>>>>>>>>>>>>>>> may >>>>>>>>>>>>>>>> be >>>>>>>>>>>>>>>> different >>>>>>>>>>>>>>>> than TYPE if an error occurs, or for >>>>>>>>>>>>>>>> certain >>>>>>>>>>>>>>>> special >>>>>>>>>>>>>>>> functions. >>>>>>>>>>>>>>>> */ >>>>>>>>>>>>>>>> @@ -12361,8 +12362,19 @@ >>>>>>>>>>>>>>>> check_special_function_return_type >>>>>>>>>>>>>>>> (special_function_kind sfk, >>>>>>>>>>>>>>>> tree >>>>>>>>>>>>>>>> type, >>>>>>>>>>>>>>>> tree >>>>>>>>>>>>>>>> optype, >>>>>>>>>>>>>>>> int >>>>>>>>>>>>>>>> type_quals, >>>>>>>>>>>>>>>> + const >>>>>>>>>>>>>>>> cp_declarator >>>>>>>>>>>>>>>> *declarator, >>>>>>>>>>>>>>>> + location_t >>>>>>>>>>>>>>>> id_loc, >>>>>>>>>>>>>>> >>>>>>>>>>>>>>> id_loc should be the same as declarator->id_loc? >>>>>>>>>>>>>> You’re right. >>>>>>>>>>>>>> >>>>>>>>>>>>>>>> const >>>>>>>>>>>>>>>> location_t* >>>>>>>>>>>>>>>> locations) >>>>>>>>>>>>>>>> { >>>>>>>>>>>>>>>> + /* If TYPE is unspecified, DECLARATOR, if set, >>>>>>>>>>>>>>>> should >>>>>>>>>>>>>>>> not >>>>>>>>>>>>>>>> represent a pointer >>>>>>>>>>>>>>>> + or a reference type. */ >>>>>>>>>>>>>>>> + if (type == NULL_TREE >>>>>>>>>>>>>>>> + && declarator >>>>>>>>>>>>>>>> + && (declarator->kind == cdk_pointer >>>>>>>>>>>>>>>> + || declarator->kind == cdk_reference)) >>>>>>>>>>>>>>>> + error_at (id_loc, "expected unqualified-id >>>>>>>>>>>>>>>> before >>>>>>>>>>>>>>>> %qs >>>>>>>>>>>>>>>> token", >>>>>>>>>>>>>>>> + declarator->kind == cdk_pointer ? >>>>>>>>>>>>>>>> "*" >>>>>>>>>>>>>>>> : >>>>>>>>>>>>>>>> "&"); >>>>>>>>>>>>>>> >>>>>>>>>>>>>>> ...and id_loc isn't the location of the ptr-operator, >>>>>>>>>>>>>>> it's >> >>>>>>>>>>>>>>> the >>>>>> >>>>>>>>>>>>>>> location of the identifier, so this indicates the wrong >>>>>>>>>>>>>>> column. >>>>>>>>>>>>>>> I >>>>>>>>>>>>>>> think using declarator->id_loc makes sense, just not >>>>>>>>>>>>>>> pretending >>>>>>>>>>>>>>> it's >>>>>>>>>>>>>>> the location of the *. >>>>>>>>>>>>>> Good catch, thanks. >>>>>>>>>>>>>> >>>>>>>>>>>>>>> Let's give diagnostics more like the others later in the >>>>>>>>>>>>>>> function >>>>>>>>>>>>>>> instead of trying to emulate cp_parser_error. >>>>>>>>>>>>>> Makes sense. This is what the updated patch does, >>>>>>>>>>>>>> successfully >>>>>>>>>>>>>> tested on >>>>>>>>>>>>>> x86_64-pc-linux-gnu. OK for GCC 16? >>>>>>>>>>>>> >>>>>>>>>>>>> OK. >>>>>>>>>>>> >>>>>>>>>>>> Does this also fix 118304? If so, let's go ahead and >>>>>>>>>>>> apply >>>>>>>>>>>> it >>>>>>>>>>>> to >>>>>>>>>>>> GCC >>>>>>>>>>>> 15. >>>>>>>>>>> I have checked just now, and we still ICE for 118304’s >> >>>>>>>>>>> testcase >>>>>>>>>>> with >>>>>>>>>>> that fix. >>>>>>>>>> >>>>>>>>>> Why doesn't the preeexisting >>>>>>>>>> >>>>>>>>>> type = void_type_node; >>>>>>>>>> >>>>>>>>>> in check_special_function_return_type fix the return type and >>>>>>>>>> avoid >>>>>> >>>>>>>>>> the ICE? >>>>>>>> >>>>>>>>> We hit the gcc_assert at method.cc:3593, that Marek’s fix >>>>>> >>>>>>>>> bypasses. >>>>>>>> >>>>>>>> Yes, but why doesn't check_special_function_return_type prevent >>>>>>>> that? >>>>>>> >>>>>>> Ah, because we call it before walking the declarator. We need >>>>>>> to >>>>>>> check again later, perhaps in grokfndecl, that the type is >>>>>>> correct. >>>>>>> Perhaps instead of your patch. >>>>>> One “issue” with adding another check in or close to >>>>>> grokfndecl >>>>>> is >>>>>> that DECLARATOR will have “been moved to the ID”, and the >>>>>> fact >>>>>> that >>>>>> we had a CDK_POINTER kind is “lost”. We could obviously >>>>>> somehow >>>>>> propagate this information, but there might be something easier. >>>>> >>>>> The information isn't lost: it's now reflected in the (wrong) >>>>> return >> >>>>> type. One place it would make sense to check would be >>>>> >>>>>> if (ctype && (sfk == sfk_constructor >>>>>> || sfk == sfk_destructor)) >>>>>> { >>>>>> /* We are within a class's scope. If our >>>>>> declarator >>>>>> name >>>>>> is the same as the class name, and we are >>>>>> defining >>>>>> >>>>>> a >>>>>> function, then it is a constructor/destructor, and >>>>>> therefore >>>>>> returns a void type. */ >>>>> >>>>> Here 'type' is still the return type, we haven't gotten to >>>>> build_function_type yet. >>>> That’s true. However, doesn’t it make sense to cram all the >>>> checks >>>> about the return type of special functions in >>>> check_special_function_return_type, and return an error if that >>>> return >>>> type is invalid? >>> >>> This error seems easily recoverable since we know what the type >>> needs >>> to be, there's no need for error return from grokdeclarator. >> ACK. >> >>> However, an alternative to my suggestion above would be to build on >>> your patch by making check_special_function_return_type actually >>> strip >>> the invalid declarators, not just complain about them. > >> Thanks for the suggestion, I like that it keeps everything within >> check_special_function_return_type. Hence the updated attached patch, >> >> successfully tested on 86_64-pc-linux-gnu. OK for trunk? > >> case sfk_constructor: >> if (type) >> error_at (smallest_type_location (type_quals, locations), >> "return type specification for constructor invalid"); >> + else if (maybe_strip_indirect_ref (declarator)) >> + error_at ((*declarator)->id_loc, >> + "return type specification for constructor invalid"); >> else if (type_quals != TYPE_UNQUALIFIED) >> error_at (smallest_type_quals_location (type_quals, locations), >> "qualifiers are not allowed on constructor declaration"); > > This looks like if someone writes e.g. > > int *A(); > > then we'll complain about the 'int' but leave the * alone. Argh you’re right, the return type is not fully groked at this point, so check_special_function_return_type should check first for indirect refs and then for type if needed. This is what the updated attached patch does Successfully tested on x86_64-pc-linux-gnu. OK for trunk? Thanks, Simon
From 140bd5fa4f98fcb2ac32b99e4403dd4875afe9c5 Mon Sep 17 00:00:00 2001 From: Simon Martin <si...@nasilyan.com> Date: Sun, 9 Feb 2025 20:38:43 +0100 Subject: [PATCH] c++: Reject cdtors and conversion operators with a single * as return type [PR118304, PR118306] We currently accept the following constructor declaration (clang, EDG and MSVC do as well), and ICE on the destructor declaration === cut here === struct A { *A (); ~A () = default; }; === cut here === The problem is that we end up in grokdeclarator with a cp_declarator of kind cdk_pointer but no type, and we happily go through (if we have a reference instead we eventually error out trying to form a reference to void). This patch makes sure that grokdeclarator errors out and strips the invalid declarator when processing a cdtor (or a conversion operator with no return type specified) with a declarator representing a pointer or a reference type. Successfully tested on x86_64-pc-linux-gnu. PR c++/118306 PR c++/118304 gcc/cp/ChangeLog: * decl.cc (maybe_strip_indirect_ref): New. (check_special_function_return_type): Take declarator as input. Call maybe_strip_indirect_ref and error out if it returns true. (grokdeclarator): Update call to check_special_function_return_type. gcc/testsuite/ChangeLog: * g++.old-deja/g++.jason/operator.C: Adjust bogus test expectation (char** vs char*). * g++.dg/parse/constructor4.C: New test. * g++.dg/parse/constructor5.C: New test. * g++.dg/parse/conv_op2.C: New test. * g++.dg/parse/default_to_int.C: New test. --- gcc/cp/decl.cc | 50 ++++++++++++----- gcc/testsuite/g++.dg/parse/constructor4.C | 54 +++++++++++++++++++ gcc/testsuite/g++.dg/parse/constructor5.C | 48 +++++++++++++++++ gcc/testsuite/g++.dg/parse/conv_op2.C | 10 ++++ gcc/testsuite/g++.dg/parse/default_to_int.C | 37 +++++++++++++ .../g++.old-deja/g++.jason/operator.C | 2 +- 6 files changed, 187 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/g++.dg/parse/constructor4.C create mode 100644 gcc/testsuite/g++.dg/parse/constructor5.C create mode 100644 gcc/testsuite/g++.dg/parse/conv_op2.C create mode 100644 gcc/testsuite/g++.dg/parse/default_to_int.C diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 84abc17ade0..552a7a2ec54 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -101,7 +101,8 @@ static void end_cleanup_fn (void); static tree cp_make_fname_decl (location_t, tree, int); static void initialize_predefined_identifiers (void); static tree check_special_function_return_type - (special_function_kind, tree, tree, int, const location_t*); + (special_function_kind, tree, tree, int, const cp_declarator**, + const location_t*); static tree push_cp_library_fn (enum tree_code, tree, int); static tree build_cp_library_fn (tree, enum tree_code, tree, int); static void store_parm_decls (tree); @@ -12441,10 +12442,27 @@ smallest_type_location (const cp_decl_specifier_seq *declspecs) return smallest_type_location (type_quals, declspecs->locations); } -/* Check that it's OK to declare a function with the indicated TYPE - and TYPE_QUALS. SFK indicates the kind of special function (if any) - that this function is. OPTYPE is the type given in a conversion - operator declaration, or the class type for a constructor/destructor. +/* Returns whether DECLARATOR represented a pointer or a reference and if so, + strip out the pointer/reference declarator(s). */ + +static bool +maybe_strip_indirect_ref (const cp_declarator** declarator) +{ + bool indirect_ref_p = false; + while (declarator && *declarator + && ((*declarator)->kind == cdk_pointer + || (*declarator)->kind == cdk_reference)) + { + indirect_ref_p = true; + *declarator = (*declarator)->declarator; + } + return indirect_ref_p; +} + +/* Check that it's OK to declare a function with the indicated TYPE, TYPE_QUALS + and DECLARATOR. SFK indicates the kind of special function (if any) that + this function is. OPTYPE is the type given in a conversion operator + declaration, or the class type for a constructor/destructor. Returns the actual return type of the function; that may be different than TYPE if an error occurs, or for certain special functions. */ @@ -12453,13 +12471,18 @@ check_special_function_return_type (special_function_kind sfk, tree type, tree optype, int type_quals, + const cp_declarator** declarator, const location_t* locations) { + gcc_assert (declarator); + location_t rettype_loc = (type + ? smallest_type_location (type_quals, locations) + : (*declarator)->id_loc); switch (sfk) { case sfk_constructor: - if (type) - error_at (smallest_type_location (type_quals, locations), + if (maybe_strip_indirect_ref (declarator) || type) + error_at (rettype_loc, "return type specification for constructor invalid"); else if (type_quals != TYPE_UNQUALIFIED) error_at (smallest_type_quals_location (type_quals, locations), @@ -12472,8 +12495,8 @@ check_special_function_return_type (special_function_kind sfk, break; case sfk_destructor: - if (type) - error_at (smallest_type_location (type_quals, locations), + if (maybe_strip_indirect_ref (declarator) || type) + error_at (rettype_loc, "return type specification for destructor invalid"); else if (type_quals != TYPE_UNQUALIFIED) error_at (smallest_type_quals_location (type_quals, locations), @@ -12488,8 +12511,8 @@ check_special_function_return_type (special_function_kind sfk, break; case sfk_conversion: - if (type) - error_at (smallest_type_location (type_quals, locations), + if (maybe_strip_indirect_ref (declarator) || type) + error_at (rettype_loc, "return type specified for %<operator %T%>", optype); else if (type_quals != TYPE_UNQUALIFIED) error_at (smallest_type_quals_location (type_quals, locations), @@ -12500,8 +12523,8 @@ check_special_function_return_type (special_function_kind sfk, break; case sfk_deduction_guide: - if (type) - error_at (smallest_type_location (type_quals, locations), + if (maybe_strip_indirect_ref (declarator) || type) + error_at (rettype_loc, "return type specified for deduction guide"); else if (type_quals != TYPE_UNQUALIFIED) error_at (smallest_type_quals_location (type_quals, locations), @@ -13181,6 +13204,7 @@ grokdeclarator (const cp_declarator *declarator, type = check_special_function_return_type (sfk, type, ctor_return_type, type_quals, + &declarator, declspecs->locations); type_quals = TYPE_UNQUALIFIED; } diff --git a/gcc/testsuite/g++.dg/parse/constructor4.C b/gcc/testsuite/g++.dg/parse/constructor4.C new file mode 100644 index 00000000000..f7e4cace451 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/constructor4.C @@ -0,0 +1,54 @@ +// PR c++/118306 +// { dg-do "compile" } + +// Constructors. +struct A { + *A (); // { dg-error "return type specification" } +}; +struct B { + **B (); // { dg-error "return type specification" } +}; +struct C { + ***C (); // { dg-error "return type specification" } +}; +struct D { + &D (); // { dg-error "return type specification|reference to" } +}; +struct E { + *&E (); // { dg-error "return type specification|reference to" } +}; +struct F { + **&F (); // { dg-error "return type specification|reference to" } +}; +struct G { + *G (const G&); // { dg-error "return type specification" } +}; +struct H { + **H (const H&); // { dg-error "return type specification" } +}; +struct I { + &I (const I&); // { dg-error "return type specification|reference to" } +}; +struct J { + const J(); // { dg-error "expected unqualified-id" } +}; + +// Destructors. +struct K { + * ~K (); // { dg-error "return type specification" } +}; +struct L { + ** ~L (); // { dg-error "return type specification" } +}; +struct M { + & ~M (); // { dg-error "return type specification|reference to" } +}; +struct N { + virtual * ~N (); // { dg-error "return type specification" } +}; +struct O { + virtual & ~O (); // { dg-error "return type specification|reference to" } +}; +struct P { + volatile ~P(); // { dg-error "qualifiers are not allowed" } +}; diff --git a/gcc/testsuite/g++.dg/parse/constructor5.C b/gcc/testsuite/g++.dg/parse/constructor5.C new file mode 100644 index 00000000000..5c86fe721a5 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/constructor5.C @@ -0,0 +1,48 @@ +// PR c++/118304 +// { dg-do "compile" { target c++11 } } + +// Constructors. +struct A { + *A () = default; // { dg-error "return type specification" } +}; +struct B { + int* B () = default; // { dg-error "return type specification" } +}; +struct C { + const int& C () = default; // { dg-error "return type specification" } +}; +struct D { + **D () = default; // { dg-error "return type specification" } +}; +struct E { + &E () = default; // { dg-error "return type specification|reference to" } +}; +struct F { + *&F () = default; // { dg-error "return type specification|reference to" } +}; +struct G { + **&G () = default; // { dg-error "return type specification|reference to" } +}; +struct H { + *H (const H&) = default; // { dg-error "return type specification" } +}; +struct I { + **I (const I&) = default; // { dg-error "return type specification" } +}; +struct J { + &J (const J&) = default; // { dg-error "return type specification|reference to" } +}; +struct K { + const K() = default; // { dg-error "expected unqualified-id" } +}; + +// Destructors. +struct L { + * ~L () = default; // { dg-error "return type specification" } +}; +struct M { + ** ~M () = default; // { dg-error "return type specification" } +}; +struct N { + & ~N () = default; // { dg-error "return type specification|reference to" } +}; diff --git a/gcc/testsuite/g++.dg/parse/conv_op2.C b/gcc/testsuite/g++.dg/parse/conv_op2.C new file mode 100644 index 00000000000..f2719135e00 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/conv_op2.C @@ -0,0 +1,10 @@ +// PR c++/118306 +// { dg-do "compile" } + +struct K { + char operator int(); // { dg-error "return type specified for" } + * operator short(); // { dg-error "return type specified for" } + ** operator float(); // { dg-error "return type specified for" } + &* operator double(); // { dg-error "return type specified for|pointer to 'double&'" } + & operator long(); // { dg-error "return type specified for" } +}; diff --git a/gcc/testsuite/g++.dg/parse/default_to_int.C b/gcc/testsuite/g++.dg/parse/default_to_int.C new file mode 100644 index 00000000000..681298ce634 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/default_to_int.C @@ -0,0 +1,37 @@ +// PR c++/118306 - "Document" various behaviours wrt. defaulting types to int. +// { dg-do "compile" } +// { dg-additional-options "-fpermissive" } + +// Members. +struct K { + * mem1; // { dg-warning "forbids declaration" } + * mem2; // { dg-warning "forbids declaration" } + const * mem3; // { dg-warning "forbids declaration" } + const ** mem4; // { dg-warning "forbids declaration" } + & mem5; // { dg-warning "forbids declaration" } + volatile & mem6; // { dg-warning "forbids declaration" } + + void foo (const& permissive_fine, // { dg-warning "forbids declaration" } + volatile* permissive_fine_as_well); // { dg-warning "forbids declaration" } + + * bar () { return 0; } // { dg-warning "forbids declaration" } + const& baz (); // { dg-warning "forbids declaration" } + + void bazz () { + try {} + catch (const *i) {} // { dg-warning "forbids" } + catch (const &i) {} // { dg-warning "forbids" } + } +}; + +// Template parameters. +template<const *i, const &j> // { dg-warning "forbids" } +void baz() {} + +// Functions. +foo(int) { return 42; } // { dg-warning "forbids declaration" } +*bar(int) { return 0; } // { dg-warning "forbids declaration" } +**bazz(int) { return 0; } // { dg-warning "forbids declaration" } +*&bazzz(int) { return 0; } // { dg-warning "forbids declaration|bind non-const" } +const bazzzz (int) { return 0; } // { dg-warning "forbids declaration" } +const* bazzzzz (int) { return 0; } // { dg-warning "forbids declaration" } diff --git a/gcc/testsuite/g++.old-deja/g++.jason/operator.C b/gcc/testsuite/g++.old-deja/g++.jason/operator.C index c18790190b5..542f305942c 100644 --- a/gcc/testsuite/g++.old-deja/g++.jason/operator.C +++ b/gcc/testsuite/g++.old-deja/g++.jason/operator.C @@ -29,4 +29,4 @@ void * operator new (A a); // { dg-error ".operator new. takes type .size_t." } void operator delete (A a); // { dg-error ".operator delete. takes type .void\\*. as first parameter" } char * operator char * (int); // { dg-error "return type" "ret" } -// { dg-error "8:.operator char\\*\\*\\(int\\). must be a non-static member function" "mem" { target *-*-* } .-1 } +// { dg-error "8:.operator char\\*\\(int\\). must be a non-static member function" "mem" { target *-*-* } .-1 } -- 2.44.0