]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
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>
Mon, 22 Jun 2020 19:33:35 +0000 (15:33 -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.  For GCC 10 I'm leaving build_clone
alone, to reduce the chance of non-C++20-mode regressions.

The decl.c changes are a hack to avoid complaining about constraints on a
non-template friend that isn't defined in the class.  In this case the
defaulted comparison operator should be considered defined, but we weren't
setting funcdef_flag properly.  For GCC 11 I fixed it properly.

gcc/cp/ChangeLog:

* cp-tree.h (copy_fndecl_with_name): Declare.
* class.c (copy_fndecl_with_name): Copy from build_clone.
(add_implicitly_declared_members): Add op== to TYPE_FIELDS.
* method.c (implicitly_declare_fn): Use copy_fndecl_with_name.
* decl.c (grokfndecl): Add initialized parm.
(grokdeclarator): Pass it down.

gcc/testsuite/ChangeLog:

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

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

index ade2ab47104c58e4338658c6cf39d9f755b18ff1..3e2e2c74392bd49b47fce5dafba37a03090ec6c1 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,6 +4692,114 @@ check_methods (tree t)
     }
 }
 
+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;
+
+  if (flag_concepts)
+    /* Clone constraints.  */
+    if (tree ci = get_constraints (fn))
+      set_constraints (clone, copy_node (ci));
+
+  SET_DECL_ASSEMBLER_NAME (clone, NULL_TREE);
+  /* There's no pending inline data for this function.  */
+  DECL_PENDING_INLINE_INFO (clone) = NULL;
+  DECL_PENDING_INLINE_P (clone) = 0;
+
+  /* The base-class destructor is not virtual.  */
+  if (name == base_dtor_identifier)
+    {
+      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)
+    gcc_assert (DECL_HAS_IN_CHARGE_PARM_P (clone));
+
+  /* If there was an in-charge parameter, drop it from the function
+     type.  */
+  if (DECL_HAS_IN_CHARGE_PARM_P (clone))
+    {
+      tree basetype = TYPE_METHOD_BASETYPE (TREE_TYPE (clone));
+      tree parmtypes = TYPE_ARG_TYPES (TREE_TYPE (clone));
+      /* Skip the `this' parameter.  */
+      parmtypes = TREE_CHAIN (parmtypes);
+      /* Skip the in-charge parameter.  */
+      parmtypes = TREE_CHAIN (parmtypes);
+      /* And the VTT parm, in a complete [cd]tor.  */
+      if (DECL_HAS_VTT_PARM_P (fn)
+         && ! DECL_NEEDS_VTT_PARM_P (clone))
+       parmtypes = TREE_CHAIN (parmtypes);
+      if (ctor_omit_inherited_parms_p)
+       {
+         /* If we're omitting inherited parms, that just leaves the VTT.  */
+         gcc_assert (DECL_NEEDS_VTT_PARM_P (clone));
+         parmtypes = tree_cons (NULL_TREE, vtt_parm_type, void_list_node);
+       }
+      TREE_TYPE (clone)
+       = build_method_type_directly (basetype,
+                                     TREE_TYPE (TREE_TYPE (clone)),
+                                     parmtypes);
+      TREE_TYPE (clone)
+       = cp_build_type_attribute_variant (TREE_TYPE (clone),
+                                          TYPE_ATTRIBUTES (TREE_TYPE (fn)));
+      TREE_TYPE (clone)
+       = cxx_copy_lang_qualifiers (TREE_TYPE (clone), TREE_TYPE (fn));
+    }
+
+  /* Copy the function parameters.  */
+  DECL_ARGUMENTS (clone) = copy_list (DECL_ARGUMENTS (clone));
+  /* Remove the in-charge parameter.  */
+  if (DECL_HAS_IN_CHARGE_PARM_P (clone))
+    {
+      DECL_CHAIN (DECL_ARGUMENTS (clone))
+       = DECL_CHAIN (DECL_CHAIN (DECL_ARGUMENTS (clone)));
+      DECL_HAS_IN_CHARGE_PARM_P (clone) = 0;
+    }
+  /* And the VTT parm, in a complete [cd]tor.  */
+  if (DECL_HAS_VTT_PARM_P (fn))
+    {
+      if (DECL_NEEDS_VTT_PARM_P (clone))
+       DECL_HAS_VTT_PARM_P (clone) = 1;
+      else
+       {
+         DECL_CHAIN (DECL_ARGUMENTS (clone))
+           = DECL_CHAIN (DECL_CHAIN (DECL_ARGUMENTS (clone)));
+         DECL_HAS_VTT_PARM_P (clone) = 0;
+       }
+    }
+
+  /* A base constructor inheriting from a virtual base doesn't get the
+     arguments.  */
+  if (ctor_omit_inherited_parms_p)
+    DECL_CHAIN (DECL_CHAIN (DECL_ARGUMENTS (clone))) = NULL_TREE;
+
+  for (tree parms = DECL_ARGUMENTS (clone); parms; parms = DECL_CHAIN (parms))
+    {
+      DECL_CONTEXT (parms) = clone;
+      cxx_dup_lang_specific_decl (parms);
+    }
+
+  /* Create the RTL for this function.  */
+  SET_DECL_RTL (clone, NULL);
+  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.  */
index f220ed9f52affec5dac68c71bacf6f6124c14554..247919936c0c8ea9c649a77a7eef60a4c90ce768 100644 (file)
@@ -6458,6 +6458,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 df07d995a9bc46d74794e23796aac2f74b413c8a..90111e4c7864b8a00402eb782c1494fd6b563825 100644 (file)
@@ -9338,6 +9338,7 @@ grokfndecl (tree ctype,
            special_function_kind sfk,
            bool funcdef_flag,
            bool late_return_type_p,
+           int initialized,
            int template_count,
            tree in_namespace,
            tree* attrlist,
@@ -9381,7 +9382,7 @@ grokfndecl (tree ctype,
       /* C++20 CA378: Remove non-templated constrained functions.  */
       if (ci && !flag_concepts_ts
          && (!processing_template_decl
-             || (friendp && !memtmpl && !funcdef_flag)))
+             || (friendp && !memtmpl && !initialized && !funcdef_flag)))
        {
          error_at (location, "constraints on a non-templated function");
          ci = NULL_TREE;
@@ -13267,6 +13268,7 @@ grokdeclarator (const cp_declarator *declarator,
                                       | (8 * consteval_p),
                               initialized == SD_DELETED, sfk,
                               funcdef_flag, late_return_type_p,
+                              initialized,
                               template_count, in_namespace,
                               attrlist, id_loc);
             decl = set_virt_specifiers (decl, virt_specifiers);
@@ -13576,6 +13578,7 @@ grokdeclarator (const cp_declarator *declarator,
                            sfk,
                            funcdef_flag,
                           late_return_type_p,
+                          initialized,
                           template_count, in_namespace, attrlist,
                           id_loc);
        if (decl == NULL_TREE)
index 12ba6a7b56d85f457017b1df99b095464be951e1..3a3e060843edda64e68c6176db23721728b04038 100644 (file)
@@ -2657,7 +2657,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);
 
@@ -2665,11 +2664,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;
 
@@ -2731,35 +2758,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 ();
     }
@@ -2777,10 +2775,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,
@@ -2802,14 +2799,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)
     {
@@ -2821,12 +2813,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))
@@ -2854,13 +2841,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)
     {
@@ -2886,17 +2866,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;
@@ -2912,12 +2887,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" }
+}