From: Jason Merrill Date: Tue, 11 Feb 2020 11:04:37 +0000 (+0100) Subject: c++: Fix implicit friend operator==. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d6ef77e023cfe0bb3b12b88ae46b77da356d7f85;p=gcc.git 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 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. --- diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index f4b04798d3c..7567bbe6722 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,12 @@ +2020-02-11 Jason Merrill + + 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 PR c++/93650 diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 719c3ece6e1..f9e46ca708f 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -3241,7 +3241,11 @@ add_implicitly_declared_members (tree t, tree* access_decls, { tree eq = implicitly_declare_fn (sfk_comparison, t, false, space, NULL_TREE); - add_method (t, eq, false); + if (DECL_FRIEND_P (space)) + do_friend (NULL_TREE, DECL_NAME (eq), eq, + NULL_TREE, NO_SPECIAL, true); + else + add_method (t, eq, false); } while (*access_decls) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 043bc404140..53de2b0afe7 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6801,6 +6801,7 @@ extern void after_nsdmi_defaulted_late_checks (tree); extern bool maybe_explain_implicit_delete (tree); extern void explain_implicit_non_constexpr (tree); extern void deduce_inheriting_ctor (tree); +extern bool decl_remember_implicit_trigger_p (tree); extern void synthesize_method (tree); extern tree lazily_declare_fn (special_function_kind, tree); diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 98d8e6a6b53..2efb2e54f37 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -5650,7 +5650,7 @@ mark_used (tree decl, tsubst_flags_t complain) /* Remember the current location for a function we will end up synthesizing. Then we can inform the user where it was required in the case of error. */ - if (DECL_ARTIFICIAL (decl)) + if (decl_remember_implicit_trigger_p (decl)) DECL_SOURCE_LOCATION (decl) = input_location; /* Synthesizing an implicitly defined member function will result in diff --git a/gcc/cp/method.c b/gcc/cp/method.c index cfc37bc1b17..790d5704092 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -1463,6 +1463,22 @@ build_comparison_op (tree fndecl, tsubst_flags_t complain) --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. */ void @@ -1479,7 +1495,7 @@ synthesize_method (tree fndecl) /* Reset the source location, we might have been previously 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 (TYPE_NAME (DECL_CONTEXT (fndecl))); @@ -2717,9 +2733,15 @@ implicitly_declare_fn (special_function_kind kind, tree type, type_set_nontrivial_flag (type, kind); /* Create the function. */ - tree this_type = cp_build_qualified_type (type, this_quals); - fn_type = build_method_type_directly (this_type, return_type, - parameter_types); + 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); + } + if (raises) { if (raises != error_mark_node) @@ -2794,12 +2816,19 @@ implicitly_declare_fn (special_function_kind kind, tree type, inheriting constructor doesn't satisfy the requirements. */ constexpr_p = DECL_DECLARED_CONSTEXPR_P (inherited_ctor); } - /* 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); + 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; + + grokclassfn (type, fn, kind == sfk_destructor ? DTOR_FLAG : NO_SPECIAL); + } + DECL_IN_AGGR_P (fn) = 1; DECL_ARTIFICIAL (fn) = 1; DECL_DEFAULTED_FN (fn) = 1; diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth2a.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth2a.C new file mode 100644 index 00000000000..11fe32f86dc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth2a.C @@ -0,0 +1,43 @@ +// Test with only spaceship defaulted. +// { dg-do run { target c++2a } } + +#include + +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); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth2b.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth2b.C new file mode 100644 index 00000000000..2632f525aad --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth2b.C @@ -0,0 +1,43 @@ +// Test with only spaceship defaulted. +// { dg-do run { target c++2a } } + +#include + +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); +}