From da2c9054f1596b71e3c81efd62b6cef348e445b4 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Tue, 16 Jun 2020 00:20:58 -0400 Subject: [PATCH] c++: Treat in-class default/delete as definitions. We were complaining about a constrained defaulted non-template friend in a template class because funcdef_flag wasn't set. grokdeclarator would set it for default/delete, but grokfield wasn't passing the 'initialized' values needed. Fixing that revealed some errors in existing tests that we weren't diagnosing. Since we accepted them for so long, I'm reducing the error to a pedwarn to ease compiler upgrade. gcc/cp/ChangeLog: * decl2.c (grokfield): Pass SD_DEFAULTED and SD_DELETED. * decl.c (duplicate_decls): Reduce error for delete after earlier declaration to pedwarn. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/pr62101.C: Expect error. * g++.dg/cpp0x/pr80259.C: Expect error. * g++.dg/cpp2a/concepts-friend8.C: New test. --- gcc/cp/decl.c | 8 ++++- gcc/cp/decl2.c | 29 ++++++++++--------- gcc/testsuite/g++.dg/cpp0x/pr62101.C | 2 +- gcc/testsuite/g++.dg/cpp0x/pr80259.C | 2 +- gcc/testsuite/g++.dg/cpp2a/concepts-friend8.C | 12 ++++++++ 5 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend8.C diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 539609e8ada..1d960be1ee6 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -2067,13 +2067,19 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) "previous declaration of %qD", olddecl); } + /* [dcl.fct.def.delete] A deleted definition of a function shall be the + first declaration of the function or, for an explicit specialization + of a function template, the first declaration of that + specialization. */ if (!(DECL_TEMPLATE_INSTANTIATION (olddecl) && DECL_TEMPLATE_SPECIALIZATION (newdecl))) { if (DECL_DELETED_FN (newdecl)) { auto_diagnostic_group d; - error_at (newdecl_loc, "deleted definition of %qD", newdecl); + pedwarn (newdecl_loc, OPT_Wpedantic, + "deleted definition of %qD is not first declaration", + newdecl); inform (olddecl_loc, "previous declaration of %qD", olddecl); } diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 449c86c66c9..93e30340454 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -838,7 +838,17 @@ grokfield (const cp_declarator *declarator, && TREE_CHAIN (init) == NULL_TREE) init = NULL_TREE; - value = grokdeclarator (declarator, declspecs, FIELD, init != 0, &attrlist); + int initialized; + if (init == ridpointers[(int)RID_DELETE]) + initialized = SD_DELETED; + else if (init == ridpointers[(int)RID_DEFAULT]) + initialized = SD_DEFAULTED; + else if (init) + initialized = SD_INITIALIZED; + else + initialized = SD_UNINITIALIZED; + + value = grokdeclarator (declarator, declspecs, FIELD, initialized, &attrlist); if (! value || value == error_mark_node) /* friend or constructor went bad. */ return error_mark_node; @@ -916,18 +926,8 @@ grokfield (const cp_declarator *declarator, { if (init == ridpointers[(int)RID_DELETE]) { - if (friendp && decl_defined_p (value)) - { - error ("redefinition of %q#D", value); - inform (DECL_SOURCE_LOCATION (value), - "%q#D previously defined here", value); - } - else - { - DECL_DELETED_FN (value) = 1; - DECL_DECLARED_INLINE_P (value) = 1; - DECL_INITIAL (value) = error_mark_node; - } + DECL_DELETED_FN (value) = 1; + DECL_DECLARED_INLINE_P (value) = 1; } else if (init == ridpointers[(int)RID_DEFAULT]) { @@ -936,6 +936,9 @@ grokfield (const cp_declarator *declarator, DECL_DEFAULTED_FN (value) = 1; DECL_INITIALIZED_IN_CLASS_P (value) = 1; DECL_DECLARED_INLINE_P (value) = 1; + /* grokfndecl set this to error_mark_node, but we want to + leave it unset until synthesize_method. */ + DECL_INITIAL (value) = NULL_TREE; } } else if (TREE_CODE (init) == DEFERRED_PARSE) diff --git a/gcc/testsuite/g++.dg/cpp0x/pr62101.C b/gcc/testsuite/g++.dg/cpp0x/pr62101.C index 5fc579c7661..07ab1b76fbc 100644 --- a/gcc/testsuite/g++.dg/cpp0x/pr62101.C +++ b/gcc/testsuite/g++.dg/cpp0x/pr62101.C @@ -17,7 +17,7 @@ void g(Y, double); struct Y { // { dg-prune-output "note" } - friend void g(Y, int) = delete; + friend void g(Y, int) = delete; // { dg-error "not first declaration" } friend void g(Y, double) {} }; diff --git a/gcc/testsuite/g++.dg/cpp0x/pr80259.C b/gcc/testsuite/g++.dg/cpp0x/pr80259.C index 9d2a109bcf3..adb354b4ee8 100644 --- a/gcc/testsuite/g++.dg/cpp0x/pr80259.C +++ b/gcc/testsuite/g++.dg/cpp0x/pr80259.C @@ -7,7 +7,7 @@ void bar (); struct A { friend void foo () = delete; // { dg-error "redefinition of" } - friend void bar () = delete; // { dg-message "previously defined here" } + friend void bar () = delete; // { dg-error "not first declaration" } }; void bar () {} // { dg-error "redefinition of" } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend8.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend8.C new file mode 100644 index 00000000000..e4930bbf80c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend8.C @@ -0,0 +1,12 @@ +// { dg-do compile { target c++20 } } + +template +struct A +{ + friend bool operator==(const A&, const A&) requires true = default; +}; + +int main() +{ + A() == A(); +} -- 2.30.2