On 9/11/24 12:54 PM, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
This PR points out the we're not implementing [dcl.fct.def.default]
properly.  Consider e.g.

   struct C {
      C(const C&&) = default;
   };

where we wrongly emit an error, but the move ctor should be just =deleted.
According to [dcl.fct.def.default], if the type of the special member
function differs from the type of the corresponding special member function
that would have been implicitly declared in a way other than as allowed
by 2.1-4, the function is defined as deleted.  There's an exception for
assignment operators in which case the program is ill-formed.

clang++ has a warning for when we delete an explicitly-defaulted function
so this patch adds it too.  I'm also downgrading an error to a pedwarn
in C++17 since the code compiles in C++20.

        PR c++/116162

gcc/c-family/ChangeLog:

        * c.opt (Wdefaulted-function-deleted): New.

gcc/cp/ChangeLog:

        * class.cc (check_bases_and_members): Call delete_defaulted_fn to set
        DECL_DELETED_FN.
        * cp-tree.h (delete_defaulted_fn): Declare.
        * method.cc (delete_defaulted_fn): New.
        (defaulted_late_check): Call delete_defaulted_fn instead of giving
        an error, unless the code is ill-formed.  Change error to pedwarn.

gcc/ChangeLog:

        * doc/invoke.texi: Document -Wdefaulted-function-deleted.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp0x/defaulted15.C: Add dg-warning.
        * g++.dg/cpp0x/defaulted51.C: Likewise.
        * g++.dg/cpp0x/defaulted52.C: Likewise.
        * g++.dg/cpp0x/defaulted53.C: Likewise.
        * g++.dg/cpp0x/defaulted54.C: Likewise.
        * g++.dg/cpp0x/defaulted56.C: Likewise.
        * g++.dg/cpp0x/defaulted57.C: Likewise.
        * g++.dg/cpp0x/defaulted58.C: Likewise.
        * g++.dg/cpp0x/defaulted59.C: Likewise.
        * g++.dg/cpp0x/defaulted63.C: New test.
        * g++.dg/cpp0x/defaulted64.C: New test.
        * g++.dg/cpp0x/defaulted65.C: New test.
        * g++.dg/cpp23/defaulted1.C: New test.
---
  gcc/c-family/c.opt                       |  4 ++
  gcc/cp/class.cc                          |  2 +-
  gcc/cp/cp-tree.h                         |  1 +
  gcc/cp/method.cc                         | 90 +++++++++++++++++++++---
  gcc/doc/invoke.texi                      |  9 +++
  gcc/testsuite/g++.dg/cpp0x/defaulted15.C |  2 +-
  gcc/testsuite/g++.dg/cpp0x/defaulted51.C |  3 +-
  gcc/testsuite/g++.dg/cpp0x/defaulted52.C |  2 +-
  gcc/testsuite/g++.dg/cpp0x/defaulted53.C |  2 +-
  gcc/testsuite/g++.dg/cpp0x/defaulted54.C |  1 +
  gcc/testsuite/g++.dg/cpp0x/defaulted56.C |  6 +-
  gcc/testsuite/g++.dg/cpp0x/defaulted57.C |  6 +-
  gcc/testsuite/g++.dg/cpp0x/defaulted58.C |  1 +
  gcc/testsuite/g++.dg/cpp0x/defaulted59.C |  2 +-
  gcc/testsuite/g++.dg/cpp0x/defaulted63.C | 39 ++++++++++
  gcc/testsuite/g++.dg/cpp0x/defaulted64.C | 27 +++++++
  gcc/testsuite/g++.dg/cpp0x/defaulted65.C | 25 +++++++
  gcc/testsuite/g++.dg/cpp23/defaulted1.C  | 23 ++++++
  18 files changed, 226 insertions(+), 19 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted63.C
  create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted64.C
  create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted65.C
  create mode 100644 gcc/testsuite/g++.dg/cpp23/defaulted1.C

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 491aa02e1a3..f5136fd2341 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -619,6 +619,10 @@ Wdeclaration-missing-parameter-type
  C ObjC Var(warn_declaration_missing_parameter) Warning Init(1)
  Warn for missing parameter types in function declarations.
+Wdefaulted-function-deleted
+C++ ObjC++ Var(warn_defaulted_fn_deleted) Init(1) Warning
+Warn when an explicitly defaulted function is deleted.
+
  Wdelete-incomplete
  C++ ObjC++ Var(warn_delete_incomplete) Init(1) Warning
  Warn when deleting a pointer to incomplete type.
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 950d83b0ea4..a4fdf7f9d11 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -6506,7 +6506,7 @@ check_bases_and_members (tree t)
              /* If the function is defaulted outside the class, we just
                 give the synthesis error.  Core Issue #1331 says this is
                 no longer ill-formed, it is defined as deleted instead.  */
-             DECL_DELETED_FN (fn) = true;
+             delete_defaulted_fn (fn);
          }
        defaulted_late_check (fn);
        }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7baa2ccbe1e..65295b3326d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6929,6 +6929,7 @@ extern bool type_build_ctor_call          (tree);
  extern bool type_build_dtor_call              (tree);
  extern void explain_non_literal_class         (tree);
  extern void inherit_targ_abi_tags             (tree);
+extern void delete_defaulted_fn                        (tree);
  extern void defaulted_late_check              (tree);
  extern bool defaultable_fn_check              (tree);
  extern void check_abi_tags                    (tree);
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 68a776d2c5a..92fb1a0b238 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -3503,6 +3503,53 @@ implicitly_declare_fn (special_function_kind kind, tree 
type,
    return fn;
  }
+/* Mark an explicitly defaulted function FN as =deleted and warn. */
+
+void
+delete_defaulted_fn (tree fn)
+{
+  DECL_DELETED_FN (fn) = true;
+  if (warn_defaulted_fn_deleted)
+    {
+      auto_diagnostic_group d;
+      const char *wmsg, *imsg;
+      switch (special_function_p (fn))
+       {
+       case sfk_copy_constructor:
+         wmsg = G_("explicitly defaulted copy constructor is "
+                   "implicitly deleted");
+         imsg = G_("function is implicitly deleted because its declared type "
+                   "does not match the type of an implicit copy constructor");
+         break;
+       case sfk_move_constructor:
+         wmsg = G_("explicitly defaulted move constructor is "
+                   "implicitly deleted");
+         imsg = G_("function is implicitly deleted because its declared type "
+                   "does not match the type of an implicit move constructor");
+         break;
+       case sfk_copy_assignment:
+         wmsg = G_("explicitly defaulted copy assignment operator is "
+                   "implicitly deleted");
+         imsg = G_("function is implicitly deleted because its declared type "
+                   "does not match the type of an implicit copy assignment "
+                   "operator");
+         break;
+       case sfk_move_assignment:
+         wmsg = G_("explicitly defaulted move assignment operator is "
+                   "implicitly deleted");
+         imsg = G_("function is implicitly deleted because its declared type "
+                   "does not match the type of an implicit move assignment "
+                   "operator");
+         break;
+       default:
+         gcc_unreachable ();
+       }
+      if (warning_at (DECL_SOURCE_LOCATION (fn),
+                     OPT_Wdefaulted_function_deleted, wmsg))
+       inform (DECL_SOURCE_LOCATION (fn), imsg);
+    }
+}
+
  /* Gives any errors about defaulted functions which need to be deferred
     until the containing class is complete.  */
@@ -3555,15 +3602,40 @@ defaulted_late_check (tree fn)
      return compparms (fn_parms, implicit_fn_parms);
    };
- if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
-                   TREE_TYPE (TREE_TYPE (implicit_fn)))
-      || !compare_fn_params (fn, implicit_fn))
-    {
-      auto_diagnostic_group d;
-      error ("defaulted declaration %q+D does not match the "
-            "expected signature", fn);
-      inform (DECL_SOURCE_LOCATION (fn),
-             "expected signature: %qD", implicit_fn);
+  const bool rettypes_same_p
+    = same_type_p (TREE_TYPE (TREE_TYPE (fn)),
+                  TREE_TYPE (TREE_TYPE (implicit_fn)));
+  if (!rettypes_same_p || !compare_fn_params (fn, implicit_fn))
+    {
+      tree parmtype
+       = TREE_VALUE (DECL_XOBJ_MEMBER_FUNCTION_P (fn)
+                     ? TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn)))
+                     : FUNCTION_FIRST_USER_PARMTYPE (fn));
+      const bool illformed_p
+       /* [dcl.fct.def.default] "if F1 is an assignment operator"...  */
+       = (SFK_ASSIGN_P (kind)
+          /* "and the return type of F1 differs from the return type of F2"  */
+          && (!rettypes_same_p
+              /* "or F1's non-object parameter type is not a reference,
+                 the program is ill-formed"  */
+             || !TYPE_REF_P (parmtype)));
+
+      if (!illformed_p
+         && cxx_dialect >= cxx20
+         && !DECL_ARTIFICIAL (fn)
+         && DECL_DEFAULTED_IN_CLASS_P (fn))
+       delete_defaulted_fn (fn);
+      else
+       {
+         auto_diagnostic_group d;
+         /* We used to emit a hard error, so this uses 0 rather than
+            OPT_Wpedantic.  */
+         if (pedwarn (DECL_SOURCE_LOCATION (fn), 0,
+                      "defaulted declaration %q+D does not match the "
+                      "expected signature", fn))
+           inform (DECL_SOURCE_LOCATION (fn),
+                   "expected signature: %qD", implicit_fn);

This should also depend on -Wdefaulted-function-deleted, and set DECL_DELETED_FN. And the C++20 case should show the expected signature. Really, the two cases should share the same code, only the diagnostic kind should change.

Jason

Reply via email to