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