return r;
}
-/* Return true iff T refers to a base field. */
+/* Return true iff T refers to a base or potentially-overlapping field, which
+ cannot be used for return by invisible reference. We avoid doing C++17
+ mandatory copy elision when this is true.
-static bool
-is_base_field_ref (tree t)
+ This returns true even if the type of T has no tail padding that other data
+ could be allocated into, because that depends on the particular ABI.
+ unsafe_copy_elision_p, below, does consider whether there is padding. */
+
+bool
+unsafe_return_slot_p (tree t)
{
STRIP_NOPS (t);
if (TREE_CODE (t) == ADDR_EXPR)
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == COMPONENT_REF)
t = TREE_OPERAND (t, 1);
- if (TREE_CODE (t) == FIELD_DECL)
- return DECL_FIELD_IS_BASE (t);
- return false;
+ if (TREE_CODE (t) != FIELD_DECL)
+ return false;
+ if (!CLASS_TYPE_P (TREE_TYPE (t)))
+ /* The middle-end will do the right thing for scalar types. */
+ return false;
+ return (DECL_FIELD_IS_BASE (t)
+ || lookup_attribute ("no_unique_address", DECL_ATTRIBUTES (t)));
}
-/* We can't elide a copy from a function returning by value to a base
- subobject, as the callee might clobber tail padding. Return true iff this
- could be that case. */
+/* We can't elide a copy from a function returning by value to a
+ potentially-overlapping subobject, as the callee might clobber tail padding.
+ Return true iff this could be that case. */
static bool
unsafe_copy_elision_p (tree target, tree exp)
return false;
tree type = TYPE_MAIN_VARIANT (TREE_TYPE (exp));
/* It's safe to elide the copy for a class with no tail padding. */
- if (tree_int_cst_equal (TYPE_SIZE (type), CLASSTYPE_SIZE (type)))
+ if (!is_empty_class (type)
+ && tree_int_cst_equal (TYPE_SIZE (type), CLASSTYPE_SIZE (type)))
return false;
/* It's safe to elide the copy if we aren't initializing a base object. */
- if (!is_base_field_ref (target))
+ if (!unsafe_return_slot_p (target))
return false;
tree init = TARGET_EXPR_INITIAL (exp);
/* build_compound_expr pushes COMPOUND_EXPR inside TARGET_EXPR. */
&& DECL_COMPLETE_CONSTRUCTOR_P (fn)
&& (DECL_COPY_CONSTRUCTOR_P (fn)
|| DECL_MOVE_CONSTRUCTOR_P (fn))
+ && !unsafe_return_slot_p (first_arg)
&& conv_binds_ref_to_prvalue (convs[0]))
{
force_elide = true;
{
tree targ;
tree arg = argarray[num_artificial_parms_for (fn)];
- tree fa;
+ tree fa = argarray[0];
bool trivial = trivial_fn_p (fn);
/* Pull out the real argument, disregarding const-correctness. */
else
arg = cp_build_fold_indirect_ref (arg);
- /* In C++17 we shouldn't be copying a TARGET_EXPR except into a base
- subobject. */
+ /* In C++17 we shouldn't be copying a TARGET_EXPR except into a
+ potentially-overlapping subobject. */
if (CHECKING_P && cxx_dialect >= cxx17)
gcc_assert (TREE_CODE (arg) != TARGET_EXPR
|| force_elide
|| convs[0]->need_temporary_p
|| seen_error ()
/* See unsafe_copy_elision_p. */
- || DECL_BASE_CONSTRUCTOR_P (fn));
+ || unsafe_return_slot_p (fa));
- fa = argarray[0];
bool unsafe = unsafe_copy_elision_p (fa, arg);
bool eliding_temp = (TREE_CODE (arg) == TARGET_EXPR && !unsafe);
resolution. */
if (cxx_dialect >= cxx17
&& args && vec_safe_length (*args) == 1
- && name == complete_ctor_identifier)
+ && !unsafe_return_slot_p (instance))
{
tree arg = (**args)[0];
sub = build3 (COMPONENT_REF, inner_type, dest, field_index,
NULL_TREE);
- code = build2 (INIT_EXPR, inner_type, sub, value);
+ if (unsafe_return_slot_p (sub))
+ {
+ /* We may need to add a copy constructor call if
+ the field has [[no_unique_address]]. */
+ releasing_vec args = make_tree_vector_single (value);
+ code = build_special_member_call
+ (sub, complete_ctor_identifier, &args, inner_type,
+ LOOKUP_NORMAL, tf_warning_or_error);
+ }
+ else
+ code = build2 (INIT_EXPR, inner_type, sub, value);
code = build_stmt (input_location, EXPR_STMT, code);
code = maybe_cleanup_point_expr_void (code);
add_stmt (code);