From 44492e248cbff60b12f5cbdaa32b265c5e8c9aff Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 15 Jun 2020 17:11:38 -0400 Subject: [PATCH] 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. --- gcc/cp/class.c | 86 ++++++++----- gcc/cp/cp-tree.h | 1 + gcc/cp/method.c | 117 +++++++----------- gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C | 27 ++++ 4 files changed, 128 insertions(+), 103 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C diff --git a/gcc/cp/class.c b/gcc/cp/class.c index f8e38ec9d8c..629d27da894 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 ee276107928..c1396686968 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 2d31462c901..1a819b29173 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 00000000000..33b547d2b50 --- /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 + +template 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 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 +{ + bool operator==(const Y&) const noexcept override; +}; + +int main() +{ + X() == X(); // { dg-error "no match" } + X x; x == x; // { dg-error "x' is not usable in a constant expression" } + Y() == Y(); // { dg-warning "nodiscard" } +} -- 2.30.2