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:
parent
e8c5013b6b
commit
c74e7f651a
6 changed files with 187 additions and 14 deletions
|
@ -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;
|
||||
}
|
||||
|
|
54
gcc/testsuite/g++.dg/parse/constructor4.C
Normal file
54
gcc/testsuite/g++.dg/parse/constructor4.C
Normal 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" }
|
||||
};
|
48
gcc/testsuite/g++.dg/parse/constructor5.C
Normal file
48
gcc/testsuite/g++.dg/parse/constructor5.C
Normal 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" }
|
||||
};
|
10
gcc/testsuite/g++.dg/parse/conv_op2.C
Normal file
10
gcc/testsuite/g++.dg/parse/conv_op2.C
Normal 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" }
|
||||
};
|
37
gcc/testsuite/g++.dg/parse/default_to_int.C
Normal file
37
gcc/testsuite/g++.dg/parse/default_to_int.C
Normal 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" }
|
|
@ -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 }
|
||||
|
|
Loading…
Add table
Reference in a new issue