https://gcc.gnu.org/g:e31e206b397d5c10d8d316a45a657e48b54f2047
commit r16-5549-ge31e206b397d5c10d8d316a45a657e48b54f2047 Author: Marek Polacek <[email protected]> Date: Fri Nov 21 15:34:35 2025 -0500 c++: refactor maybe_delete_defaulted_fn [PR119964] This does three things: 1) [dcl.fct.def.default]/2.6 says "if F1 is explicitly defaulted on its first declaration, it is defined as deleted;", but I wasn't heeding the "on its first declaration" part, so fix that; 2) when the decl is actually ill-formed, don't talk about it being implicitly deleted, because it's not; 3) there is no need to export maybe_delete_defaulted_fn. PR c++/119964 gcc/cp/ChangeLog: * cp-tree.h (maybe_delete_defaulted_fn): Remove. * method.cc (maybe_delete_defaulted_fn): Make static. Refactor. If FN is not explicitly defaulted on its first declaration, emit an error. gcc/testsuite/ChangeLog: * g++.dg/cpp1y/defaulted1.C: New test. * g++.dg/cpp1y/defaulted2.C: New test. Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/cp-tree.h | 1 - gcc/cp/method.cc | 51 +++++++++++++++++---------------- gcc/testsuite/g++.dg/cpp1y/defaulted1.C | 17 +++++++++++ gcc/testsuite/g++.dg/cpp1y/defaulted2.C | 15 ++++++++++ 4 files changed, 58 insertions(+), 26 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 4efddd98537d..180c0cad5933 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7186,7 +7186,6 @@ 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 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); diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index bc721a5d7e3f..39f931eafc9d 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -3652,49 +3652,49 @@ implicitly_declare_fn (special_function_kind kind, tree type, return fn; } -/* Mark an explicitly defaulted function FN as =deleted and warn. +/* Maybe mark an explicitly defaulted function FN as =deleted and warn, + or emit an error, as per [dcl.fct.def.default]. IMPLICIT_FN is the corresponding special member function that - would have been implicitly declared. */ + would have been implicitly declared. We've already compared FN and + IMPLICIT_FN and they are not the same. */ -void +static void maybe_delete_defaulted_fn (tree fn, tree implicit_fn) { - if (DECL_ARTIFICIAL (fn) || !DECL_DEFAULTED_IN_CLASS_P (fn)) + if (DECL_ARTIFICIAL (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) + if (/* [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. */ - enum diagnostics::kind diag_kind; - int opt; - if (illformed_p) - { - diag_kind = diagnostics::kind::error; - opt = 0; - } - else - { - diag_kind = (cxx_dialect >= cxx20 - ? diagnostics::kind::warning - : diagnostics::kind::pedwarn); - opt = OPT_Wdefaulted_function_deleted; + || !TYPE_REF_P (parmtype))) + /* If F1 is *not* explicitly defaulted on its first declaration, the + program is ill-formed. */ + || !DECL_DEFAULTED_IN_CLASS_P (fn)) + { + error ("defaulted declaration %q+D does not match the expected " + "signature", fn); + inform (DECL_SOURCE_LOCATION (fn), "expected signature: %qD", + implicit_fn); + return; } + DECL_DELETED_FN (fn) = true; + + const enum diagnostics::kind diag_kind = (cxx_dialect >= cxx20 + ? diagnostics::kind::warning + : diagnostics::kind::pedwarn); + /* Don't warn for template instantiations. */ if (DECL_TEMPLATE_INSTANTIATION (fn) && diag_kind == diagnostics::kind::warning) @@ -3726,7 +3726,8 @@ maybe_delete_defaulted_fn (tree fn, tree implicit_fn) default: gcc_unreachable (); } - if (emit_diagnostic (diag_kind, DECL_SOURCE_LOCATION (fn), opt, wmsg)) + if (emit_diagnostic (diag_kind, DECL_SOURCE_LOCATION (fn), + OPT_Wdefaulted_function_deleted, wmsg)) inform (DECL_SOURCE_LOCATION (fn), "expected signature: %qD", implicit_fn); } diff --git a/gcc/testsuite/g++.dg/cpp1y/defaulted1.C b/gcc/testsuite/g++.dg/cpp1y/defaulted1.C new file mode 100644 index 000000000000..7b38f975d419 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/defaulted1.C @@ -0,0 +1,17 @@ +// PR c++/119964 +// { dg-do compile { target c++14 } } + +struct A { + int i; + constexpr A(int v) : i(v) {} + constexpr A(const A&&); +}; + +constexpr int f() { + A a(1); + A b = static_cast<const A&&>( a ); + return b.i; +} + +constexpr A::A(const A&&) = default; // { dg-error "does not match the expected signature" } +static_assert(f () == 1, ""); diff --git a/gcc/testsuite/g++.dg/cpp1y/defaulted2.C b/gcc/testsuite/g++.dg/cpp1y/defaulted2.C new file mode 100644 index 000000000000..9e5e6a69eead --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/defaulted2.C @@ -0,0 +1,15 @@ +// PR c++/119964 +// { dg-do compile { target c++14 } } + +struct A { + int i; + constexpr A(int v) : i(v) {} + constexpr A(const A&&) = default; // { dg-error "implicitly deleted" "" { target c++17_down } } + // { dg-warning "implicitly deleted" "" { target c++20 } .-1 } +}; + +constexpr int f() { + A a(1); + A b = static_cast<const A&&>( a ); // { dg-error "use of deleted function" } + return b.i; +}
