PR c++/61982 - dead stores to destroyed objects.

gcc/cp/
	* call.c (build_trivial_dtor_call): New, assigns a clobber.
	(build_over_call, build_special_member_call): Use it.
	* cp-tree.h: Declare it.
	* init.c (build_delete): Remove trivial path.
gcc/
	* gimplify.c (gimplify_modify_expr): Simplify complex lvalue on LHS
	of clobber.

From-SVN: r259772
This commit is contained in:
Jason Merrill 2018-04-30 11:21:01 -04:00 committed by Jason Merrill
parent 4d20f49036
commit cdc184174c
7 changed files with 181 additions and 122 deletions

View File

@ -1,3 +1,9 @@
2018-04-30 Jason Merrill <jason@redhat.com>
PR c++/61982 - dead stores to destroyed objects.
* gimplify.c (gimplify_modify_expr): Simplify complex lvalue on LHS
of clobber.
2018-04-30 Jason Merrill <jason@redhat.com>
* tree.c (build_clobber): New.

View File

@ -1,5 +1,11 @@
2018-04-30 Jason Merrill <jason@redhat.com>
PR c++/61982 - dead stores to destroyed objects.
* call.c (build_trivial_dtor_call): New, assigns a clobber.
(build_over_call, build_special_member_call): Use it.
* cp-tree.h: Declare it.
* init.c (build_delete): Remove trivial path.
* init.c (build_dtor_call): Use build_special_member_call.
(build_delete): Remove redundant uses of save_addr.

View File

@ -7629,6 +7629,33 @@ conv_binds_ref_to_prvalue (conversion *c)
return false;
}
/* Call the trivial destructor for INSTANCE, which can be either an lvalue of
class type or a pointer to class type. */
tree
build_trivial_dtor_call (tree instance)
{
gcc_assert (!is_dummy_object (instance));
if (!flag_lifetime_dse)
{
no_clobber:
return fold_convert (void_type_node, instance);
}
if (POINTER_TYPE_P (TREE_TYPE (instance)))
{
if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (instance))))
goto no_clobber;
instance = cp_build_fold_indirect_ref (instance);
}
/* A trivial destructor should still clobber the object. */
tree clobber = build_clobber (TREE_TYPE (instance));
return build2 (MODIFY_EXPR, void_type_node,
instance, clobber);
}
/* Subroutine of the various build_*_call functions. Overload resolution
has chosen a winning candidate CAND; build up a CALL_EXPR accordingly.
ARGS is a TREE_LIST of the unconverted arguments to the call. FLAGS is a
@ -8240,7 +8267,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
else if (trivial_fn_p (fn))
{
if (DECL_DESTRUCTOR_P (fn))
return fold_convert (void_type_node, argarray[0]);
return build_trivial_dtor_call (argarray[0]);
else if (default_ctor_p (fn))
{
if (is_dummy_object (argarray[0]))
@ -8863,6 +8890,18 @@ build_special_member_call (tree instance, tree name, vec<tree, va_gc> **args,
tree ret;
gcc_assert (IDENTIFIER_CDTOR_P (name) || name == assign_op_identifier);
if (error_operand_p (instance))
return error_mark_node;
if (IDENTIFIER_DTOR_P (name))
{
gcc_assert (args == NULL || vec_safe_is_empty (*args));
if (!type_build_dtor_call (TREE_TYPE (instance)))
/* Shortcut to avoid lazy destructor declaration. */
return build_trivial_dtor_call (instance);
}
if (TYPE_P (binfo))
{
/* Resolve the name. */
@ -8881,9 +8920,6 @@ build_special_member_call (tree instance, tree name, vec<tree, va_gc> **args,
instance = build_dummy_object (class_type);
else
{
if (IDENTIFIER_DTOR_P (name))
gcc_assert (args == NULL || vec_safe_is_empty (*args));
/* Convert to the base class, if necessary. */
if (!same_type_ignoring_top_level_qualifiers_p
(TREE_TYPE (instance), BINFO_TYPE (binfo)))

View File

@ -6037,6 +6037,7 @@ extern bool null_member_pointer_value_p (tree);
extern bool sufficient_parms_p (const_tree);
extern tree type_decays_to (tree);
extern tree extract_call_expr (tree);
extern tree build_trivial_dtor_call (tree);
extern tree build_user_type_conversion (tree, tree, int,
tsubst_flags_t);
extern tree build_new_function_call (tree, vec<tree, va_gc> **,

View File

@ -4664,126 +4664,113 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
addr = convert_force (build_pointer_type (type), addr, 0, complain);
}
if (TYPE_HAS_TRIVIAL_DESTRUCTOR (type))
tree head = NULL_TREE;
tree do_delete = NULL_TREE;
tree ifexp;
bool virtual_p = false;
if (type_build_dtor_call (type))
{
/* Make sure the destructor is callable. */
if (type_build_dtor_call (type))
{
expr = build_dtor_call (cp_build_fold_indirect_ref (addr),
sfk_complete_destructor, flags, complain);
if (expr == error_mark_node)
return error_mark_node;
}
if (auto_delete != sfk_deleting_destructor)
return void_node;
return build_op_delete_call (DELETE_EXPR, addr,
cxx_sizeof_nowarn (type),
use_global_delete,
/*placement=*/NULL_TREE,
/*alloc_fn=*/NULL_TREE,
complain);
}
else
{
tree head = NULL_TREE;
tree do_delete = NULL_TREE;
tree ifexp;
if (CLASSTYPE_LAZY_DESTRUCTOR (type))
lazily_declare_fn (sfk_destructor, type);
/* For `::delete x', we must not use the deleting destructor
since then we would not be sure to get the global `operator
delete'. */
if (use_global_delete && auto_delete == sfk_deleting_destructor)
{
/* We will use ADDR multiple times so we must save it. */
addr = save_expr (addr);
head = get_target_expr (build_headof (addr));
/* Delete the object. */
do_delete = build_op_delete_call (DELETE_EXPR,
head,
cxx_sizeof_nowarn (type),
/*global_p=*/true,
/*placement=*/NULL_TREE,
/*alloc_fn=*/NULL_TREE,
complain);
/* Otherwise, treat this like a complete object destructor
call. */
auto_delete = sfk_complete_destructor;
}
/* If the destructor is non-virtual, there is no deleting
variant. Instead, we must explicitly call the appropriate
`operator delete' here. */
else if (!DECL_VIRTUAL_P (CLASSTYPE_DESTRUCTOR (type))
&& auto_delete == sfk_deleting_destructor)
{
/* We will use ADDR multiple times so we must save it. */
addr = save_expr (addr);
/* Build the call. */
do_delete = build_op_delete_call (DELETE_EXPR,
addr,
cxx_sizeof_nowarn (type),
/*global_p=*/false,
/*placement=*/NULL_TREE,
/*alloc_fn=*/NULL_TREE,
complain);
/* Call the complete object destructor. */
auto_delete = sfk_complete_destructor;
}
else if (auto_delete == sfk_deleting_destructor
&& TYPE_GETS_REG_DELETE (type))
{
/* Make sure we have access to the member op delete, even though
we'll actually be calling it from the destructor. */
build_op_delete_call (DELETE_EXPR, addr, cxx_sizeof_nowarn (type),
/*global_p=*/false,
/*placement=*/NULL_TREE,
/*alloc_fn=*/NULL_TREE,
complain);
}
expr = build_dtor_call (cp_build_fold_indirect_ref (addr),
auto_delete, flags, complain);
if (expr == error_mark_node)
return error_mark_node;
if (do_delete)
/* The delete operator must be called, regardless of whether
the destructor throws.
[expr.delete]/7 The deallocation function is called
regardless of whether the destructor for the object or some
element of the array throws an exception. */
expr = build2 (TRY_FINALLY_EXPR, void_type_node, expr, do_delete);
/* We need to calculate this before the dtor changes the vptr. */
if (head)
expr = build2 (COMPOUND_EXPR, void_type_node, head, expr);
if (flags & LOOKUP_DESTRUCTOR)
/* Explicit destructor call; don't check for null pointer. */
ifexp = integer_one_node;
else
{
/* Handle deleting a null pointer. */
warning_sentinel s (warn_address);
ifexp = cp_build_binary_op (input_location, NE_EXPR, addr,
nullptr_node, complain);
if (ifexp == error_mark_node)
return error_mark_node;
/* This is a compiler generated comparison, don't emit
e.g. -Wnonnull-compare warning for it. */
else if (TREE_CODE (ifexp) == NE_EXPR)
TREE_NO_WARNING (ifexp) = 1;
}
if (ifexp != integer_one_node)
expr = build3 (COND_EXPR, void_type_node, ifexp, expr, void_node);
return expr;
virtual_p = DECL_VIRTUAL_P (CLASSTYPE_DESTRUCTOR (type));
}
/* For `::delete x', we must not use the deleting destructor
since then we would not be sure to get the global `operator
delete'. */
if (use_global_delete && auto_delete == sfk_deleting_destructor)
{
/* We will use ADDR multiple times so we must save it. */
addr = save_expr (addr);
head = get_target_expr (build_headof (addr));
/* Delete the object. */
do_delete = build_op_delete_call (DELETE_EXPR,
head,
cxx_sizeof_nowarn (type),
/*global_p=*/true,
/*placement=*/NULL_TREE,
/*alloc_fn=*/NULL_TREE,
complain);
/* Otherwise, treat this like a complete object destructor
call. */
auto_delete = sfk_complete_destructor;
}
/* If the destructor is non-virtual, there is no deleting
variant. Instead, we must explicitly call the appropriate
`operator delete' here. */
else if (!virtual_p
&& auto_delete == sfk_deleting_destructor)
{
/* We will use ADDR multiple times so we must save it. */
addr = save_expr (addr);
/* Build the call. */
do_delete = build_op_delete_call (DELETE_EXPR,
addr,
cxx_sizeof_nowarn (type),
/*global_p=*/false,
/*placement=*/NULL_TREE,
/*alloc_fn=*/NULL_TREE,
complain);
/* Call the complete object destructor. */
auto_delete = sfk_complete_destructor;
}
else if (auto_delete == sfk_deleting_destructor
&& TYPE_GETS_REG_DELETE (type))
{
/* Make sure we have access to the member op delete, even though
we'll actually be calling it from the destructor. */
build_op_delete_call (DELETE_EXPR, addr, cxx_sizeof_nowarn (type),
/*global_p=*/false,
/*placement=*/NULL_TREE,
/*alloc_fn=*/NULL_TREE,
complain);
}
if (type_build_dtor_call (type))
expr = build_dtor_call (cp_build_fold_indirect_ref (addr),
auto_delete, flags, complain);
else
expr = build_trivial_dtor_call (addr);
if (expr == error_mark_node)
return error_mark_node;
if (do_delete && !TREE_SIDE_EFFECTS (expr))
expr = do_delete;
else if (do_delete)
/* The delete operator must be called, regardless of whether
the destructor throws.
[expr.delete]/7 The deallocation function is called
regardless of whether the destructor for the object or some
element of the array throws an exception. */
expr = build2 (TRY_FINALLY_EXPR, void_type_node, expr, do_delete);
/* We need to calculate this before the dtor changes the vptr. */
if (head)
expr = build2 (COMPOUND_EXPR, void_type_node, head, expr);
if (flags & LOOKUP_DESTRUCTOR)
/* Explicit destructor call; don't check for null pointer. */
ifexp = integer_one_node;
else
{
/* Handle deleting a null pointer. */
warning_sentinel s (warn_address);
ifexp = cp_build_binary_op (input_location, NE_EXPR, addr,
nullptr_node, complain);
if (ifexp == error_mark_node)
return error_mark_node;
/* This is a compiler generated comparison, don't emit
e.g. -Wnonnull-compare warning for it. */
else if (TREE_CODE (ifexp) == NE_EXPR)
TREE_NO_WARNING (ifexp) = 1;
}
if (ifexp != integer_one_node)
expr = build3 (COND_EXPR, void_type_node, ifexp, expr, void_node);
return expr;
}
/* At the beginning of a destructor, push cleanups that will call the

View File

@ -5558,8 +5558,13 @@ gimplify_modify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p,
ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
if (ret == GS_ERROR)
return ret;
gcc_assert (!want_value
&& (VAR_P (*to_p) || TREE_CODE (*to_p) == MEM_REF));
gcc_assert (!want_value);
if (!VAR_P (*to_p) && TREE_CODE (*to_p) != MEM_REF)
{
tree addr = get_initialized_tmp_var (build_fold_addr_expr (*to_p),
pre_p, post_p);
*to_p = build_simple_mem_ref_loc (EXPR_LOCATION (*to_p), addr);
}
gimplify_seq_add_stmt (pre_p, gimple_build_assign (*to_p, *from_p));
*expr_p = NULL;
return GS_ALL_DONE;

View File

@ -0,0 +1,18 @@
// PR c++/61982
// { dg-additional-options "-O2 -fdump-tree-optimized" }
// { dg-final { scan-tree-dump-not "= 0" "optimized" } }
struct X {
int i;
void clear() { i = 0; }
};
void f(X* x) {
x->clear();
x->~X();
}
void g(X* x) {
x->clear();
delete x;
}