https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99728
--- Comment #18 from Richard Biener <rguenth at gcc dot gnu.org> --- OTOH we call is_really_empty_class which for not trivially empty classes ends up walking all non-FIELD_DECL fields as well: for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) if (TREE_CODE (field) == FIELD_DECL && !DECL_ARTIFICIAL (field) /* An unnamed bit-field is not a data member. */ && !DECL_UNNAMED_BIT_FIELD (field) && !is_really_empty_class (TREE_TYPE (field), ignore_vptr)) return false; So the following performs single-member copying optimization (also avoiding the as-base special-casing when the copy involves a single member). The copy_single_member function is based on is_really_empty_class which could be refactored to also compute this as alternate output. In principle the single member to copy could be inside an aggregate member/base so as-is this doesn't always avoid an aggregate copy if the single member in 'type' is an aggregate. It fixes the testcase fully. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index e4df72ec1a3..31eea7c935b 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -8881,6 +8881,41 @@ immediate_invocation_p (tree fn, int nargs) && (nargs > 1 || !source_location_current_p (fn))); } +/* Return the FIELD_DECL of the single member of TYPE that needs to be copied + by copy assignment if any or NULL_TREE if there are none or multiple. */ + +static tree +copy_single_member (tree type) +{ + if (!CLASS_TYPE_P (type)) + return NULL_TREE; + + tree binfo; + tree base_binfo; + int i; + for (binfo = TYPE_BINFO (type), i = 0; + BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i) + if (!is_really_empty_class (BINFO_TYPE (base_binfo), true)) + return NULL_TREE; + + tree single_field = NULL_TREE; + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (TREE_CODE (field) != FIELD_DECL) + continue; + if (!(!DECL_ARTIFICIAL (field) + /* An unnamed bit-field is not a data member. */ + && !DECL_UNNAMED_BIT_FIELD (field) + && !is_really_empty_class (TREE_TYPE (field), true))) + continue; + if (single_field) + return NULL_TREE; + single_field = field; + } + + return single_field; +} + /* 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 @@ -9501,6 +9536,16 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) val = build2 (COMPOUND_EXPR, type, arg, to); suppress_warning (val, OPT_Wunused); } + else if (tree field = copy_single_member (type)) + { + arg = cp_build_fold_indirect_ref (arg); + tree t = build2 (MODIFY_EXPR, TREE_TYPE (field), + build3 (COMPONENT_REF, TREE_TYPE (field), + to, field, NULL_TREE), + build3 (COMPONENT_REF, TREE_TYPE (field), + arg, field, NULL_TREE)); + val = build2 (COMPOUND_EXPR, type, t, to); + suppress_warning (val, OPT_Wunused); + } else if (tree_int_cst_equal (TYPE_SIZE (type), TYPE_SIZE (as_base))) { if (is_std_init_list (type)