; 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=
+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.
&& 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;
}
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);
}
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);
{
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
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. */
#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 *);
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;
--- /dev/null
+// 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" }
+}
--- /dev/null
+// 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" }
+}