Message ID | 20240911165411.459519-1-polacek@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: deleting explicitly-defaulted functions [PR116162] | expand |
On 9/11/24 12:54 PM, Marek Polacek wrote: > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > -- >8 -- > This PR points out the we're not implementing [dcl.fct.def.default] > properly. Consider e.g. > > struct C { > C(const C&&) = default; > }; > > where we wrongly emit an error, but the move ctor should be just =deleted. > According to [dcl.fct.def.default], if the type of the special member > function differs from the type of the corresponding special member function > that would have been implicitly declared in a way other than as allowed > by 2.1-4, the function is defined as deleted. There's an exception for > assignment operators in which case the program is ill-formed. > > clang++ has a warning for when we delete an explicitly-defaulted function > so this patch adds it too. I'm also downgrading an error to a pedwarn > in C++17 since the code compiles in C++20. > > PR c++/116162 > > gcc/c-family/ChangeLog: > > * c.opt (Wdefaulted-function-deleted): New. > > gcc/cp/ChangeLog: > > * class.cc (check_bases_and_members): Call delete_defaulted_fn to set > DECL_DELETED_FN. > * cp-tree.h (delete_defaulted_fn): Declare. > * method.cc (delete_defaulted_fn): New. > (defaulted_late_check): Call delete_defaulted_fn instead of giving > an error, unless the code is ill-formed. Change error to pedwarn. > > gcc/ChangeLog: > > * doc/invoke.texi: Document -Wdefaulted-function-deleted. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp0x/defaulted15.C: Add dg-warning. > * g++.dg/cpp0x/defaulted51.C: Likewise. > * g++.dg/cpp0x/defaulted52.C: Likewise. > * g++.dg/cpp0x/defaulted53.C: Likewise. > * g++.dg/cpp0x/defaulted54.C: Likewise. > * g++.dg/cpp0x/defaulted56.C: Likewise. > * g++.dg/cpp0x/defaulted57.C: Likewise. > * g++.dg/cpp0x/defaulted58.C: Likewise. > * g++.dg/cpp0x/defaulted59.C: Likewise. > * g++.dg/cpp0x/defaulted63.C: New test. > * g++.dg/cpp0x/defaulted64.C: New test. > * g++.dg/cpp0x/defaulted65.C: New test. > * g++.dg/cpp23/defaulted1.C: New test. > --- > gcc/c-family/c.opt | 4 ++ > gcc/cp/class.cc | 2 +- > gcc/cp/cp-tree.h | 1 + > gcc/cp/method.cc | 90 +++++++++++++++++++++--- > gcc/doc/invoke.texi | 9 +++ > gcc/testsuite/g++.dg/cpp0x/defaulted15.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/defaulted51.C | 3 +- > gcc/testsuite/g++.dg/cpp0x/defaulted52.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/defaulted53.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/defaulted54.C | 1 + > gcc/testsuite/g++.dg/cpp0x/defaulted56.C | 6 +- > gcc/testsuite/g++.dg/cpp0x/defaulted57.C | 6 +- > gcc/testsuite/g++.dg/cpp0x/defaulted58.C | 1 + > gcc/testsuite/g++.dg/cpp0x/defaulted59.C | 2 +- > gcc/testsuite/g++.dg/cpp0x/defaulted63.C | 39 ++++++++++ > gcc/testsuite/g++.dg/cpp0x/defaulted64.C | 27 +++++++ > gcc/testsuite/g++.dg/cpp0x/defaulted65.C | 25 +++++++ > gcc/testsuite/g++.dg/cpp23/defaulted1.C | 23 ++++++ > 18 files changed, 226 insertions(+), 19 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted63.C > create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted64.C > create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted65.C > create mode 100644 gcc/testsuite/g++.dg/cpp23/defaulted1.C > > diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt > index 491aa02e1a3..f5136fd2341 100644 > --- a/gcc/c-family/c.opt > +++ b/gcc/c-family/c.opt > @@ -619,6 +619,10 @@ Wdeclaration-missing-parameter-type > C ObjC Var(warn_declaration_missing_parameter) Warning Init(1) > Warn for missing parameter types in function declarations. > > +Wdefaulted-function-deleted > +C++ ObjC++ Var(warn_defaulted_fn_deleted) Init(1) Warning > +Warn when an explicitly defaulted function is deleted. > + > Wdelete-incomplete > C++ ObjC++ Var(warn_delete_incomplete) Init(1) Warning > Warn when deleting a pointer to incomplete type. > diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc > index 950d83b0ea4..a4fdf7f9d11 100644 > --- a/gcc/cp/class.cc > +++ b/gcc/cp/class.cc > @@ -6506,7 +6506,7 @@ check_bases_and_members (tree t) > /* If the function is defaulted outside the class, we just > give the synthesis error. Core Issue #1331 says this is > no longer ill-formed, it is defined as deleted instead. */ > - DECL_DELETED_FN (fn) = true; > + delete_defaulted_fn (fn); > } > defaulted_late_check (fn); > } > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 7baa2ccbe1e..65295b3326d 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -6929,6 +6929,7 @@ extern bool type_build_ctor_call (tree); > extern bool type_build_dtor_call (tree); > extern void explain_non_literal_class (tree); > extern void inherit_targ_abi_tags (tree); > +extern void delete_defaulted_fn (tree); > extern void defaulted_late_check (tree); > extern bool defaultable_fn_check (tree); > extern void check_abi_tags (tree); > diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc > index 68a776d2c5a..92fb1a0b238 100644 > --- a/gcc/cp/method.cc > +++ b/gcc/cp/method.cc > @@ -3503,6 +3503,53 @@ implicitly_declare_fn (special_function_kind kind, tree type, > return fn; > } > > +/* Mark an explicitly defaulted function FN as =deleted and warn. */ > + > +void > +delete_defaulted_fn (tree fn) > +{ > + DECL_DELETED_FN (fn) = true; > + if (warn_defaulted_fn_deleted) > + { > + auto_diagnostic_group d; > + const char *wmsg, *imsg; > + switch (special_function_p (fn)) > + { > + case sfk_copy_constructor: > + wmsg = G_("explicitly defaulted copy constructor is " > + "implicitly deleted"); > + imsg = G_("function is implicitly deleted because its declared type " > + "does not match the type of an implicit copy constructor"); > + break; > + case sfk_move_constructor: > + wmsg = G_("explicitly defaulted move constructor is " > + "implicitly deleted"); > + imsg = G_("function is implicitly deleted because its declared type " > + "does not match the type of an implicit move constructor"); > + break; > + case sfk_copy_assignment: > + wmsg = G_("explicitly defaulted copy assignment operator is " > + "implicitly deleted"); > + imsg = G_("function is implicitly deleted because its declared type " > + "does not match the type of an implicit copy assignment " > + "operator"); > + break; > + case sfk_move_assignment: > + wmsg = G_("explicitly defaulted move assignment operator is " > + "implicitly deleted"); > + imsg = G_("function is implicitly deleted because its declared type " > + "does not match the type of an implicit move assignment " > + "operator"); > + break; > + default: > + gcc_unreachable (); > + } > + if (warning_at (DECL_SOURCE_LOCATION (fn), > + OPT_Wdefaulted_function_deleted, wmsg)) > + inform (DECL_SOURCE_LOCATION (fn), imsg); > + } > +} > + > /* Gives any errors about defaulted functions which need to be deferred > until the containing class is complete. */ > > @@ -3555,15 +3602,40 @@ defaulted_late_check (tree fn) > return compparms (fn_parms, implicit_fn_parms); > }; > > - if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)), > - TREE_TYPE (TREE_TYPE (implicit_fn))) > - || !compare_fn_params (fn, implicit_fn)) > - { > - auto_diagnostic_group d; > - error ("defaulted declaration %q+D does not match the " > - "expected signature", fn); > - inform (DECL_SOURCE_LOCATION (fn), > - "expected signature: %qD", implicit_fn); > + const bool rettypes_same_p > + = same_type_p (TREE_TYPE (TREE_TYPE (fn)), > + TREE_TYPE (TREE_TYPE (implicit_fn))); > + if (!rettypes_same_p || !compare_fn_params (fn, implicit_fn)) > + { > + tree parmtype > + = TREE_VALUE (DECL_XOBJ_MEMBER_FUNCTION_P (fn) > + ? TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn))) > + : FUNCTION_FIRST_USER_PARMTYPE (fn)); > + const bool illformed_p > + /* [dcl.fct.def.default] "if F1 is an assignment operator"... */ > + = (SFK_ASSIGN_P (kind) > + /* "and the return type of F1 differs from the return type of F2" */ > + && (!rettypes_same_p > + /* "or F1's non-object parameter type is not a reference, > + the program is ill-formed" */ > + || !TYPE_REF_P (parmtype))); > + > + if (!illformed_p > + && cxx_dialect >= cxx20 > + && !DECL_ARTIFICIAL (fn) > + && DECL_DEFAULTED_IN_CLASS_P (fn)) > + delete_defaulted_fn (fn); > + else > + { > + auto_diagnostic_group d; > + /* We used to emit a hard error, so this uses 0 rather than > + OPT_Wpedantic. */ > + if (pedwarn (DECL_SOURCE_LOCATION (fn), 0, > + "defaulted declaration %q+D does not match the " > + "expected signature", fn)) > + inform (DECL_SOURCE_LOCATION (fn), > + "expected signature: %qD", implicit_fn); This should also depend on -Wdefaulted-function-deleted, and set DECL_DELETED_FN. And the C++20 case should show the expected signature. Really, the two cases should share the same code, only the diagnostic kind should change. Jason
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 491aa02e1a3..f5136fd2341 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -619,6 +619,10 @@ Wdeclaration-missing-parameter-type C ObjC Var(warn_declaration_missing_parameter) Warning Init(1) Warn for missing parameter types in function declarations. +Wdefaulted-function-deleted +C++ ObjC++ Var(warn_defaulted_fn_deleted) Init(1) Warning +Warn when an explicitly defaulted function is deleted. + Wdelete-incomplete C++ ObjC++ Var(warn_delete_incomplete) Init(1) Warning Warn when deleting a pointer to incomplete type. diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index 950d83b0ea4..a4fdf7f9d11 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -6506,7 +6506,7 @@ check_bases_and_members (tree t) /* If the function is defaulted outside the class, we just give the synthesis error. Core Issue #1331 says this is no longer ill-formed, it is defined as deleted instead. */ - DECL_DELETED_FN (fn) = true; + delete_defaulted_fn (fn); } defaulted_late_check (fn); } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 7baa2ccbe1e..65295b3326d 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6929,6 +6929,7 @@ extern bool type_build_ctor_call (tree); extern bool type_build_dtor_call (tree); extern void explain_non_literal_class (tree); extern void inherit_targ_abi_tags (tree); +extern void delete_defaulted_fn (tree); extern void defaulted_late_check (tree); extern bool defaultable_fn_check (tree); extern void check_abi_tags (tree); diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index 68a776d2c5a..92fb1a0b238 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -3503,6 +3503,53 @@ implicitly_declare_fn (special_function_kind kind, tree type, return fn; } +/* Mark an explicitly defaulted function FN as =deleted and warn. */ + +void +delete_defaulted_fn (tree fn) +{ + DECL_DELETED_FN (fn) = true; + if (warn_defaulted_fn_deleted) + { + auto_diagnostic_group d; + const char *wmsg, *imsg; + switch (special_function_p (fn)) + { + case sfk_copy_constructor: + wmsg = G_("explicitly defaulted copy constructor is " + "implicitly deleted"); + imsg = G_("function is implicitly deleted because its declared type " + "does not match the type of an implicit copy constructor"); + break; + case sfk_move_constructor: + wmsg = G_("explicitly defaulted move constructor is " + "implicitly deleted"); + imsg = G_("function is implicitly deleted because its declared type " + "does not match the type of an implicit move constructor"); + break; + case sfk_copy_assignment: + wmsg = G_("explicitly defaulted copy assignment operator is " + "implicitly deleted"); + imsg = G_("function is implicitly deleted because its declared type " + "does not match the type of an implicit copy assignment " + "operator"); + break; + case sfk_move_assignment: + wmsg = G_("explicitly defaulted move assignment operator is " + "implicitly deleted"); + imsg = G_("function is implicitly deleted because its declared type " + "does not match the type of an implicit move assignment " + "operator"); + break; + default: + gcc_unreachable (); + } + if (warning_at (DECL_SOURCE_LOCATION (fn), + OPT_Wdefaulted_function_deleted, wmsg)) + inform (DECL_SOURCE_LOCATION (fn), imsg); + } +} + /* Gives any errors about defaulted functions which need to be deferred until the containing class is complete. */ @@ -3555,15 +3602,40 @@ defaulted_late_check (tree fn) return compparms (fn_parms, implicit_fn_parms); }; - if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)), - TREE_TYPE (TREE_TYPE (implicit_fn))) - || !compare_fn_params (fn, implicit_fn)) - { - auto_diagnostic_group d; - error ("defaulted declaration %q+D does not match the " - "expected signature", fn); - inform (DECL_SOURCE_LOCATION (fn), - "expected signature: %qD", implicit_fn); + const bool rettypes_same_p + = same_type_p (TREE_TYPE (TREE_TYPE (fn)), + TREE_TYPE (TREE_TYPE (implicit_fn))); + if (!rettypes_same_p || !compare_fn_params (fn, implicit_fn)) + { + tree parmtype + = TREE_VALUE (DECL_XOBJ_MEMBER_FUNCTION_P (fn) + ? TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn))) + : FUNCTION_FIRST_USER_PARMTYPE (fn)); + const bool illformed_p + /* [dcl.fct.def.default] "if F1 is an assignment operator"... */ + = (SFK_ASSIGN_P (kind) + /* "and the return type of F1 differs from the return type of F2" */ + && (!rettypes_same_p + /* "or F1's non-object parameter type is not a reference, + the program is ill-formed" */ + || !TYPE_REF_P (parmtype))); + + if (!illformed_p + && cxx_dialect >= cxx20 + && !DECL_ARTIFICIAL (fn) + && DECL_DEFAULTED_IN_CLASS_P (fn)) + delete_defaulted_fn (fn); + else + { + auto_diagnostic_group d; + /* We used to emit a hard error, so this uses 0 rather than + OPT_Wpedantic. */ + if (pedwarn (DECL_SOURCE_LOCATION (fn), 0, + "defaulted declaration %q+D does not match the " + "expected signature", fn)) + inform (DECL_SOURCE_LOCATION (fn), + "expected signature: %qD", implicit_fn); + } } if (DECL_DELETED_FN (implicit_fn)) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index b9a86a9a181..ca9e745f62e 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -251,6 +251,7 @@ in the following sections. -Wcomma-subscript -Wconditionally-supported -Wno-conversion-null -Wctad-maybe-unsupported -Wctor-dtor-privacy -Wdangling-reference +-Wno-defaulted-function-deleted -Wno-delete-incomplete -Wdelete-non-virtual-dtor -Wno-deprecated-array-compare -Wdeprecated-copy -Wdeprecated-copy-dtor @@ -4797,6 +4798,14 @@ not caught by reference. @option{-Wcatch-value} is enabled by @option{-Wall}. @item -Wconditionally-supported @r{(C++ and Objective-C++ only)} Warn for conditionally-supported (C++11 [intro.defs]) constructs. +@opindex Wdefaulted-function-deleted +@opindex Wno-defaulted-function-deleted +@item -Wno-defaulted-function-deleted @r{(C++ and Objective-C++ only)} +Warn when an explicitly defaulted function is deleted by the compiler. +That can occur when the function's declared type does not match the type +of the function that would have been implicitly declared. This warning +is enabled by default. + @opindex Wdelete-incomplete @opindex Wno-delete-incomplete @item -Wno-delete-incomplete @r{(C++ and Objective-C++ only)} diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted15.C b/gcc/testsuite/g++.dg/cpp0x/defaulted15.C index 1e0b3545840..a2f7c438b95 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted15.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted15.C @@ -48,7 +48,7 @@ struct F struct G: public F { - G(const G&) = default; + G(const G&) = default; // { dg-warning "implicitly deleted" } }; struct H diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted51.C b/gcc/testsuite/g++.dg/cpp0x/defaulted51.C index 0a7d308707c..561f8998177 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted51.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted51.C @@ -4,7 +4,8 @@ template<int> struct A { A(); - A(volatile A&) = default; // { dg-error "defaulted" } + A(volatile A&) = default; // { dg-error "defaulted" "" { target c++17_down } } + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 } }; struct B diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted52.C b/gcc/testsuite/g++.dg/cpp0x/defaulted52.C index c617230b493..8a8a1cfb09e 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted52.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted52.C @@ -13,7 +13,7 @@ template<typename T> struct W { W(); // This should now compile and be =deleted. - W(const W&) = default; + W(const W&) = default; // { dg-warning "implicitly deleted" } T t; }; diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted53.C b/gcc/testsuite/g++.dg/cpp0x/defaulted53.C index 8147e7e2ad1..786051adae0 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted53.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted53.C @@ -14,7 +14,7 @@ struct R struct S { - S& operator=(const S&) = default; + S& operator=(const S&) = default; // { dg-warning "implicitly deleted" } M m; }; diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted54.C b/gcc/testsuite/g++.dg/cpp0x/defaulted54.C index f8ddc4e47ce..35ab94ff1f6 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted54.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted54.C @@ -11,6 +11,7 @@ template<typename T> struct W { W(); W(const W&) = default; // { dg-error "binding" } +// { dg-warning "implicitly deleted" "" { target *-*-* } .-1 } T t; }; diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted56.C b/gcc/testsuite/g++.dg/cpp0x/defaulted56.C index e7ce12c5566..0e36fd293f6 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted56.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted56.C @@ -11,12 +11,14 @@ struct S struct T { - constexpr T(volatile T &) = default; // { dg-error "defaulted" } + constexpr T(volatile T &) = default; // { dg-error "defaulted" "" { target c++17_down } } + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 } }; struct U { - constexpr U(const volatile U &) = default; // { dg-error "defaulted" } + constexpr U(const volatile U &) = default; // { dg-error "defaulted" "" { target c++17_down } } + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 } }; struct V diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted57.C b/gcc/testsuite/g++.dg/cpp0x/defaulted57.C index 37fb7dd6e1d..feca9662b4a 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted57.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted57.C @@ -11,12 +11,14 @@ struct S struct T { - T& operator=(volatile T &) = default; // { dg-error "defaulted" } + T& operator=(volatile T &) = default; // { dg-error "defaulted" "" { target c++17_down } } + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 } }; struct U { - U& operator=(const volatile U &) = default; // { dg-error "defaulted" } + U& operator=(const volatile U &) = default; // { dg-error "defaulted" "" { target c++17_down } } + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 } }; struct V diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted58.C b/gcc/testsuite/g++.dg/cpp0x/defaulted58.C index 920a4ad0c6d..e499414cd62 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted58.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted58.C @@ -11,6 +11,7 @@ template<typename T> struct W { W() = default; W& operator=(const W&) = default; // { dg-error "binding" } +// { dg-warning "implicitly deleted" "" { target *-*-* } .-1 } T t; }; diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted59.C b/gcc/testsuite/g++.dg/cpp0x/defaulted59.C index 4f871d7f5b1..1c13f4066dd 100644 --- a/gcc/testsuite/g++.dg/cpp0x/defaulted59.C +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted59.C @@ -8,5 +8,5 @@ struct M struct W : public M { - W(const W&) = default; + W(const W&) = default; // { dg-warning "implicitly deleted" } }; diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted63.C b/gcc/testsuite/g++.dg/cpp0x/defaulted63.C new file mode 100644 index 00000000000..99f92ff69c5 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted63.C @@ -0,0 +1,39 @@ +// PR c++/116162 +// { dg-do compile { target c++11 } } + +struct C0 { + C0(C0&) = default; +}; + +struct C1 { + C1(volatile C1&) = default; // { dg-warning "implicitly deleted" "" { target c++20 } } + // { dg-error "does not match" "" { target c++17_down } .-1 } +}; + +struct C2 { + C2(const C2&) = default; +}; + +struct C3 { + C3(const volatile C3&) = default; // { dg-warning "implicitly deleted" "" { target c++20 } } + // { dg-error "does not match" "" { target c++17_down } .-1 } +}; + +struct M0 { + M0(M0&&) = default; +}; + +struct M1 { + M1(const M1&&) = default; // { dg-warning "implicitly deleted" "" { target c++20 } } + // { dg-error "does not match" "" { target c++17_down } .-1 } +}; + +struct M2 { + M2(volatile M2&&) = default; // { dg-warning "implicitly deleted" "" { target c++20 } } + // { dg-error "does not match" "" { target c++17_down } .-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 } +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted64.C b/gcc/testsuite/g++.dg/cpp0x/defaulted64.C new file mode 100644 index 00000000000..f20030192c3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted64.C @@ -0,0 +1,27 @@ +// PR c++/116162 +// { dg-do compile { target c++11 } } + +struct M +{ + M& operator=(M&&); +}; + +struct R +{ + R& operator=(R&&) = default; + M m; +}; + +struct S +{ + S& operator=(const S&&) = default; // { dg-warning "implicitly deleted" "" { target c++20 } } + // { dg-error "does not match" "" { target c++17_down } .-1 } + + M m; +}; + +struct T +{ + T operator=(T&&) = default; // { dg-error "defaulted" } + M m; +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted65.C b/gcc/testsuite/g++.dg/cpp0x/defaulted65.C new file mode 100644 index 00000000000..88ca1d96084 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted65.C @@ -0,0 +1,25 @@ +// PR c++/116162 +// { dg-do compile { target c++11 } } + +struct S +{ + S& operator=(S &&) = default; +}; + +struct T +{ + T& operator=(volatile T &&) = default; // { dg-error "defaulted" "" { target c++17_down } } + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 } +}; + +struct U +{ + U& operator=(const volatile U &&) = default; // { dg-error "defaulted" "" { target c++17_down } } + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 } +}; + +struct V +{ + V& operator=(const V &&) = default; // { dg-error "defaulted" "" { target c++17_down } } + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/defaulted1.C b/gcc/testsuite/g++.dg/cpp23/defaulted1.C new file mode 100644 index 00000000000..00cf894fa1d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/defaulted1.C @@ -0,0 +1,23 @@ +// PR c++/116162 +// { dg-do compile { target c++23 } } + +struct M +{ + M& operator=(M&); +}; + +struct T +{ + // if F1 is an assignment operator, and the return type of F1 differs + // from the return type, the program is ill-formed. + T operator=(this T&, T&) = default; // { dg-error "defaulted" } + M m; +}; + +struct U +{ + // if F1's non-object parameter type is not a reference, the program + // is ill-formed. + U& operator=(this U&, U) = default; // { dg-error "defaulted" } + M m; +};