diff options
author | Marek Polacek <polacek@redhat.com> | 2024-09-09 14:23:33 -0400 |
---|---|---|
committer | Marek Polacek <polacek@redhat.com> | 2024-09-19 17:10:26 -0400 |
commit | a2746e4347076ea48f4aeb28e13e6337ff7799ad (patch) | |
tree | 1bef9fe2725a13696351897cbbefcb095bdbc274 /gcc/cp | |
parent | 3790ff7530bce0b551457db1d26aafbe47d1cabe (diff) | |
download | gcc-a2746e4347076ea48f4aeb28e13e6337ff7799ad.zip gcc-a2746e4347076ea48f4aeb28e13e6337ff7799ad.tar.gz gcc-a2746e4347076ea48f4aeb28e13e6337ff7799ad.tar.bz2 |
c++: deleting explicitly-defaulted functions [PR116162]
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.
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/class.cc | 27 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 3 | ||||
-rw-r--r-- | gcc/cp/method.cc | 98 |
3 files changed, 97 insertions, 31 deletions
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index 950d83b..646072d 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 7baa2cc..32252e7 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 21c06c7..d704db2 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)) { |