Hi! >From >https://www.reddit.com/r/cpp/comments/dtuov8/201911_belfast_iso_c_committee_trip_report/ I understood P1946R0 made it into C++20, so here is my attempt at implementing it, you had most of it implemented anyway because in system headers friend constexpr bool operator==(partial_ordering, partial_ordering) noexcept = default; etc. has been already accepted.
Tested so far with make check-c++-all RUNTESTFLAGS=dg.exp=spaceship* and make check-target-libstdc++-v3 RUNTESTFLAGS=conformance.exp=18_support/comparisons/common/1.cc Ok for trunk if it passes full bootstrap/regtest? 2019-11-11 Jakub Jelinek <ja...@redhat.com> Implement P1946R0 - Allow defaulting comparisons by value * method.c (early_check_defaulted_comparison): Remove unused variable i. For non-static data members always require argument type to be const C &, for friends allow either both arguments to be const C &, or both to be C. * g++.dg/cpp2a/spaceship-synth1-neg.C: New test. * g++.dg/cpp2a/spaceship-synth4.C: New test. * g++.dg/cpp2a/spaceship-synth5.C: New test. --- gcc/cp/method.c.jj 2019-11-07 21:21:27.097760879 +0100 +++ gcc/cp/method.c 2019-11-11 08:28:22.633822845 +0100 @@ -1098,34 +1098,39 @@ early_check_defaulted_comparison (tree f ok = false; } - int i = DECL_NONSTATIC_MEMBER_FUNCTION_P (fn); - if (i && type_memfn_quals (TREE_TYPE (fn)) != TYPE_QUAL_CONST) + if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + && type_memfn_quals (TREE_TYPE (fn)) != TYPE_QUAL_CONST) { error_at (loc, "defaulted %qD must be %<const%>", fn); ok = false; } - tree parmnode = FUNCTION_FIRST_USER_PARMTYPE (fn); - for (; parmnode != void_list_node; parmnode = TREE_CHAIN (parmnode)) + tree firstparmnode = FUNCTION_FIRST_USER_PARMTYPE (fn); + for (tree parmnode = firstparmnode; parmnode != void_list_node; + parmnode = TREE_CHAIN (parmnode)) { - ++i; tree parmtype = TREE_VALUE (parmnode); - diagnostic_t kind = DK_UNSPECIFIED; - int opt = 0; - if (same_type_p (parmtype, ctx)) - /* The draft specifies const reference, but let's also allow by-value - unless -Wpedantic, hopefully it will be added soon. */ - kind = DK_PEDWARN, - opt = OPT_Wpedantic; - else if (TREE_CODE (parmtype) != REFERENCE_TYPE - || TYPE_QUALS (TREE_TYPE (parmtype)) != TYPE_QUAL_CONST - || !(same_type_ignoring_top_level_qualifiers_p - (TREE_TYPE (parmtype), ctx))) - kind = DK_ERROR; - if (kind) - emit_diagnostic (kind, loc, opt, "defaulted %qD must have " - "parameter type %<const %T&%>", fn, ctx); - if (kind == DK_ERROR) - ok = false; + /* a non-static const member of C having one parameter of type const C&, + or a friend of C having either two parameters of type const C& or two + parameters of type C. */ + if ((!DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + && !same_type_p (TREE_VALUE (firstparmnode), parmtype)) + || ((DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + || !same_type_p (parmtype, ctx)) + && (TREE_CODE (parmtype) != REFERENCE_TYPE + || TYPE_QUALS (TREE_TYPE (parmtype)) != TYPE_QUAL_CONST + || !(same_type_ignoring_top_level_qualifiers_p + (TREE_TYPE (parmtype), ctx))))) + { + if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)) + error_at (loc, "defaulted %qD must have parameter type " + "%<const %T&%>", fn, ctx); + else + error_at (loc, "defaulted %qD must have parameter types " + "%<const %T&%>, %<const %T&%> or " + "%qT, %qT", fn, ctx, ctx, ctx, ctx); + ok = false; + break; + } } /* We still need to deduce deleted/constexpr/noexcept and maybe return. */ --- gcc/testsuite/g++.dg/cpp2a/spaceship-synth1-neg.C.jj 2019-11-11 08:23:34.040215264 +0100 +++ gcc/testsuite/g++.dg/cpp2a/spaceship-synth1-neg.C 2019-11-11 08:32:56.206659041 +0100 @@ -0,0 +1,15 @@ +// Test with all operators explicitly defaulted. +// { dg-do compile { target c++2a } } + +#include <compare> + +struct D +{ + int i; + auto operator<=>(D& x) const = default; // { dg-error "defaulted \[^\n\r]* must have parameter type 'const D&'" } + bool operator==(int x) const = default; // { dg-error "defaulted \[^\n\r]* must have parameter type 'const D&'" } + bool operator!=(const int& x) const = default; // { dg-error "defaulted \[^\n\r]* must have parameter type 'const D&'" } + friend bool operator<(int& x, D& y) = default; // { dg-error "defaulted \[^\n\r]* must have parameter types 'const D&', 'const D&' or 'D', 'D'" } + friend bool operator<=(const D& x, D y) = default; // { dg-error "defaulted \[^\n\r]* must have parameter types 'const D&', 'const D&' or 'D', 'D'" } + friend bool operator>(D x, const D& y) = default; // { dg-error "defaulted \[^\n\r]* must have parameter types 'const D&', 'const D&' or 'D', 'D'" } +}; --- gcc/testsuite/g++.dg/cpp2a/spaceship-synth4.C.jj 2019-11-10 16:35:34.296158460 +0100 +++ gcc/testsuite/g++.dg/cpp2a/spaceship-synth4.C 2019-11-10 16:37:05.881767368 +0100 @@ -0,0 +1,43 @@ +// Test with all operators explicitly defaulted. +// { dg-do run { target c++2a } } + +#include <compare> + +struct D +{ + int i; + friend auto operator<=>(const D& x, const D& y) = default; + friend bool operator==(const D& x, const D& y) = default; + friend bool operator!=(const D& x, const D& y) = default; + friend bool operator<(const D& x, const D& y) = default; + friend bool operator<=(const D& x, const D& y) = default; + friend bool operator>(const D& x, const D& y) = default; + friend bool operator>=(const D& x, const D& y) = 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); +} --- gcc/testsuite/g++.dg/cpp2a/spaceship-synth5.C.jj 2019-11-10 16:41:54.673380871 +0100 +++ gcc/testsuite/g++.dg/cpp2a/spaceship-synth5.C 2019-11-11 08:32:49.270764604 +0100 @@ -0,0 +1,43 @@ +// Test with all operators explicitly defaulted. +// { dg-do run { target c++2a } } + +#include <compare> + +struct D +{ + int i; + friend auto operator<=>(D x, D y) = default; + friend bool operator==(D x, D y) = default; + friend bool operator!=(D x, D y) = default; + friend bool operator<(D x, D y) = default; + friend bool operator<=(D x, D y) = default; + friend bool operator>(D x, D y) = default; + friend bool operator>=(const D x, const D y) = 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); +} Jakub