aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2020-06-15 17:11:38 -0400
committerJason Merrill <jason@redhat.com>2020-06-17 15:08:42 -0400
commit44492e248cbff60b12f5cbdaa32b265c5e8c9aff (patch)
tree4be0a18736a2e007ab79abef27ef49154840f581
parent68df8e8c3438dda209c3ad9843933da640c9a71c (diff)
downloadgcc-44492e248cbff60b12f5cbdaa32b265c5e8c9aff.zip
gcc-44492e248cbff60b12f5cbdaa32b265c5e8c9aff.tar.gz
gcc-44492e248cbff60b12f5cbdaa32b265c5e8c9aff.tar.bz2
c++: implicit operator== adjustments from P2002.
P2002R1, adopted at the February C++ meeting, made several refinements to the wording for operator<=>. This implements clarifications in how the implicit operator== is declared: as a duplicate of the operator<=>, with only the return type and name changed. To that end I factored out the declaration copying from build_clone. gcc/cp/ChangeLog: * cp-tree.h (copy_fndecl_with_name): Declare. * class.c (copy_fndecl_with_name): Split out from... (build_clone): ...here. (add_implicitly_declared_members): Add op== to TYPE_FIELDS. * method.c (implicitly_declare_fn): Use copy_fndecl_with_name. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/spaceship-synth9.C: New test.
-rw-r--r--gcc/cp/class.c86
-rw-r--r--gcc/cp/cp-tree.h1
-rw-r--r--gcc/cp/method.c117
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C27
4 files changed, 128 insertions, 103 deletions
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index f8e38ec..629d27d 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -3266,7 +3266,12 @@ add_implicitly_declared_members (tree t, tree* access_decls,
do_friend (NULL_TREE, DECL_NAME (eq), eq,
NULL_TREE, NO_SPECIAL, true);
else
- add_method (t, eq, false);
+ {
+ add_method (t, eq, false);
+ DECL_CHAIN (eq) = TYPE_FIELDS (t);
+ TYPE_FIELDS (t) = eq;
+ }
+ maybe_add_class_template_decl_list (t, eq, DECL_FRIEND_P (space));
}
while (*access_decls)
@@ -4687,37 +4692,13 @@ check_methods (tree t)
}
}
-/* FN is a constructor or destructor. Clone the declaration to create
- a specialized in-charge or not-in-charge version, as indicated by
- NAME. */
-
-static tree
-build_clone (tree fn, tree name)
+tree
+copy_fndecl_with_name (tree fn, tree name)
{
/* Copy the function. */
tree clone = copy_decl (fn);
/* Reset the function name. */
DECL_NAME (clone) = name;
- /* Remember where this function came from. */
- DECL_ABSTRACT_ORIGIN (clone) = fn;
-
- /* Make it easy to find the CLONE given the FN. Note the
- template_result of a template will be chained this way too. */
- DECL_CHAIN (clone) = DECL_CHAIN (fn);
- DECL_CHAIN (fn) = clone;
-
- /* If this is a template, do the rest on the DECL_TEMPLATE_RESULT. */
- if (TREE_CODE (clone) == TEMPLATE_DECL)
- {
- tree result = build_clone (DECL_TEMPLATE_RESULT (clone), name);
- DECL_TEMPLATE_RESULT (clone) = result;
-
- DECL_TEMPLATE_INFO (result) = copy_node (DECL_TEMPLATE_INFO (result));
- DECL_TI_TEMPLATE (result) = clone;
-
- TREE_TYPE (clone) = TREE_TYPE (result);
- return clone;
- }
if (flag_concepts)
/* Clone constraints. */
@@ -4725,7 +4706,6 @@ build_clone (tree fn, tree name)
set_constraints (clone, copy_node (ci));
SET_DECL_ASSEMBLER_NAME (clone, NULL_TREE);
- DECL_CLONED_FUNCTION (clone) = fn;
/* There's no pending inline data for this function. */
DECL_PENDING_INLINE_INFO (clone) = NULL;
DECL_PENDING_INLINE_P (clone) = 0;
@@ -4736,6 +4716,14 @@ build_clone (tree fn, tree name)
DECL_VIRTUAL_P (clone) = 0;
DECL_VINDEX (clone) = NULL_TREE;
}
+ else if (IDENTIFIER_OVL_OP_P (name))
+ {
+ const ovl_op_info_t *ovl_op = IDENTIFIER_OVL_OP_INFO (name);
+ DECL_OVERLOADED_OPERATOR_CODE_RAW (clone) = ovl_op->ovl_op_code;
+ }
+
+ if (DECL_VIRTUAL_P (clone))
+ IDENTIFIER_VIRTUAL_P (name) = true;
bool ctor_omit_inherited_parms_p = ctor_omit_inherited_parms (clone);
if (ctor_omit_inherited_parms_p)
@@ -4807,7 +4795,47 @@ build_clone (tree fn, tree name)
/* Create the RTL for this function. */
SET_DECL_RTL (clone, NULL);
- rest_of_decl_compilation (clone, /*top_level=*/1, at_eof);
+ rest_of_decl_compilation (clone, namespace_bindings_p (), at_eof);
+
+ return clone;
+}
+
+/* FN is a constructor or destructor. Clone the declaration to create
+ a specialized in-charge or not-in-charge version, as indicated by
+ NAME. */
+
+static tree
+build_clone (tree fn, tree name)
+{
+ tree clone;
+
+ /* If this is a template, do the rest on the DECL_TEMPLATE_RESULT. */
+ if (TREE_CODE (fn) == TEMPLATE_DECL)
+ {
+ clone = copy_decl (fn);
+ DECL_NAME (clone) = name;
+
+ tree result = build_clone (DECL_TEMPLATE_RESULT (clone), name);
+ DECL_TEMPLATE_RESULT (clone) = result;
+
+ DECL_TEMPLATE_INFO (result) = copy_node (DECL_TEMPLATE_INFO (result));
+ DECL_TI_TEMPLATE (result) = clone;
+
+ TREE_TYPE (clone) = TREE_TYPE (result);
+ }
+ else
+ {
+ clone = copy_fndecl_with_name (fn, name);
+ DECL_CLONED_FUNCTION (clone) = fn;
+ }
+
+ /* Remember where this function came from. */
+ DECL_ABSTRACT_ORIGIN (clone) = fn;
+
+ /* Make it easy to find the CLONE given the FN. Note the
+ template_result of a template will be chained this way too. */
+ DECL_CHAIN (clone) = DECL_CHAIN (fn);
+ DECL_CHAIN (fn) = clone;
return clone;
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ee27610..c139668 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6448,6 +6448,7 @@ extern void check_abi_tags (tree);
extern tree missing_abi_tags (tree);
extern void fixup_type_variants (tree);
extern void fixup_attribute_variants (tree);
+extern tree copy_fndecl_with_name (tree, tree);
extern void clone_function_decl (tree, bool);
extern void adjust_clone_args (tree);
extern void deduce_noexcept_on_destructor (tree);
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 2d31462..1a819b2 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -2632,7 +2632,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
HOST_WIDE_INT saved_processing_template_decl;
bool deleted_p = false;
bool constexpr_p = false;
- bool friend_p = (kind == sfk_comparison && DECL_FRIEND_P (pattern_fn));
tree inherited_ctor = (kind == sfk_inheriting_constructor
? pattern_fn : NULL_TREE);
@@ -2640,11 +2639,39 @@ implicitly_declare_fn (special_function_kind kind, tree type,
lazily, we may be creating the declaration for a member of TYPE
while in some completely different context. However, TYPE will
never be a dependent class (because we never want to do lookups
- for implicitly defined functions in a dependent class).
- Furthermore, we must set PROCESSING_TEMPLATE_DECL to zero here
+ for implicitly defined functions in a dependent class). */
+ gcc_assert (!dependent_type_p (type));
+
+ /* If the member-specification does not explicitly declare any member or
+ friend named operator==, an == operator function is declared
+ implicitly for each three-way comparison operator function defined as
+ defaulted in the member-specification, with the same access and
+ function-definition and in the same class scope as the respective
+ three-way comparison operator function, except that the return type is
+ replaced with bool and the declarator-id is replaced with
+ operator==.
+
+ [Note: Such an implicitly-declared == operator for a class X is
+ defined as defaulted in the definition of X and has the same
+ parameter-declaration-clause and trailing requires-clause as the
+ respective three-way comparison operator. It is declared with friend,
+ virtual, constexpr, or consteval if the three-way comparison operator
+ function is so declared. If the three-way comparison operator function
+ has no noexcept-specifier, the implicitly-declared == operator
+ function has an implicit exception specification (14.5) that may
+ differ from the implicit exception specification of the three-way
+ comparison operator function. --end note] */
+ if (kind == sfk_comparison)
+ {
+ fn = copy_fndecl_with_name (pattern_fn, ovl_op_identifier (EQ_EXPR));
+ DECL_ARTIFICIAL (fn) = 1;
+ TREE_TYPE (fn) = change_return_type (boolean_type_node, TREE_TYPE (fn));
+ return fn;
+ }
+
+ /* Furthermore, we must set PROCESSING_TEMPLATE_DECL to zero here
because we only create clones for constructors and destructors
when not in a template. */
- gcc_assert (!dependent_type_p (type));
saved_processing_template_decl = processing_template_decl;
processing_template_decl = 0;
@@ -2706,35 +2733,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
break;
}
- case sfk_comparison:
- /* If the class definition does not explicitly declare an == operator
- function, but declares a defaulted three-way comparison operator
- function, an == operator function is declared implicitly with the same
- access as the three-way comparison operator function.
-
- The implicitly-declared == operator for a class X is an inline member
- and is defined as defaulted in the definition of X.
-
- If the three-way comparison operator function is declared as a
- non-static const member, the implicitly-declared == operator function
- is a member of the form
-
- bool X::operator==(const X&) const;
-
- Otherwise, the implicitly-declared == operator function is of the form
-
- friend bool operator==(const X&, const X&); */
- /* No other comparison operator is implicitly declared. */
- name = ovl_op_identifier (false, EQ_EXPR);
- return_type = boolean_type_node;
- rhs_parm_type = cp_build_qualified_type (type, TYPE_QUAL_CONST);
- rhs_parm_type = cp_build_reference_type (rhs_parm_type, false);
- parameter_types = tree_cons (NULL_TREE, rhs_parm_type, parameter_types);
- if (friend_p)
- parameter_types = tree_cons (NULL_TREE, rhs_parm_type, parameter_types);
- this_quals = TYPE_QUAL_CONST;
- break;
-
default:
gcc_unreachable ();
}
@@ -2752,10 +2750,9 @@ implicitly_declare_fn (special_function_kind kind, tree type,
else if (cxx_dialect >= cxx11)
{
raises = noexcept_deferred_spec;
- if (kind != sfk_comparison)
- synthesized_method_walk (type, kind, const_p, NULL, &trivial_p,
- &deleted_p, &constexpr_p, false,
- &inherited_ctor, inherited_parms);
+ synthesized_method_walk (type, kind, const_p, NULL, &trivial_p,
+ &deleted_p, &constexpr_p, false,
+ &inherited_ctor, inherited_parms);
}
else
synthesized_method_walk (type, kind, const_p, &raises, &trivial_p,
@@ -2777,14 +2774,9 @@ implicitly_declare_fn (special_function_kind kind, tree type,
type_set_nontrivial_flag (type, kind);
/* Create the function. */
- if (friend_p)
- fn_type = build_function_type (return_type, parameter_types);
- else
- {
- tree this_type = cp_build_qualified_type (type, this_quals);
- fn_type = build_method_type_directly (this_type, return_type,
- parameter_types);
- }
+ tree this_type = cp_build_qualified_type (type, this_quals);
+ fn_type = build_method_type_directly (this_type, return_type,
+ parameter_types);
if (raises)
{
@@ -2796,12 +2788,7 @@ implicitly_declare_fn (special_function_kind kind, tree type,
gcc_assert (seen_error ());
}
fn = build_lang_decl (FUNCTION_DECL, name, fn_type);
- if (kind == sfk_comparison)
- {
- DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (pattern_fn);
- DECL_MAYBE_DELETED (fn) = true;
- }
- else if (kind != sfk_inheriting_constructor)
+ if (kind != sfk_inheriting_constructor)
DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (TYPE_NAME (type));
if (IDENTIFIER_OVL_OP_P (name))
@@ -2829,13 +2816,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
retrofit_lang_decl (decl);
DECL_PARM_INDEX (decl) = DECL_PARM_LEVEL (decl) = 1;
DECL_ARGUMENTS (fn) = decl;
- if (friend_p)
- {
- /* The second parm of friend op==. */
- tree decl2 = copy_decl (decl);
- DECL_CHAIN (decl) = decl2;
- DECL_PARM_INDEX (decl2) = 2;
- }
}
else if (kind == sfk_inheriting_constructor)
{
@@ -2861,17 +2841,12 @@ implicitly_declare_fn (special_function_kind kind, tree type,
constexpr_p = DECL_DECLARED_CONSTEXPR_P (inherited_ctor);
}
- if (friend_p)
- DECL_CONTEXT (fn) = DECL_CONTEXT (pattern_fn);
- else
- {
- /* Add the "this" parameter. */
- this_parm = build_this_parm (fn, fn_type, this_quals);
- DECL_CHAIN (this_parm) = DECL_ARGUMENTS (fn);
- DECL_ARGUMENTS (fn) = this_parm;
+ /* Add the "this" parameter. */
+ this_parm = build_this_parm (fn, fn_type, this_quals);
+ DECL_CHAIN (this_parm) = DECL_ARGUMENTS (fn);
+ DECL_ARGUMENTS (fn) = this_parm;
- grokclassfn (type, fn, kind == sfk_destructor ? DTOR_FLAG : NO_SPECIAL);
- }
+ grokclassfn (type, fn, kind == sfk_destructor ? DTOR_FLAG : NO_SPECIAL);
DECL_IN_AGGR_P (fn) = 1;
DECL_ARTIFICIAL (fn) = 1;
@@ -2887,12 +2862,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
set_linkage_according_to_type (type, fn);
if (TREE_PUBLIC (fn))
DECL_COMDAT (fn) = 1;
- if (kind == sfk_comparison && !friend_p)
- {
- /* The implicit op== has the same access as the op<=>. */
- TREE_PRIVATE (fn) = TREE_PRIVATE (pattern_fn);
- TREE_PROTECTED (fn) = TREE_PROTECTED (pattern_fn);
- }
rest_of_decl_compilation (fn, namespace_bindings_p (), at_eof);
gcc_assert (!TREE_USED (fn));
diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
new file mode 100644
index 0000000..33b547d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
@@ -0,0 +1,27 @@
+// Test that most properties of <=> are copied to ==.
+// { dg-do compile { target c++20 } }
+
+#include <compare>
+
+template<typename T> struct X {
+ T t;
+ friend consteval std::partial_ordering operator<=>(X, X) requires (sizeof(T) != 1) = default;
+ // implicitly declares: friend constexpr bool operator==(X, X) requires (sizeof(T) != 1) = default;
+};
+
+template<typename T> struct Y {
+ [[nodiscard]] virtual std::strong_ordering operator<=>(const Y&) const = default;
+ // implicitly declares: [[nodiscard]] virtual bool operator==(const Y&) const = default;
+};
+
+struct Z: Y<int>
+{
+ bool operator==(const Y&) const noexcept override;
+};
+
+int main()
+{
+ X<char>() == X<char>(); // { dg-error "no match" }
+ X<int> x; x == x; // { dg-error "x' is not usable in a constant expression" }
+ Y<int>() == Y<int>(); // { dg-warning "nodiscard" }
+}