From patchwork Thu Aug 15 12:38:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jason Merrill X-Patchwork-Id: 1147590 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-507050-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="yR0QO+t3"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 468Qx93Nnzz9sN6 for ; Thu, 15 Aug 2019 22:38:51 +1000 (AEST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:subject:date:message-id:mime-version :content-transfer-encoding; q=dns; s=default; b=E7dJfHx73roOzLxW tUfoqRxYClV41ja1R5DipneL/23kDam3QEcvQ5nC4L5/K9ww+OLpeh8tAg8Tremd UKcALSTFe/KCyxTi4nXme/Ru6rddehV+s7VaWeqlDzvaXC8WjvDbFUvmh3VTaO74 MUNtnnIq01phx7VWwXGG7Vi+8Yc= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:subject:date:message-id:mime-version :content-transfer-encoding; s=default; bh=x0JyCeI2zmKdeLwe5cr+Js oQ5S0=; b=yR0QO+t3kpT6p0eTJByOpmJrf0AYZco7MHUmv1htwze7AsgYwm2Dth fjGFV7lYWP0UKZiDP67fck6+5429XXsA9IpWch1UGbuml1nSlR8KPw08zQRgVmkv uQF/Sy2hVnfS3OUPoa4PGa0g5SmFvx43+VFPvHPWRFDRJihtph/NA= Received: (qmail 99543 invoked by alias); 15 Aug 2019 12:38:43 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 99530 invoked by uid 89); 15 Aug 2019 12:38:43 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-18.3 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 spammy=engaged, Member, dtor X-HELO: mail-qk1-f169.google.com Received: from mail-qk1-f169.google.com (HELO mail-qk1-f169.google.com) (209.85.222.169) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 15 Aug 2019 12:38:40 +0000 Received: by mail-qk1-f169.google.com with SMTP id r21so1720797qke.2 for ; Thu, 15 Aug 2019 05:38:40 -0700 (PDT) Received: from orpheus.redhat.com (209-6-216-142.s141.c3-0.smr-cbr1.sbo-smr.ma.cable.rcncustomer.com. [209.6.216.142]) by smtp.gmail.com with ESMTPSA id q25sm1357295qkm.30.2019.08.15.05.38.37 for (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Thu, 15 Aug 2019 05:38:37 -0700 (PDT) From: Jason Merrill To: gcc-patches@gcc.gnu.org Subject: [C++ PATCH] Implement P0848R3, Conditionally Trivial Special Member Functions. Date: Thu, 15 Aug 2019 08:38:36 -0400 Message-Id: <20190815123836.6697-1-jason@redhat.com> MIME-Version: 1.0 X-IsSubscribed: yes With Concepts, overloads of special member functions can differ in constraints, and this paper clarifies how that affects class properties: if a class has a more constrained trivial copy constructor and a less constrained non-trivial copy constructor, it is still trivially copyable. Tested x86_64-pc-linux-gnu, applying to trunk. * tree.c (special_memfn_p): New. * class.c (add_method): When overloading, hide ineligible special member fns. (check_methods): Set TYPE_HAS_COMPLEX_* here. * decl.c (grok_special_member_properties): Not here. * name-lookup.c (push_class_level_binding_1): Move overloaded functions case down, accept FUNCTION_DECL as target_decl. --- gcc/cp/cp-tree.h | 3 +- gcc/cp/class.c | 98 ++++++++++++++++++++++-- gcc/cp/decl.c | 8 -- gcc/cp/name-lookup.c | 6 +- gcc/cp/tree.c | 25 ++++++ gcc/testsuite/g++.dg/concepts/pr89036.C | 10 +++ gcc/testsuite/g++.dg/cpp2a/cond-triv1.C | 46 +++++++++++ gcc/testsuite/g++.dg/cpp2a/cond-triv1a.C | 46 +++++++++++ gcc/cp/ChangeLog | 11 +++ 9 files changed, 233 insertions(+), 20 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/cond-triv1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/cond-triv1a.C base-commit: 53c84344969b155bcd8b23b88a087a719187c6b6 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index bdb7778c04b..05f91861b42 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6313,7 +6313,7 @@ extern void determine_key_method (tree); extern void check_for_override (tree, tree); extern void push_class_stack (void); extern void pop_class_stack (void); -extern bool default_ctor_p (tree); +extern bool default_ctor_p (const_tree); extern bool type_has_user_nondefault_constructor (tree); extern tree in_class_defaulted_default_constructor (tree); extern bool user_provided_p (tree); @@ -7322,6 +7322,7 @@ extern tree cp_build_qualified_type_real (tree, int, tsubst_flags_t); extern bool cv_qualified_p (const_tree); extern tree cv_unqualified (tree); extern special_function_kind special_function_p (const_tree); +extern special_function_kind special_memfn_p (const_tree); extern int count_trees (tree); extern int char_type_p (tree); extern void verify_stmt_tree (tree); diff --git a/gcc/cp/class.c b/gcc/cp/class.c index b61152c7e72..cc53b15401a 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -994,6 +994,9 @@ add_method (tree type, tree method, bool via_using) tree *slot = find_member_slot (type, DECL_NAME (method)); tree current_fns = slot ? *slot : NULL_TREE; + /* See below. */ + int losem = -1; + /* Check to see if we've already got this method. */ for (ovl_iterator iter (current_fns); iter; ++iter) { @@ -1070,9 +1073,48 @@ add_method (tree type, tree method, bool via_using) if (compparms (parms1, parms2) && (!DECL_CONV_FN_P (fn) || same_type_p (TREE_TYPE (fn_type), - TREE_TYPE (method_type))) - && equivalently_constrained (fn, method)) + TREE_TYPE (method_type)))) { + if (!equivalently_constrained (fn, method)) + { + special_function_kind sfk = special_memfn_p (method); + + if (sfk == sfk_none) + /* Non-special member functions coexist if they are not + equivalently constrained. */ + continue; + + /* P0848: For special member functions, deleted, unsatisfied, or + less constrained overloads are ineligible. We implement this + by removing them from CLASSTYPE_MEMBER_VEC. Destructors don't + use the notion of eligibility, and the selected destructor can + be deleted, but removing unsatisfied or less constrained + overloads has the same effect as overload resolution. */ + bool dtor = (sfk == sfk_destructor); + if (losem == -1) + losem = ((!dtor && DECL_DELETED_FN (method)) + || !constraints_satisfied_p (method)); + bool losef = ((!dtor && DECL_DELETED_FN (fn)) + || !constraints_satisfied_p (fn)); + int win; + if (losem || losef) + win = losem - losef; + else + win = more_constrained (fn, method); + if (win > 0) + /* Leave FN in the method vec, discard METHOD. */ + return false; + else if (win < 0) + { + /* Remove FN, add METHOD. */ + current_fns = iter.remove_node (current_fns); + continue; + } + else + /* Let them coexist for now. */ + continue; + } + /* If these are versions of the same function, process and move on. */ if (TREE_CODE (fn) == FUNCTION_DECL @@ -4468,11 +4510,6 @@ check_methods (tree t) vec_safe_push (CLASSTYPE_PURE_VIRTUALS (t), x); } - /* All user-provided destructors are non-trivial. - Constructors and assignment ops are handled in - grok_special_member_properties. */ - if (DECL_DESTRUCTOR_P (x) && user_provided_p (x)) - TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) = 1; if (!DECL_VIRTUAL_P (x) && lookup_attribute ("transaction_safe_dynamic", DECL_ATTRIBUTES (x))) @@ -4480,6 +4517,51 @@ check_methods (tree t) "% may only be specified for " "a virtual function"); } + + /* Check whether the eligible special member functions (P0848) are + user-provided. add_method arranged that the CLASSTYPE_MEMBER_VEC only + has the eligible ones; TYPE_FIELDS also contains ineligible overloads, + which is why this needs to be separate from the loop above. */ + + if (tree dtor = CLASSTYPE_DESTRUCTOR (t)) + { + if (TREE_CODE (dtor) == OVERLOAD) + { + /* P0848: At the end of the definition of a class, overload + resolution is performed among the prospective destructors declared + in that class with an empty argument list to select the destructor + for the class, also known as the selected destructor. The program + is ill-formed if overload resolution fails. */ + auto_diagnostic_group d; + error_at (location_of (t), "destructor for %qT is ambiguous", t); + print_candidates (dtor); + } + else if (user_provided_p (dtor)) + TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) = true; + } + + for (ovl_iterator i (CLASSTYPE_CONSTRUCTORS (t)); i; ++i) + { + tree fn = *i; + if (!user_provided_p (fn)) + /* Might be trivial. */; + else if (copy_fn_p (fn)) + TYPE_HAS_COMPLEX_COPY_CTOR (t) = true; + else if (move_fn_p (fn)) + TYPE_HAS_COMPLEX_MOVE_CTOR (t) = true; + } + + for (ovl_iterator i (get_class_binding_direct (t, assign_op_identifier)); + i; ++i) + { + tree fn = *i; + if (!user_provided_p (fn)) + /* Might be trivial. */; + else if (copy_fn_p (fn)) + TYPE_HAS_COMPLEX_COPY_ASSIGN (t) = true; + else if (move_fn_p (fn)) + TYPE_HAS_COMPLEX_MOVE_ASSIGN (t) = true; + } } /* FN is a constructor or destructor. Clone the declaration to create @@ -4950,7 +5032,7 @@ set_method_tm_attributes (tree t) /* Returns true if FN is a default constructor. */ bool -default_ctor_p (tree fn) +default_ctor_p (const_tree fn) { return (DECL_CONSTRUCTOR_P (fn) && sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (fn))); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index ff3b90dba54..08b7baa40e0 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -13534,15 +13534,11 @@ grok_special_member_properties (tree decl) are no other parameters or else all other parameters have default arguments. */ TYPE_HAS_COPY_CTOR (class_type) = 1; - if (user_provided_p (decl)) - TYPE_HAS_COMPLEX_COPY_CTOR (class_type) = 1; if (ctor > 1) TYPE_HAS_CONST_COPY_CTOR (class_type) = 1; } else if (sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (decl))) TYPE_HAS_DEFAULT_CONSTRUCTOR (class_type) = 1; - else if (move_fn_p (decl) && user_provided_p (decl)) - TYPE_HAS_COMPLEX_MOVE_CTOR (class_type) = 1; else if (is_list_ctor (decl)) TYPE_HAS_LIST_CTOR (class_type) = 1; @@ -13563,13 +13559,9 @@ grok_special_member_properties (tree decl) if (assop) { TYPE_HAS_COPY_ASSIGN (class_type) = 1; - if (user_provided_p (decl)) - TYPE_HAS_COMPLEX_COPY_ASSIGN (class_type) = 1; if (assop != 1) TYPE_HAS_CONST_COPY_ASSIGN (class_type) = 1; } - else if (move_fn_p (decl) && user_provided_p (decl)) - TYPE_HAS_COMPLEX_MOVE_ASSIGN (class_type) = 1; } else if (IDENTIFIER_CONV_OP_P (DECL_NAME (decl))) TYPE_HAS_CONVERSION (class_type) = true; diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 16c74287bb1..5f5ff81f405 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -4504,9 +4504,6 @@ push_class_level_binding_1 (tree name, tree x) binding->type = NULL_TREE; } } - else if (TREE_CODE (target_decl) == OVERLOAD - && OVL_P (target_bval)) - old_decl = bval; else if (TREE_CODE (decl) == USING_DECL && TREE_CODE (bval) == USING_DECL && same_type_p (USING_DECL_SCOPE (decl), @@ -4525,6 +4522,9 @@ push_class_level_binding_1 (tree name, tree x) else if (TREE_CODE (bval) == USING_DECL && OVL_P (target_decl)) return true; + else if (OVL_P (target_decl) + && OVL_P (target_bval)) + old_decl = bval; if (old_decl && binding->scope == class_binding_level) { diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 27bc35198bb..bca92100621 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -5015,6 +5015,31 @@ special_function_p (const_tree decl) return sfk_none; } +/* As above, but only if DECL is a special member function as per 11.3.3 + [special]: default/copy/move ctor, copy/move assignment, or destructor. */ + +special_function_kind +special_memfn_p (const_tree decl) +{ + switch (special_function_kind sfk = special_function_p (decl)) + { + case sfk_constructor: + if (!default_ctor_p (decl)) + break; + gcc_fallthrough(); + case sfk_copy_constructor: + case sfk_copy_assignment: + case sfk_move_assignment: + case sfk_move_constructor: + case sfk_destructor: + return sfk; + + default: + break; + } + return sfk_none; +} + /* Returns nonzero if TYPE is a character type, including wchar_t. */ int diff --git a/gcc/testsuite/g++.dg/concepts/pr89036.C b/gcc/testsuite/g++.dg/concepts/pr89036.C index f83ef8b9679..5dcd649d1f8 100644 --- a/gcc/testsuite/g++.dg/concepts/pr89036.C +++ b/gcc/testsuite/g++.dg/concepts/pr89036.C @@ -6,3 +6,13 @@ struct Y { ~Y() requires(true) = default; ~Y() requires(false) {} }; + +Y y; + +template +struct X { + ~X() requires(sizeof(T) == 8) = default; + ~X() requires(sizeof(T) != 8) {} +}; + +X x; diff --git a/gcc/testsuite/g++.dg/cpp2a/cond-triv1.C b/gcc/testsuite/g++.dg/cpp2a/cond-triv1.C new file mode 100644 index 00000000000..8f5806ee1af --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/cond-triv1.C @@ -0,0 +1,46 @@ +// Testcase from P0848R0 +// { dg-do compile { target concepts } } + +#include + +template +class optional +{ + struct empty {}; + union { + empty _ = { }; + T value; + }; + bool engaged = false; + +public: + constexpr optional() = default; + + constexpr optional(optional const&) + requires std::is_trivially_copy_constructible_v + = default; + constexpr optional(optional const& o) + : engaged (o.engaged) + { + if (engaged) + new (&value) T (o.value); + } + + ~optional() + requires std::is_trivially_destructible_v + = default; + ~optional() + { + if (engaged) + value.~T(); + } + + // ... +}; + +struct A { A(); A(const A&); ~A(); }; + +static_assert(std::is_trivially_copy_constructible_v>); +static_assert(!std::is_trivially_copy_constructible_v>); +static_assert(std::is_trivially_destructible_v>); +static_assert(!std::is_trivially_destructible_v>); diff --git a/gcc/testsuite/g++.dg/cpp2a/cond-triv1a.C b/gcc/testsuite/g++.dg/cpp2a/cond-triv1a.C new file mode 100644 index 00000000000..febd109abd7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/cond-triv1a.C @@ -0,0 +1,46 @@ +// Like cond-triv1.C, but with the declaration order swapped. +// { dg-do compile { target concepts } } + +#include + +template +class optional +{ + struct empty {}; + union { + empty _ = { }; + T value; + }; + bool engaged = false; + +public: + constexpr optional() = default; + + constexpr optional(optional const& o) + : engaged (o.engaged) + { + if (engaged) + new (&value) T (o.value); + } + constexpr optional(optional const&) + requires std::is_trivially_copy_constructible_v + = default; + + ~optional() + { + if (engaged) + value.~T(); + } + ~optional() + requires std::is_trivially_destructible_v + = default; + + // ... +}; + +struct A { A(); A(const A&); ~A(); }; + +static_assert(std::is_trivially_copy_constructible_v>); +static_assert(!std::is_trivially_copy_constructible_v>); +static_assert(std::is_trivially_destructible_v>); +static_assert(!std::is_trivially_destructible_v>); diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 9eeba3dadc3..25172ace36f 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,14 @@ +2019-08-14 Jason Merrill + + Implement P0848R3, Conditionally Trivial Special Member Functions. + * tree.c (special_memfn_p): New. + * class.c (add_method): When overloading, hide ineligible special + member fns. + (check_methods): Set TYPE_HAS_COMPLEX_* here. + * decl.c (grok_special_member_properties): Not here. + * name-lookup.c (push_class_level_binding_1): Move overloaded + functions case down, accept FUNCTION_DECL as target_decl. + 2019-08-14 Jonathan Wakely PR c++/91436