PR c++/80178 - parameter passing for uncopyable classes
authorJason Merrill <jason@redhat.com>
Mon, 8 May 2017 19:08:07 +0000 (15:08 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Mon, 8 May 2017 19:08:07 +0000 (15:08 -0400)
* tree.c (type_has_nontrivial_copy_init): True for classes with only
deleted copy/move ctors.
(remember_deleted_copy, maybe_warn_parm_abi): New.
* decl.c (require_complete_types_for_parms, check_function_type):
Call maybe_warn_parm_abi.
* call.c (convert_for_arg_passing, build_cxx_call): Likewise.

From-SVN: r247757

gcc/common.opt
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/tree.c
gcc/testsuite/g++.dg/abi/invisiref1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/abi/invisiref1a.C [new file with mode: 0644]

index a5c3aeaa33635a3f4486bb3983efa0ac0c5fb09d..13305558d2d99a317f7b493ed7fe79e8367c8864 100644 (file)
@@ -910,6 +910,10 @@ Driver Undocumented
 ;     and introduces new inheriting constructor handling.
 ;     Default in G++ 7.
 ;
+; 12: Corrects the calling convention for classes with only deleted copy/move
+;     constructors.
+;     Default in G++ 8.
+;
 ; Additional positive integers will be assigned as new versions of
 ; the ABI become the default version of the ABI.
 fabi-version=
index 369a885e68b5b374f3b3fb6ba2a10bec3af9d6f7..3119d5fb2b2c09fe93e2d330939663db34c89afc 100644 (file)
@@ -1,3 +1,13 @@
+2017-05-08  Jason Merrill  <jason@redhat.com>
+
+       PR c++/80178 - parameter passing for uncopyable classes
+       * tree.c (type_has_nontrivial_copy_init): True for classes with only
+       deleted copy/move ctors.
+       (remember_deleted_copy, maybe_warn_parm_abi): New.
+       * decl.c (require_complete_types_for_parms, check_function_type):
+       Call maybe_warn_parm_abi.
+       * call.c (convert_for_arg_passing, build_cxx_call): Likewise.
+
 2017-05-08  Nathan Sidwell  <nathan@acm.org>
 
        * decl.c (builtin_function_1): Set DCL_ANTICIPATED before pushing.
index f1e431c6b15c784ac0fc421169f7a54b77816c54..d9accd16401a365bd6ca6b848e1901aea131aa15 100644 (file)
@@ -7352,17 +7352,21 @@ convert_for_arg_passing (tree type, tree val, tsubst_flags_t complain)
           && COMPLETE_TYPE_P (type)
           && tree_int_cst_lt (TYPE_SIZE (type), TYPE_SIZE (integer_type_node)))
     val = cp_perform_integral_promotions (val, complain);
-  if ((complain & tf_warning)
-      && warn_suggest_attribute_format)
+  if (complain & tf_warning)
     {
-      tree rhstype = TREE_TYPE (val);
-      const enum tree_code coder = TREE_CODE (rhstype);
-      const enum tree_code codel = TREE_CODE (type);
-      if ((codel == POINTER_TYPE || codel == REFERENCE_TYPE)
-         && coder == codel
-         && check_missing_format_attribute (type, rhstype))
-       warning (OPT_Wsuggest_attribute_format,
-                "argument of function call might be a candidate for a format attribute");
+      if (warn_suggest_attribute_format)
+       {
+         tree rhstype = TREE_TYPE (val);
+         const enum tree_code coder = TREE_CODE (rhstype);
+         const enum tree_code codel = TREE_CODE (type);
+         if ((codel == POINTER_TYPE || codel == REFERENCE_TYPE)
+             && coder == codel
+             && check_missing_format_attribute (type, rhstype))
+           warning (OPT_Wsuggest_attribute_format,
+                    "argument of function call might be a candidate "
+                    "for a format attribute");
+       }
+      maybe_warn_parm_abi (type, EXPR_LOC_OR_LOC (val, input_location));
     }
   return val;
 }
@@ -8234,7 +8238,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
        return error_mark_node;
 
       if (MAYBE_CLASS_TYPE_P (TREE_TYPE (fn)))
-       fn = build_cplus_new (TREE_TYPE (fn), fn, complain);
+       {
+         fn = build_cplus_new (TREE_TYPE (fn), fn, complain);
+         maybe_warn_parm_abi (TREE_TYPE (fn), loc);
+       }
     }
   return convert_from_reference (fn);
 }
index d2d48e7342a494ac26c46a8f974cf2782f3f5b61..b64fa6dafbc7d476e367da8069d2c7cf0251d16e 100644 (file)
@@ -6614,6 +6614,7 @@ extern bool type_has_unique_obj_representations (const_tree);
 extern bool scalarish_type_p                   (const_tree);
 extern bool type_has_nontrivial_default_init   (const_tree);
 extern bool type_has_nontrivial_copy_init      (const_tree);
+extern void maybe_warn_parm_abi                        (tree, location_t);
 extern bool class_tmpl_impl_spec_p             (const_tree);
 extern int zero_init_p                         (const_tree);
 extern bool check_abi_tag_redeclaration                (const_tree, const_tree, const_tree);
index 4023c699e1fc381e89d1ae84491d354b8f82ba02..74cf018fa31dfd5b69a2c09f4436f2303308d087 100644 (file)
@@ -12387,6 +12387,9 @@ require_complete_types_for_parms (tree parms)
        {
          relayout_decl (parms);
          DECL_ARG_TYPE (parms) = type_passed_as (TREE_TYPE (parms));
+
+         maybe_warn_parm_abi (TREE_TYPE (parms),
+                              DECL_SOURCE_LOCATION (parms));
        }
       else
        /* grokparms or complete_type_or_else will have already issued
@@ -14639,7 +14642,11 @@ check_function_type (tree decl, tree current_function_parms)
       TREE_TYPE (decl) = fntype;
     }
   else
-    abstract_virtuals_error (decl, TREE_TYPE (fntype));
+    {
+      abstract_virtuals_error (decl, TREE_TYPE (fntype));
+      maybe_warn_parm_abi (TREE_TYPE (fntype),
+                          DECL_SOURCE_LOCATION (decl));
+    }
 }
 
 /* True iff FN is an implicitly-defined default constructor.  */
index ba1cb33bffaa8000a4607f6071a08e1cfd117ba2..858d0d42537e7b47b5489aacc4013f5533a4f485 100644 (file)
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "convert.h"
 #include "gimplify.h"
 #include "attribs.h"
+#include "flags.h"
 
 static tree bot_manip (tree *, int *, void *);
 static tree bot_replace (tree *, int *, void *);
@@ -3596,21 +3597,112 @@ type_has_nontrivial_default_init (const_tree t)
     return 0;
 }
 
+/* Track classes with only deleted copy/move constructors so that we can warn
+   if they are used in call/return by value.  */
+
+static GTY(()) hash_set<tree>* deleted_copy_types;
+static void
+remember_deleted_copy (const_tree t)
+{
+  if (!deleted_copy_types)
+    deleted_copy_types = hash_set<tree>::create_ggc(37);
+  deleted_copy_types->add (CONST_CAST_TREE (t));
+}
+void
+maybe_warn_parm_abi (tree t, location_t loc)
+{
+  if (!deleted_copy_types
+      || !deleted_copy_types->contains (t))
+    return;
+
+  warning_at (loc, OPT_Wabi, "the calling convention for %qT changes in "
+             "-fabi-version=12 (GCC 8)", t);
+  static bool explained = false;
+  if (!explained)
+    {
+      inform (loc, " because all of its copy and move constructors "
+             "are deleted");
+      explained = true;
+    }
+}
+
 /* Returns true iff copying an object of type T (including via move
    constructor) is non-trivial.  That is, T has no non-trivial copy
-   constructors and no non-trivial move constructors.  */
+   constructors and no non-trivial move constructors, and not all copy/move
+   constructors are deleted.  This function implements the ABI notion of
+   non-trivial copy, which has diverged from the one in the standard.  */
 
 bool
-type_has_nontrivial_copy_init (const_tree t)
+type_has_nontrivial_copy_init (const_tree type)
 {
-  t = strip_array_types (CONST_CAST_TREE (t));
+  tree t = strip_array_types (CONST_CAST_TREE (type));
 
   if (CLASS_TYPE_P (t))
     {
       gcc_assert (COMPLETE_TYPE_P (t));
-      return ((TYPE_HAS_COPY_CTOR (t)
-              && TYPE_HAS_COMPLEX_COPY_CTOR (t))
-             || TYPE_HAS_COMPLEX_MOVE_CTOR (t));
+
+      if (TYPE_HAS_COMPLEX_COPY_CTOR (t)
+         || TYPE_HAS_COMPLEX_MOVE_CTOR (t))
+       /* Nontrivial.  */
+       return true;
+
+      if (cxx_dialect < cxx11)
+       /* No deleted functions before C++11.  */
+       return false;
+
+      /* Before ABI v12 we did a bitwise copy of types with only deleted
+        copy/move constructors.  */
+      if (!abi_version_at_least (12)
+         && !(warn_abi && abi_version_crosses (12)))
+       return false;
+
+      bool saw_copy = false;
+      bool saw_non_deleted = false;
+
+      if (CLASSTYPE_LAZY_MOVE_CTOR (t))
+       saw_copy = saw_non_deleted = true;
+      else if (CLASSTYPE_LAZY_COPY_CTOR (t))
+       {
+         saw_copy = true;
+         if (type_has_user_declared_move_constructor (t)
+             || type_has_user_declared_move_assign (t))
+           /* [class.copy]/8 If the class definition declares a move
+              constructor or move assignment operator, the implicitly declared
+              copy constructor is defined as deleted.... */;
+         else
+           /* Any other reason the implicitly-declared function would be
+              deleted would also cause TYPE_HAS_COMPLEX_COPY_CTOR to be
+              set.  */
+           saw_non_deleted = true;
+       }
+
+      if (!saw_non_deleted && CLASSTYPE_METHOD_VEC (t))
+       for (tree fns = CLASSTYPE_CONSTRUCTORS (t); fns; fns = OVL_NEXT (fns))
+         {
+           tree fn = OVL_CURRENT (fns);
+           if (copy_fn_p (fn))
+             {
+               saw_copy = true;
+               if (!DECL_DELETED_FN (fn))
+                 {
+                   /* Not deleted, therefore trivial.  */
+                   saw_non_deleted = true;
+                   break;
+                 }
+             }
+         }
+
+      gcc_assert (saw_copy);
+
+      if (saw_copy && !saw_non_deleted)
+       {
+         if (warn_abi && abi_version_crosses (12))
+           remember_deleted_copy (t);
+         if (abi_version_at_least (12))
+           return true;
+       }
+
+      return false;
     }
   else
     return 0;
diff --git a/gcc/testsuite/g++.dg/abi/invisiref1.C b/gcc/testsuite/g++.dg/abi/invisiref1.C
new file mode 100644 (file)
index 0000000..5534b89
--- /dev/null
@@ -0,0 +1,24 @@
+// PR c++/80178
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wabi=10 -fdump-tree-gimple" }
+// { dg-final { scan-tree-dump "foo .&D" "gimple" } }
+
+struct A {
+  A();
+  A &operator=(A &&o);
+  void *p;
+};
+void notdefined(A);
+
+void foo(A) { }                        // { dg-warning "calling convention" }
+
+A baz()                                // { dg-warning "calling convention" }
+{
+  return {};
+}
+
+void bar() {
+  foo({});                     // { dg-warning "calling convention" }
+  notdefined({});              // { dg-warning "calling convention" }
+  baz();                       // { dg-warning "calling convention" }
+}
diff --git a/gcc/testsuite/g++.dg/abi/invisiref1a.C b/gcc/testsuite/g++.dg/abi/invisiref1a.C
new file mode 100644 (file)
index 0000000..aff7b1a
--- /dev/null
@@ -0,0 +1,24 @@
+// PR c++/80178
+// { dg-do compile { target c++11 } }
+// { dg-options "-fabi-version=11 -Wabi -fdump-tree-gimple" }
+// { dg-final { scan-tree-dump "foo .D" "gimple" } }
+
+struct A {
+  A();
+  A &operator=(A &&o);
+  void *p;
+};
+void notdefined(A);
+
+void foo(A) { }                        // { dg-warning "calling convention" }
+
+A baz()                                // { dg-warning "calling convention" }
+{
+  return {};
+}
+
+void bar() {
+  foo({});                     // { dg-warning "calling convention" }
+  notdefined({});              // { dg-warning "calling convention" }
+  baz();                       // { dg-warning "calling convention" }
+}