c++: Treat in-class default/delete as definitions.
authorJason Merrill <jason@redhat.com>
Tue, 16 Jun 2020 04:20:58 +0000 (00:20 -0400)
committerJason Merrill <jason@redhat.com>
Wed, 17 Jun 2020 19:08:42 +0000 (15:08 -0400)
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
gcc/cp/decl2.c
gcc/testsuite/g++.dg/cpp0x/pr62101.C
gcc/testsuite/g++.dg/cpp0x/pr80259.C
gcc/testsuite/g++.dg/cpp2a/concepts-friend8.C [new file with mode: 0644]

index 539609e8ada0ed2b14e529fd9e87c8dc7931ba86..1d960be1ee688238c063528cae71f73915b90c9f 100644 (file)
@@ -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);
            }
index 449c86c66c9f4769e03c5501e20bfa645f3f4087..93e3034045404b5453805957f45214d4151bb061 100644 (file)
@@ -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)
index 5fc579c7661738094aa781437997c564824f691c..07ab1b76fbc02499d55b7831a2e5421c52aa63a6 100644 (file)
@@ -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) {}
 };
 
index 9d2a109bcf3aa4d6d3fdcc5e3fcf39f9effd15ea..adb354b4ee82c154afeb28250951a39e21718b54 100644 (file)
@@ -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 (file)
index 0000000..e4930bb
--- /dev/null
@@ -0,0 +1,12 @@
+// { dg-do compile { target c++20 } }
+
+template <class T>
+struct A
+{
+  friend bool operator==(const A&, const A&) requires true = default;
+};
+
+int main()
+{
+  A<int>() == A<int>();
+}