[gcc(refs/users/marxin/heads/marxin-gcc-benchmark-branch)] c++: Fix implicit friend operator==.

Martin Liska marxin@gcc.gnu.org
Mon Mar 30 10:59:11 GMT 2020


https://gcc.gnu.org/g:d6ef77e023cfe0bb3b12b88ae46b77da356d7f85

commit d6ef77e023cfe0bb3b12b88ae46b77da356d7f85
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Feb 11 12:04:37 2020 +0100

    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  <jason@redhat.com>
    
            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:
---
 gcc/cp/ChangeLog                               |  9 +++++
 gcc/cp/class.c                                 |  6 +++-
 gcc/cp/cp-tree.h                               |  1 +
 gcc/cp/decl2.c                                 |  2 +-
 gcc/cp/method.c                                | 47 +++++++++++++++++++++-----
 gcc/testsuite/g++.dg/cpp2a/spaceship-synth2a.C | 43 +++++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/spaceship-synth2b.C | 43 +++++++++++++++++++++++
 7 files changed, 140 insertions(+), 11 deletions(-)

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  <jason@redhat.com>
+
+	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  <jason@redhat.com>
 
 	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 <compare>
+
+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 <compare>
+
+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);
+}


More information about the Gcc-cvs mailing list