Commit d6ef77e0 by Jason Merrill

c++: Fix implicit friend operator==.

It seems that in writing testcases for the operator<=> proposal I didn't
include any tests for implicitly declared friend operator==, and
consequently it didn't work.

2020-02-11  Jason Merrill  <jason@redhat.com>

	PR c++/93675
	* class.c (add_implicitly_declared_members): Use do_friend.
	* method.c (implicitly_declare_fn): Fix friend handling.
	(decl_remember_implicit_trigger_p): New.
	(synthesize_method): Use it.
	* decl2.c (mark_used): Use it.
parent 9a5338e5
2020-02-11 Jason Merrill <jason@redhat.com> 2020-02-11 Jason Merrill <jason@redhat.com>
PR c++/93675
* class.c (add_implicitly_declared_members): Use do_friend.
* method.c (implicitly_declare_fn): Fix friend handling.
(decl_remember_implicit_trigger_p): New.
(synthesize_method): Use it.
* decl2.c (mark_used): Use it.
2020-02-11 Jason Merrill <jason@redhat.com>
PR c++/93650 PR c++/93650
PR c++/90691 PR c++/90691
* constexpr.c (maybe_constant_value): Correct earlier change. * constexpr.c (maybe_constant_value): Correct earlier change.
......
...@@ -3241,6 +3241,10 @@ add_implicitly_declared_members (tree t, tree* access_decls, ...@@ -3241,6 +3241,10 @@ add_implicitly_declared_members (tree t, tree* access_decls,
{ {
tree eq = implicitly_declare_fn (sfk_comparison, t, false, space, tree eq = implicitly_declare_fn (sfk_comparison, t, false, space,
NULL_TREE); NULL_TREE);
if (DECL_FRIEND_P (space))
do_friend (NULL_TREE, DECL_NAME (eq), eq,
NULL_TREE, NO_SPECIAL, true);
else
add_method (t, eq, false); add_method (t, eq, false);
} }
......
...@@ -6801,6 +6801,7 @@ extern void after_nsdmi_defaulted_late_checks (tree); ...@@ -6801,6 +6801,7 @@ extern void after_nsdmi_defaulted_late_checks (tree);
extern bool maybe_explain_implicit_delete (tree); extern bool maybe_explain_implicit_delete (tree);
extern void explain_implicit_non_constexpr (tree); extern void explain_implicit_non_constexpr (tree);
extern void deduce_inheriting_ctor (tree); extern void deduce_inheriting_ctor (tree);
extern bool decl_remember_implicit_trigger_p (tree);
extern void synthesize_method (tree); extern void synthesize_method (tree);
extern tree lazily_declare_fn (special_function_kind, extern tree lazily_declare_fn (special_function_kind,
tree); tree);
......
...@@ -5650,7 +5650,7 @@ mark_used (tree decl, tsubst_flags_t complain) ...@@ -5650,7 +5650,7 @@ mark_used (tree decl, tsubst_flags_t complain)
/* Remember the current location for a function we will end up /* Remember the current location for a function we will end up
synthesizing. Then we can inform the user where it was synthesizing. Then we can inform the user where it was
required in the case of error. */ required in the case of error. */
if (DECL_ARTIFICIAL (decl)) if (decl_remember_implicit_trigger_p (decl))
DECL_SOURCE_LOCATION (decl) = input_location; DECL_SOURCE_LOCATION (decl) = input_location;
/* Synthesizing an implicitly defined member function will result in /* Synthesizing an implicitly defined member function will result in
......
...@@ -1463,6 +1463,22 @@ build_comparison_op (tree fndecl, tsubst_flags_t complain) ...@@ -1463,6 +1463,22 @@ build_comparison_op (tree fndecl, tsubst_flags_t complain)
--cp_unevaluated_operand; --cp_unevaluated_operand;
} }
/* True iff DECL is an implicitly-declared special member function with no real
source location, so we can use its DECL_SOURCE_LOCATION to remember where we
triggered its synthesis. */
bool
decl_remember_implicit_trigger_p (tree decl)
{
if (!DECL_ARTIFICIAL (decl))
return false;
special_function_kind sfk = special_function_p (decl);
/* Inherited constructors have the location of their using-declaration, and
operator== has the location of the corresponding operator<=>. */
return (sfk != sfk_inheriting_constructor
&& sfk != sfk_comparison);
}
/* Synthesize FNDECL, a non-static member function. */ /* Synthesize FNDECL, a non-static member function. */
void void
...@@ -1479,7 +1495,7 @@ synthesize_method (tree fndecl) ...@@ -1479,7 +1495,7 @@ synthesize_method (tree fndecl)
/* Reset the source location, we might have been previously /* Reset the source location, we might have been previously
deferred, and thus have saved where we were first needed. */ deferred, and thus have saved where we were first needed. */
if (DECL_ARTIFICIAL (fndecl) && !DECL_INHERITED_CTOR (fndecl)) if (decl_remember_implicit_trigger_p (fndecl))
DECL_SOURCE_LOCATION (fndecl) DECL_SOURCE_LOCATION (fndecl)
= DECL_SOURCE_LOCATION (TYPE_NAME (DECL_CONTEXT (fndecl))); = DECL_SOURCE_LOCATION (TYPE_NAME (DECL_CONTEXT (fndecl)));
...@@ -2717,9 +2733,15 @@ implicitly_declare_fn (special_function_kind kind, tree type, ...@@ -2717,9 +2733,15 @@ 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)
fn_type = build_function_type (return_type, parameter_types);
else
{
tree this_type = cp_build_qualified_type (type, this_quals); tree this_type = cp_build_qualified_type (type, this_quals);
fn_type = build_method_type_directly (this_type, return_type, fn_type = build_method_type_directly (this_type, return_type,
parameter_types); parameter_types);
}
if (raises) if (raises)
{ {
if (raises != error_mark_node) if (raises != error_mark_node)
...@@ -2794,12 +2816,19 @@ implicitly_declare_fn (special_function_kind kind, tree type, ...@@ -2794,12 +2816,19 @@ implicitly_declare_fn (special_function_kind kind, tree type,
inheriting constructor doesn't satisfy the requirements. */ inheriting constructor doesn't satisfy the requirements. */
constexpr_p = DECL_DECLARED_CONSTEXPR_P (inherited_ctor); constexpr_p = DECL_DECLARED_CONSTEXPR_P (inherited_ctor);
} }
if (friend_p)
DECL_CONTEXT (fn) = DECL_CONTEXT (pattern_fn);
else
{
/* Add the "this" parameter. */ /* Add the "this" parameter. */
this_parm = build_this_parm (fn, fn_type, this_quals); this_parm = build_this_parm (fn, fn_type, this_quals);
DECL_CHAIN (this_parm) = DECL_ARGUMENTS (fn); DECL_CHAIN (this_parm) = DECL_ARGUMENTS (fn);
DECL_ARGUMENTS (fn) = this_parm; 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;
DECL_DEFAULTED_FN (fn) = 1; DECL_DEFAULTED_FN (fn) = 1;
......
// Test with only spaceship defaulted.
// { dg-do run { target c++2a } }
#include <compare>
struct D
{
int i;
friend auto operator<=>(const D&, const D&) = default;
// friend auto operator==(const D& x, const D&) = default;
// friend auto operator!=(const D& x, const D&) = default;
// friend auto operator< (const D& x, const D&) = default;
// friend auto operator<=(const D& x, const D&) = default;
// friend auto operator> (const D& x, const D&) = default;
// friend auto operator>=(const D& x, const D&) = default;
};
#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
int main()
{
D d{42};
D d2{24};
assert (is_eq (d <=> d));
assert (is_lteq (d <=> d));
assert (is_gteq (d <=> d));
assert (is_lt (d2 <=> d));
assert (is_lteq (d2 <=> d));
assert (is_gt (d <=> d2));
assert (is_gteq (d <=> d2));
assert (d == d);
assert (!(d2 == d));
assert (!(d == d2));
assert (d != d2);
assert (!(d2 != d2));
assert (d2 < d);
assert (d2 <= d);
assert (d > d2);
assert (d >= d2);
}
// Test with only spaceship defaulted.
// { dg-do run { target c++2a } }
#include <compare>
struct D
{
int i;
friend auto operator<=>(D, D) = default;
// friend auto operator==(D, D) = default;
// friend auto operator!=(D, D) = default;
// friend auto operator< (D, D) = default;
// friend auto operator<=(D, D) = default;
// friend auto operator> (D, D) = default;
// friend auto operator>=(D, D) = default;
};
#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
int main()
{
D d{42};
D d2{24};
assert (is_eq (d <=> d));
assert (is_lteq (d <=> d));
assert (is_gteq (d <=> d));
assert (is_lt (d2 <=> d));
assert (is_lteq (d2 <=> d));
assert (is_gt (d <=> d2));
assert (is_gteq (d <=> d2));
assert (d == d);
assert (!(d2 == d));
assert (!(d == d2));
assert (d != d2);
assert (!(d2 != d2));
assert (d2 < d);
assert (d2 <= d);
assert (d > d2);
assert (d >= d2);
}
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