mirror of
https://gcc.gnu.org/git/gcc.git
synced 2026-02-22 20:01:22 -05:00
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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
117
gcc/cp/method.c
117
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));
|
||||
|
||||
|
||||
27
gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
Normal file
27
gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
Normal file
@@ -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" }
|
||||
}
|
||||
Reference in New Issue
Block a user