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, Andrew > > === cut here === > struct A { > *A (); > }; > === 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 when processing a > constructor or a conversion operator with no return type specified but > also a declarator representing a pointer or a reference type. > > Successfully tested on x86_64-pc-linux-gnu. OK for GCC 16? > > PR c++/118306 > > gcc/cp/ChangeLog: > > * decl.cc (check_special_function_return_type): Take declarator > and location as input. Reject return type specifiers with only > a * or &. > (grokdeclarator): Update call to > check_special_function_return_type. > > gcc/testsuite/ChangeLog: > > * g++.dg/parse/constructor4.C: New test. > * g++.dg/parse/conv_op2.C: New test. > * g++.dg/parse/default_to_int.C: New test. > > --- > gcc/cp/decl.cc | 21 +++++++++--- > gcc/testsuite/g++.dg/parse/constructor4.C | 36 +++++++++++++++++++++ > gcc/testsuite/g++.dg/parse/conv_op2.C | 8 +++++ > gcc/testsuite/g++.dg/parse/default_to_int.C | 33 +++++++++++++++++++ > 4 files changed, 94 insertions(+), 4 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/parse/constructor4.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 5c6a4996a89..b57df261e76 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*, > + location_t, 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); > @@ -12349,9 +12350,9 @@ 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 > +/* 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, > 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 ? "*" : "&"); > + > switch (sfk) > { > case sfk_constructor: > @@ -13089,6 +13101,7 @@ grokdeclarator (const cp_declarator *declarator, > type = check_special_function_return_type (sfk, type, > ctor_return_type, > type_quals, > + declarator, id_loc, > 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..7d5a8ecaa97 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/parse/constructor4.C > @@ -0,0 +1,36 @@ > +// PR c++/118306 > +// { dg-do "compile" } > + > +// Constructors. > +struct A { > + *A (); // { dg-error "expected unqualified-id" } > +}; > +struct B { > + &B (); // { dg-error "expected unqualified-id|reference to" } > +}; > +struct C { > + *C (const C&); // { dg-error "expected unqualified-id" } > +}; > +struct D { > + &D (const D&); // { dg-error "expected unqualified-id|reference to" } > +}; > +struct E { > + const E(); // { dg-error "expected unqualified-id" } > +}; > + > +// Destructors. > +struct F { > + * ~F (); // { dg-error "expected unqualified-id" } > +}; > +struct G { > + & ~G (); // { dg-error "expected unqualified-id|reference to" } > +}; > +struct H { > + virtual * ~H (); // { dg-error "expected unqualified-id" } > +}; > +struct I { > + virtual & ~I (); // { dg-error "expected unqualified-id|reference to" } > +}; > +struct J { > + volatile ~J(); // { dg-error "qualifiers are not allowed" } > +}; > 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..33477de6274 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/parse/conv_op2.C > @@ -0,0 +1,8 @@ > +// PR c++/118306 > +// { dg-do "compile" } > + > +struct K { > + char operator int(); // { dg-error "return type specified for" } > + * operator short(); // { dg-error "expected unqualified-id" } > + & operator long(); // { dg-error "expected unqualified-id" } > +}; > 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..5c3ec37e007 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/parse/default_to_int.C > @@ -0,0 +1,33 @@ > +// 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" } > + const * mem2; // { dg-warning "forbids declaration" } > + & mem3; // { dg-warning "forbids declaration" } > + volatile & mem4; // { 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" } > +const bazz (int) { return 0; } // { dg-warning "forbids declaration" } > +const* bazzz (int) { return 0; } // { dg-warning "forbids declaration" } > -- > 2.44.0 >