We currently accept the following invalid code (EDG and MSVC do as well) === 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