aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2024-09-09 14:23:33 -0400
committerMarek Polacek <polacek@redhat.com>2024-09-19 17:10:26 -0400
commita2746e4347076ea48f4aeb28e13e6337ff7799ad (patch)
tree1bef9fe2725a13696351897cbbefcb095bdbc274 /gcc/cp
parent3790ff7530bce0b551457db1d26aafbe47d1cabe (diff)
downloadgcc-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.cc27
-rw-r--r--gcc/cp/cp-tree.h3
-rw-r--r--gcc/cp/method.cc98
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))
{