From 4058454c9e0ee141d049cefa8db315a345a4b30a Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Thu, 18 Jun 2020 17:41:43 -0400 Subject: [PATCH] c++: Allow defaulted comparison outside class. Implementing P2085, another refinement to the operator<=> specification from the Prague meeting. It was deemed desirable to be able to have a non-inline defaulted definition of a comparison operator just like you can with other defaulted functions. gcc/cp/ChangeLog: * method.c (early_check_defaulted_comparison): Allow defaulting comparison outside class. Complain if non-member operator isn't a friend. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/spaceship-friend1.C: New test. * g++.dg/cpp2a/spaceship-err4.C: Adjust diagnostic. --- gcc/cp/method.c | 38 +++++++++---------- gcc/testsuite/g++.dg/cpp2a/spaceship-err4.C | 6 +-- .../g++.dg/cpp2a/spaceship-friend1.C | 26 +++++++++++++ 3 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/spaceship-friend1.C diff --git a/gcc/cp/method.c b/gcc/cp/method.c index b23764b3d54..2a98907bfa1 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -1102,17 +1102,6 @@ early_check_defaulted_comparison (tree fn) return false; } - if (!ctx) - { - if (DECL_OVERLOADED_OPERATOR_IS (fn, SPACESHIP_EXPR)) - error_at (loc, "three-way comparison operator can only be defaulted " - "in a class definition"); - else - error_at (loc, "equality comparison operator can only be defaulted " - "in a class definition"); - return false; - } - if (!DECL_OVERLOADED_OPERATOR_IS (fn, SPACESHIP_EXPR) && !same_type_p (TREE_TYPE (TREE_TYPE (fn)), boolean_type_node)) { @@ -1146,16 +1135,27 @@ early_check_defaulted_comparison (tree fn) for (; parmnode != void_list_node; parmnode = TREE_CHAIN (parmnode)) { tree parmtype = TREE_VALUE (parmnode); - if (same_type_p (parmtype, ctx)) + if (CLASS_TYPE_P (parmtype)) saw_byval = true; - else if (TREE_CODE (parmtype) != REFERENCE_TYPE - || TYPE_REF_IS_RVALUE (parmtype) - || TYPE_QUALS (TREE_TYPE (parmtype)) != TYPE_QUAL_CONST - || !(same_type_ignoring_top_level_qualifiers_p - (TREE_TYPE (parmtype), ctx))) - saw_bad = true; + else if (TREE_CODE (parmtype) == REFERENCE_TYPE + && !TYPE_REF_IS_RVALUE (parmtype) + && TYPE_QUALS (TREE_TYPE (parmtype)) == TYPE_QUAL_CONST) + { + saw_byref = true; + parmtype = TREE_TYPE (parmtype); + } else - saw_byref = true; + saw_bad = true; + + if (!saw_bad && !ctx) + { + /* Defaulted outside the class body. */ + ctx = TYPE_MAIN_VARIANT (parmtype); + if (!is_friend (ctx, fn)) + error_at (loc, "defaulted %qD is not a friend of %qT", fn, ctx); + } + else if (!same_type_ignoring_top_level_qualifiers_p (parmtype, ctx)) + saw_bad = true; } if (saw_bad || (saw_byval && saw_byref)) diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-err4.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-err4.C index b044914bbfc..a39e5069957 100644 --- a/gcc/testsuite/g++.dg/cpp2a/spaceship-err4.C +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-err4.C @@ -2,6 +2,6 @@ // { dg-do compile { target c++20 } } struct B {}; -bool operator!=(const B&, const B&) = default; // { dg-error "equality comparison operator can only be defaulted in a class definition" } -bool operator==(const B&, const B&) = default; // { dg-error "equality comparison operator can only be defaulted in a class definition" } -bool operator<=>(const B&, const B&) = default; // { dg-error "three-way comparison operator can only be defaulted in a class definition" } +bool operator!=(const B&, const B&) = default; // { dg-error "not a friend" } +bool operator==(const B&, const B&) = default; // { dg-error "not a friend" } +bool operator<=>(const B&, const B&) = default; // { dg-error "not a friend" } diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-friend1.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-friend1.C new file mode 100644 index 00000000000..24bbc74a2d1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-friend1.C @@ -0,0 +1,26 @@ +// P2085, separate definition of defaulted comparisons +// { dg-do compile { target c++20 } } + +namespace X { + + struct A { + int i; + friend constexpr bool operator==(const A&,const A&); + }; + + inline constexpr bool operator==(const A&,const A&)=default; + + static_assert (A() == A()); + +} + +namespace Y { + + struct A { + int i; + // friend bool operator==(const A&,const A&); + }; + + inline bool operator==(const A&,const A&)=default; // { dg-error "not a friend" } + +} -- 2.30.2