diff mbox series

[pushed] c++: Allow defaulted comparison outside class.

Message ID 20200619162555.27311-1-jason@redhat.com
State New
Headers show
Series [pushed] c++: Allow defaulted comparison outside class. | expand

Commit Message

Jason Merrill June 19, 2020, 4:25 p.m. UTC
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.

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

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


base-commit: 4cea81adabd7660838ebb3e59e8d28f820a3b789
diff mbox series

Patch

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" }
+
+}