Commit 1271bdf0 by Jason Merrill

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.
parent 4dd2fd9c
...@@ -3266,7 +3266,12 @@ add_implicitly_declared_members (tree t, tree* access_decls, ...@@ -3266,7 +3266,12 @@ add_implicitly_declared_members (tree t, tree* access_decls,
do_friend (NULL_TREE, DECL_NAME (eq), eq, do_friend (NULL_TREE, DECL_NAME (eq), eq,
NULL_TREE, NO_SPECIAL, true); NULL_TREE, NO_SPECIAL, true);
else 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) while (*access_decls)
...@@ -4687,6 +4692,114 @@ check_methods (tree t) ...@@ -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 /* FN is a constructor or destructor. Clone the declaration to create
a specialized in-charge or not-in-charge version, as indicated by a specialized in-charge or not-in-charge version, as indicated by
NAME. */ NAME. */
......
...@@ -6458,6 +6458,7 @@ extern void check_abi_tags (tree); ...@@ -6458,6 +6458,7 @@ extern void check_abi_tags (tree);
extern tree missing_abi_tags (tree); extern tree missing_abi_tags (tree);
extern void fixup_type_variants (tree); extern void fixup_type_variants (tree);
extern void fixup_attribute_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 clone_function_decl (tree, bool);
extern void adjust_clone_args (tree); extern void adjust_clone_args (tree);
extern void deduce_noexcept_on_destructor (tree); extern void deduce_noexcept_on_destructor (tree);
......
...@@ -9338,6 +9338,7 @@ grokfndecl (tree ctype, ...@@ -9338,6 +9338,7 @@ grokfndecl (tree ctype,
special_function_kind sfk, special_function_kind sfk,
bool funcdef_flag, bool funcdef_flag,
bool late_return_type_p, bool late_return_type_p,
int initialized,
int template_count, int template_count,
tree in_namespace, tree in_namespace,
tree* attrlist, tree* attrlist,
...@@ -9381,7 +9382,7 @@ grokfndecl (tree ctype, ...@@ -9381,7 +9382,7 @@ grokfndecl (tree ctype,
/* C++20 CA378: Remove non-templated constrained functions. */ /* C++20 CA378: Remove non-templated constrained functions. */
if (ci && !flag_concepts_ts if (ci && !flag_concepts_ts
&& (!processing_template_decl && (!processing_template_decl
|| (friendp && !memtmpl && !funcdef_flag))) || (friendp && !memtmpl && !initialized && !funcdef_flag)))
{ {
error_at (location, "constraints on a non-templated function"); error_at (location, "constraints on a non-templated function");
ci = NULL_TREE; ci = NULL_TREE;
...@@ -13267,6 +13268,7 @@ grokdeclarator (const cp_declarator *declarator, ...@@ -13267,6 +13268,7 @@ grokdeclarator (const cp_declarator *declarator,
| (8 * consteval_p), | (8 * consteval_p),
initialized == SD_DELETED, sfk, initialized == SD_DELETED, sfk,
funcdef_flag, late_return_type_p, funcdef_flag, late_return_type_p,
initialized,
template_count, in_namespace, template_count, in_namespace,
attrlist, id_loc); attrlist, id_loc);
decl = set_virt_specifiers (decl, virt_specifiers); decl = set_virt_specifiers (decl, virt_specifiers);
...@@ -13576,6 +13578,7 @@ grokdeclarator (const cp_declarator *declarator, ...@@ -13576,6 +13578,7 @@ grokdeclarator (const cp_declarator *declarator,
sfk, sfk,
funcdef_flag, funcdef_flag,
late_return_type_p, late_return_type_p,
initialized,
template_count, in_namespace, attrlist, template_count, in_namespace, attrlist,
id_loc); id_loc);
if (decl == NULL_TREE) if (decl == NULL_TREE)
......
...@@ -2657,7 +2657,6 @@ implicitly_declare_fn (special_function_kind kind, tree type, ...@@ -2657,7 +2657,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
HOST_WIDE_INT saved_processing_template_decl; HOST_WIDE_INT saved_processing_template_decl;
bool deleted_p = false; bool deleted_p = false;
bool constexpr_p = false; bool constexpr_p = false;
bool friend_p = (kind == sfk_comparison && DECL_FRIEND_P (pattern_fn));
tree inherited_ctor = (kind == sfk_inheriting_constructor tree inherited_ctor = (kind == sfk_inheriting_constructor
? pattern_fn : NULL_TREE); ? pattern_fn : NULL_TREE);
...@@ -2665,11 +2664,39 @@ implicitly_declare_fn (special_function_kind kind, tree type, ...@@ -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 lazily, we may be creating the declaration for a member of TYPE
while in some completely different context. However, TYPE will while in some completely different context. However, TYPE will
never be a dependent class (because we never want to do lookups never be a dependent class (because we never want to do lookups
for implicitly defined functions in a dependent class). for implicitly defined functions in a dependent class). */
Furthermore, we must set PROCESSING_TEMPLATE_DECL to zero here 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 because we only create clones for constructors and destructors
when not in a template. */ when not in a template. */
gcc_assert (!dependent_type_p (type));
saved_processing_template_decl = processing_template_decl; saved_processing_template_decl = processing_template_decl;
processing_template_decl = 0; processing_template_decl = 0;
...@@ -2731,35 +2758,6 @@ implicitly_declare_fn (special_function_kind kind, tree type, ...@@ -2731,35 +2758,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
break; 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: default:
gcc_unreachable (); gcc_unreachable ();
} }
...@@ -2777,10 +2775,9 @@ implicitly_declare_fn (special_function_kind kind, tree type, ...@@ -2777,10 +2775,9 @@ implicitly_declare_fn (special_function_kind kind, tree type,
else if (cxx_dialect >= cxx11) else if (cxx_dialect >= cxx11)
{ {
raises = noexcept_deferred_spec; raises = noexcept_deferred_spec;
if (kind != sfk_comparison) synthesized_method_walk (type, kind, const_p, NULL, &trivial_p,
synthesized_method_walk (type, kind, const_p, NULL, &trivial_p, &deleted_p, &constexpr_p, false,
&deleted_p, &constexpr_p, false, &inherited_ctor, inherited_parms);
&inherited_ctor, inherited_parms);
} }
else else
synthesized_method_walk (type, kind, const_p, &raises, &trivial_p, synthesized_method_walk (type, kind, const_p, &raises, &trivial_p,
...@@ -2802,14 +2799,9 @@ implicitly_declare_fn (special_function_kind kind, tree type, ...@@ -2802,14 +2799,9 @@ implicitly_declare_fn (special_function_kind kind, tree type,
type_set_nontrivial_flag (type, kind); type_set_nontrivial_flag (type, kind);
/* Create the function. */ /* Create the function. */
if (friend_p) tree this_type = cp_build_qualified_type (type, this_quals);
fn_type = build_function_type (return_type, parameter_types); fn_type = build_method_type_directly (this_type, return_type,
else 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) if (raises)
{ {
...@@ -2821,12 +2813,7 @@ implicitly_declare_fn (special_function_kind kind, tree type, ...@@ -2821,12 +2813,7 @@ implicitly_declare_fn (special_function_kind kind, tree type,
gcc_assert (seen_error ()); gcc_assert (seen_error ());
} }
fn = build_lang_decl (FUNCTION_DECL, name, fn_type); fn = build_lang_decl (FUNCTION_DECL, name, fn_type);
if (kind == sfk_comparison) if (kind != sfk_inheriting_constructor)
{
DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (pattern_fn);
DECL_MAYBE_DELETED (fn) = true;
}
else if (kind != sfk_inheriting_constructor)
DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (TYPE_NAME (type)); DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (TYPE_NAME (type));
if (IDENTIFIER_OVL_OP_P (name)) if (IDENTIFIER_OVL_OP_P (name))
...@@ -2854,13 +2841,6 @@ implicitly_declare_fn (special_function_kind kind, tree type, ...@@ -2854,13 +2841,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
retrofit_lang_decl (decl); retrofit_lang_decl (decl);
DECL_PARM_INDEX (decl) = DECL_PARM_LEVEL (decl) = 1; DECL_PARM_INDEX (decl) = DECL_PARM_LEVEL (decl) = 1;
DECL_ARGUMENTS (fn) = decl; 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) else if (kind == sfk_inheriting_constructor)
{ {
...@@ -2886,17 +2866,12 @@ implicitly_declare_fn (special_function_kind kind, tree type, ...@@ -2886,17 +2866,12 @@ implicitly_declare_fn (special_function_kind kind, tree type,
constexpr_p = DECL_DECLARED_CONSTEXPR_P (inherited_ctor); constexpr_p = DECL_DECLARED_CONSTEXPR_P (inherited_ctor);
} }
if (friend_p) /* Add the "this" parameter. */
DECL_CONTEXT (fn) = DECL_CONTEXT (pattern_fn); this_parm = build_this_parm (fn, fn_type, this_quals);
else 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_IN_AGGR_P (fn) = 1;
DECL_ARTIFICIAL (fn) = 1; DECL_ARTIFICIAL (fn) = 1;
...@@ -2912,12 +2887,6 @@ implicitly_declare_fn (special_function_kind kind, tree type, ...@@ -2912,12 +2887,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
set_linkage_according_to_type (type, fn); set_linkage_according_to_type (type, fn);
if (TREE_PUBLIC (fn)) if (TREE_PUBLIC (fn))
DECL_COMDAT (fn) = 1; 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); rest_of_decl_compilation (fn, namespace_bindings_p (), at_eof);
gcc_assert (!TREE_USED (fn)); gcc_assert (!TREE_USED (fn));
......
// 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" }
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment