Hi!

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.

There is one case in cpp29/defaulted5.C test that I believe is wrong,
struct F {
  F (F &);
};

struct K {
  F a;
  K &operator= (const K &);
};

K &K::operator= (const K &) = default;
This is accepted by both g++ and clang++ before C++29 and by
g++ even in C++29 with this patch, but I think the implicitly
defined copy ctor in that case would be K &operator= (K &);
if it wasn't explicitly declared, and is not in the C++26
https://eel.is/c++draft/dcl.fct.def.default#2 (2.1) to (2.4)
exception list, so I think https://eel.is/c++draft/dcl.fct.def.default#2.7
should apply and make it ill-formed.  But we don't actually
pass imp_const that would indicate that it should be non-const,
we pass unknown state when it is defaulted outside of class.
And for C++29, I think it should be also ill-formed.

So far tested on make check-g++ with 98,11,14,17,20,23,26,29 modes,
ok for trunk if it passes full bootstrap/regtest?

2026-07-01  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.
        (defaulted_late_check): For C++29 also call
        maybe_delete_defaulted_fn if FUNCTION_RVALUE_QUALIFIED.

        * 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.

--- gcc/cp/method.cc.jj 2026-07-01 11:54:35.403958883 +0200
+++ gcc/cp/method.cc    2026-07-01 13:12:57.375384759 +0200
@@ -3768,6 +3768,22 @@ maybe_delete_defaulted_fn (tree fn, tree
     = 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));
+  auto bad_xobj_parm = [](tree fn, tree implicit_fn) {
+    tree fn_parms = TYPE_ARG_TYPES (TREE_TYPE (fn));
+    if (!DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+      return false;
+
+    tree fn_obj_ref_type = TREE_VALUE (fn_parms);
+    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 true;
+    return false;
+  };
+
   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"  */
@@ -3778,7 +3794,16 @@ maybe_delete_defaulted_fn (tree fn, tree
           || !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))
+             || bad_xobj_parm (fn, implicit_fn)
+             || 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);
@@ -3892,7 +3917,8 @@ defaulted_late_check (tree fn, tristate
 
   if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
                    TREE_TYPE (TREE_TYPE (implicit_fn)))
-      || !compare_fn_params (fn, implicit_fn))
+      || !compare_fn_params (fn, implicit_fn)
+      || (cxx_dialect >= cxx29 && FUNCTION_RVALUE_QUALIFIED (TREE_TYPE (fn))))
     maybe_delete_defaulted_fn (fn, implicit_fn);
 
   if (DECL_DELETED_FN (implicit_fn))
--- gcc/testsuite/g++.dg/cpp0x/defaulted51.C.jj 2026-03-27 10:17:15.447309313 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/defaulted51.C    2026-07-01 13:18:53.030968435 
+0200
@@ -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
--- gcc/testsuite/g++.dg/cpp0x/defaulted55.C.jj 2026-03-27 10:17:15.448309297 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/defaulted55.C    2026-07-01 13:19:27.363542000 
+0200
@@ -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;
 };
 
--- gcc/testsuite/g++.dg/cpp0x/defaulted56.C.jj 2026-03-27 10:17:15.448309297 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/defaulted56.C    2026-07-01 13:20:27.757791856 
+0200
@@ -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
--- gcc/testsuite/g++.dg/cpp0x/defaulted57.C.jj 2026-03-27 10:17:15.448309297 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/defaulted57.C    2026-07-01 13:23:52.404249986 
+0200
@@ -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
--- gcc/testsuite/g++.dg/cpp0x/defaulted63.C.jj 2026-03-27 10:17:15.448309297 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/defaulted63.C    2026-07-01 13:25:16.841201212 
+0200
@@ -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 }
 };
--- gcc/testsuite/g++.dg/cpp0x/defaulted64.C.jj 2026-03-27 10:17:15.448309297 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/defaulted64.C    2026-07-01 13:26:07.242575189 
+0200
@@ -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;
 };
--- gcc/testsuite/g++.dg/cpp0x/defaulted65.C.jj 2026-03-27 10:17:15.448309297 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/defaulted65.C    2026-07-01 13:26:49.260053298 
+0200
@@ -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 }
 };
--- gcc/testsuite/g++.dg/cpp0x/defaulted66.C.jj 2026-03-27 10:17:15.448309297 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/defaulted66.C    2026-07-01 13:37:33.278047365 
+0200
@@ -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 {
--- gcc/testsuite/g++.dg/cpp0x/defaulted67.C.jj 2026-03-27 10:17:15.448309297 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/defaulted67.C    2026-07-01 13:29:08.256326856 
+0200
@@ -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 } }
 };
--- gcc/testsuite/g++.dg/cpp0x/defaulted68.C.jj 2026-03-27 10:17:15.448309297 
+0100
+++ gcc/testsuite/g++.dg/cpp0x/defaulted68.C    2026-07-01 13:30:22.018410671 
+0200
@@ -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 } }
 };
--- gcc/testsuite/g++.dg/cpp1y/defaulted2.C.jj  2026-03-27 10:17:15.576307208 
+0100
+++ gcc/testsuite/g++.dg/cpp1y/defaulted2.C     2026-07-01 13:39:02.618936132 
+0200
@@ -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;
 }
--- gcc/testsuite/g++.dg/cpp29/defaulted1.C.jj  2026-07-01 13:52:40.149818219 
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted1.C     2026-07-01 14:45:53.310313080 
+0200
@@ -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 }
--- gcc/testsuite/g++.dg/cpp29/defaulted2.C.jj  2026-07-01 14:56:57.971072514 
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted2.C     2026-07-01 14:59:10.010426134 
+0200
@@ -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 &>, "");
--- gcc/testsuite/g++.dg/cpp29/defaulted3.C.jj  2026-07-01 15:47:28.122272984 
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted3.C     2026-07-01 15:54:14.989191390 
+0200
@@ -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;
--- gcc/testsuite/g++.dg/cpp29/defaulted4.C.jj  2026-07-01 16:00:09.822758856 
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted4.C     2026-07-01 16:14:52.238724393 
+0200
@@ -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 }
--- gcc/testsuite/g++.dg/cpp29/defaulted5.C.jj  2026-07-01 16:16:42.217348064 
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted5.C     2026-07-01 16:50:27.952012076 
+0200
@@ -0,0 +1,65 @@
+// 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 }
--- gcc/testsuite/g++.dg/cpp29/defaulted6.C.jj  2026-07-01 16:55:41.720088112 
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted6.C     2026-07-01 17:10:58.424628776 
+0200
@@ -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" }
+};

        Jakub

Reply via email to