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);