Hi Jason,

On 7 Feb 2025, at 14:21, Jason Merrill wrote:

> On 2/6/25 3:05 PM, Simon Martin wrote:
>> Hi Jason,
>>
>> On 6 Feb 2025, at 16:48, Jason Merrill wrote:
>>
>>> On 2/5/25 2:21 PM, Simon Martin wrote:
>>>> Hi Jason,
>>>>
>>>> On 4 Feb 2025, at 21:23, Jason Merrill wrote:
>>>>
>>>>> On 2/4/25 3:03 PM, Jason Merrill wrote:
>>>>>> On 2/4/25 11:45 AM, Simon Martin wrote:
>>>>>>> On 4 Feb 2025, at 17:17, Jason Merrill wrote:
>>>>>>>
>>>>>>>> On 2/4/25 10:56 AM, Simon Martin wrote:
>>>>>>>>> Hi Jason,
>>>>>>>>>
>>>>>>>>> On 4 Feb 2025, at 16:39, Jason Merrill wrote:
>>>>>>>>>
>>>>>>>>>> On 1/15/25 9:56 AM, Jason Merrill wrote:
>>>>>>>>>>> On 1/15/25 7:24 AM, Simon Martin wrote:
>>>>>>>>>>>> Hi,
>>>>>>>>>>>>
>>>>>>>>>>>> On 14 Jan 2025, at 23:31, Jason Merrill wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>> On 1/14/25 2:13 PM, Simon Martin wrote:
>>>>>>>>>>>>>> On 10 Jan 2025, at 19:10, Andrew Pinski wrote:
>>>>>>>>>>>>>>> On Fri, Jan 10, 2025 at 3:18 AM Simon Martin
>>>>>>>>>>>>>>> <si...@nasilyan.com>
>>>>>>>>>>>>>>> wrote:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> We currently accept the following invalid code (EDG and

>>>>>>>>>>>>>>>> MSVC
>>>>>>>>>>>>>>>> do
>>>>>>>>>>>>>>>> as
>>>>>>>>>>>>>>>> well)
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> clang does too:
>>>>>>>>>>>>>>> https://github.com/llvm/llvm-project/issues/121706 .
>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Note it might be useful if a testcase with multiply `*` 
>>>>>>>>>>>>>>> is
>>>>>>>>>>>>>>> included
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> too:
>>>>>>>>>>>>>>> ```
>>>>>>>>>>>>>>> struct A {
>>>>>>>>>>>>>>>         ****A ();
>>>>>>>>>>>>>>> };
>>>>>>>>>>>>>>> ```
>>>>>>>>>>>>>> Thanks, makes sense to add those. Done in the attached
>>>>>>>>>>>>>> updated
>>>>>>>>>>>>>> revision,
>>>>>>>>>>>>>> successfully tested on x86_64-pc-linux-gnu.
>>>>>>>>>>>>>
>>>>>>>>>>>>>> +/* 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,
>>>>>>>>>>>>>
>>>>>>>>>>>>> id_loc should be the same as declarator->id_loc?
>>>>>>>>>>>> You’re right.
>>>>>>>>>>>>
>>>>>>>>>>>>>>                           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 ? "*" 
>>>>>>>>>>>>>> :
>>>>>>>>>>>>>> "&");
>>>>>>>>>>>>>
>>>>>>>>>>>>> ...and id_loc isn't the location of the ptr-operator, it's

>>>>>>>>>>>>> the
>>>>
>>>>>>>>>>>>> location of the identifier, so this indicates the wrong
>>>>>>>>>>>>> column.
>>>>>>>>>>>>> I
>>>>>>>>>>>>> think using declarator->id_loc makes sense, just not
>>>>>>>>>>>>> pretending
>>>>>>>>>>>>> it's
>>>>>>>>>>>>> the location of the *.
>>>>>>>>>>>> Good catch, thanks.
>>>>>>>>>>>>
>>>>>>>>>>>>> Let's give diagnostics more like the others later in the
>>>>>>>>>>>>> function
>>>>>>>>>>>>> instead of trying to emulate cp_parser_error.
>>>>>>>>>>>> Makes sense. This is what the updated patch does,
>>>>>>>>>>>> successfully
>>>>>>>>>>>> tested on
>>>>>>>>>>>> x86_64-pc-linux-gnu. OK for GCC 16?
>>>>>>>>>>>
>>>>>>>>>>> OK.
>>>>>>>>>>
>>>>>>>>>> Does this also fix 118304?  If so, let's go ahead and apply 
>>>>>>>>>> it
>>>>>>>>>> to
>>>>>>>>>> GCC
>>>>>>>>>> 15.
>>>>>>>>> I have checked just now, and we still ICE for 118304’s

>>>>>>>>> testcase
>>>>>>>>> with
>>>>>>>>> that fix.
>>>>>>>>
>>>>>>>> Why doesn't the preeexisting
>>>>>>>>
>>>>>>>> type = void_type_node;
>>>>>>>>
>>>>>>>> in check_special_function_return_type fix the return type and
>>>>>>>> avoid
>>>>
>>>>>>>> the ICE?
>>>>>>
>>>>>>> We hit the gcc_assert at method.cc:3593, that Marek’s fix
>>>>
>>>>>>> bypasses.
>>>>>>
>>>>>> Yes, but why doesn't check_special_function_return_type prevent
>>>>>> that?
>>>>>
>>>>> Ah, because we call it before walking the declarator.  We need to
>>>>> check again later, perhaps in grokfndecl, that the type is 
>>>>> correct.
>>>>> Perhaps instead of your patch.
>>>> One “issue” with adding another check in or close to grokfndecl
>>>> is
>>>> that DECLARATOR will have “been moved to the ID”, and the fact
>>>> that
>>>> we had a CDK_POINTER kind is “lost”. We could obviously somehow
>>>> propagate this information, but there might be something easier.
>>>
>>> The information isn't lost: it's now reflected in the (wrong) return

>>> type.  One place it would make sense to check would be
>>>
>>>>              if (ctype && (sfk == sfk_constructor
>>>>                            || sfk == sfk_destructor))
>>>>                {
>>>>                  /* We are within a class's scope. If our 
>>>> declarator
>>>> name
>>>>                is the same as the class name, and we are defining
>>>>                                                                            
>>>>    a
>>>> function, then it is a constructor/destructor, and
>>>>                                                               therefore
>>>> returns a void type.  */
>>>
>>> Here 'type' is still the return type, we haven't gotten to
>>> build_function_type yet.
>> That’s true. However, doesn’t it make sense to cram all the 
>> checks
>> about the return type of special functions in
>> check_special_function_return_type, and return an error if that 
>> return
>> type is invalid?
>
> This error seems easily recoverable since we know what the type needs 
> to be, there's no need for error return from grokdeclarator.
ACK.

> However, an alternative to my suggestion above would be to build on 

> your patch by making check_special_function_return_type actually strip 
> the invalid declarators, not just complain about them.
Thanks for the suggestion, I like that it keeps everything within 
check_special_function_return_type. Hence the updated attached patch, 

successfully tested on 86_64-pc-linux-gnu. OK for trunk?

Thanks, Simon
From c8dd4266c61a9d758b813762327536e7d8829d2b Mon Sep 17 00:00:00 2001
From: Simon Martin <si...@nasilyan.com>
Date: Fri, 7 Feb 2025 18:05:49 +0100
Subject: [PATCH] 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.

Successfully tested on x86_64-pc-linux-gnu.

        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++.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                              | 43 ++++++++++++++--
 gcc/testsuite/g++.dg/parse/constructor4.C   | 54 +++++++++++++++++++++
 gcc/testsuite/g++.dg/parse/constructor5.C   | 38 +++++++++++++++
 gcc/testsuite/g++.dg/parse/conv_op2.C       | 10 ++++
 gcc/testsuite/g++.dg/parse/default_to_int.C | 37 ++++++++++++++
 5 files changed, 177 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/parse/constructor4.C
 create mode 100644 gcc/testsuite/g++.dg/parse/constructor5.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 84abc17ade0..f28f8ba0957 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,14 +12471,19 @@ 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);
   switch (sfk)
     {
     case sfk_constructor:
       if (type)
        error_at (smallest_type_location (type_quals, locations),
                  "return type specification for constructor invalid");
+      else if (maybe_strip_indirect_ref (declarator))
+       error_at ((*declarator)->id_loc,
+                 "return type specification for constructor invalid");
       else if (type_quals != TYPE_UNQUALIFIED)
        error_at (smallest_type_quals_location (type_quals, locations),
                  "qualifiers are not allowed on constructor declaration");
@@ -12475,6 +12498,9 @@ check_special_function_return_type 
(special_function_kind sfk,
       if (type)
        error_at (smallest_type_location (type_quals, locations),
                  "return type specification for destructor invalid");
+      else if (maybe_strip_indirect_ref (declarator))
+       error_at ((*declarator)->id_loc,
+                 "return type specification for destructor invalid");
       else if (type_quals != TYPE_UNQUALIFIED)
        error_at (smallest_type_quals_location (type_quals, locations),
                  "qualifiers are not allowed on destructor declaration");
@@ -12491,6 +12517,9 @@ check_special_function_return_type 
(special_function_kind sfk,
       if (type)
        error_at (smallest_type_location (type_quals, locations),
                  "return type specified for %<operator %T%>", optype);
+      else if (maybe_strip_indirect_ref (declarator))
+       error_at ((*declarator)->id_loc,
+                 "return type specified for %<operator %T%>", optype);
       else if (type_quals != TYPE_UNQUALIFIED)
        error_at (smallest_type_quals_location (type_quals, locations),
                  "qualifiers are not allowed on declaration of "
@@ -12503,6 +12532,9 @@ check_special_function_return_type 
(special_function_kind sfk,
       if (type)
        error_at (smallest_type_location (type_quals, locations),
                  "return type specified for deduction guide");
+      else if (maybe_strip_indirect_ref (declarator))
+       error_at ((*declarator)->id_loc,
+                 "return type specified for deduction guide");
       else if (type_quals != TYPE_UNQUALIFIED)
        error_at (smallest_type_quals_location (type_quals, locations),
                  "qualifiers are not allowed on declaration of "
@@ -13181,6 +13213,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 00000000000..f7e4cace451
--- /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 00000000000..f3dfc372a6c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/parse/constructor5.C
@@ -0,0 +1,38 @@
+// PR c++/118304
+// { dg-do "compile" { target c++11 } }
+
+struct A {
+  *A() = default; // { dg-error "return type specification" }
+};
+
+struct B {
+  **B() = default; // { dg-error "return type specification" }
+};
+
+struct C {
+  &*C () = default; // { dg-error "return type specification" }
+};
+
+struct D {
+  *&D () = default; // { dg-error "return type specification" }
+};
+
+struct E {
+  &E (const E&) = default; // { dg-error "return type specification|reference 
to" }
+};
+
+struct F {
+  **F (const F&) = default; // { dg-error "return type specification|reference 
to" }
+};
+
+struct G {
+  & ~G () = default; // { dg-error "return type specification|reference to" }
+};
+
+struct H {
+  * ~H () = default; // { dg-error "return type specification|reference to" }
+};
+
+struct I {
+  &* ~I () = 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 00000000000..f2719135e00
--- /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 00000000000..681298ce634
--- /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" }
-- 
2.44.0

Reply via email to