From: Jason Merrill Date: Thu, 15 Aug 2019 12:38:50 +0000 (-0400) Subject: Implement P0848R3, Conditionally Trivial Special Member Functions. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=c735f8f1a0c5a5d1e114e45390b35882f539ff69;p=gcc.git Implement P0848R3, Conditionally Trivial Special Member Functions. 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. * 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. From-SVN: r274534 --- 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 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/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/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>);