mirror of git://gcc.gnu.org/git/gcc.git
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:
parent
4d20f49036
commit
cdc184174c
|
|
@ -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>
|
2018-04-30 Jason Merrill <jason@redhat.com>
|
||||||
|
|
||||||
* tree.c (build_clobber): New.
|
* tree.c (build_clobber): New.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
2018-04-30 Jason Merrill <jason@redhat.com>
|
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.
|
* init.c (build_dtor_call): Use build_special_member_call.
|
||||||
(build_delete): Remove redundant uses of save_addr.
|
(build_delete): Remove redundant uses of save_addr.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7629,6 +7629,33 @@ conv_binds_ref_to_prvalue (conversion *c)
|
||||||
return false;
|
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
|
/* Subroutine of the various build_*_call functions. Overload resolution
|
||||||
has chosen a winning candidate CAND; build up a CALL_EXPR accordingly.
|
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
|
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))
|
else if (trivial_fn_p (fn))
|
||||||
{
|
{
|
||||||
if (DECL_DESTRUCTOR_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))
|
else if (default_ctor_p (fn))
|
||||||
{
|
{
|
||||||
if (is_dummy_object (argarray[0]))
|
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;
|
tree ret;
|
||||||
|
|
||||||
gcc_assert (IDENTIFIER_CDTOR_P (name) || name == assign_op_identifier);
|
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))
|
if (TYPE_P (binfo))
|
||||||
{
|
{
|
||||||
/* Resolve the name. */
|
/* 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);
|
instance = build_dummy_object (class_type);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (IDENTIFIER_DTOR_P (name))
|
|
||||||
gcc_assert (args == NULL || vec_safe_is_empty (*args));
|
|
||||||
|
|
||||||
/* Convert to the base class, if necessary. */
|
/* Convert to the base class, if necessary. */
|
||||||
if (!same_type_ignoring_top_level_qualifiers_p
|
if (!same_type_ignoring_top_level_qualifiers_p
|
||||||
(TREE_TYPE (instance), BINFO_TYPE (binfo)))
|
(TREE_TYPE (instance), BINFO_TYPE (binfo)))
|
||||||
|
|
|
||||||
|
|
@ -6037,6 +6037,7 @@ extern bool null_member_pointer_value_p (tree);
|
||||||
extern bool sufficient_parms_p (const_tree);
|
extern bool sufficient_parms_p (const_tree);
|
||||||
extern tree type_decays_to (tree);
|
extern tree type_decays_to (tree);
|
||||||
extern tree extract_call_expr (tree);
|
extern tree extract_call_expr (tree);
|
||||||
|
extern tree build_trivial_dtor_call (tree);
|
||||||
extern tree build_user_type_conversion (tree, tree, int,
|
extern tree build_user_type_conversion (tree, tree, int,
|
||||||
tsubst_flags_t);
|
tsubst_flags_t);
|
||||||
extern tree build_new_function_call (tree, vec<tree, va_gc> **,
|
extern tree build_new_function_call (tree, vec<tree, va_gc> **,
|
||||||
|
|
|
||||||
|
|
@ -4664,35 +4664,17 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
|
||||||
addr = convert_force (build_pointer_type (type), addr, 0, complain);
|
addr = convert_force (build_pointer_type (type), addr, 0, complain);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TYPE_HAS_TRIVIAL_DESTRUCTOR (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 head = NULL_TREE;
|
||||||
tree do_delete = NULL_TREE;
|
tree do_delete = NULL_TREE;
|
||||||
tree ifexp;
|
tree ifexp;
|
||||||
|
|
||||||
|
bool virtual_p = false;
|
||||||
|
if (type_build_dtor_call (type))
|
||||||
|
{
|
||||||
if (CLASSTYPE_LAZY_DESTRUCTOR (type))
|
if (CLASSTYPE_LAZY_DESTRUCTOR (type))
|
||||||
lazily_declare_fn (sfk_destructor, type);
|
lazily_declare_fn (sfk_destructor, type);
|
||||||
|
virtual_p = DECL_VIRTUAL_P (CLASSTYPE_DESTRUCTOR (type));
|
||||||
|
}
|
||||||
|
|
||||||
/* For `::delete x', we must not use the deleting destructor
|
/* For `::delete x', we must not use the deleting destructor
|
||||||
since then we would not be sure to get the global `operator
|
since then we would not be sure to get the global `operator
|
||||||
|
|
@ -4717,7 +4699,7 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
|
||||||
/* If the destructor is non-virtual, there is no deleting
|
/* If the destructor is non-virtual, there is no deleting
|
||||||
variant. Instead, we must explicitly call the appropriate
|
variant. Instead, we must explicitly call the appropriate
|
||||||
`operator delete' here. */
|
`operator delete' here. */
|
||||||
else if (!DECL_VIRTUAL_P (CLASSTYPE_DESTRUCTOR (type))
|
else if (!virtual_p
|
||||||
&& auto_delete == sfk_deleting_destructor)
|
&& auto_delete == sfk_deleting_destructor)
|
||||||
{
|
{
|
||||||
/* We will use ADDR multiple times so we must save it. */
|
/* We will use ADDR multiple times so we must save it. */
|
||||||
|
|
@ -4745,11 +4727,17 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
|
||||||
complain);
|
complain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type_build_dtor_call (type))
|
||||||
expr = build_dtor_call (cp_build_fold_indirect_ref (addr),
|
expr = build_dtor_call (cp_build_fold_indirect_ref (addr),
|
||||||
auto_delete, flags, complain);
|
auto_delete, flags, complain);
|
||||||
|
else
|
||||||
|
expr = build_trivial_dtor_call (addr);
|
||||||
if (expr == error_mark_node)
|
if (expr == error_mark_node)
|
||||||
return error_mark_node;
|
return error_mark_node;
|
||||||
if (do_delete)
|
|
||||||
|
if (do_delete && !TREE_SIDE_EFFECTS (expr))
|
||||||
|
expr = do_delete;
|
||||||
|
else if (do_delete)
|
||||||
/* The delete operator must be called, regardless of whether
|
/* The delete operator must be called, regardless of whether
|
||||||
the destructor throws.
|
the destructor throws.
|
||||||
|
|
||||||
|
|
@ -4784,7 +4772,6 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
|
||||||
|
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* At the beginning of a destructor, push cleanups that will call the
|
/* At the beginning of a destructor, push cleanups that will call the
|
||||||
destructors for our base classes and members.
|
destructors for our base classes and members.
|
||||||
|
|
|
||||||
|
|
@ -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);
|
ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
|
||||||
if (ret == GS_ERROR)
|
if (ret == GS_ERROR)
|
||||||
return ret;
|
return ret;
|
||||||
gcc_assert (!want_value
|
gcc_assert (!want_value);
|
||||||
&& (VAR_P (*to_p) || TREE_CODE (*to_p) == MEM_REF));
|
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));
|
gimplify_seq_add_stmt (pre_p, gimple_build_assign (*to_p, *from_p));
|
||||||
*expr_p = NULL;
|
*expr_p = NULL;
|
||||||
return GS_ALL_DONE;
|
return GS_ALL_DONE;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue