aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2019-08-15 08:38:50 -0400
committerJason Merrill <jason@gcc.gnu.org>2019-08-15 08:38:50 -0400
commitc735f8f1a0c5a5d1e114e45390b35882f539ff69 (patch)
tree1ea2d4c11b3a6cdb3a137df04d979591a4e9277a /gcc
parent84cc60bf83e03267f5794a16b3eb83508a3196ce (diff)
downloadgcc-c735f8f1a0c5a5d1e114e45390b35882f539ff69.zip
gcc-c735f8f1a0c5a5d1e114e45390b35882f539ff69.tar.gz
gcc-c735f8f1a0c5a5d1e114e45390b35882f539ff69.tar.bz2
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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/ChangeLog11
-rw-r--r--gcc/cp/class.c98
-rw-r--r--gcc/cp/cp-tree.h3
-rw-r--r--gcc/cp/decl.c8
-rw-r--r--gcc/cp/name-lookup.c6
-rw-r--r--gcc/cp/tree.c25
-rw-r--r--gcc/testsuite/g++.dg/concepts/pr89036.C10
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/cond-triv1.C46
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/cond-triv1a.C46
9 files changed, 233 insertions, 20 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 9eeba3d..25172ac 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -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>
PR c++/91436
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index b61152c..cc53b15 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)
"%<transaction_safe_dynamic%> 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 bdb7778..05f9186 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 ff3b90d..08b7baa 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 16c7428..5f5ff81 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 27bc351..bca9210 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 f83ef8b..5dcd649 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<int> y;
+
+template<typename T>
+struct X {
+ ~X() requires(sizeof(T) == 8) = default;
+ ~X() requires(sizeof(T) != 8) {}
+};
+
+X<int> 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 0000000..8f5806e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/cond-triv1.C
@@ -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>>);
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 0000000..febd109
--- /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 <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>>);