https://gcc.gnu.org/g:c74e7f651a014d59631361bcc9be05d797928c5c

commit r15-7475-gc74e7f651a014d59631361bcc9be05d797928c5c
Author: Simon Martin <si...@nasilyan.com>
Date:   Tue Feb 11 15:59:02 2025 +0100

    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.

Diff:
---
 gcc/cp/decl.cc                                  | 50 +++++++++++++++++------
 gcc/testsuite/g++.dg/parse/constructor4.C       | 54 +++++++++++++++++++++++++
 gcc/testsuite/g++.dg/parse/constructor5.C       | 48 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/parse/conv_op2.C           | 10 +++++
 gcc/testsuite/g++.dg/parse/default_to_int.C     | 37 +++++++++++++++++
 gcc/testsuite/g++.old-deja/g++.jason/operator.C |  2 +-
 6 files changed, 187 insertions(+), 14 deletions(-)

diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 84abc17ade0f..552a7a2ec546 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**,
+       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
index 000000000000..f7e4cace4510
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/constructor4.C
@@ -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
index 000000000000..5c86fe721a59
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/constructor5.C
@@ -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
index 000000000000..f2719135e000
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/conv_op2.C
@@ -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
index 000000000000..681298ce634d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/default_to_int.C
@@ -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" }
diff --git a/gcc/testsuite/g++.old-deja/g++.jason/operator.C 
b/gcc/testsuite/g++.old-deja/g++.jason/operator.C
index c18790190b52..542f305942c5 100644
--- a/gcc/testsuite/g++.old-deja/g++.jason/operator.C
+++ b/gcc/testsuite/g++.old-deja/g++.jason/operator.C
@@ -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 }

Reply via email to