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.

	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.
This commit is contained in:
Simon Martin 2025-02-11 15:59:02 +01:00
parent e8c5013b6b
commit c74e7f651a
6 changed files with 187 additions and 14 deletions

View file

@ -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;
}

View file

@ -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" }
};

View file

@ -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" }
};

View file

@ -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" }
};

View file

@ -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" }

View file

@ -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 }