From: Jason Merrill Date: Mon, 15 Jun 2020 21:11:38 +0000 (-0400) Subject: c++: implicit operator== adjustments from P2002. X-Git-Tag: releases/gcc-10.2.0~172 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1271bdf0d17442040ee4b0ec66da9cab2d4dd3e9;p=thirdparty%2Fgcc.git 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. For GCC 10 I'm leaving build_clone alone, to reduce the chance of non-C++20-mode regressions. The decl.c changes are a hack to avoid complaining about constraints on a non-template friend that isn't defined in the class. In this case the defaulted comparison operator should be considered defined, but we weren't setting funcdef_flag properly. For GCC 11 I fixed it properly. gcc/cp/ChangeLog: * cp-tree.h (copy_fndecl_with_name): Declare. * class.c (copy_fndecl_with_name): Copy from build_clone. (add_implicitly_declared_members): Add op== to TYPE_FIELDS. * method.c (implicitly_declare_fn): Use copy_fndecl_with_name. * decl.c (grokfndecl): Add initialized parm. (grokdeclarator): Pass it down. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/spaceship-synth9.C: New test. --- diff --git a/gcc/cp/class.c b/gcc/cp/class.c index ade2ab47104c..3e2e2c74392b 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,6 +4692,114 @@ check_methods (tree t) } } +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; + + if (flag_concepts) + /* Clone constraints. */ + if (tree ci = get_constraints (fn)) + set_constraints (clone, copy_node (ci)); + + SET_DECL_ASSEMBLER_NAME (clone, NULL_TREE); + /* There's no pending inline data for this function. */ + DECL_PENDING_INLINE_INFO (clone) = NULL; + DECL_PENDING_INLINE_P (clone) = 0; + + /* The base-class destructor is not virtual. */ + if (name == base_dtor_identifier) + { + 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) + gcc_assert (DECL_HAS_IN_CHARGE_PARM_P (clone)); + + /* If there was an in-charge parameter, drop it from the function + type. */ + if (DECL_HAS_IN_CHARGE_PARM_P (clone)) + { + tree basetype = TYPE_METHOD_BASETYPE (TREE_TYPE (clone)); + tree parmtypes = TYPE_ARG_TYPES (TREE_TYPE (clone)); + /* Skip the `this' parameter. */ + parmtypes = TREE_CHAIN (parmtypes); + /* Skip the in-charge parameter. */ + parmtypes = TREE_CHAIN (parmtypes); + /* And the VTT parm, in a complete [cd]tor. */ + if (DECL_HAS_VTT_PARM_P (fn) + && ! DECL_NEEDS_VTT_PARM_P (clone)) + parmtypes = TREE_CHAIN (parmtypes); + if (ctor_omit_inherited_parms_p) + { + /* If we're omitting inherited parms, that just leaves the VTT. */ + gcc_assert (DECL_NEEDS_VTT_PARM_P (clone)); + parmtypes = tree_cons (NULL_TREE, vtt_parm_type, void_list_node); + } + TREE_TYPE (clone) + = build_method_type_directly (basetype, + TREE_TYPE (TREE_TYPE (clone)), + parmtypes); + TREE_TYPE (clone) + = cp_build_type_attribute_variant (TREE_TYPE (clone), + TYPE_ATTRIBUTES (TREE_TYPE (fn))); + TREE_TYPE (clone) + = cxx_copy_lang_qualifiers (TREE_TYPE (clone), TREE_TYPE (fn)); + } + + /* Copy the function parameters. */ + DECL_ARGUMENTS (clone) = copy_list (DECL_ARGUMENTS (clone)); + /* Remove the in-charge parameter. */ + if (DECL_HAS_IN_CHARGE_PARM_P (clone)) + { + DECL_CHAIN (DECL_ARGUMENTS (clone)) + = DECL_CHAIN (DECL_CHAIN (DECL_ARGUMENTS (clone))); + DECL_HAS_IN_CHARGE_PARM_P (clone) = 0; + } + /* And the VTT parm, in a complete [cd]tor. */ + if (DECL_HAS_VTT_PARM_P (fn)) + { + if (DECL_NEEDS_VTT_PARM_P (clone)) + DECL_HAS_VTT_PARM_P (clone) = 1; + else + { + DECL_CHAIN (DECL_ARGUMENTS (clone)) + = DECL_CHAIN (DECL_CHAIN (DECL_ARGUMENTS (clone))); + DECL_HAS_VTT_PARM_P (clone) = 0; + } + } + + /* A base constructor inheriting from a virtual base doesn't get the + arguments. */ + if (ctor_omit_inherited_parms_p) + DECL_CHAIN (DECL_CHAIN (DECL_ARGUMENTS (clone))) = NULL_TREE; + + for (tree parms = DECL_ARGUMENTS (clone); parms; parms = DECL_CHAIN (parms)) + { + DECL_CONTEXT (parms) = clone; + cxx_dup_lang_specific_decl (parms); + } + + /* Create the RTL for this function. */ + SET_DECL_RTL (clone, NULL); + 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. */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f220ed9f52af..247919936c0c 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6458,6 +6458,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/decl.c b/gcc/cp/decl.c index df07d995a9bc..90111e4c7864 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -9338,6 +9338,7 @@ grokfndecl (tree ctype, special_function_kind sfk, bool funcdef_flag, bool late_return_type_p, + int initialized, int template_count, tree in_namespace, tree* attrlist, @@ -9381,7 +9382,7 @@ grokfndecl (tree ctype, /* C++20 CA378: Remove non-templated constrained functions. */ if (ci && !flag_concepts_ts && (!processing_template_decl - || (friendp && !memtmpl && !funcdef_flag))) + || (friendp && !memtmpl && !initialized && !funcdef_flag))) { error_at (location, "constraints on a non-templated function"); ci = NULL_TREE; @@ -13267,6 +13268,7 @@ grokdeclarator (const cp_declarator *declarator, | (8 * consteval_p), initialized == SD_DELETED, sfk, funcdef_flag, late_return_type_p, + initialized, template_count, in_namespace, attrlist, id_loc); decl = set_virt_specifiers (decl, virt_specifiers); @@ -13576,6 +13578,7 @@ grokdeclarator (const cp_declarator *declarator, sfk, funcdef_flag, late_return_type_p, + initialized, template_count, in_namespace, attrlist, id_loc); if (decl == NULL_TREE) diff --git a/gcc/cp/method.c b/gcc/cp/method.c index 12ba6a7b56d8..3a3e060843ed 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -2657,7 +2657,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); @@ -2665,11 +2664,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; @@ -2731,35 +2758,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 (); } @@ -2777,10 +2775,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, @@ -2802,14 +2799,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) { @@ -2821,12 +2813,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)) @@ -2854,13 +2841,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) { @@ -2886,17 +2866,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; @@ -2912,12 +2887,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 000000000000..33b547d2b505 --- /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" } +}