c++: implicit operator== adjustments from P2002.
authorJason Merrill <jason@redhat.com>
Mon, 15 Jun 2020 21:11:38 +0000 (17:11 -0400)
committerJason Merrill <jason@redhat.com>
Wed, 17 Jun 2020 19:08:42 +0000 (15:08 -0400)
P2002R1, adopted at the February C++ meeting, made several refinements to
the wording for operator<=>.  This implements clarifications in how the
implicit operator== is declared: as a duplicate of the operator<=>, with
only the return type and name changed.  To that end I factored out the
declaration copying from build_clone.

gcc/cp/ChangeLog:

* cp-tree.h (copy_fndecl_with_name): Declare.
* class.c (copy_fndecl_with_name): Split out from...
(build_clone): ...here.
(add_implicitly_declared_members): Add op== to TYPE_FIELDS.
* method.c (implicitly_declare_fn): Use copy_fndecl_with_name.

gcc/testsuite/ChangeLog:

* g++.dg/cpp2a/spaceship-synth9.C: New test.

gcc/cp/class.c
gcc/cp/cp-tree.h
gcc/cp/method.c
gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C [new file with mode: 0644]

index f8e38ec9d8c88c0ba0d53467874ab733fe23dbe5..629d27da89494b99215efd719b1f24ee1c935827 100644 (file)
@@ -3266,7 +3266,12 @@ add_implicitly_declared_members (tree t, tree* access_decls,
          do_friend (NULL_TREE, DECL_NAME (eq), eq,
                     NULL_TREE, NO_SPECIAL, true);
        else
-         add_method (t, eq, false);
+         {
+           add_method (t, eq, false);
+           DECL_CHAIN (eq) = TYPE_FIELDS (t);
+           TYPE_FIELDS (t) = eq;
+         }
+       maybe_add_class_template_decl_list (t, eq, DECL_FRIEND_P (space));
       }
 
   while (*access_decls)
@@ -4687,37 +4692,13 @@ check_methods (tree t)
     }
 }
 
-/* FN is a constructor or destructor.  Clone the declaration to create
-   a specialized in-charge or not-in-charge version, as indicated by
-   NAME.  */
-
-static tree
-build_clone (tree fn, tree name)
+tree
+copy_fndecl_with_name (tree fn, tree name)
 {
   /* Copy the function.  */
   tree clone = copy_decl (fn);
   /* Reset the function name.  */
   DECL_NAME (clone) = name;
-  /* Remember where this function came from.  */
-  DECL_ABSTRACT_ORIGIN (clone) = fn;
-
-  /* Make it easy to find the CLONE given the FN.  Note the
-     template_result of a template will be chained this way too.  */
-  DECL_CHAIN (clone) = DECL_CHAIN (fn);
-  DECL_CHAIN (fn) = clone;
-
-  /* If this is a template, do the rest on the DECL_TEMPLATE_RESULT.  */
-  if (TREE_CODE (clone) == TEMPLATE_DECL)
-    {
-      tree result = build_clone (DECL_TEMPLATE_RESULT (clone), name);
-      DECL_TEMPLATE_RESULT (clone) = result;
-
-      DECL_TEMPLATE_INFO (result) = copy_node (DECL_TEMPLATE_INFO (result));
-      DECL_TI_TEMPLATE (result) = clone;
-
-      TREE_TYPE (clone) = TREE_TYPE (result);
-      return clone;
-    }
 
   if (flag_concepts)
     /* Clone constraints.  */
@@ -4725,7 +4706,6 @@ build_clone (tree fn, tree name)
       set_constraints (clone, copy_node (ci));
 
   SET_DECL_ASSEMBLER_NAME (clone, NULL_TREE);
-  DECL_CLONED_FUNCTION (clone) = fn;
   /* There's no pending inline data for this function.  */
   DECL_PENDING_INLINE_INFO (clone) = NULL;
   DECL_PENDING_INLINE_P (clone) = 0;
@@ -4736,6 +4716,14 @@ build_clone (tree fn, tree name)
       DECL_VIRTUAL_P (clone) = 0;
       DECL_VINDEX (clone) = NULL_TREE;
     }
+  else if (IDENTIFIER_OVL_OP_P (name))
+    {
+      const ovl_op_info_t *ovl_op = IDENTIFIER_OVL_OP_INFO (name);
+      DECL_OVERLOADED_OPERATOR_CODE_RAW (clone) = ovl_op->ovl_op_code;
+    }
+
+  if (DECL_VIRTUAL_P (clone))
+    IDENTIFIER_VIRTUAL_P (name) = true;
 
   bool ctor_omit_inherited_parms_p = ctor_omit_inherited_parms (clone);
   if (ctor_omit_inherited_parms_p)
@@ -4807,7 +4795,47 @@ build_clone (tree fn, tree name)
 
   /* Create the RTL for this function.  */
   SET_DECL_RTL (clone, NULL);
-  rest_of_decl_compilation (clone, /*top_level=*/1, at_eof);
+  rest_of_decl_compilation (clone, namespace_bindings_p (), at_eof);
+
+  return clone;
+}
+
+/* FN is a constructor or destructor.  Clone the declaration to create
+   a specialized in-charge or not-in-charge version, as indicated by
+   NAME.  */
+
+static tree
+build_clone (tree fn, tree name)
+{
+  tree clone;
+
+  /* If this is a template, do the rest on the DECL_TEMPLATE_RESULT.  */
+  if (TREE_CODE (fn) == TEMPLATE_DECL)
+    {
+      clone = copy_decl (fn);
+      DECL_NAME (clone) = name;
+
+      tree result = build_clone (DECL_TEMPLATE_RESULT (clone), name);
+      DECL_TEMPLATE_RESULT (clone) = result;
+
+      DECL_TEMPLATE_INFO (result) = copy_node (DECL_TEMPLATE_INFO (result));
+      DECL_TI_TEMPLATE (result) = clone;
+
+      TREE_TYPE (clone) = TREE_TYPE (result);
+    }
+  else
+    {
+      clone = copy_fndecl_with_name (fn, name);
+      DECL_CLONED_FUNCTION (clone) = fn;
+    }
+
+  /* Remember where this function came from.  */
+  DECL_ABSTRACT_ORIGIN (clone) = fn;
+
+  /* Make it easy to find the CLONE given the FN.  Note the
+     template_result of a template will be chained this way too.  */
+  DECL_CHAIN (clone) = DECL_CHAIN (fn);
+  DECL_CHAIN (fn) = clone;
 
   return clone;
 }
index ee276107928ebd1cf4f26d566f3e1058baa068b1..c139668696818a6a50b698df1dcb32c1a972c9da 100644 (file)
@@ -6448,6 +6448,7 @@ extern void check_abi_tags                        (tree);
 extern tree missing_abi_tags                   (tree);
 extern void fixup_type_variants                        (tree);
 extern void fixup_attribute_variants           (tree);
+extern tree copy_fndecl_with_name              (tree, tree);
 extern void clone_function_decl                        (tree, bool);
 extern void adjust_clone_args                  (tree);
 extern void deduce_noexcept_on_destructor       (tree);
index 2d31462c90149ead50fdcb5cbd27d7d94e4fe686..1a819b291738f5f1d1ddbe6f539046c70f94a794 100644 (file)
@@ -2632,7 +2632,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
   HOST_WIDE_INT saved_processing_template_decl;
   bool deleted_p = false;
   bool constexpr_p = false;
-  bool friend_p = (kind == sfk_comparison && DECL_FRIEND_P (pattern_fn));
   tree inherited_ctor = (kind == sfk_inheriting_constructor
                         ? pattern_fn : NULL_TREE);
 
@@ -2640,11 +2639,39 @@ implicitly_declare_fn (special_function_kind kind, tree type,
      lazily, we may be creating the declaration for a member of TYPE
      while in some completely different context.  However, TYPE will
      never be a dependent class (because we never want to do lookups
-     for implicitly defined functions in a dependent class).
-     Furthermore, we must set PROCESSING_TEMPLATE_DECL to zero here
+     for implicitly defined functions in a dependent class).  */
+  gcc_assert (!dependent_type_p (type));
+
+  /* If the member-specification does not explicitly declare any member or
+     friend named operator==, an == operator function is declared
+     implicitly for each three-way comparison operator function defined as
+     defaulted in the member-specification, with the same access and
+     function-definition and in the same class scope as the respective
+     three-way comparison operator function, except that the return type is
+     replaced with bool and the declarator-id is replaced with
+     operator==.
+
+     [Note: Such an implicitly-declared == operator for a class X is
+     defined as defaulted in the definition of X and has the same
+     parameter-declaration-clause and trailing requires-clause as the
+     respective three-way comparison operator. It is declared with friend,
+     virtual, constexpr, or consteval if the three-way comparison operator
+     function is so declared. If the three-way comparison operator function
+     has no noexcept-specifier, the implicitly-declared == operator
+     function has an implicit exception specification (14.5) that may
+     differ from the implicit exception specification of the three-way
+     comparison operator function. --end note]  */
+  if (kind == sfk_comparison)
+    {
+      fn = copy_fndecl_with_name (pattern_fn, ovl_op_identifier (EQ_EXPR));
+      DECL_ARTIFICIAL (fn) = 1;
+      TREE_TYPE (fn) = change_return_type (boolean_type_node, TREE_TYPE (fn));
+      return fn;
+    }
+
+  /* Furthermore, we must set PROCESSING_TEMPLATE_DECL to zero here
      because we only create clones for constructors and destructors
      when not in a template.  */
-  gcc_assert (!dependent_type_p (type));
   saved_processing_template_decl = processing_template_decl;
   processing_template_decl = 0;
 
@@ -2706,35 +2733,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
       break;
     }
 
-    case sfk_comparison:
-      /* If the class definition does not explicitly declare an == operator
-        function, but declares a defaulted three-way comparison operator
-        function, an == operator function is declared implicitly with the same
-        access as the three-way comparison operator function.
-
-        The implicitly-declared == operator for a class X is an inline member
-        and is defined as defaulted in the definition of X.
-
-        If the three-way comparison operator function is declared as a
-        non-static const member, the implicitly-declared == operator function
-        is a member of the form
-
-          bool X::operator==(const X&) const;
-
-        Otherwise, the implicitly-declared == operator function is of the form
-
-          friend bool operator==(const X&, const X&); */
-      /* No other comparison operator is implicitly declared.  */
-      name = ovl_op_identifier (false, EQ_EXPR);
-      return_type = boolean_type_node;
-      rhs_parm_type = cp_build_qualified_type (type, TYPE_QUAL_CONST);
-      rhs_parm_type = cp_build_reference_type (rhs_parm_type, false);
-      parameter_types = tree_cons (NULL_TREE, rhs_parm_type, parameter_types);
-      if (friend_p)
-       parameter_types = tree_cons (NULL_TREE, rhs_parm_type, parameter_types);
-      this_quals = TYPE_QUAL_CONST;
-      break;
-
     default:
       gcc_unreachable ();
     }
@@ -2752,10 +2750,9 @@ implicitly_declare_fn (special_function_kind kind, tree type,
   else if (cxx_dialect >= cxx11)
     {
       raises = noexcept_deferred_spec;
-      if (kind != sfk_comparison)
-       synthesized_method_walk (type, kind, const_p, NULL, &trivial_p,
-                                &deleted_p, &constexpr_p, false,
-                                &inherited_ctor, inherited_parms);
+      synthesized_method_walk (type, kind, const_p, NULL, &trivial_p,
+                              &deleted_p, &constexpr_p, false,
+                              &inherited_ctor, inherited_parms);
     }
   else
     synthesized_method_walk (type, kind, const_p, &raises, &trivial_p,
@@ -2777,14 +2774,9 @@ implicitly_declare_fn (special_function_kind kind, tree type,
     type_set_nontrivial_flag (type, kind);
 
   /* Create the function.  */
-  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);
-    }
+  tree this_type = cp_build_qualified_type (type, this_quals);
+  fn_type = build_method_type_directly (this_type, return_type,
+                                       parameter_types);
 
   if (raises)
     {
@@ -2796,12 +2788,7 @@ implicitly_declare_fn (special_function_kind kind, tree type,
        gcc_assert (seen_error ());
     }
   fn = build_lang_decl (FUNCTION_DECL, name, fn_type);
-  if (kind == sfk_comparison)
-    {
-      DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (pattern_fn);
-      DECL_MAYBE_DELETED (fn) = true;
-    }
-  else if (kind != sfk_inheriting_constructor)
+  if (kind != sfk_inheriting_constructor)
     DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (TYPE_NAME (type));
 
   if (IDENTIFIER_OVL_OP_P (name))
@@ -2829,13 +2816,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
       retrofit_lang_decl (decl);
       DECL_PARM_INDEX (decl) = DECL_PARM_LEVEL (decl) = 1;
       DECL_ARGUMENTS (fn) = decl;
-      if (friend_p)
-       {
-         /* The second parm of friend op==.  */
-         tree decl2 = copy_decl (decl);
-         DECL_CHAIN (decl) = decl2;
-         DECL_PARM_INDEX (decl2) = 2;
-       }
     }
   else if (kind == sfk_inheriting_constructor)
     {
@@ -2861,17 +2841,12 @@ implicitly_declare_fn (special_function_kind kind, tree type,
       constexpr_p = DECL_DECLARED_CONSTEXPR_P (inherited_ctor);
     }
 
-  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;
+  /* 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);
-    }
+  grokclassfn (type, fn, kind == sfk_destructor ? DTOR_FLAG : NO_SPECIAL);
 
   DECL_IN_AGGR_P (fn) = 1;
   DECL_ARTIFICIAL (fn) = 1;
@@ -2887,12 +2862,6 @@ implicitly_declare_fn (special_function_kind kind, tree type,
   set_linkage_according_to_type (type, fn);
   if (TREE_PUBLIC (fn))
     DECL_COMDAT (fn) = 1;
-  if (kind == sfk_comparison && !friend_p)
-    {
-      /* The implicit op== has the same access as the op<=>.  */
-      TREE_PRIVATE (fn) = TREE_PRIVATE (pattern_fn);
-      TREE_PROTECTED (fn) = TREE_PROTECTED (pattern_fn);
-    }
   rest_of_decl_compilation (fn, namespace_bindings_p (), at_eof);
   gcc_assert (!TREE_USED (fn));
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth9.C
new file mode 100644 (file)
index 0000000..33b547d
--- /dev/null
@@ -0,0 +1,27 @@
+// Test that most properties of <=> are copied to ==.
+// { dg-do compile { target c++20 } }
+
+#include <compare>
+
+template<typename T> struct X {
+  T t;
+  friend consteval std::partial_ordering operator<=>(X, X) requires (sizeof(T) != 1) = default;
+  // implicitly declares: friend constexpr bool operator==(X, X) requires (sizeof(T) != 1) = default;
+};
+
+template<typename T> struct Y {
+  [[nodiscard]] virtual std::strong_ordering operator<=>(const Y&) const = default;
+  // implicitly declares: [[nodiscard]] virtual bool operator==(const Y&) const = default;
+};
+
+struct Z: Y<int>
+{
+  bool operator==(const Y&) const noexcept override;
+};
+
+int main()
+{
+  X<char>() == X<char>();      // { dg-error "no match" }
+  X<int> x; x == x;            // { dg-error "x' is not usable in a constant expression" }
+  Y<int>()  == Y<int>();       // { dg-warning "nodiscard" }
+}