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.
--- gcc/cp/method.cc.jj 2026-07-02 09:55:05.912561353 +0200
+++ gcc/cp/method.cc 2026-07-03 09:33:00.297535236 +0200
@@ -3753,8 +3753,7 @@ implicitly_declare_fn (special_function_
/* 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
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
/*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))
{
--- gcc/testsuite/g++.dg/cpp0x/defaulted51.C.jj 2026-07-02 09:55:05.921561236
+0200
+++ gcc/testsuite/g++.dg/cpp0x/defaulted51.C 2026-07-03 09:16:39.020429028
+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-07-02 09:55:05.921561236
+0200
+++ gcc/testsuite/g++.dg/cpp0x/defaulted55.C 2026-07-03 09:16:39.020641894
+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-07-02 09:55:05.921561236 +0200
+++ gcc/testsuite/g++.dg/cpp0x/defaulted56.C 2026-07-03 09:16:39.020808859
+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-07-02 09:55:05.921561236
+0200
+++ gcc/testsuite/g++.dg/cpp0x/defaulted57.C 2026-07-03 09:16:39.020943183
+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-07-02 09:55:05.921561236
+0200
+++ gcc/testsuite/g++.dg/cpp0x/defaulted63.C 2026-07-03 09:16:39.021068012
+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-07-02 09:55:05.921561236
+0200
+++ gcc/testsuite/g++.dg/cpp0x/defaulted64.C 2026-07-03 09:16:39.021178000
+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-07-02 09:55:05.922561222
+0200
+++ gcc/testsuite/g++.dg/cpp0x/defaulted65.C 2026-07-03 09:16:39.021353776
+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-07-02 09:55:05.922561222
+0200
+++ gcc/testsuite/g++.dg/cpp0x/defaulted66.C 2026-07-03 09:16:39.021516903
+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-07-02 09:55:05.922561222
+0200
+++ gcc/testsuite/g++.dg/cpp0x/defaulted67.C 2026-07-03 09:16:39.021632391
+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-07-02 09:55:05.922561222
+0200
+++ gcc/testsuite/g++.dg/cpp0x/defaulted68.C 2026-07-03 09:16:39.021757498
+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-07-02 09:55:05.922561222
+0200
+++ gcc/testsuite/g++.dg/cpp1y/defaulted2.C 2026-07-03 09:16:39.021898004
+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-03 09:16:39.022085110
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted1.C 2026-07-03 09:16:39.022085110
+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-03 09:16:39.022195882
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted2.C 2026-07-03 09:16:39.022195882
+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-03 09:16:39.022282946
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted3.C 2026-07-03 09:16:39.022282946
+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-03 09:16:39.022376429
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted4.C 2026-07-03 09:16:39.022376429
+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-03 09:16:39.022472735
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted5.C 2026-07-03 10:12:29.504071695
+0200
@@ -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 }
--- gcc/testsuite/g++.dg/cpp29/defaulted6.C.jj 2026-07-03 09:16:39.022579237
+0200
+++ gcc/testsuite/g++.dg/cpp29/defaulted6.C 2026-07-03 09:16:39.022579237
+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