[v5] c++: deleting explicitly-defaulted functions [PR116162]
Checks
Context |
Check |
Description |
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 |
success
|
Test passed
|
linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
linaro-tcwg-bot/tcwg_gcc_check--master-arm |
success
|
Test passed
|
Commit Message
On Tue, Sep 17, 2024 at 12:50:46PM -0400, Jason Merrill wrote:
> On 9/16/24 7:14 PM, Marek Polacek wrote:
> > +/* Mark an explicitly defaulted function FN as =deleted and warn.
> > + IMPLICIT_FN is the corresponding special member function that
> > + would have been implicitly declared. */
> > +
> > +void
> > +maybe_delete_defaulted_fn (tree fn, tree implicit_fn)
> > +{
> > + if (DECL_ARTIFICIAL (fn) || !DECL_DEFAULTED_IN_CLASS_P (fn))
> > + return;
> > +
> > + DECL_DELETED_FN (fn) = true;
> > +
> > + if (!warn_defaulted_fn_deleted)
> > + return;
>
> The flag shouldn't affect the error cases; I'd drop this check.
Dropped.
> > + auto_diagnostic_group d;
> > + const special_function_kind kind = special_function_p (fn);
> > + tree parmtype
> > + = TREE_VALUE (DECL_XOBJ_MEMBER_FUNCTION_P (fn)
> > + ? TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn)))
> > + : FUNCTION_FIRST_USER_PARMTYPE (fn));
> > + const bool illformed_p
> > + /* [dcl.fct.def.default] "if F1 is an assignment operator"... */
> > + = (SFK_ASSIGN_P (kind)
> > + /* "and the return type of F1 differs from the return type of F2" */
> > + && (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
> > + TREE_TYPE (TREE_TYPE (implicit_fn)))
> > + /* "or F1's non-object parameter type is not a reference,
> > + the program is ill-formed" */
> > + || !TYPE_REF_P (parmtype)));
> > + /* Decide if we want to emit a pedwarn, error, or a warning. */
> > + diagnostic_t diag_kind;
> > + if (cxx_dialect >= cxx20)
> > + diag_kind = illformed_p ? DK_ERROR : DK_WARNING;
> > + else
> > + diag_kind = DK_PEDWARN;
>
> Error should be errors in all standard modes; it doesn't make sense to have
> a softer diagnostic in an older mode when it's ill-formed in all.
>
> Non-errors should be warnings or pedwarns depending on the standard mode.
Aaah, I misunderstood. Hopefully I got it right this time.
> > + /* Don't warn for template instantiations. */
> > + if (DECL_TEMPLATE_INSTANTIATION (fn) && diag_kind == DK_WARNING)
> > + return;
> > +
> > + const char *wmsg;
> > + switch (kind)
> > + {
> > + case sfk_copy_constructor:
> > + wmsg = G_("explicitly defaulted copy constructor 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 "
> > + "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 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 because its declared type does not match the type "
> > + "of an implicit move assignment operator");
> > + break;
> > + default:
> > + gcc_unreachable ();
> > + }
> > + if (emit_diagnostic (diag_kind, DECL_SOURCE_LOCATION (fn),
> > + OPT_Wdefaulted_function_deleted, wmsg))
>
> Let's not pass the OPT when DK_ERROR.
Done.
I've added new tests to cover -Wno-defaulted-function-deleted.
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.
When the code is ill-formed, we emit an error in all modes. Otherwise,
we emit a pedwarn in C++17 and a warning 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): Don't set DECL_DELETED_FN here,
leave it to defaulted_late_check.
* cp-tree.h (maybe_delete_defaulted_fn): Declare.
(defaulted_late_check): Add a tristate parameter.
* method.cc (maybe_delete_defaulted_fn): New.
(defaulted_late_check): Add a tristate parameter. Call
maybe_delete_defaulted_fn instead of giving an error.
gcc/ChangeLog:
* doc/invoke.texi: Document -Wdefaulted-function-deleted.
gcc/testsuite/ChangeLog:
* g++.dg/cpp0x/defaulted15.C: Add dg-warning/dg-error.
* 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/cpp0x/defaulted66.C: New test.
* g++.dg/cpp0x/defaulted67.C: New test.
* g++.dg/cpp0x/defaulted68.C: New test.
* g++.dg/cpp0x/defaulted69.C: New test.
* g++.dg/cpp23/defaulted1.C: New test.
---
gcc/c-family/c.opt | 4 +
gcc/cp/class.cc | 27 ++-----
gcc/cp/cp-tree.h | 3 +-
gcc/cp/method.cc | 98 +++++++++++++++++++++---
gcc/doc/invoke.texi | 9 +++
gcc/testsuite/g++.dg/cpp0x/defaulted15.C | 3 +-
gcc/testsuite/g++.dg/cpp0x/defaulted51.C | 2 +-
gcc/testsuite/g++.dg/cpp0x/defaulted52.C | 2 +-
gcc/testsuite/g++.dg/cpp0x/defaulted53.C | 3 +-
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 | 3 +-
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/cpp0x/defaulted66.C | 35 +++++++++
gcc/testsuite/g++.dg/cpp0x/defaulted67.C | 23 ++++++
gcc/testsuite/g++.dg/cpp0x/defaulted68.C | 35 +++++++++
gcc/testsuite/g++.dg/cpp0x/defaulted69.C | 24 ++++++
gcc/testsuite/g++.dg/cpp23/defaulted1.C | 23 ++++++
22 files changed, 359 insertions(+), 40 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/cpp0x/defaulted66.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted67.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted68.C
create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted69.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/defaulted1.C
base-commit: cee868ae0dfb5bef590a0a82426d32ba1d2b1a92
Comments
On 9/19/24 5:35 PM, Marek Polacek wrote:
> On Tue, Sep 17, 2024 at 12:50:46PM -0400, Jason Merrill wrote:
>> On 9/16/24 7:14 PM, Marek Polacek wrote:
>>> +/* Mark an explicitly defaulted function FN as =deleted and warn.
>>> + IMPLICIT_FN is the corresponding special member function that
>>> + would have been implicitly declared. */
>>> +
>>> +void
>>> +maybe_delete_defaulted_fn (tree fn, tree implicit_fn)
>>> +{
>>> + if (DECL_ARTIFICIAL (fn) || !DECL_DEFAULTED_IN_CLASS_P (fn))
>>> + return;
>>> +
>>> + DECL_DELETED_FN (fn) = true;
>>> +
>>> + if (!warn_defaulted_fn_deleted)
>>> + return;
>>
>> The flag shouldn't affect the error cases; I'd drop this check.
>
> Dropped.
>
>>> + auto_diagnostic_group d;
>>> + const special_function_kind kind = special_function_p (fn);
>>> + tree parmtype
>>> + = TREE_VALUE (DECL_XOBJ_MEMBER_FUNCTION_P (fn)
>>> + ? TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn)))
>>> + : FUNCTION_FIRST_USER_PARMTYPE (fn));
>>> + const bool illformed_p
>>> + /* [dcl.fct.def.default] "if F1 is an assignment operator"... */
>>> + = (SFK_ASSIGN_P (kind)
>>> + /* "and the return type of F1 differs from the return type of F2" */
>>> + && (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
>>> + TREE_TYPE (TREE_TYPE (implicit_fn)))
>>> + /* "or F1's non-object parameter type is not a reference,
>>> + the program is ill-formed" */
>>> + || !TYPE_REF_P (parmtype)));
>>> + /* Decide if we want to emit a pedwarn, error, or a warning. */
>>> + diagnostic_t diag_kind;
>>> + if (cxx_dialect >= cxx20)
>>> + diag_kind = illformed_p ? DK_ERROR : DK_WARNING;
>>> + else
>>> + diag_kind = DK_PEDWARN;
>>
>> Error should be errors in all standard modes; it doesn't make sense to have
>> a softer diagnostic in an older mode when it's ill-formed in all.
>>
>> Non-errors should be warnings or pedwarns depending on the standard mode.
>
> Aaah, I misunderstood. Hopefully I got it right this time.
>
>>> + /* Don't warn for template instantiations. */
>>> + if (DECL_TEMPLATE_INSTANTIATION (fn) && diag_kind == DK_WARNING)
>>> + return;
>>> +
>>> + const char *wmsg;
>>> + switch (kind)
>>> + {
>>> + case sfk_copy_constructor:
>>> + wmsg = G_("explicitly defaulted copy constructor 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 "
>>> + "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 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 because its declared type does not match the type "
>>> + "of an implicit move assignment operator");
>>> + break;
>>> + default:
>>> + gcc_unreachable ();
>>> + }
>>> + if (emit_diagnostic (diag_kind, DECL_SOURCE_LOCATION (fn),
>>> + OPT_Wdefaulted_function_deleted, wmsg))
>>
>> Let's not pass the OPT when DK_ERROR.
>
> Done.
>
> I've added new tests to cover -Wno-defaulted-function-deleted.
>
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
OK.
> -- >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.
>
> When the code is ill-formed, we emit an error in all modes. Otherwise,
> we emit a pedwarn in C++17 and a warning 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): Don't set DECL_DELETED_FN here,
> leave it to defaulted_late_check.
> * cp-tree.h (maybe_delete_defaulted_fn): Declare.
> (defaulted_late_check): Add a tristate parameter.
> * method.cc (maybe_delete_defaulted_fn): New.
> (defaulted_late_check): Add a tristate parameter. Call
> maybe_delete_defaulted_fn instead of giving an error.
>
> gcc/ChangeLog:
>
> * doc/invoke.texi: Document -Wdefaulted-function-deleted.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/cpp0x/defaulted15.C: Add dg-warning/dg-error.
> * 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/cpp0x/defaulted66.C: New test.
> * g++.dg/cpp0x/defaulted67.C: New test.
> * g++.dg/cpp0x/defaulted68.C: New test.
> * g++.dg/cpp0x/defaulted69.C: New test.
> * g++.dg/cpp23/defaulted1.C: New test.
> ---
> gcc/c-family/c.opt | 4 +
> gcc/cp/class.cc | 27 ++-----
> gcc/cp/cp-tree.h | 3 +-
> gcc/cp/method.cc | 98 +++++++++++++++++++++---
> gcc/doc/invoke.texi | 9 +++
> gcc/testsuite/g++.dg/cpp0x/defaulted15.C | 3 +-
> gcc/testsuite/g++.dg/cpp0x/defaulted51.C | 2 +-
> gcc/testsuite/g++.dg/cpp0x/defaulted52.C | 2 +-
> gcc/testsuite/g++.dg/cpp0x/defaulted53.C | 3 +-
> 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 | 3 +-
> 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/cpp0x/defaulted66.C | 35 +++++++++
> gcc/testsuite/g++.dg/cpp0x/defaulted67.C | 23 ++++++
> gcc/testsuite/g++.dg/cpp0x/defaulted68.C | 35 +++++++++
> gcc/testsuite/g++.dg/cpp0x/defaulted69.C | 24 ++++++
> gcc/testsuite/g++.dg/cpp23/defaulted1.C | 23 ++++++
> 22 files changed, 359 insertions(+), 40 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/cpp0x/defaulted66.C
> create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted67.C
> create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted68.C
> create mode 100644 gcc/testsuite/g++.dg/cpp0x/defaulted69.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 ec23249c959..98a35f043c7 100644
> --- a/gcc/c-family/c.opt
> +++ b/gcc/c-family/c.opt
> @@ -629,6 +629,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..646072d4f20 100644
> --- a/gcc/cp/class.cc
> +++ b/gcc/cp/class.cc
> @@ -6488,27 +6488,14 @@ check_bases_and_members (tree t)
> for (fn = TYPE_FIELDS (t); fn; fn = DECL_CHAIN (fn))
> if (DECL_DECLARES_FUNCTION_P (fn)
> && !DECL_ARTIFICIAL (fn)
> - && DECL_DEFAULTED_IN_CLASS_P (fn))
> - {
> + && DECL_DEFAULTED_IN_CLASS_P (fn)
> /* ...except handle comparisons later, in finish_struct_1. */
> - if (special_function_p (fn) == sfk_comparison)
> - continue;
> -
> - int copy = copy_fn_p (fn);
> - if (copy > 0)
> - {
> - bool imp_const_p
> - = (DECL_CONSTRUCTOR_P (fn) ? !cant_have_const_ctor
> - : !no_const_asn_ref);
> - bool fn_const_p = (copy == 2);
> -
> - if (fn_const_p && !imp_const_p)
> - /* 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;
> - }
> - defaulted_late_check (fn);
> + && special_function_p (fn) != sfk_comparison)
> + {
> + bool imp_const_p
> + = (DECL_CONSTRUCTOR_P (fn) ? !cant_have_const_ctor
> + : !no_const_asn_ref);
> + defaulted_late_check (fn, imp_const_p);
> }
>
> if (LAMBDA_TYPE_P (t))
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 7baa2ccbe1e..32252e7f32f 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -6929,7 +6929,8 @@ 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 defaulted_late_check (tree);
> +extern void maybe_delete_defaulted_fn (tree, tree);
> +extern void defaulted_late_check (tree, tristate = tristate::unknown ());
> extern bool defaultable_fn_check (tree);
> extern void check_abi_tags (tree);
> extern tree missing_abi_tags (tree);
> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> index 21c06c744c9..d704db2af06 100644
> --- a/gcc/cp/method.cc
> +++ b/gcc/cp/method.cc
> @@ -3509,11 +3509,89 @@ implicitly_declare_fn (special_function_kind kind, tree type,
> return fn;
> }
>
> +/* Mark an explicitly defaulted function FN as =deleted and warn.
> + IMPLICIT_FN is the corresponding special member function that
> + would have been implicitly declared. */
> +
> +void
> +maybe_delete_defaulted_fn (tree fn, tree implicit_fn)
> +{
> + if (DECL_ARTIFICIAL (fn) || !DECL_DEFAULTED_IN_CLASS_P (fn))
> + return;
> +
> + DECL_DELETED_FN (fn) = true;
> +
> + auto_diagnostic_group d;
> + const special_function_kind kind = special_function_p (fn);
> + tree parmtype
> + = TREE_VALUE (DECL_XOBJ_MEMBER_FUNCTION_P (fn)
> + ? TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn)))
> + : FUNCTION_FIRST_USER_PARMTYPE (fn));
> + const bool illformed_p
> + /* [dcl.fct.def.default] "if F1 is an assignment operator"... */
> + = (SFK_ASSIGN_P (kind)
> + /* "and the return type of F1 differs from the return type of F2" */
> + && (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
> + TREE_TYPE (TREE_TYPE (implicit_fn)))
> + /* "or F1's non-object parameter type is not a reference,
> + the program is ill-formed" */
> + || !TYPE_REF_P (parmtype)));
> + /* Decide if we want to emit a pedwarn, error, or a warning. */
> + diagnostic_t diag_kind;
> + int opt;
> + if (illformed_p)
> + {
> + diag_kind = DK_ERROR;
> + opt = 0;
> + }
> + else
> + {
> + diag_kind = cxx_dialect >= cxx20 ? DK_WARNING : DK_PEDWARN;
> + opt = OPT_Wdefaulted_function_deleted;
> + }
> +
> + /* Don't warn for template instantiations. */
> + if (DECL_TEMPLATE_INSTANTIATION (fn) && diag_kind == DK_WARNING)
> + return;
> +
> + const char *wmsg;
> + switch (kind)
> + {
> + case sfk_copy_constructor:
> + wmsg = G_("explicitly defaulted copy constructor 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 "
> + "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 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 because its declared type does not match the type "
> + "of an implicit move assignment operator");
> + break;
> + default:
> + gcc_unreachable ();
> + }
> + if (emit_diagnostic (diag_kind, DECL_SOURCE_LOCATION (fn), opt, wmsg))
> + inform (DECL_SOURCE_LOCATION (fn),
> + "expected signature: %qD", implicit_fn);
> +}
> +
> /* Gives any errors about defaulted functions which need to be deferred
> - until the containing class is complete. */
> + until the containing class is complete. IMP_CONST is false or true
> + if we are called from check_bases_and_members and signals whether
> + the implicit function has a non-object parameter of type const C&. */
>
> void
> -defaulted_late_check (tree fn)
> +defaulted_late_check (tree fn, tristate imp_const/*=tristate::unknown()*/)
> {
> /* Complain about invalid signature for defaulted fn. */
> tree ctx = DECL_CONTEXT (fn);
> @@ -3534,8 +3612,14 @@ defaulted_late_check (tree fn)
> }
>
> bool fn_const_p = (copy_fn_p (fn) == 2);
> + /* "if F2 has a non-object parameter of type const C&, the corresponding
> + non-object parameter of F1 may be of type C&." But not the other way
> + around. */
> + if (fn_const_p && imp_const.is_false ())
> + fn_const_p = false;
> tree implicit_fn = implicitly_declare_fn (kind, ctx, fn_const_p,
> - NULL, NULL);
> + /*pattern_fn=*/NULL_TREE,
> + /*inherited_parms=*/NULL_TREE);
> tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
>
> /* Includes special handling for a default xobj operator. */
> @@ -3564,13 +3648,7 @@ defaulted_late_check (tree fn)
> 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);
> - }
> + maybe_delete_defaulted_fn (fn, implicit_fn);
>
> if (DECL_DELETED_FN (implicit_fn))
> {
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 2bcf71c35f7..032adfff5fc 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
> @@ -4798,6 +4799,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..6bd02d74dc3 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/defaulted15.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted15.C
> @@ -48,7 +48,8 @@ struct F
>
> struct G: public F
> {
> - G(const G&) = default;
> + G(const G&) = default; // { dg-error "implicitly deleted" "" { target c++17_down } }
> + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
> };
>
> struct H
> diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted51.C b/gcc/testsuite/g++.dg/cpp0x/defaulted51.C
> index 0a7d308707c..d81baa0059d 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/defaulted51.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted51.C
> @@ -4,7 +4,7 @@
> template<int> struct A
> {
> A();
> - A(volatile A&) = default; // { dg-error "defaulted" }
> + A(volatile A&) = default; // { dg-error "defaulted" "" { target c++17_down } }
> };
>
> struct B
> diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted52.C b/gcc/testsuite/g++.dg/cpp0x/defaulted52.C
> index c617230b493..e6128f40aeb 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-error "implicitly deleted" "" { target c++17_down } }
> T t;
> };
>
> diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted53.C b/gcc/testsuite/g++.dg/cpp0x/defaulted53.C
> index 8147e7e2ad1..cc039672277 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/defaulted53.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted53.C
> @@ -14,7 +14,8 @@ struct R
>
> struct S
> {
> - S& operator=(const S&) = default;
> + S& operator=(const S&) = default; // { dg-error "implicitly deleted" "" { target c++17_down } }
> + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
> M m;
> };
>
> diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted54.C b/gcc/testsuite/g++.dg/cpp0x/defaulted54.C
> index f8ddc4e47ce..04cc0cd7eeb 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-error "implicitly deleted" "" { target c++17_down } .-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..6ddae46cd63 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-error "implicitly deleted" "" { target c++17_down } .-1 }
> T t;
> };
>
> diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted59.C b/gcc/testsuite/g++.dg/cpp0x/defaulted59.C
> index 4f871d7f5b1..26510b4ee33 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/defaulted59.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted59.C
> @@ -8,5 +8,6 @@ struct M
>
> struct W : public M
> {
> - W(const W&) = default;
> + W(const W&) = default; // { dg-error "implicitly deleted" "" { target c++17_down } }
> + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
> };
> 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/cpp0x/defaulted66.C b/gcc/testsuite/g++.dg/cpp0x/defaulted66.C
> new file mode 100644
> index 00000000000..00d3e43e89f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted66.C
> @@ -0,0 +1,35 @@
> +// PR c++/116162
> +// { dg-do compile { target c++11 } }
> +// Check that there is no -Wdefaulted-function-deleted for template
> +// instantiations.
> +
> +template<typename>
> +struct C {
> + C();
> + C(const C&&) = default; // { dg-error "implicitly deleted" "" { target c++17_down} }
> +};
> +
> +struct D {
> + D(const D&&) = default; // { dg-error "implicitly deleted" "" { target c++17_down} }
> + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
> +};
> +
> +struct M {
> + M();
> + // So that W wouldn't have had "const W&" copy ctor if it were
> + // implicitly declared.
> + M(M&);
> +};
> +
> +struct W {
> + W();
> + W(const W&) = default; // { dg-error "implicitly deleted" "" { target c++17_down} }
> + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
> + M m;
> +};
> +
> +void
> +g ()
> +{
> + C<int> c;
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted67.C b/gcc/testsuite/g++.dg/cpp0x/defaulted67.C
> new file mode 100644
> index 00000000000..a47761c3a26
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted67.C
> @@ -0,0 +1,23 @@
> +// PR c++/116162
> +// { dg-do compile { target c++11 } }
> +// { dg-additional-options "-Wno-defaulted-function-deleted" }
> +
> +struct S
> +{
> + S& operator=(S &&) = default;
> +};
> +
> +struct T
> +{
> + T& operator=(volatile T &&) = default;
> +};
> +
> +struct U
> +{
> + U& operator=(const volatile U &&) = default;
> +};
> +
> +struct V
> +{
> + V& operator=(const V &&) = default;
> +};
> diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted68.C b/gcc/testsuite/g++.dg/cpp0x/defaulted68.C
> new file mode 100644
> index 00000000000..c8c1261de42
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted68.C
> @@ -0,0 +1,35 @@
> +// PR c++/116162
> +// { dg-do compile { target c++11 } }
> +// { dg-additional-options "-Wno-defaulted-function-deleted" }
> +
> +struct C0 {
> + C0(C0&) = default;
> +};
> +
> +struct C1 {
> + C1(volatile C1&) = default;
> +};
> +
> +struct C2 {
> + C2(const C2&) = default;
> +};
> +
> +struct C3 {
> + C3(const volatile C3&) = default;
> +};
> +
> +struct M0 {
> + M0(M0&&) = default;
> +};
> +
> +struct M1 {
> + M1(const M1&&) = default;
> +};
> +
> +struct M2 {
> + M2(volatile M2&&) = default;
> +};
> +
> +struct M3 {
> + M3(const volatile M3&&) = default;
> +};
> diff --git a/gcc/testsuite/g++.dg/cpp0x/defaulted69.C b/gcc/testsuite/g++.dg/cpp0x/defaulted69.C
> new file mode 100644
> index 00000000000..a466259dfd7
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/defaulted69.C
> @@ -0,0 +1,24 @@
> +// PR c++/116162
> +// { dg-do compile { target c++11 } }
> +// { dg-additional-options "-Wno-defaulted-function-deleted" }
> +
> +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=(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=(U) = default; // { dg-error "defaulted" }
> + M m;
> +};
> 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;
> +};
>
> base-commit: cee868ae0dfb5bef590a0a82426d32ba1d2b1a92
@@ -629,6 +629,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.
@@ -6488,27 +6488,14 @@ check_bases_and_members (tree t)
for (fn = TYPE_FIELDS (t); fn; fn = DECL_CHAIN (fn))
if (DECL_DECLARES_FUNCTION_P (fn)
&& !DECL_ARTIFICIAL (fn)
- && DECL_DEFAULTED_IN_CLASS_P (fn))
- {
+ && DECL_DEFAULTED_IN_CLASS_P (fn)
/* ...except handle comparisons later, in finish_struct_1. */
- if (special_function_p (fn) == sfk_comparison)
- continue;
-
- int copy = copy_fn_p (fn);
- if (copy > 0)
- {
- bool imp_const_p
- = (DECL_CONSTRUCTOR_P (fn) ? !cant_have_const_ctor
- : !no_const_asn_ref);
- bool fn_const_p = (copy == 2);
-
- if (fn_const_p && !imp_const_p)
- /* 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;
- }
- defaulted_late_check (fn);
+ && special_function_p (fn) != sfk_comparison)
+ {
+ bool imp_const_p
+ = (DECL_CONSTRUCTOR_P (fn) ? !cant_have_const_ctor
+ : !no_const_asn_ref);
+ defaulted_late_check (fn, imp_const_p);
}
if (LAMBDA_TYPE_P (t))
@@ -6929,7 +6929,8 @@ 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 defaulted_late_check (tree);
+extern void maybe_delete_defaulted_fn (tree, tree);
+extern void defaulted_late_check (tree, tristate = tristate::unknown ());
extern bool defaultable_fn_check (tree);
extern void check_abi_tags (tree);
extern tree missing_abi_tags (tree);
@@ -3509,11 +3509,89 @@ implicitly_declare_fn (special_function_kind kind, tree type,
return fn;
}
+/* Mark an explicitly defaulted function FN as =deleted and warn.
+ IMPLICIT_FN is the corresponding special member function that
+ would have been implicitly declared. */
+
+void
+maybe_delete_defaulted_fn (tree fn, tree implicit_fn)
+{
+ if (DECL_ARTIFICIAL (fn) || !DECL_DEFAULTED_IN_CLASS_P (fn))
+ return;
+
+ DECL_DELETED_FN (fn) = true;
+
+ auto_diagnostic_group d;
+ const special_function_kind kind = special_function_p (fn);
+ tree parmtype
+ = TREE_VALUE (DECL_XOBJ_MEMBER_FUNCTION_P (fn)
+ ? TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn)))
+ : FUNCTION_FIRST_USER_PARMTYPE (fn));
+ const bool illformed_p
+ /* [dcl.fct.def.default] "if F1 is an assignment operator"... */
+ = (SFK_ASSIGN_P (kind)
+ /* "and the return type of F1 differs from the return type of F2" */
+ && (!same_type_p (TREE_TYPE (TREE_TYPE (fn)),
+ TREE_TYPE (TREE_TYPE (implicit_fn)))
+ /* "or F1's non-object parameter type is not a reference,
+ the program is ill-formed" */
+ || !TYPE_REF_P (parmtype)));
+ /* Decide if we want to emit a pedwarn, error, or a warning. */
+ diagnostic_t diag_kind;
+ int opt;
+ if (illformed_p)
+ {
+ diag_kind = DK_ERROR;
+ opt = 0;
+ }
+ else
+ {
+ diag_kind = cxx_dialect >= cxx20 ? DK_WARNING : DK_PEDWARN;
+ opt = OPT_Wdefaulted_function_deleted;
+ }
+
+ /* Don't warn for template instantiations. */
+ if (DECL_TEMPLATE_INSTANTIATION (fn) && diag_kind == DK_WARNING)
+ return;
+
+ const char *wmsg;
+ switch (kind)
+ {
+ case sfk_copy_constructor:
+ wmsg = G_("explicitly defaulted copy constructor 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 "
+ "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 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 because its declared type does not match the type "
+ "of an implicit move assignment operator");
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ if (emit_diagnostic (diag_kind, DECL_SOURCE_LOCATION (fn), opt, wmsg))
+ inform (DECL_SOURCE_LOCATION (fn),
+ "expected signature: %qD", implicit_fn);
+}
+
/* Gives any errors about defaulted functions which need to be deferred
- until the containing class is complete. */
+ until the containing class is complete. IMP_CONST is false or true
+ if we are called from check_bases_and_members and signals whether
+ the implicit function has a non-object parameter of type const C&. */
void
-defaulted_late_check (tree fn)
+defaulted_late_check (tree fn, tristate imp_const/*=tristate::unknown()*/)
{
/* Complain about invalid signature for defaulted fn. */
tree ctx = DECL_CONTEXT (fn);
@@ -3534,8 +3612,14 @@ defaulted_late_check (tree fn)
}
bool fn_const_p = (copy_fn_p (fn) == 2);
+ /* "if F2 has a non-object parameter of type const C&, the corresponding
+ non-object parameter of F1 may be of type C&." But not the other way
+ around. */
+ if (fn_const_p && imp_const.is_false ())
+ fn_const_p = false;
tree implicit_fn = implicitly_declare_fn (kind, ctx, fn_const_p,
- NULL, NULL);
+ /*pattern_fn=*/NULL_TREE,
+ /*inherited_parms=*/NULL_TREE);
tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn));
/* Includes special handling for a default xobj operator. */
@@ -3564,13 +3648,7 @@ defaulted_late_check (tree fn)
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);
- }
+ maybe_delete_defaulted_fn (fn, implicit_fn);
if (DECL_DELETED_FN (implicit_fn))
{
@@ -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
@@ -4798,6 +4799,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)}
@@ -48,7 +48,8 @@ struct F
struct G: public F
{
- G(const G&) = default;
+ G(const G&) = default; // { dg-error "implicitly deleted" "" { target c++17_down } }
+ // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
};
struct H
@@ -4,7 +4,7 @@
template<int> struct A
{
A();
- A(volatile A&) = default; // { dg-error "defaulted" }
+ A(volatile A&) = default; // { dg-error "defaulted" "" { target c++17_down } }
};
struct B
@@ -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-error "implicitly deleted" "" { target c++17_down } }
T t;
};
@@ -14,7 +14,8 @@ struct R
struct S
{
- S& operator=(const S&) = default;
+ S& operator=(const S&) = default; // { dg-error "implicitly deleted" "" { target c++17_down } }
+ // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
M m;
};
@@ -11,6 +11,7 @@ template<typename T> struct W
{
W();
W(const W&) = default; // { dg-error "binding" }
+// { dg-error "implicitly deleted" "" { target c++17_down } .-1 }
T t;
};
@@ -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
@@ -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
@@ -11,6 +11,7 @@ template<typename T> struct W
{
W() = default;
W& operator=(const W&) = default; // { dg-error "binding" }
+// { dg-error "implicitly deleted" "" { target c++17_down } .-1 }
T t;
};
@@ -8,5 +8,6 @@ struct M
struct W : public M
{
- W(const W&) = default;
+ W(const W&) = default; // { dg-error "implicitly deleted" "" { target c++17_down } }
+ // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
};
new file mode 100644
@@ -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 }
+};
new file mode 100644
@@ -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;
+};
new file mode 100644
@@ -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 }
+};
new file mode 100644
@@ -0,0 +1,35 @@
+// PR c++/116162
+// { dg-do compile { target c++11 } }
+// Check that there is no -Wdefaulted-function-deleted for template
+// instantiations.
+
+template<typename>
+struct C {
+ C();
+ C(const C&&) = default; // { dg-error "implicitly deleted" "" { target c++17_down} }
+};
+
+struct D {
+ D(const D&&) = default; // { dg-error "implicitly deleted" "" { target c++17_down} }
+ // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
+};
+
+struct M {
+ M();
+ // So that W wouldn't have had "const W&" copy ctor if it were
+ // implicitly declared.
+ M(M&);
+};
+
+struct W {
+ W();
+ W(const W&) = default; // { dg-error "implicitly deleted" "" { target c++17_down} }
+ // { dg-warning "implicitly deleted" "" { target c++20 } .-1 }
+ M m;
+};
+
+void
+g ()
+{
+ C<int> c;
+}
new file mode 100644
@@ -0,0 +1,23 @@
+// PR c++/116162
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-defaulted-function-deleted" }
+
+struct S
+{
+ S& operator=(S &&) = default;
+};
+
+struct T
+{
+ T& operator=(volatile T &&) = default;
+};
+
+struct U
+{
+ U& operator=(const volatile U &&) = default;
+};
+
+struct V
+{
+ V& operator=(const V &&) = default;
+};
new file mode 100644
@@ -0,0 +1,35 @@
+// PR c++/116162
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-defaulted-function-deleted" }
+
+struct C0 {
+ C0(C0&) = default;
+};
+
+struct C1 {
+ C1(volatile C1&) = default;
+};
+
+struct C2 {
+ C2(const C2&) = default;
+};
+
+struct C3 {
+ C3(const volatile C3&) = default;
+};
+
+struct M0 {
+ M0(M0&&) = default;
+};
+
+struct M1 {
+ M1(const M1&&) = default;
+};
+
+struct M2 {
+ M2(volatile M2&&) = default;
+};
+
+struct M3 {
+ M3(const volatile M3&&) = default;
+};
new file mode 100644
@@ -0,0 +1,24 @@
+// PR c++/116162
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wno-defaulted-function-deleted" }
+
+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=(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=(U) = default; // { dg-error "defaulted" }
+ M m;
+};
new file mode 100644
@@ -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;
+};