Commit c735f8f1 by Jason Merrill Committed by Jason Merrill

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
parent 84cc60bf
2019-08-14 Jason Merrill <jason@redhat.com>
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 <jwakely@redhat.com> 2019-08-14 Jonathan Wakely <jwakely@redhat.com>
PR c++/91436 PR c++/91436
......
...@@ -994,6 +994,9 @@ add_method (tree type, tree method, bool via_using) ...@@ -994,6 +994,9 @@ add_method (tree type, tree method, bool via_using)
tree *slot = find_member_slot (type, DECL_NAME (method)); tree *slot = find_member_slot (type, DECL_NAME (method));
tree current_fns = slot ? *slot : NULL_TREE; tree current_fns = slot ? *slot : NULL_TREE;
/* See below. */
int losem = -1;
/* Check to see if we've already got this method. */ /* Check to see if we've already got this method. */
for (ovl_iterator iter (current_fns); iter; ++iter) for (ovl_iterator iter (current_fns); iter; ++iter)
{ {
...@@ -1070,9 +1073,48 @@ add_method (tree type, tree method, bool via_using) ...@@ -1070,9 +1073,48 @@ add_method (tree type, tree method, bool via_using)
if (compparms (parms1, parms2) if (compparms (parms1, parms2)
&& (!DECL_CONV_FN_P (fn) && (!DECL_CONV_FN_P (fn)
|| same_type_p (TREE_TYPE (fn_type), || same_type_p (TREE_TYPE (fn_type),
TREE_TYPE (method_type))) TREE_TYPE (method_type))))
&& equivalently_constrained (fn, method))
{ {
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 /* If these are versions of the same function, process and
move on. */ move on. */
if (TREE_CODE (fn) == FUNCTION_DECL if (TREE_CODE (fn) == FUNCTION_DECL
...@@ -4468,11 +4510,6 @@ check_methods (tree t) ...@@ -4468,11 +4510,6 @@ check_methods (tree t)
vec_safe_push (CLASSTYPE_PURE_VIRTUALS (t), x); 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) if (!DECL_VIRTUAL_P (x)
&& lookup_attribute ("transaction_safe_dynamic", && lookup_attribute ("transaction_safe_dynamic",
DECL_ATTRIBUTES (x))) DECL_ATTRIBUTES (x)))
...@@ -4480,6 +4517,51 @@ check_methods (tree t) ...@@ -4480,6 +4517,51 @@ check_methods (tree t)
"%<transaction_safe_dynamic%> may only be specified for " "%<transaction_safe_dynamic%> may only be specified for "
"a virtual function"); "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 /* FN is a constructor or destructor. Clone the declaration to create
...@@ -4950,7 +5032,7 @@ set_method_tm_attributes (tree t) ...@@ -4950,7 +5032,7 @@ set_method_tm_attributes (tree t)
/* Returns true if FN is a default constructor. */ /* Returns true if FN is a default constructor. */
bool bool
default_ctor_p (tree fn) default_ctor_p (const_tree fn)
{ {
return (DECL_CONSTRUCTOR_P (fn) return (DECL_CONSTRUCTOR_P (fn)
&& sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (fn))); && sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (fn)));
......
...@@ -6313,7 +6313,7 @@ extern void determine_key_method (tree); ...@@ -6313,7 +6313,7 @@ extern void determine_key_method (tree);
extern void check_for_override (tree, tree); extern void check_for_override (tree, tree);
extern void push_class_stack (void); extern void push_class_stack (void);
extern void pop_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 bool type_has_user_nondefault_constructor (tree);
extern tree in_class_defaulted_default_constructor (tree); extern tree in_class_defaulted_default_constructor (tree);
extern bool user_provided_p (tree); extern bool user_provided_p (tree);
...@@ -7322,6 +7322,7 @@ extern tree cp_build_qualified_type_real (tree, int, tsubst_flags_t); ...@@ -7322,6 +7322,7 @@ extern tree cp_build_qualified_type_real (tree, int, tsubst_flags_t);
extern bool cv_qualified_p (const_tree); extern bool cv_qualified_p (const_tree);
extern tree cv_unqualified (tree); extern tree cv_unqualified (tree);
extern special_function_kind special_function_p (const_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 count_trees (tree);
extern int char_type_p (tree); extern int char_type_p (tree);
extern void verify_stmt_tree (tree); extern void verify_stmt_tree (tree);
......
...@@ -13534,15 +13534,11 @@ grok_special_member_properties (tree decl) ...@@ -13534,15 +13534,11 @@ grok_special_member_properties (tree decl)
are no other parameters or else all other parameters have are no other parameters or else all other parameters have
default arguments. */ default arguments. */
TYPE_HAS_COPY_CTOR (class_type) = 1; TYPE_HAS_COPY_CTOR (class_type) = 1;
if (user_provided_p (decl))
TYPE_HAS_COMPLEX_COPY_CTOR (class_type) = 1;
if (ctor > 1) if (ctor > 1)
TYPE_HAS_CONST_COPY_CTOR (class_type) = 1; TYPE_HAS_CONST_COPY_CTOR (class_type) = 1;
} }
else if (sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (decl))) else if (sufficient_parms_p (FUNCTION_FIRST_USER_PARMTYPE (decl)))
TYPE_HAS_DEFAULT_CONSTRUCTOR (class_type) = 1; 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)) else if (is_list_ctor (decl))
TYPE_HAS_LIST_CTOR (class_type) = 1; TYPE_HAS_LIST_CTOR (class_type) = 1;
...@@ -13563,13 +13559,9 @@ grok_special_member_properties (tree decl) ...@@ -13563,13 +13559,9 @@ grok_special_member_properties (tree decl)
if (assop) if (assop)
{ {
TYPE_HAS_COPY_ASSIGN (class_type) = 1; TYPE_HAS_COPY_ASSIGN (class_type) = 1;
if (user_provided_p (decl))
TYPE_HAS_COMPLEX_COPY_ASSIGN (class_type) = 1;
if (assop != 1) if (assop != 1)
TYPE_HAS_CONST_COPY_ASSIGN (class_type) = 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))) else if (IDENTIFIER_CONV_OP_P (DECL_NAME (decl)))
TYPE_HAS_CONVERSION (class_type) = true; TYPE_HAS_CONVERSION (class_type) = true;
......
...@@ -4504,9 +4504,6 @@ push_class_level_binding_1 (tree name, tree x) ...@@ -4504,9 +4504,6 @@ push_class_level_binding_1 (tree name, tree x)
binding->type = NULL_TREE; 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 else if (TREE_CODE (decl) == USING_DECL
&& TREE_CODE (bval) == USING_DECL && TREE_CODE (bval) == USING_DECL
&& same_type_p (USING_DECL_SCOPE (decl), && same_type_p (USING_DECL_SCOPE (decl),
...@@ -4525,6 +4522,9 @@ push_class_level_binding_1 (tree name, tree x) ...@@ -4525,6 +4522,9 @@ push_class_level_binding_1 (tree name, tree x)
else if (TREE_CODE (bval) == USING_DECL else if (TREE_CODE (bval) == USING_DECL
&& OVL_P (target_decl)) && OVL_P (target_decl))
return true; return true;
else if (OVL_P (target_decl)
&& OVL_P (target_bval))
old_decl = bval;
if (old_decl && binding->scope == class_binding_level) if (old_decl && binding->scope == class_binding_level)
{ {
......
...@@ -5015,6 +5015,31 @@ special_function_p (const_tree decl) ...@@ -5015,6 +5015,31 @@ special_function_p (const_tree decl)
return sfk_none; 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. */ /* Returns nonzero if TYPE is a character type, including wchar_t. */
int int
......
...@@ -6,3 +6,13 @@ struct Y { ...@@ -6,3 +6,13 @@ struct Y {
~Y() requires(true) = default; ~Y() requires(true) = default;
~Y() requires(false) {} ~Y() requires(false) {}
}; };
Y<int> y;
template<typename T>
struct X {
~X() requires(sizeof(T) == 8) = default;
~X() requires(sizeof(T) != 8) {}
};
X<int> x;
// Testcase from P0848R0
// { dg-do compile { target concepts } }
#include <type_traits>
template <typename T>
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<T>
= default;
constexpr optional(optional const& o)
: engaged (o.engaged)
{
if (engaged)
new (&value) T (o.value);
}
~optional()
requires std::is_trivially_destructible_v<T>
= default;
~optional()
{
if (engaged)
value.~T();
}
// ...
};
struct A { A(); A(const A&); ~A(); };
static_assert(std::is_trivially_copy_constructible_v<optional<int>>);
static_assert(!std::is_trivially_copy_constructible_v<optional<A>>);
static_assert(std::is_trivially_destructible_v<optional<int>>);
static_assert(!std::is_trivially_destructible_v<optional<A>>);
// Like cond-triv1.C, but with the declaration order swapped.
// { dg-do compile { target concepts } }
#include <type_traits>
template <typename T>
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<T>
= default;
~optional()
{
if (engaged)
value.~T();
}
~optional()
requires std::is_trivially_destructible_v<T>
= default;
// ...
};
struct A { A(); A(const A&); ~A(); };
static_assert(std::is_trivially_copy_constructible_v<optional<int>>);
static_assert(!std::is_trivially_copy_constructible_v<optional<A>>);
static_assert(std::is_trivially_destructible_v<optional<int>>);
static_assert(!std::is_trivially_destructible_v<optional<A>>);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment