From patchwork Thu Sep 19 15:35:22 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Polacek X-Patchwork-Id: 97716 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2CD1E3858C31 for ; Thu, 19 Sep 2024 15:36:03 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 2BE243858D28 for ; Thu, 19 Sep 2024 15:35:31 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2BE243858D28 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 2BE243858D28 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1726760135; cv=none; b=g2aWggxKR7+FiNu+e9YyyzHAud8Y2tuBovlZGsFzciN6ct6hV/cqaQYYLwLgthxwJZGPbQxMEHPUqGUG/xwlXRczDxQ7a8nhQMN++OjeapkTHDddyrGej7aJ3+yUNQj8ZTIvmfhrigtga1UanLeMewxVB4hxjcoTuSoJSEbr/7U= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1726760135; c=relaxed/simple; bh=JjhXaETx/FR/yvt/1IPdPwOZ0ZeISfv/+zvsj3zIIoY=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=hMHqEsIezNDF25dSsYV70tesMN+zloHASTaGpmei5M1uezmsFvjQQgqFpSC94dPHoIej6u4t4jfsZglWWl+DffqZEk8pOi1FVzxqrjZi9Pe6JAHxU6SQm7MAbI58qn6XqQDYSjBtogYkmI67/iBzDqgM8eW2Nd5cIREKlkJ3FhI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1726760130; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=KFcXGBTrcFQQ0SefaviVBPPMverQbaW2pxkJ3JZxNXA=; b=ipZDfUNIEZWKZhcKMnXoNOFxo+vLtjMCN1ztZ6fBZWPWrk00eR0XdqKyQhMUSMzYerjTRo rgZp5jlPyaSAsjFiMvkhxJvB1SayHSQ0WROWh+MECSGxZd92kH6H5dTkZy3+e5ByzD8i/s dFqREhK1kg1T8cv4bV4z3VnMa6eBDyw= Received: from mail-qk1-f199.google.com (mail-qk1-f199.google.com [209.85.222.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-576-JxEuPvzUNz6W2Bfv8GpM0A-1; Thu, 19 Sep 2024 11:35:28 -0400 X-MC-Unique: JxEuPvzUNz6W2Bfv8GpM0A-1 Received: by mail-qk1-f199.google.com with SMTP id af79cd13be357-7a9cfe4442cso165859185a.3 for ; Thu, 19 Sep 2024 08:35:28 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726760128; x=1727364928; h=user-agent:in-reply-to:content-disposition:mime-version:references :message-id:subject:cc:to:from:date:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=KFcXGBTrcFQQ0SefaviVBPPMverQbaW2pxkJ3JZxNXA=; b=DmVHdxcFSB+G3YAjLNPHKOrJ3eZ/1tWG9rg1qF1Kw1yL2TgHJOhEalBhHZq7RtjEm2 uJ2dmbxCP0VN8S7OdW21VYAzcztEXD+SPm1TPc+qqM4n5/o/LB5iq5RTjKl3O3ywInch jAIFnZqxLi2QVEva/QlzSWEAFNXzGpJt5sbbLiSsQTaCARzktRK53xOLYl1NFD+P5cNf waGDAhfyotW1VpVJN1R6GeMsPcMyVphA0TEZY4jKMcDaz+SCsh6wQ1Ul4ifI9nIWsPSg MHiwwk6J1s5OTrAxvUSBgIEf7kD6wuknc4g2/PB7nkt9WJxZIp9wtfMxQUkebeNCiVxe OvsQ== X-Gm-Message-State: AOJu0YyeUpaYzPIx1xUbNfuN+OVJs9VwDrqBS0xyyUFCBc7/zJOqVVNZ ciYPKUcAx7uWGkdJoTFPPCktEaALABwWekksgtZPsU5X/nDPOm2xVyJcubUvaKcQa1SjVB1NAZQ ZuN432ZegjrLO1pMZbSpqRqneAW3x6GhKK9ve0wUW6O9dFSYvGMhQ6HnKHlbMzXQ= X-Received: by 2002:a05:620a:3711:b0:79d:6d7d:e5b3 with SMTP id af79cd13be357-7a9e5f673d4mr4036396685a.42.1726760127084; Thu, 19 Sep 2024 08:35:27 -0700 (PDT) X-Google-Smtp-Source: AGHT+IErh0PZUSKp2QMd6VsLdD6fjxYV5A+1AxtFj4k7Na0+N0pawvyPumD2RKeQif03pSiTQprpaw== X-Received: by 2002:a05:620a:3711:b0:79d:6d7d:e5b3 with SMTP id af79cd13be357-7a9e5f673d4mr4036388685a.42.1726760126192; Thu, 19 Sep 2024 08:35:26 -0700 (PDT) Received: from redhat.com ([2603:7000:9500:34a5::1db4]) by smtp.gmail.com with ESMTPSA id af79cd13be357-7acb082d2f8sm85731185a.69.2024.09.19.08.35.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 Sep 2024 08:35:24 -0700 (PDT) Date: Thu, 19 Sep 2024 11:35:22 -0400 From: Marek Polacek To: Jason Merrill Cc: GCC Patches Subject: [PATCH v5] c++: deleting explicitly-defaulted functions [PR116162] Message-ID: References: <20240911165411.459519-1-polacek@redhat.com> <1b4fb2c4-b7c5-46d1-af2e-6046b27222d1@redhat.com> <671cd4bb-c6cc-49e6-bd93-0fb7cc43445f@redhat.com> <0d5c506f-dae2-41c5-9d2b-e4ff9cf9e4a4@redhat.com> MIME-Version: 1.0 In-Reply-To: User-Agent: Mutt/2.2.12 (2023-09-09) X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-12.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org 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 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 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 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 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 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 +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 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; +};