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

commit r17-2127-gc1a7d0a2140563198fef1a704029c5a9546e77a0
Author: Jakub Jelinek <[email protected]>
Date:   Fri Jul 3 20:43:22 2026 +0200

    c++: Implement C++29 P2953R5 - Adding restrictions to defaulted assignment 
operator functions [PR125826]
    
    The following patch attempts to implement the C++29 P2953R5
    Adding restrictions to defaulted assignment operator functions
    paper.
    The paper seems to be misnamed to me because it changes the validity
    of defaulted constructors as well in some cases.
    
    What the patch does is that it calls maybe_delete_defaulted_fn
    also for the FUNCTION_RVALUE_QUALIFIED case for C++29, and then in
    maybe_delete_defaulted_fn for C++29 errors rather than making the
    function deleted in most cases, with the exception of
    [dcl.fct.def.default]/(2.5) case which ought to be still deleted
    rather than ill-formed (but only if that is the sole change that
    is not on the whitelist of possible differences).
    
    I've tried to include all the tests I found in the paper (referenced or
    directly in it) with the exception of the https://gcc.gnu.org/PR86646
    case, added some further ones and tweaked anything in the existing
    test that behaves differently for -std=c++29 with the patch.
    
    2026-07-03  Jakub Jelinek  <[email protected]>
    
            PR c++/125826
            * method.cc: Implement C++29 P2953R5 - Adding restrictions to
            defaulted assignment operator functions.
            (maybe_delete_defaulted_fn): For C++29, error instead of
            deleting always, with the exception of F1 having parmtype
            const C & and F2 having implicit_parmtype C & and no other
            non-permitted changes.  Move checks whether defaulted fn
            should be deleted or ill-formed at all from defaulted_late_check
            to this function.  Also error for C++29 if
            FUNCTION_RVALUE_QUALIFIED.
            (defaulted_late_check): Call maybe_delete_defaulted_fn
            unconditionally.
    
            * g++.dg/cpp0x/defaulted51.C: Adjust expected diagnostics
            for C++29.
            * g++.dg/cpp0x/defaulted55.C: Likewise.
            * g++.dg/cpp0x/defaulted56.C: Likewise.
            * g++.dg/cpp0x/defaulted57.C: Likewise.
            * g++.dg/cpp0x/defaulted63.C: Likewise.
            * g++.dg/cpp0x/defaulted64.C: Likewise.
            * g++.dg/cpp0x/defaulted65.C: Likewise.
            * g++.dg/cpp0x/defaulted66.C: Likewise.
            * g++.dg/cpp0x/defaulted67.C: Likewise.
            * g++.dg/cpp0x/defaulted68.C: Likewise.
            * g++.dg/cpp1y/defaulted2.C: Likewise.
            * g++.dg/cpp29/defaulted1.C: New test.
            * g++.dg/cpp29/defaulted2.C: New test.
            * g++.dg/cpp29/defaulted3.C: New test.
            * g++.dg/cpp29/defaulted4.C: New test.
            * g++.dg/cpp29/defaulted5.C: New test.
            * g++.dg/cpp29/defaulted6.C: New test.
    
    Reviewed-by: Jason Merrill <[email protected]>

Diff:
---
 gcc/cp/method.cc                         |  82 +++++++++++++---------
 gcc/testsuite/g++.dg/cpp0x/defaulted51.C |   2 +-
 gcc/testsuite/g++.dg/cpp0x/defaulted55.C |   2 +-
 gcc/testsuite/g++.dg/cpp0x/defaulted56.C |   8 +--
 gcc/testsuite/g++.dg/cpp0x/defaulted57.C |   8 +--
 gcc/testsuite/g++.dg/cpp0x/defaulted63.C |  20 +++---
 gcc/testsuite/g++.dg/cpp0x/defaulted64.C |   4 +-
 gcc/testsuite/g++.dg/cpp0x/defaulted65.C |  12 ++--
 gcc/testsuite/g++.dg/cpp0x/defaulted66.C |   8 ++-
 gcc/testsuite/g++.dg/cpp0x/defaulted67.C |   6 +-
 gcc/testsuite/g++.dg/cpp0x/defaulted68.C |  10 +--
 gcc/testsuite/g++.dg/cpp1y/defaulted2.C  |   5 +-
 gcc/testsuite/g++.dg/cpp29/defaulted1.C  | 113 +++++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp29/defaulted2.C  |  15 ++++
 gcc/testsuite/g++.dg/cpp29/defaulted3.C  |  20 ++++++
 gcc/testsuite/g++.dg/cpp29/defaulted4.C  |  40 +++++++++++
 gcc/testsuite/g++.dg/cpp29/defaulted5.C  | 102 ++++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp29/defaulted6.C  |  33 +++++++++
 18 files changed, 417 insertions(+), 73 deletions(-)

diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 4c432efb56ae..cec98bdc89b6 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -3753,8 +3753,7 @@ implicitly_declare_fn (special_function_kind kind, tree 
type,
 /* 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.  We've already compared FN and
-   IMPLICIT_FN and they are not the same.  */
+   would have been implicitly declared.  */
 
 static void
 maybe_delete_defaulted_fn (tree fn, tree implicit_fn)
@@ -3762,23 +3761,68 @@ maybe_delete_defaulted_fn (tree fn, tree implicit_fn)
   if (DECL_ARTIFICIAL (fn))
     return;
 
+  /* Includes special handling for a default xobj operator.
+     Returns 2 for xobj parameter mismatch, 1 if parameters are
+     different and 0 if they are the same.  */
+  auto compare_fn_params = [] (tree fn, tree implicit_fn)
+  {
+    tree fn_parms = TYPE_ARG_TYPES (TREE_TYPE (fn));
+    tree implicit_fn_parms = TYPE_ARG_TYPES (TREE_TYPE (implicit_fn));
+
+    if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+      {
+       tree fn_obj_ref_type = TREE_VALUE (fn_parms);
+       /* We can't default xobj operators with an xobj parameter that is not
+          an lvalue reference, even if it would correspond.  */
+       if (!TYPE_REF_P (fn_obj_ref_type)
+           || TYPE_REF_IS_RVALUE (fn_obj_ref_type)
+           || !object_parms_correspond (fn, implicit_fn,
+                                        DECL_CONTEXT (implicit_fn)))
+         return 2;
+       /* We just compared the object parameters, skip over them before
+          passing to compparms.  */
+       fn_parms = TREE_CHAIN (fn_parms);
+       implicit_fn_parms = TREE_CHAIN (implicit_fn_parms);
+      }
+    return compparms (fn_parms, implicit_fn_parms) ? 0 : 1;
+  };
+
+  bool same_ret_type = same_type_p (TREE_TYPE (TREE_TYPE (fn)),
+                                   TREE_TYPE (TREE_TYPE (implicit_fn)));
+  int cmp_params = compare_fn_params (fn, implicit_fn);
+  if (same_ret_type
+      && cmp_params == 0
+      && (cxx_dialect < cxx29 || !FUNCTION_RVALUE_QUALIFIED (TREE_TYPE (fn))))
+    return;
+
   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));
+  tree implicit_parmtype
+    = TREE_VALUE (FUNCTION_FIRST_USER_PARMTYPE (implicit_fn));
+
   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)))
+       && (!same_ret_type
           /* "or F1's non-object parameter type is not a reference,
              the program is ill-formed"  */
           || !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))
+      || !DECL_DEFAULTED_IN_CLASS_P (fn)
+      || (cxx_dialect >= cxx29
+         /* For C++29, the only case which is deleted rather than
+            ill-formed is when F1 has const C & argument and F2 C &
+            and no other non-allowed differences.  */
+         && (FUNCTION_RVALUE_QUALIFIED (TREE_TYPE (fn))
+             || cmp_params == 2
+             || TYPE_REF_IS_RVALUE (parmtype)
+             || TYPE_QUALS (TREE_TYPE (parmtype)) != TYPE_QUAL_CONST
+             || TYPE_QUALS (TREE_TYPE (implicit_parmtype)))))
     {
       error ("defaulted declaration %q+D does not match the expected "
             "signature", fn);
@@ -3867,33 +3911,7 @@ defaulted_late_check (tree fn, tristate 
imp_const/*=tristate::unknown()*/)
                                            /*inherited_parms=*/NULL_TREE);
   tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
 
-  /* Includes special handling for a default xobj operator.  */
-  auto compare_fn_params = [](tree fn, tree implicit_fn){
-    tree fn_parms = TYPE_ARG_TYPES (TREE_TYPE (fn));
-    tree implicit_fn_parms = TYPE_ARG_TYPES (TREE_TYPE (implicit_fn));
-
-    if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
-      {
-       tree fn_obj_ref_type = TREE_VALUE (fn_parms);
-       /* We can't default xobj operators with an xobj parameter that is not
-          an lvalue reference, even if it would correspond.  */
-       if (!TYPE_REF_P (fn_obj_ref_type)
-           || TYPE_REF_IS_RVALUE (fn_obj_ref_type)
-           || !object_parms_correspond (fn, implicit_fn,
-                                        DECL_CONTEXT (implicit_fn)))
-         return false;
-       /* We just compared the object parameters, skip over them before
-          passing to compparms.  */
-       fn_parms = TREE_CHAIN (fn_parms);
-       implicit_fn_parms = TREE_CHAIN (implicit_fn_parms);
-      }
-    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))
-    maybe_delete_defaulted_fn (fn, implicit_fn);
+  maybe_delete_defaulted_fn (fn, implicit_fn);
 
   if (DECL_DELETED_FN (implicit_fn))
     {
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted51.C 
b/gcc/testsuite/g++.dg/cpp0x/defaulted51.C
index d81baa0059da..69a91fa2ffc9 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted51.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted51.C
@@ -4,7 +4,7 @@
 template<int> struct A
 {
   A();
-  A(volatile A&) = default;  // { dg-error "defaulted" "" { target c++17_down 
} }
+  A(volatile A&) = default;  // { dg-error "defaulted" "" { target { 
c++17_down || c++29 } } }
 };
 
 struct B
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted55.C 
b/gcc/testsuite/g++.dg/cpp0x/defaulted55.C
index 04cfc1720006..444ac8c89c77 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted55.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted55.C
@@ -12,7 +12,7 @@ template<typename T> struct W
   W();
   W(W&) = default;
   // T1 and T2 may have differing ref-qualifiers (copy assign op).
-  constexpr W& operator=(const W&) && = default; // { dg-error "defaulted" "" 
{ target c++11_down } }
+  constexpr W& operator=(const W&) && = default; // { dg-error "defaulted" "" 
{ target { c++11_down || c++29 } } }
   T t;
 };
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted56.C 
b/gcc/testsuite/g++.dg/cpp0x/defaulted56.C
index 0e36fd293f61..2cba02a499eb 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted56.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted56.C
@@ -11,14 +11,14 @@ struct S
 
 struct T
 {
-  constexpr T(volatile T &) = default; // { dg-error "defaulted" "" { target 
c++17_down } }
-                                      // { dg-warning "implicitly deleted" "" 
{ target c++20 } .-1 }
+  constexpr T(volatile T &) = default; // { dg-error "defaulted" "" { target { 
c++17_down || c++29 } } }
+                                      // { dg-warning "implicitly deleted" "" 
{ target { c++20 && c++26_down } } .-1 }
 };
 
 struct U
 {
-  constexpr U(const volatile U &) = default; // { dg-error "defaulted" "" { 
target c++17_down } }
-                                            // { dg-warning "implicitly 
deleted" "" { target c++20 } .-1 }
+  constexpr U(const volatile U &) = default; // { dg-error "defaulted" "" { 
target { c++17_down || c++29 } } }
+                                            // { dg-warning "implicitly 
deleted" "" { target { c++20 && c++26_down } } .-1 }
 };
 
 struct V
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted57.C 
b/gcc/testsuite/g++.dg/cpp0x/defaulted57.C
index feca9662b4a5..7994cba2019b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted57.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted57.C
@@ -11,14 +11,14 @@ struct S
 
 struct T
 {
-  T& operator=(volatile T &) = default; // { dg-error "defaulted" "" { target 
c++17_down } }
-                                       // { dg-warning "implicitly deleted" "" 
{ target c++20 } .-1 }
+  T& operator=(volatile T &) = default; // { dg-error "defaulted" "" { target 
{ c++17_down || c++29 } } }
+                                       // { dg-warning "implicitly deleted" "" 
{ target { c++20 && c++26_down } } .-1 }
 };
 
 struct U
 {
-  U& operator=(const volatile U &) = default; // { dg-error "defaulted" "" { 
target c++17_down } }
-                                             // { dg-warning "implicitly 
deleted" "" { target c++20 } .-1 }
+  U& operator=(const volatile U &) = default; // { dg-error "defaulted" "" { 
target { c++17_down || c++29 } } }
+                                             // { dg-warning "implicitly 
deleted" "" { target { c++20 && c++26_down } } .-1 }
 };
 
 struct V
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted63.C 
b/gcc/testsuite/g++.dg/cpp0x/defaulted63.C
index 99f92ff69c52..9e9d1052bf3f 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted63.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted63.C
@@ -6,8 +6,8 @@ struct C0 {
 };
 
 struct C1 {
-  C1(volatile C1&) = default; // { dg-warning "implicitly deleted" "" { target 
c++20 } }
-                             // { dg-error "does not match" "" { target 
c++17_down } .-1 }
+  C1(volatile C1&) = default; // { dg-warning "implicitly deleted" "" { target 
{ c++20 && c++26_down } } }
+                             // { dg-error "does not match" "" { target { 
c++17_down || c++29 } } .-1 }
 };
 
 struct C2 {
@@ -15,8 +15,8 @@ struct C2 {
 };
 
 struct C3 {
-  C3(const volatile C3&) = default;  // { dg-warning "implicitly deleted" "" { 
target c++20 } }
-                                     // { dg-error "does not match" "" { 
target c++17_down } .-1 }
+  C3(const volatile C3&) = default;  // { dg-warning "implicitly deleted" "" { 
target { c++20 && c++26_down } } }
+                                     // { dg-error "does not match" "" { 
target { c++17_down || c++29 } } .-1 }
 };
 
 struct M0 {
@@ -24,16 +24,16 @@ struct M0 {
 };
 
 struct M1 {
-  M1(const M1&&) = default; // { dg-warning "implicitly deleted" "" { target 
c++20 } }
-                           // { dg-error "does not match" "" { target 
c++17_down } .-1 }
+  M1(const M1&&) = default; // { dg-warning "implicitly deleted" "" { target { 
c++20 && c++26_down } } }
+                           // { dg-error "does not match" "" { target { 
c++17_down || c++29 } } .-1 }
 };
 
 struct M2 {
-  M2(volatile M2&&) = default; // { dg-warning "implicitly deleted" "" { 
target c++20 } }
-                               // { dg-error "does not match" "" { target 
c++17_down } .-1 }
+  M2(volatile M2&&) = default; // { dg-warning "implicitly deleted" "" { 
target { c++20 && c++26_down } } }
+                               // { dg-error "does not match" "" { target { 
c++17_down || c++29 } } .-1 }
 };
 
 struct M3 {
-  M3(const volatile M3&&) = default;  // { dg-warning "implicitly deleted" "" 
{ target c++20 } }
-                                     // { dg-error "does not match" "" { 
target c++17_down } .-1 }
+  M3(const volatile M3&&) = default;  // { dg-warning "implicitly deleted" "" 
{ target { c++20 && c++26_down } } }
+                                     // { dg-error "does not match" "" { 
target { c++17_down || c++29 } } .-1 }
 };
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted64.C 
b/gcc/testsuite/g++.dg/cpp0x/defaulted64.C
index f20030192c37..b2d8325688f3 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted64.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted64.C
@@ -14,8 +14,8 @@ struct R
 
 struct S
 {
-  S& operator=(const S&&) = default; // { dg-warning "implicitly deleted" "" { 
target c++20 } }
-                                    // { dg-error "does not match" "" { target 
c++17_down } .-1 }
+  S& operator=(const S&&) = default; // { dg-warning "implicitly deleted" "" { 
target { c++20 && c++26_down } } }
+                                    // { dg-error "does not match" "" { target 
{ c++17_down || c++29 } } .-1 }
 
   M m;
 };
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted65.C 
b/gcc/testsuite/g++.dg/cpp0x/defaulted65.C
index 88ca1d96084b..aadd33c6d6a9 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted65.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted65.C
@@ -8,18 +8,18 @@ struct S
 
 struct T
 {
-  T& operator=(volatile T &&) = default; // { dg-error "defaulted" "" { target 
c++17_down } }
-                                        // { dg-warning "implicitly deleted" 
"" { target c++20 } .-1 }
+  T& operator=(volatile T &&) = default; // { dg-error "defaulted" "" { target 
{ c++17_down || c++29 } } }
+                                        // { dg-warning "implicitly deleted" 
"" { target { c++20 && c++26_down } } .-1 }
 };
 
 struct U
 {
-  U& operator=(const volatile U &&) = default; // { dg-error "defaulted" "" { 
target c++17_down } }
-                                              // { dg-warning "implicitly 
deleted" "" { target c++20 } .-1 }
+  U& operator=(const volatile U &&) = default; // { dg-error "defaulted" "" { 
target { c++17_down || c++29 } } }
+                                              // { dg-warning "implicitly 
deleted" "" { target { c++20 && c++26_down } } .-1 }
 };
 
 struct V
 {
-  V& operator=(const V &&) = default; // { dg-error "defaulted" "" { target 
c++17_down } }
-                                     // { dg-warning "implicitly deleted" "" { 
target c++20 } .-1 }
+  V& operator=(const V &&) = default; // { dg-error "defaulted" "" { target { 
c++17_down || c++29 } } }
+                                     // { dg-warning "implicitly deleted" "" { 
target { c++20 && c++26_down } } .-1 }
 };
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted66.C 
b/gcc/testsuite/g++.dg/cpp0x/defaulted66.C
index 00d3e43e89fe..289bcc18e787 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted66.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted66.C
@@ -6,12 +6,14 @@
 template<typename>
 struct C {
    C();
-   C(const C&&) = default; // { dg-error "implicitly deleted" "" { target 
c++17_down} }
+   C(const C&&) = default; // { dg-error "implicitly deleted" "" { target 
c++17_down } }
+   // { dg-error "does not match the expected signature" "" { target c++29 } 
.-1 }
 };
 
 struct D {
-  D(const D&&) = default; // { dg-error "implicitly deleted" "" { target 
c++17_down} }
-  // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
+  D(const D&&) = default; // { dg-error "implicitly deleted" "" { target 
c++17_down } }
+  // { dg-warning "implicitly deleted" "" { target { c++20 && c++26_down } } 
.-1 }
+  // { dg-error "does not match the expected signature" "" { target c++29 } 
.-2 }
 };
 
 struct M {
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted67.C 
b/gcc/testsuite/g++.dg/cpp0x/defaulted67.C
index a47761c3a265..cbe6e740d5a2 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted67.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted67.C
@@ -9,15 +9,15 @@ struct S
 
 struct T
 {
-  T& operator=(volatile T &&) = default;
+  T& operator=(volatile T &&) = default;       // { dg-error "does not match 
the expected signature" "" { target c++29 } }
 };
 
 struct U
 {
-  U& operator=(const volatile U &&) = default;
+  U& operator=(const volatile U &&) = default; // { dg-error "does not match 
the expected signature" "" { target c++29 } }
 };
 
 struct V
 {
-  V& operator=(const V &&) = default;
+  V& operator=(const V &&) = default;          // { dg-error "does not match 
the expected signature" "" { target c++29 } }
 };
diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted68.C 
b/gcc/testsuite/g++.dg/cpp0x/defaulted68.C
index c8c1261de425..8ba881e93135 100644
--- a/gcc/testsuite/g++.dg/cpp0x/defaulted68.C
+++ b/gcc/testsuite/g++.dg/cpp0x/defaulted68.C
@@ -7,7 +7,7 @@ struct C0 {
 };
 
 struct C1 {
-  C1(volatile C1&) = default;
+  C1(volatile C1&) = default;          // { dg-error "does not match the 
expected signature" "" { target c++29 } }
 };
 
 struct C2 {
@@ -15,7 +15,7 @@ struct C2 {
 };
 
 struct C3 {
-  C3(const volatile C3&) = default;
+  C3(const volatile C3&) = default;    // { dg-error "does not match the 
expected signature" "" { target c++29 } }
 };
 
 struct M0 {
@@ -23,13 +23,13 @@ struct M0 {
 };
 
 struct M1 {
-  M1(const M1&&) = default;
+  M1(const M1&&) = default;            // { dg-error "does not match the 
expected signature" "" { target c++29 } }
 };
 
 struct M2 {
-  M2(volatile M2&&) = default;
+  M2(volatile M2&&) = default;         // { dg-error "does not match the 
expected signature" "" { target c++29 } }
 };
 
 struct M3 {
-  M3(const volatile M3&&) = default;
+  M3(const volatile M3&&) = default;   // { dg-error "does not match the 
expected signature" "" { target c++29 } }
 };
diff --git a/gcc/testsuite/g++.dg/cpp1y/defaulted2.C 
b/gcc/testsuite/g++.dg/cpp1y/defaulted2.C
index 9e5e6a69eead..eb6bed3f220f 100644
--- a/gcc/testsuite/g++.dg/cpp1y/defaulted2.C
+++ b/gcc/testsuite/g++.dg/cpp1y/defaulted2.C
@@ -5,11 +5,12 @@ 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 }
+                                      // { dg-warning "implicitly deleted" "" 
{ target { c++20 && c++26_down } } .-1 }
+                                      // { dg-error "does not match the 
expected signature" "" { target c++29 } .-2 }
 };
 
 constexpr int f() {
     A a(1);
-    A b = static_cast<const A&&>( a ); // { dg-error "use of deleted function" 
}
+    A b = static_cast<const A&&>( a ); // { dg-error "use of deleted function" 
"" { target c++26_down } }
     return b.i;
 }
diff --git a/gcc/testsuite/g++.dg/cpp29/defaulted1.C 
b/gcc/testsuite/g++.dg/cpp29/defaulted1.C
new file mode 100644
index 000000000000..0be0c0d924b5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp29/defaulted1.C
@@ -0,0 +1,113 @@
+// P2953R5 - Adding restrictions to defaulted assignment operator functions
+// { dg-do compile { target c++11 } }
+
+// Define std::move.
+namespace std {
+  template <typename T>
+  struct remove_reference
+  { typedef T type; };
+
+  template <typename T>
+  struct remove_reference <T &>
+  { typedef T type; };
+
+  template <typename T>
+  struct remove_reference <T &&>
+  { typedef T type; };
+
+  template <typename T>
+  constexpr typename std::remove_reference <T>::type &&
+  move (T &&t) noexcept
+  { return static_cast <typename std::remove_reference <T>::type &&> (t); }
+}
+
+struct A {
+  A (A &) = default;
+  A &operator= (A &) = default;
+};
+
+void
+foo (A a)
+{
+  a = a;
+  A b = a;
+}
+
+struct B {
+  B (const B &&) = default;            // { dg-error "explicitly defaulted 
move constructor is implicitly deleted because its declared type does not match 
the type of an implicit move constructor" "" { target c++17_down } }
+                                       // { dg-warning "explicitly defaulted 
move constructor is implicitly deleted because its declared type does not match 
the type of an implicit move constructor" "" { target { c++20 && c++26_down } } 
.-1 }
+                                       // { dg-error "defaulted declaration 
'B::B\\\(const B\\\&\\\&\\\)' does not match the expected signature" "" { 
target c++29 } .-2 }
+                                       // { dg-message "note: expected 
signature: 'constexpr B::B\\\(B\\\&\\\&\\\)'" "" { target *-*-* } .-3 }
+  B &operator= (const B &&) = default; // { dg-error "explicitly defaulted 
move assignment operator is implicitly deleted because its declared type does 
not match the type of an implicit move assignment operator" "" { target 
c++17_down } }
+                                       // { dg-warning "explicitly defaulted 
move assignment operator is implicitly deleted because its declared type does 
not match the type of an implicit move assignment operator" "" { target { c++20 
&& c++26_down } } .-1 }
+};                                     // { dg-error "defaulted declaration 
'B\\\& B::operator=\\\(const B\\\&\\\&\\\)' does not match the expected 
signature" "" { target c++29 } .-2 }
+                                       // { dg-message "note: expected 
signature: 'constexpr B\\\& B::operator=\\\(B\\\&\\\&\\\)'" "" { target c++14 } 
.-3 }
+                                       // { dg-message "note: expected 
signature: 'B\\\& B::operator=\\\(B\\\&\\\&\\\)'" "" { target c++11_only } .-4 }
+
+void
+foo (B a)
+{
+  a = std::move (a);   // { dg-error "use of deleted function 'constexpr B\\\& 
B::operator=\\\(const B\\\&\\\&\\\)'" "" { target { c++14 && c++26_down } } }
+                       // { dg-error "use of deleted function 'B\\\& 
B::operator=\\\(const B\\\&\\\&\\\)'" "" { target c++11_only } .-1 }
+  B b = std::move (a); // { dg-error "use of deleted function 'constexpr 
B::B\\\(const B\\\&\\\&\\\)'" "" { target c++26_down } }
+}
+
+struct C {
+  C &operator= (const C &) const = default; // { dg-error "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target c++17_down } }
+};                                     // { dg-warning "explicitly defaulted 
copy assignment operator is implicitly deleted because its declared type does 
not match the type of an implicit copy assignment operator" "" { target { c++20 
&& c++26_down } } .-1 }
+                                       // { dg-error "defaulted declaration 
'C\\\& C::operator=\\\(const C\\\&\\\) const' does not match the expected 
signature" "" { target c++29 } .-2 }
+                                       // { dg-message "note: expected 
signature: 'constexpr C\\\& C::operator=\\\(const C\\\&\\\)'" "" { target c++14 
} .-3 }
+                                       // { dg-message "note: expected 
signature: 'C\\\& C::operator=\\\(const C\\\&\\\)'" "" { target c++11_only } 
.-4 }
+
+void
+foo (C &)
+{
+  C c;
+  c = c;       // { dg-error "use of deleted function 'constexpr C\\\& 
C::operator=\\\(const C\\\&\\\) const'" "" { target { c++14 && c++26_down } } }
+}              // { dg-error "use of deleted function 'C\\\& 
C::operator=\\\(const C\\\&\\\) const'" "" { target c++11_only } .-1 }
+
+struct D {
+  D &operator= (const D &) && = default; // { dg-error "defaulted declaration 
'D\\\& D::operator=\\\(const D\\\&\\\) \\\&\\\&' does not match the expected 
signature" "" { target c++29 } }
+};             // { dg-message "note: expected signature: 'constexpr D\\\& 
D::operator=\\\(const D\\\&\\\)'" "" { target c++29 } .-1 }
+
+void
+foo (D a)
+{
+  std::move (a) = a;
+}
+
+struct E {
+  E &&operator= (const E &) && = default; // { dg-error "defaulted declaration 
'E\\\&\\\& E::operator=\\\(const E\\\&\\\) \\\&\\\&' does not match the 
expected signature" }
+};                                     // { dg-message "note: expected 
signature: 'constexpr E\\\& E::operator=\\\(const E\\\&\\\)'" "" { target c++14 
} .-1 }
+                                       // { dg-message "note: expected 
signature: 'E\\\& E::operator=\\\(const E\\\&\\\)'" "" { target c++11_only } 
.-2 }
+
+void
+foo (E a)
+{
+  std::move (a) = a;
+}
+
+struct F {        
+  F &operator= (const F &) volatile;   // { dg-message "note: initializing 
argument 1 of 'F\\\& F::operator=\\\(const F\\\&\\\) volatile'" }
+};
+struct G {
+  volatile F f;
+  G &operator= (const G &) = default;  // { dg-error "binding reference of 
type 'const F\\\&' to 'const volatile F' discards qualifiers" }
+}; // { dg-message "note: 'G\\\& G::operator=\\\(const G\\\&\\\)' is 
implicitly deleted because the default definition would be ill-formed:" "" { 
target *-*-* } .-1 }
+
+void
+foo (G &)
+{
+  G a;
+  decltype(a = a) b = a;       // { dg-error "cannot convert 'G' to 'int' in 
initialization" }
+} // { dg-error "use of deleted function 'G\\\& G::operator=\\\(const 
G\\\&\\\)'" "" { target *-*-* } .-1 }
+
+struct H {
+  H &operator= (H &);          // { dg-message "note: initializing argument 1 
of 'H\\\& H::operator=\\\(H\\\&\\\)'" }
+};
+struct I {
+  H h;
+  I &operator= (const I &);
+};
+I &I::operator= (const I &) = default; // { dg-error "binding reference of 
type 'H\\\&' to 'const H' discards qualifiers" }
+// { dg-message "note: 'I\\\& I::operator=\\\(const I\\\&\\\)' is implicitly 
deleted because the default definition would be ill-formed:" "" { target *-*-* 
} .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp29/defaulted2.C 
b/gcc/testsuite/g++.dg/cpp29/defaulted2.C
new file mode 100644
index 000000000000..82d030c46039
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp29/defaulted2.C
@@ -0,0 +1,15 @@
+// P2953R5 - Adding restrictions to defaulted assignment operator functions
+// { dg-do compile { target c++17 } }
+
+#include <type_traits>
+
+struct A {
+  A &operator= (A &);
+};
+struct B {
+  A a;
+  B &operator= (const B &) = default;  // { dg-error "explicitly defaulted 
copy assignment operator is implicitly deleted because its declared type does 
not match the type of an implicit copy assignment operator" "" { target 
c++17_only } }
+};                                     // { dg-warning "explicitly defaulted 
copy assignment operator is implicitly deleted because its declared type does 
not match the type of an implicit copy assignment operator" "" { target c++20 } 
.-1 }
+                                       // { dg-message "note: expected 
signature: 'B\\\& B::operator=\\\(B\\\&\\\)'" "" { target *-*-* } .-2 }
+static_assert (!std::is_assignable_v <B &, const B &>, "");
+static_assert (!std::is_assignable_v <B &, B &>, "");
diff --git a/gcc/testsuite/g++.dg/cpp29/defaulted3.C 
b/gcc/testsuite/g++.dg/cpp29/defaulted3.C
new file mode 100644
index 000000000000..d205acc5e3b2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp29/defaulted3.C
@@ -0,0 +1,20 @@
+// P2953R5 - Adding restrictions to defaulted assignment operator functions
+// { dg-do compile { target c++11 } }
+
+struct A {
+  A (A &) = default;
+  A &operator= (A &) = default;
+};
+
+template <class T>
+struct B {
+  T b;
+  explicit B ();
+  B (const B &) = default;             // { dg-error "explicitly defaulted 
copy constructor is implicitly deleted because its declared type does not match 
the type of an implicit copy constructor" "" { target c++17_down } }
+                                       // { dg-message "note: expected 
signature: 'constexpr B<A>::B\\\(B<A>\\\&\\\)'" "" { target c++17_down } .-1 }
+  B &operator= (const B &) = default;  // { dg-error "explicitly defaulted 
copy assignment operator is implicitly deleted because its declared type does 
not match the type of an implicit copy assignment operator" "" { target 
c++17_down } }
+                                       // { dg-message "note: expected 
signature: 'constexpr B<A>\\\& B<A>::operator=\\\(B<A>\\\&\\\)'" "" { target { 
c++14 && c++17_down } } .-1 }
+                                       // { dg-message "note: expected 
signature: 'B<A>\\\& B<A>::operator=\\\(B<A>\\\&\\\)'" "" { target c++11_only } 
.-2 }
+};
+
+B <A> c;
diff --git a/gcc/testsuite/g++.dg/cpp29/defaulted4.C 
b/gcc/testsuite/g++.dg/cpp29/defaulted4.C
new file mode 100644
index 000000000000..ad9a7818ee12
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp29/defaulted4.C
@@ -0,0 +1,40 @@
+// P2953R5 - Adding restrictions to defaulted assignment operator functions
+// { dg-do compile { target c++11 } }
+
+struct A {
+  A (A &);
+};
+
+struct B {
+  A a;
+  B (const B &) = default;     // { dg-error "explicitly defaulted copy 
constructor is implicitly deleted because its declared type does not match the 
type of an implicit copy constructor" "" { target c++17_down } }
+// { dg-warning "explicitly defaulted copy constructor is implicitly deleted 
because its declared type does not match the type of an implicit copy 
constructor" "" { target c++20 } .-1 }
+}; // { dg-message "note: expected signature: 'B::B\\\(B\\\&\\\)'" "" { target 
*-*-* } .-2 }
+
+struct C {
+  C (C &);
+};
+
+struct D {
+  C a;
+  D &&operator= (const D &) = default; // { dg-error "defaulted declaration 
'D\\\&\\\& D::operator=\\\(const D\\\&\\\)' does not match the expected 
signature" }
+}; // { dg-message "note: expected signature: 'constexpr D\\\& 
D::operator=\\\(const D\\\&\\\)'" "" { target c++14 } .-1 }
+// { dg-message "note: expected signature: 'D\\\& D::operator=\\\(const 
D\\\&\\\)'" "" { target c++11_only } .-2 }
+
+struct E {
+  E &operator= (const E &) && = default;       // { dg-error "defaulted 
declaration 'E\\\& E::operator=\\\(const E\\\&\\\) \\\&\\\&' does not match the 
expected signature" "" { target c++29 } }
+}; // { dg-message "note: expected signature: 'constexpr E\\\& 
E::operator=\\\(const E\\\&\\\)'" "" { target c++29 } .-1 }
+
+struct F {
+  F &operator= (const F &) const = default;    // { dg-error "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target c++17_down } }
+// { dg-warning "explicitly defaulted copy assignment operator is implicitly 
deleted because its declared type does not match the type of an implicit copy 
assignment operator" "" { target { c++20 && c++26_down } } .-1 }
+// { dg-error "defaulted declaration 'F\\\& F::operator=\\\(const F\\\&\\\) 
const' does not match the expected signature" "" { target c++29 } .-2 }
+}; // { dg-message "note: expected signature: 'constexpr F\\\& 
F::operator=\\\(const F\\\&\\\)'" "" { target c++14 } .-3 }
+// { dg-message "note: expected signature: 'F\\\& F::operator=\\\(const 
F\\\&\\\)'" "" { target c++11_only } .-4 }
+
+struct G {
+  G &operator= (const G &&) = default; // { dg-error "explicitly defaulted 
move assignment operator is implicitly deleted because its declared type does 
not match the type of an implicit move assignment operator" "" { target 
c++17_down } }
+// { dg-warning "explicitly defaulted move assignment operator is implicitly 
deleted because its declared type does not match the type of an implicit move 
assignment operator" "" { target { c++20 && c++26_down } } .-1 }
+// { dg-error "defaulted declaration 'G\\\& G::operator=\\\(const 
G\\\&\\\&\\\)' does not match the expected signature" "" { target c++29 } .-2 }
+}; // { dg-message "note: expected signature: 'constexpr G\\\& 
G::operator=\\\(G\\\&\\\&\\\)'" "" { target c++14 } .-3 }
+// { dg-message "note: expected signature: 'G\\\& 
G::operator=\\\(G\\\&\\\&\\\)'" "" { target c++11_only } .-4 }
diff --git a/gcc/testsuite/g++.dg/cpp29/defaulted5.C 
b/gcc/testsuite/g++.dg/cpp29/defaulted5.C
new file mode 100644
index 000000000000..d3b4b7d16791
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp29/defaulted5.C
@@ -0,0 +1,102 @@
+// P2953R5 - Adding restrictions to defaulted assignment operator functions
+// { dg-do compile { target c++11 } }
+
+struct A {
+  A &operator= (const A &) & = default;
+};
+
+struct B {
+  B &&operator= (const B &) & = default;       // { dg-error "defaulted 
declaration 'B\\\&\\\& B::operator=\\\(const B\\\&\\\) \\\&' does not match the 
expected signature" }
+};                                             // { dg-message "note: expected 
signature: 'constexpr B\\\& B::operator=\\\(const B\\\&\\\)'" "" { target c++14 
} .-1 }
+                                               // { dg-message "note: expected 
signature: 'B\\\& B::operator=\\\(const B\\\&\\\)'" "" { target c++11_only } 
.-2 }
+
+struct C {
+  C &operator= (const C &) noexcept (true) = default;
+};
+
+struct D {
+  D &operator= (const D &) noexcept (false) = default;
+};
+
+struct E {
+  E &operator= (E &) = default;
+};
+
+struct F {
+  F (F &);
+};
+
+struct G {
+  F a;
+  G &operator= (const G &) & = default;
+};
+
+struct H {
+  F a;
+  H &operator= (const H &) && = default;       // { dg-error "defaulted 
declaration 'H\\\& H::operator=\\\(const H\\\&\\\) \\\&\\\&' does not match the 
expected signature" "" { target c++29 } }
+};                                             // { dg-message "note: expected 
signature: 'constexpr H\\\& H::operator=\\\(const H\\\&\\\)'" "" { target c++29 
} .-1 }
+
+struct I {
+  F a;
+  I &operator= (const I &) const = default;    // { dg-error "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target c++17_down } }
+};                                             // { dg-message "note: expected 
signature: 'constexpr I\\\& I::operator=\\\(const I\\\&\\\)'" "" { target c++14 
} .-1 }
+                                               // { dg-message "note: expected 
signature: 'I\\\& I::operator=\\\(const I\\\&\\\)'" "" { target c++11_only } 
.-2 }
+                                               // { dg-warning "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target { c++20 && c++26_down } } .-3 }
+                                               // { dg-error "defaulted 
declaration 'I\\\& I::operator=\\\(const I\\\&\\\) const' does not match the 
expected signature" "" { target c++29 } .-4 }
+
+struct J {
+  F a;
+  J &operator= (const J &) volatile = default; // { dg-error "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target c++17_down } }
+};                                             // { dg-message "note: expected 
signature: 'constexpr J\\\& J::operator=\\\(const J\\\&\\\)'" "" { target c++14 
} .-1 }
+                                               // { dg-message "note: expected 
signature: 'J\\\& J::operator=\\\(const J\\\&\\\)'" "" { target c++11_only } 
.-2 }
+                                               // { dg-warning "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target { c++20 && c++26_down } } .-3 }
+                                               // { dg-error "defaulted 
declaration 'J\\\& J::operator=\\\(const J\\\&\\\) volatile' does not match the 
expected signature" "" { target c++29 } .-4 }
+
+struct K {
+  F a;
+  K &operator= (const K &);
+};
+
+K &K::operator= (const K &) = default;
+
+struct L {
+  L &&operator= (L &) = default;               // { dg-error "defaulted 
declaration 'L\\\&\\\& L::operator=\\\(L\\\&\\\)' does not match the expected 
signature" }
+};                                             // { dg-message "note: expected 
signature: 'constexpr L\\\& L::operator=\\\(L\\\&\\\)'" "" { target c++14 } .-1 
}
+                                               // { dg-message "note: expected 
signature: 'L\\\& L::operator=\\\(L\\\&\\\)'" "" { target c++11_only } .-2 }
+
+struct M {
+  M &operator= (M &);
+};
+
+struct N {
+  M a;
+  N &operator= (const N &) & = default;                // { dg-error 
"explicitly defaulted copy assignment operator is implicitly deleted because 
its declared type does not match the type of an implicit copy assignment 
operator" "" { target c++17_down } }
+};                                             // { dg-message "note: expected 
signature: 'N\\\& N::operator=\\\(N\\\&\\\)'" "" { target *-*-* } .-1 }
+                                               // { dg-warning "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target c++20 } .-2 }
+
+struct O {
+  M a;
+  O &operator= (const O &) && = default;       // { dg-error "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target { c++17_down } } }
+};                                             // { dg-message "note: expected 
signature: 'O\\\& O::operator=\\\(O\\\&\\\)'" "" { target *-*-* } .-1 }
+                                               // { dg-warning "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target { c++20 && c++26_down } } .-2 }
+                                               // { dg-error "defaulted 
declaration 'O\\\& O::operator=\\\(const O\\\&\\\) \\\&\\\&' does not match the 
expected signature" "" { target c++29 } .-3 }
+
+struct P {
+  M a;
+  P &operator= (const P &) const = default;    // { dg-error "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target c++17_down } }
+};                                             // { dg-message "note: expected 
signature: 'P\\\& P::operator=\\\(P\\\&\\\)'" "" { target *-*-* } .-1 }
+                                               // { dg-warning "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target c++20 } .-2 }
+
+struct Q {
+  M a;
+  Q &operator= (const Q &) volatile = default; // { dg-error "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target c++17_down } }
+};                                             // { dg-message "note: expected 
signature: 'Q\\\& Q::operator=\\\(Q\\\&\\\)'" "" { target *-*-* } .-1 }
+                                               // { dg-warning "explicitly 
defaulted copy assignment operator is implicitly deleted because its declared 
type does not match the type of an implicit copy assignment operator" "" { 
target c++20 } .-2 }
+
+struct R {
+  M a;
+  R &operator= (const R &);
+};
+
+R &R::operator= (const R &) = default;         // { dg-error "binding 
reference of type 'M\\\&' to 'const M' discards qualifiers" }
+                                               // { dg-message "note: 'R\\\& 
R::operator=\\\(const R\\\&\\\)' is implicitly deleted because the default 
definition would be ill-formed:" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/cpp29/defaulted6.C 
b/gcc/testsuite/g++.dg/cpp29/defaulted6.C
new file mode 100644
index 000000000000..99116655e870
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp29/defaulted6.C
@@ -0,0 +1,33 @@
+// P2953R5 - Adding restrictions to defaulted assignment operator functions
+// { dg-do compile { target c++23 } }
+
+struct A {
+  A &operator= (this A &, const A &) = default;
+};
+
+struct D {
+  D &&operator= (this D &, const D &) = default;       // { dg-error 
"defaulted declaration 'D\\\&\\\& D::operator=\\\(this D\\\&, const D\\\&\\\)' 
does not match the expected signature" }
+};                                                     // { dg-message "note: 
expected signature: 'constexpr D\\\& D::operator=\\\(const D\\\&\\\)'" "" { 
target *-*-* } .-1 }
+
+struct E {
+  E &operator= (this E, const E &) = default;          // { dg-error "'E\\\& 
E::operator=\\\(this E, const E\\\&\\\)' cannot be defaulted" }
+};
+
+struct F {
+  F (F &);
+};
+
+struct G {
+  F a;
+  G &operator= (this G &, const G &) = default;
+};
+
+struct H {
+  F a;
+  H &operator= (this H &, H &) = default;
+};
+
+struct I {
+  F a;
+  I &operator= (this I, const I &) = default;          // { dg-error "'I\\\& 
I::operator=\\\(this I, const I\\\&\\\)' cannot be defaulted" }
+};

Reply via email to