]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Reject cdtors and conversion operators with a single * as return type [PR118304...
authorSimon Martin <simon@nasilyan.com>
Tue, 11 Feb 2025 14:59:02 +0000 (15:59 +0100)
committerSimon Martin <simon@nasilyan.com>
Tue, 11 Feb 2025 14:59:02 +0000 (15:59 +0100)
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.

gcc/cp/decl.cc
gcc/testsuite/g++.dg/parse/constructor4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/parse/constructor5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/parse/conv_op2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/parse/default_to_int.C [new file with mode: 0644]
gcc/testsuite/g++.old-deja/g++.jason/operator.C

index 84abc17ade0fb3409f899b0d4bac11f2aedceccc..552a7a2ec546d45ec79cabd7a7c192f9f7cab165 100644 (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;
     }
diff --git a/gcc/testsuite/g++.dg/parse/constructor4.C b/gcc/testsuite/g++.dg/parse/constructor4.C
new file mode 100644 (file)
index 0000000..f7e4cac
--- /dev/null
@@ -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 (file)
index 0000000..5c86fe7
--- /dev/null
@@ -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 (file)
index 0000000..f271913
--- /dev/null
@@ -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 (file)
index 0000000..681298c
--- /dev/null
@@ -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" }
index c18790190b52b3c291e2bc2f32953c8de563fa9b..542f305942c51c5f10cd97aedae3dbedb115051c 100644 (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 }