tree init = TREE_OPERAND (t, 1);
- if (TREE_CLOBBER_P (init)
- && CLOBBER_KIND (init) < CLOBBER_OBJECT_END)
- /* Only handle clobbers ending the lifetime of objects.
- ??? We should probably set CONSTRUCTOR_NO_CLEARING. */
- return void_node;
-
/* First we figure out where we're storing to. */
tree target = TREE_OPERAND (t, 0);
}
/* Handle explicit end-of-lifetime. */
- if (TREE_CLOBBER_P (init))
+ if (TREE_CLOBBER_P (init)
+ && CLOBBER_KIND (init) >= CLOBBER_OBJECT_END)
{
if (refs->is_empty ())
- ctx->global->destroy_value (object);
- return void_node;
+ {
+ ctx->global->destroy_value (object);
+ return void_node;
+ }
}
type = TREE_TYPE (object);
*non_constant_p = true;
}
else if (!is_access_expr
+ || (TREE_CLOBBER_P (init)
+ && CLOBBER_KIND (init) >= CLOBBER_OBJECT_END)
|| (TREE_CODE (t) == MODIFY_EXPR
&& CLASS_TYPE_P (inner)
&& !type_has_non_deleted_trivial_default_ctor (inner)))
type = reftype;
}
+ /* Change an "as-base" clobber to the real type;
+ we don't need to worry about padding in constexpr. */
+ tree itype = initialized_type (init);
+ if (IS_FAKE_BASE_TYPE (itype))
+ itype = TYPE_CONTEXT (itype);
+
/* For initialization of an empty base, the original target will be
*(base*)this, evaluation of which resolves to the object
argument, which has the derived type rather than the base type. */
if (!empty_base && !(same_type_ignoring_top_level_qualifiers_p
- (initialized_type (init), type)))
+ (itype, type)))
{
gcc_assert (is_empty_class (TREE_TYPE (target)));
empty_base = true;
/* Don't share a CONSTRUCTOR that might be changed later. */
init = unshare_constructor (init);
- gcc_checking_assert (!*valp || (same_type_ignoring_top_level_qualifiers_p
- (TREE_TYPE (*valp), type)));
+ gcc_checking_assert (!*valp
+ || *valp == void_node
+ || (same_type_ignoring_top_level_qualifiers_p
+ (TREE_TYPE (*valp), type)));
if (empty_base)
{
/* Just evaluate the initializer and return, since there's no actual data
CONSTRUCTOR_ZERO_PADDING_BITS (*valp) = zero_padding_bits;
}
}
+ else if (TREE_CLOBBER_P (init))
+ {
+ if (AGGREGATE_TYPE_P (type))
+ {
+ if (*valp)
+ CONSTRUCTOR_ELTS (*valp) = nullptr;
+ else
+ *valp = build_constructor (type, nullptr);
+ TREE_CONSTANT (*valp) = true;
+ TREE_SIDE_EFFECTS (*valp) = false;
+ CONSTRUCTOR_NO_CLEARING (*valp) = true;
+ CONSTRUCTOR_ZERO_PADDING_BITS (*valp) = zero_padding_bits;
+ }
+ else
+ *valp = void_node;
+ }
else if (*valp && TREE_CODE (*valp) == CONSTRUCTOR
&& TREE_CODE (init) == CONSTRUCTOR)
{
&& TREE_CODE (*valp) == CONSTRUCTOR
&& TYPE_READONLY (type))
{
+ tree target_type = TREE_TYPE (target);
+ if (IS_FAKE_BASE_TYPE (target_type))
+ target_type = TYPE_CONTEXT (target_type);
if (INDIRECT_REF_P (target)
&& (is_this_parameter
(tree_strip_nop_conversions (TREE_OPERAND (target, 0)))))
constructor of a delegating constructor). Leave it up to the
caller that set 'this' to set TREE_READONLY appropriately. */
gcc_checking_assert (same_type_ignoring_top_level_qualifiers_p
- (TREE_TYPE (target), type) || empty_base);
+ (target_type, type) || empty_base);
else
TREE_READONLY (*valp) = true;
}
&& !FUNC_OR_METHOD_TYPE_P (TREE_TYPE (t))
&& !NULLPTR_TYPE_P (TREE_TYPE (t)))
{
+ if (TREE_CLOBBER_P (t))
+ {
+ /* We should have caught any clobbers in INIT/MODIFY_EXPR. */
+ gcc_checking_assert (false);
+ return true;
+ }
+
if (flags & tf_error)
constexpr_error (loc, fundef_p, "lvalue-to-rvalue conversion of "
"a volatile lvalue %qE with type %qT", t,
}
/* FALLTHRU */
case INIT_EXPR:
+ if (TREE_CLOBBER_P (TREE_OPERAND (t, 1)))
+ return true;
return RECUR (TREE_OPERAND (t, 1), rval);
case CONSTRUCTOR:
alloc_expr = maybe_wrap_new_for_constexpr (alloc_expr, type,
cookie_size);
+ bool std_placement = std_placement_new_fn_p (alloc_fn);
+
+ /* For std placement new, clobber the object if the constructor won't do it
+ in start_preparsed_function. This is most important for activating an
+ array in a union (c++/121068), but should also help the optimizers. */
+ const bool do_clobber
+ = (std_placement && !*init && flag_lifetime_dse > 1
+ && (!CLASS_TYPE_P (elt_type)
+ || type_has_non_user_provided_default_constructor (elt_type)));
+
/* In the simple case, we can stop now. */
pointer_type = build_pointer_type (type);
- if (!cookie_size && !is_initialized && !member_delete_p)
+ if (!cookie_size && !is_initialized && !member_delete_p && !do_clobber)
return build_nop (pointer_type, alloc_expr);
/* Store the result of the allocation call in a variable so that we can
So check for a null exception spec on the op new we just called. */
nothrow = TYPE_NOTHROW_P (TREE_TYPE (alloc_fn));
- check_new
- = flag_check_new || (nothrow && !std_placement_new_fn_p (alloc_fn));
+ check_new = flag_check_new || (nothrow && !std_placement);
if (cookie_size)
{
/* Any further uses of alloc_node will want this type, too. */
alloc_node = fold_convert (non_const_pointer_type, alloc_node);
+ tree clobber_expr = NULL_TREE;
+ if (do_clobber)
+ {
+ tree clobber = build_clobber (elt_type, CLOBBER_OBJECT_BEGIN);
+ CONSTRUCTOR_IS_DIRECT_INIT (clobber) = true;
+ if (array_p)
+ {
+ /* Clobber each element rather than the array at once. */
+ tree maxindex = cp_build_binary_op (input_location,
+ MINUS_EXPR, outer_nelts,
+ integer_one_node,
+ complain);
+ clobber_expr = build_vec_init (data_addr, maxindex, clobber,
+ /*valinit*/false, /*from_arr*/0,
+ complain, nullptr);
+ }
+ else
+ {
+ tree targ = cp_build_fold_indirect_ref (data_addr);
+ clobber_expr = cp_build_init_expr (targ, clobber);
+ }
+ }
+
/* Now initialize the allocated object. Note that we preevaluate the
initialization expression, apart from the actual constructor call or
assignment--we do this because we want to delay the allocation as long
if (init_expr)
rval = build2 (COMPOUND_EXPR, TREE_TYPE (rval), init_expr, rval);
+ if (clobber_expr)
+ rval = build2 (COMPOUND_EXPR, TREE_TYPE (rval), clobber_expr, rval);
if (cookie_expr)
rval = build2 (COMPOUND_EXPR, TREE_TYPE (rval), cookie_expr, rval);
the partially constructed array if an exception is thrown.
But don't do this if we're assigning. */
if (flag_exceptions && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type)
+ /* And don't clean up from clobbers, the actual initialization will
+ follow as a separate build_vec_init. */
+ && !(init && TREE_CLOBBER_P (init))
&& from_array != 2)
{
tree e;