https://gcc.gnu.org/g:e31e206b397d5c10d8d316a45a657e48b54f2047

commit r16-5549-ge31e206b397d5c10d8d316a45a657e48b54f2047
Author: Marek Polacek <[email protected]>
Date:   Fri Nov 21 15:34:35 2025 -0500

    c++: refactor maybe_delete_defaulted_fn [PR119964]
    
    This does three things:
    
    1) [dcl.fct.def.default]/2.6 says "if F1 is explicitly defaulted on its
    first declaration, it is defined as deleted;", but I wasn't heeding the
    "on its first declaration" part, so fix that;
    
    2) when the decl is actually ill-formed, don't talk about it being
    implicitly deleted, because it's not;
    
    3) there is no need to export maybe_delete_defaulted_fn.
    
            PR c++/119964
    
    gcc/cp/ChangeLog:
    
            * cp-tree.h (maybe_delete_defaulted_fn): Remove.
            * method.cc (maybe_delete_defaulted_fn): Make static.  Refactor.  
If FN
            is not explicitly defaulted on its first declaration, emit an error.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp1y/defaulted1.C: New test.
            * g++.dg/cpp1y/defaulted2.C: New test.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/cp-tree.h                        |  1 -
 gcc/cp/method.cc                        | 51 +++++++++++++++++----------------
 gcc/testsuite/g++.dg/cpp1y/defaulted1.C | 17 +++++++++++
 gcc/testsuite/g++.dg/cpp1y/defaulted2.C | 15 ++++++++++
 4 files changed, 58 insertions(+), 26 deletions(-)

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4efddd98537d..180c0cad5933 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7186,7 +7186,6 @@ 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 maybe_delete_defaulted_fn          (tree, tree);
 extern void defaulted_late_check               (tree, tristate = 
tristate::unknown ());
 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 bc721a5d7e3f..39f931eafc9d 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -3652,49 +3652,49 @@ implicitly_declare_fn (special_function_kind kind, tree 
type,
   return fn;
 }
 
-/* Mark an explicitly defaulted function FN as =deleted and warn.
+/* Maybe mark an explicitly defaulted function FN as =deleted and warn,
+   or emit an error, as per [dcl.fct.def.default].
    IMPLICIT_FN is the corresponding special member function that
-   would have been implicitly declared.  */
+   would have been implicitly declared.  We've already compared FN and
+   IMPLICIT_FN and they are not the same.  */
 
-void
+static void
 maybe_delete_defaulted_fn (tree fn, tree implicit_fn)
 {
-  if (DECL_ARTIFICIAL (fn) || !DECL_DEFAULTED_IN_CLASS_P (fn))
+  if (DECL_ARTIFICIAL (fn))
     return;
 
-  DECL_DELETED_FN (fn) = true;
-
   auto_diagnostic_group d;
   const special_function_kind kind = special_function_p (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)
+  if (/* [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"  */
        && (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
                         TREE_TYPE (TREE_TYPE (implicit_fn)))
           /* "or F1's non-object parameter type is not a reference,
              the program is ill-formed"  */
-          || !TYPE_REF_P (parmtype)));
-  /* Decide if we want to emit a pedwarn, error, or a warning.  */
-  enum diagnostics::kind diag_kind;
-  int opt;
-  if (illformed_p)
-    {
-      diag_kind = diagnostics::kind::error;
-      opt = 0;
-    }
-  else
-    {
-      diag_kind = (cxx_dialect >= cxx20
-                  ? diagnostics::kind::warning
-                  : diagnostics::kind::pedwarn);
-      opt = OPT_Wdefaulted_function_deleted;
+          || !TYPE_REF_P (parmtype)))
+      /* If F1 is *not* explicitly defaulted on its first declaration, the
+        program is ill-formed.  */
+      || !DECL_DEFAULTED_IN_CLASS_P (fn))
+    {
+      error ("defaulted declaration %q+D does not match the expected "
+            "signature", fn);
+      inform (DECL_SOURCE_LOCATION (fn), "expected signature: %qD",
+             implicit_fn);
+      return;
     }
 
+  DECL_DELETED_FN (fn) = true;
+
+  const enum diagnostics::kind diag_kind = (cxx_dialect >= cxx20
+                                           ? diagnostics::kind::warning
+                                           : diagnostics::kind::pedwarn);
+
   /* Don't warn for template instantiations.  */
   if (DECL_TEMPLATE_INSTANTIATION (fn)
       && diag_kind == diagnostics::kind::warning)
@@ -3726,7 +3726,8 @@ maybe_delete_defaulted_fn (tree fn, tree implicit_fn)
     default:
       gcc_unreachable ();
     }
-  if (emit_diagnostic (diag_kind, DECL_SOURCE_LOCATION (fn), opt, wmsg))
+  if (emit_diagnostic (diag_kind, DECL_SOURCE_LOCATION (fn),
+                      OPT_Wdefaulted_function_deleted, wmsg))
     inform (DECL_SOURCE_LOCATION (fn),
            "expected signature: %qD", implicit_fn);
 }
diff --git a/gcc/testsuite/g++.dg/cpp1y/defaulted1.C 
b/gcc/testsuite/g++.dg/cpp1y/defaulted1.C
new file mode 100644
index 000000000000..7b38f975d419
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/defaulted1.C
@@ -0,0 +1,17 @@
+// PR c++/119964
+// { dg-do compile { target c++14 } }
+
+struct A {
+    int i;
+    constexpr A(int v) : i(v) {}
+    constexpr A(const A&&);
+};
+
+constexpr int f() {
+    A a(1);
+    A b = static_cast<const A&&>( a );
+    return b.i;
+}
+
+constexpr A::A(const A&&) = default; // { dg-error "does not match the 
expected signature" }
+static_assert(f () == 1, "");
diff --git a/gcc/testsuite/g++.dg/cpp1y/defaulted2.C 
b/gcc/testsuite/g++.dg/cpp1y/defaulted2.C
new file mode 100644
index 000000000000..9e5e6a69eead
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/defaulted2.C
@@ -0,0 +1,15 @@
+// PR c++/119964
+// { dg-do compile { target c++14 } }
+
+struct A {
+    int i;
+    constexpr A(int v) : i(v) {}
+    constexpr A(const A&&) = default;  // { dg-error "implicitly deleted" "" { 
target c++17_down } }
+                                      // { dg-warning "implicitly deleted" "" 
{ target c++20 } .-1 }
+};
+
+constexpr int f() {
+    A a(1);
+    A b = static_cast<const A&&>( a ); // { dg-error "use of deleted function" 
}
+    return b.i;
+}

Reply via email to