Commit f3ec182d by Jason Merrill Committed by Jason Merrill

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.

From-SVN: r247757
parent 49f0c04c
...@@ -910,6 +910,10 @@ Driver Undocumented ...@@ -910,6 +910,10 @@ Driver Undocumented
; and introduces new inheriting constructor handling. ; and introduces new inheriting constructor handling.
; Default in G++ 7. ; 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 ; Additional positive integers will be assigned as new versions of
; the ABI become the default version of the ABI. ; the ABI become the default version of the ABI.
fabi-version= 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> 2017-05-08 Nathan Sidwell <nathan@acm.org>
* decl.c (builtin_function_1): Set DCL_ANTICIPATED before pushing. * decl.c (builtin_function_1): Set DCL_ANTICIPATED before pushing.
......
...@@ -7352,8 +7352,9 @@ convert_for_arg_passing (tree type, tree val, tsubst_flags_t complain) ...@@ -7352,8 +7352,9 @@ convert_for_arg_passing (tree type, tree val, tsubst_flags_t complain)
&& COMPLETE_TYPE_P (type) && COMPLETE_TYPE_P (type)
&& tree_int_cst_lt (TYPE_SIZE (type), TYPE_SIZE (integer_type_node))) && tree_int_cst_lt (TYPE_SIZE (type), TYPE_SIZE (integer_type_node)))
val = cp_perform_integral_promotions (val, complain); val = cp_perform_integral_promotions (val, complain);
if ((complain & tf_warning) if (complain & tf_warning)
&& warn_suggest_attribute_format) {
if (warn_suggest_attribute_format)
{ {
tree rhstype = TREE_TYPE (val); tree rhstype = TREE_TYPE (val);
const enum tree_code coder = TREE_CODE (rhstype); const enum tree_code coder = TREE_CODE (rhstype);
...@@ -7362,7 +7363,10 @@ convert_for_arg_passing (tree type, tree val, tsubst_flags_t complain) ...@@ -7362,7 +7363,10 @@ convert_for_arg_passing (tree type, tree val, tsubst_flags_t complain)
&& coder == codel && coder == codel
&& check_missing_format_attribute (type, rhstype)) && check_missing_format_attribute (type, rhstype))
warning (OPT_Wsuggest_attribute_format, warning (OPT_Wsuggest_attribute_format,
"argument of function call might be a candidate for a format attribute"); "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 val;
} }
...@@ -8234,7 +8238,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray, ...@@ -8234,7 +8238,10 @@ build_cxx_call (tree fn, int nargs, tree *argarray,
return error_mark_node; return error_mark_node;
if (MAYBE_CLASS_TYPE_P (TREE_TYPE (fn))) 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); return convert_from_reference (fn);
} }
......
...@@ -6614,6 +6614,7 @@ extern bool type_has_unique_obj_representations (const_tree); ...@@ -6614,6 +6614,7 @@ extern bool type_has_unique_obj_representations (const_tree);
extern bool scalarish_type_p (const_tree); extern bool scalarish_type_p (const_tree);
extern bool type_has_nontrivial_default_init (const_tree); extern bool type_has_nontrivial_default_init (const_tree);
extern bool type_has_nontrivial_copy_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 bool class_tmpl_impl_spec_p (const_tree);
extern int zero_init_p (const_tree); extern int zero_init_p (const_tree);
extern bool check_abi_tag_redeclaration (const_tree, const_tree, const_tree); extern bool check_abi_tag_redeclaration (const_tree, const_tree, const_tree);
......
...@@ -12387,6 +12387,9 @@ require_complete_types_for_parms (tree parms) ...@@ -12387,6 +12387,9 @@ require_complete_types_for_parms (tree parms)
{ {
relayout_decl (parms); relayout_decl (parms);
DECL_ARG_TYPE (parms) = type_passed_as (TREE_TYPE (parms)); DECL_ARG_TYPE (parms) = type_passed_as (TREE_TYPE (parms));
maybe_warn_parm_abi (TREE_TYPE (parms),
DECL_SOURCE_LOCATION (parms));
} }
else else
/* grokparms or complete_type_or_else will have already issued /* grokparms or complete_type_or_else will have already issued
...@@ -14639,7 +14642,11 @@ check_function_type (tree decl, tree current_function_parms) ...@@ -14639,7 +14642,11 @@ check_function_type (tree decl, tree current_function_parms)
TREE_TYPE (decl) = fntype; TREE_TYPE (decl) = fntype;
} }
else 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. */ /* True iff FN is an implicitly-defined default constructor. */
......
...@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see
#include "convert.h" #include "convert.h"
#include "gimplify.h" #include "gimplify.h"
#include "attribs.h" #include "attribs.h"
#include "flags.h"
static tree bot_manip (tree *, int *, void *); static tree bot_manip (tree *, int *, void *);
static tree bot_replace (tree *, int *, void *); static tree bot_replace (tree *, int *, void *);
...@@ -3596,21 +3597,112 @@ type_has_nontrivial_default_init (const_tree t) ...@@ -3596,21 +3597,112 @@ type_has_nontrivial_default_init (const_tree t)
return 0; 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 /* Returns true iff copying an object of type T (including via move
constructor) is non-trivial. That is, T has no non-trivial copy 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 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)) if (CLASS_TYPE_P (t))
{ {
gcc_assert (COMPLETE_TYPE_P (t)); gcc_assert (COMPLETE_TYPE_P (t));
return ((TYPE_HAS_COPY_CTOR (t)
&& TYPE_HAS_COMPLEX_COPY_CTOR (t)) if (TYPE_HAS_COMPLEX_COPY_CTOR (t)
|| TYPE_HAS_COMPLEX_MOVE_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 else
return 0; return 0;
......
// 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" }
}
// 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" }
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment