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
This commit is contained in:
parent
84cc60bf83
commit
c735f8f1a0
|
@ -1,3 +1,14 @@
|
||||||
|
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
"%<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)
|
||||||
/* 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);
|
||||||
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);
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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 {
|
||||||
~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;
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
// 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>>);
|
|
@ -0,0 +1,46 @@
|
||||||
|
// 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>>);
|
Loading…
Reference in New Issue