[C++] Implement P1946R0 - Allow defaulting comparisons by value
diff mbox series

Message ID 20191111080708.GV4650@tucnak
State New
Headers show
Series
  • [C++] Implement P1946R0 - Allow defaulting comparisons by value
Related show

Commit Message

Jakub Jelinek Nov. 11, 2019, 8:07 a.m. UTC
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  <jakub@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.


	Jakub

Comments

Jakub Jelinek Nov. 11, 2019, 8:02 p.m. UTC | #1
On Mon, Nov 11, 2019 at 09:07:08AM +0100, Jakub Jelinek wrote:
> 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?

Bootstrapped/regtested successfully on powerpc64le-linux.

> 2019-11-11  Jakub Jelinek  <jakub@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.

	Jakub
Jason Merrill Nov. 12, 2019, 4:56 a.m. UTC | #2
On 11/11/19 8:07 AM, Jakub Jelinek wrote:
> 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.

And I implemented it the rest of the way at the meeting, I just hadn't 
committed it yet.  Thanks for the additional tests, though.

Tested x86_64-pc-linux-gnu, applying to trunk.

Patch
diff mbox series

--- 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);
+}