From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2122) id 084423858C55; Sat, 8 Oct 2022 00:28:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 084423858C55 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1665188900; bh=AMNJ+cQbCNa6gb+7fWwuEDVvDHi9GOz5hubcpfnae30=; h=From:To:Subject:Date:From; b=f6uFjINEZoPRsCwHRcg9rOhDHNHwepk6YC0cr0DqLD9qG8Il0wYQlxU2UUbHprUXy ZGKS1EOOO+vmFUPLKcRNbFVNwrUuNO3GCnwvdZznBHYRprbHUTsXP3C0kj4bWKqi6c 2PotRye21z67SQ8wYCJ0n967Eo3ng2yPDhRYDsG8= MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jason Merrill To: gcc-cvs@gcc.gnu.org Subject: [gcc r13-3175] c++: track whether we expect a TARGET_EXPR to be elided X-Act-Checkin: gcc X-Git-Author: Jason Merrill X-Git-Refname: refs/heads/master X-Git-Oldrev: 9ff6c33e2ec0d75958d3f19089519034e8f96a30 X-Git-Newrev: 6ffbf87ca66f4ed9cd79cff675fabe2109e46e85 Message-Id: <20221008002820.084423858C55@sourceware.org> Date: Sat, 8 Oct 2022 00:28:19 +0000 (GMT) List-Id: https://gcc.gnu.org/g:6ffbf87ca66f4ed9cd79cff675fabe2109e46e85 commit r13-3175-g6ffbf87ca66f4ed9cd79cff675fabe2109e46e85 Author: Jason Merrill Date: Sat Sep 17 12:04:05 2022 +0200 c++: track whether we expect a TARGET_EXPR to be elided A discussion at Cauldron made me think that with the formalization of copy elision in C++17, we should be able to determine before optimization which TARGET_EXPRs will become temporaries and which are initializers. This patch implements that: we set TARGET_EXPR_ELIDING_P if it's used as an initializer, and later check that we were right. There's an exception in the cp_gimplify_expr check to allow extra temporaries of non-addressable type: this is used by gimplify_init_ctor_preeval to materialize subobjects of a CONSTRUCTOR on the rhs of a MODIFY_EXPR rather than materializing the whole object. If the type isn't addressable, there's no way for a program to tell the difference, so this is a valid optimization. I considered changing replace_placeholders_for_class_temp_r to check TARGET_EXPR_ELIDING_P instead of potential_prvalue_result_of, but decided that would be wrong: if we have an eliding TARGET_EXPR inside a non-eliding one, we would miss replacing its placeholders. gcc/cp/ChangeLog: * cp-tree.h (TARGET_EXPR_ELIDING_P): New. (unsafe_copy_elision_p, set_target_expr_eliding) (cp_build_init_expr): Declare. * call.cc (unsafe_copy_elision_p): No longer static. (build_over_call, build_special_member_call) (build_new_method_call): Use cp_build_init_expr. * coroutines.cc (expand_one_await_expression) (build_actor_fn, flatten_await_stmt, handle_nested_conditionals) (await_statement_walker, morph_fn_to_coro): Use cp_build_init_expr. * cp-gimplify.cc (cp_gimplify_init_expr) (cp_gimplify_expr): Check TARGET_EXPR_ELIDING_P. (cp_fold_r): Propagate it. (cp_fold): Use cp_build_init_expr. * decl.cc (check_initializer): Use cp_build_init_expr. * except.cc (build_throw): Use cp_build_init_expr. * init.cc (get_nsdmi): Call set_target_expr_eliding. (perform_member_init, expand_default_init, expand_aggr_init_1) (build_new_1, build_vec_init): Use cp_build_init_expr. * method.cc (do_build_copy_constructor): Use cp_build_init_expr. * semantics.cc (simplify_aggr_init_expr, finalize_nrv_r) (finish_omp_reduction_clause): Use cp_build_init_expr. * tree.cc (build_target_expr): Call set_target_expr_eliding. (bot_manip): Copy TARGET_EXPR_ELIDING_P. * typeck.cc (cp_build_modify_expr): Call set_target_expr_eliding. (check_return_expr): Use cp_build_modify_expr. * typeck2.cc (split_nonconstant_init_1) (split_nonconstant_init): Use cp_build_init_expr. (massage_init_elt): Call set_target_expr_eliding. (process_init_constructor_record): Clear TARGET_EXPR_ELIDING_P on unsafe copy elision. (set_target_expr_eliding, cp_build_init_expr): New. Diff: --- gcc/cp/cp-tree.h | 11 ++++++++++ gcc/cp/call.cc | 16 ++++++--------- gcc/cp/coroutines.cc | 43 +++++++++++++++++++-------------------- gcc/cp/cp-gimplify.cc | 13 ++++++++++-- gcc/cp/decl.cc | 2 +- gcc/cp/except.cc | 2 +- gcc/cp/init.cc | 31 ++++++++++++++-------------- gcc/cp/method.cc | 4 ++-- gcc/cp/semantics.cc | 12 +++++++---- gcc/cp/tree.cc | 4 ++++ gcc/cp/typeck.cc | 6 ++++-- gcc/cp/typeck2.cc | 56 ++++++++++++++++++++++++++++++++++++++++++++++++--- 12 files changed, 138 insertions(+), 62 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 469eb2fdb25..ab6f85a2490 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -505,6 +505,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; DECL_MODULE_EXPORT_P (in _DECL) PACK_EXPANSION_FORCE_EXTRA_ARGS_P (in *_PACK_EXPANSION) LAMBDA_EXPR_STATIC_P (in LAMBDA_EXPR) + TARGET_EXPR_ELIDING_P (in TARGET_EXPR) 4: IDENTIFIER_MARKED (IDENTIFIER_NODEs) TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR, CALL_EXPR, or FIELD_DECL). @@ -5370,6 +5371,11 @@ get_vec_init_expr (tree t) #define TARGET_EXPR_DIRECT_INIT_P(NODE) \ TREE_LANG_FLAG_2 (TARGET_EXPR_CHECK (NODE)) +/* True if we expect this TARGET_EXPR to be used as an initializer, not to + materialize as a temporary. */ +#define TARGET_EXPR_ELIDING_P(NODE) \ + TREE_LANG_FLAG_3 (TARGET_EXPR_CHECK (NODE)) + /* True if NODE is a TARGET_EXPR that just expresses a copy of its INITIAL; if the initializer has void type, it's doing something more complicated. */ #define SIMPLE_TARGET_EXPR_P(NODE) \ @@ -6657,6 +6663,7 @@ extern bool is_list_ctor (tree); extern void validate_conversion_obstack (void); extern void mark_versions_used (tree); extern int unsafe_return_slot_p (tree); +extern bool unsafe_copy_elision_p (tree, tree); extern bool make_safe_copy_elision (tree, tree); extern bool cp_handle_deprecated_or_unavailable (tree, tsubst_flags_t = tf_warning_or_error); extern void cp_warn_deprecated_use_scopes (tree); @@ -8182,6 +8189,10 @@ extern tree build_functional_cast (location_t, tree, tree, tsubst_flags_t); extern tree add_exception_specifier (tree, tree, tsubst_flags_t); extern tree merge_exception_specifiers (tree, tree); +extern void set_target_expr_eliding (tree); +extern tree cp_build_init_expr (location_t, tree, tree); +inline tree cp_build_init_expr (tree t, tree i) +{ return cp_build_init_expr (input_location, t, i); } /* in mangle.cc */ extern void init_mangle (void); diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 7771d80ff31..70ec964a219 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -9144,7 +9144,7 @@ init_by_return_slot_p (tree exp) Places that use this function (or _opt) to decide to elide a copy should probably use make_safe_copy_elision instead. */ -static bool +bool unsafe_copy_elision_p (tree target, tree exp) { return unsafe_return_slot_p (target) && init_by_return_slot_p (exp); @@ -9941,7 +9941,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) && !unsafe) { tree to = cp_build_fold_indirect_ref (fa); - val = build2 (INIT_EXPR, DECL_CONTEXT (fn), to, arg); + val = cp_build_init_expr (to, arg); return val; } } @@ -10100,7 +10100,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) } call = cxx_constant_value (call, obj_arg, complain); if (obj_arg && !error_operand_p (call)) - call = build2 (INIT_EXPR, void_type_node, obj_arg, call); + call = cp_build_init_expr (obj_arg, call); call = convert_from_reference (call); } } @@ -10765,7 +10765,7 @@ build_special_member_call (tree instance, tree name, vec **args, check_self_delegation (arg); /* Avoid change of behavior on Wunused-var-2.C. */ instance = mark_lvalue_use (instance); - return build2 (INIT_EXPR, class_type, instance, arg); + return cp_build_init_expr (instance, arg); } } @@ -11183,9 +11183,7 @@ build_new_method_call (tree instance, tree fns, vec **args, { if (is_dummy_object (instance)) return get_target_expr (init, complain); - init = build2 (INIT_EXPR, TREE_TYPE (instance), instance, init); - TREE_SIDE_EFFECTS (init) = true; - return init; + return cp_build_init_expr (instance, init); } /* Otherwise go ahead with overload resolution. */ @@ -11232,9 +11230,7 @@ build_new_method_call (tree instance, tree fns, vec **args, ctor = digest_init (basetype, ctor, complain); if (ctor == error_mark_node) return error_mark_node; - ctor = build2 (INIT_EXPR, TREE_TYPE (instance), instance, ctor); - TREE_SIDE_EFFECTS (ctor) = true; - return ctor; + return cp_build_init_expr (instance, ctor); } } if (complain & tf_error) diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index 60b846600b9..01a3e831ee5 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -1732,7 +1732,7 @@ expand_one_await_expression (tree *stmt, tree *await_expr, void *d) if (!same_type_ignoring_top_level_qualifiers_p (susp_type, void_coro_handle_type)) r = build1_loc (loc, VIEW_CONVERT_EXPR, void_coro_handle_type, r); - r = build2_loc (loc, INIT_EXPR, void_coro_handle_type, data->conthand, r); + r = cp_build_init_expr (loc, data->conthand, r); r = build1 (CONVERT_EXPR, void_type_node, r); append_to_statement_list (r, &body_list); is_cont = true; @@ -1755,7 +1755,7 @@ expand_one_await_expression (tree *stmt, tree *await_expr, void *d) r = build_call_expr_internal_loc (loc, IFN_CO_YIELD, integer_type_node, 5, susp_idx, final_susp, r_l, d_l, data->coro_fp); - r = build2 (INIT_EXPR, integer_type_node, cond, r); + r = cp_build_init_expr (cond, r); finish_switch_cond (r, sw); r = build_case_label (build_int_cst (integer_type_node, 0), NULL_TREE, create_anon_label_with_ctx (loc, actor)); @@ -2304,7 +2304,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, vec *args = make_tree_vector_single (r); tree hfa = build_new_method_call (ash, hfa_m, &args, NULL_TREE, LOOKUP_NORMAL, NULL, tf_warning_or_error); - r = build2 (INIT_EXPR, handle_type, ash, hfa); + r = cp_build_init_expr (ash, hfa); r = coro_build_cvt_void_expr_stmt (r, loc); add_stmt (r); release_tree_vector (args); @@ -2776,17 +2776,19 @@ flatten_await_stmt (var_nest_node *n, hash_set *promoted, if (!VOID_TYPE_P (TREE_TYPE (then_cl))) { gcc_checking_assert (TREE_CODE (then_cl) != STATEMENT_LIST); - then_cl - = build2 (init_expr ? INIT_EXPR : MODIFY_EXPR, var_type, - var, then_cl); + if (init_expr) + then_cl = cp_build_init_expr (var, then_cl); + else + then_cl = build2 (MODIFY_EXPR, var_type, var, then_cl); } tree else_cl = COND_EXPR_ELSE (old_expr); if (!VOID_TYPE_P (TREE_TYPE (else_cl))) { gcc_checking_assert (TREE_CODE (else_cl) != STATEMENT_LIST); - else_cl - = build2 (init_expr ? INIT_EXPR : MODIFY_EXPR, var_type, - var, else_cl); + if (init_expr) + else_cl = cp_build_init_expr (var, else_cl); + else + else_cl = build2 (MODIFY_EXPR, var_type, var, else_cl); } n->init = build3 (COND_EXPR, var_type, cond, then_cl, else_cl); } @@ -2804,7 +2806,7 @@ flatten_await_stmt (var_nest_node *n, hash_set *promoted, DECL_ARTIFICIAL (cond_var) = true; layout_decl (cond_var, 0); gcc_checking_assert (!TYPE_NEEDS_CONSTRUCTING (cond_type)); - cond = build2 (INIT_EXPR, cond_type, cond_var, cond); + cond = cp_build_init_expr (cond_var, cond); var_nest_node *ins = new var_nest_node (cond_var, cond, n->prev, n); COND_EXPR_COND (n->init) = cond_var; @@ -2957,8 +2959,7 @@ handle_nested_conditionals (var_nest_node *n, vec& list, expression that performs the init and then records that the variable is live (and the DTOR should be run at the scope exit. */ - tree set_flag = build2 (INIT_EXPR, boolean_type_node, - flag, boolean_true_node); + tree set_flag = cp_build_init_expr (flag, boolean_true_node); n->init = build2 (COMPOUND_EXPR, boolean_type_node, n->init, set_flag); } @@ -3471,8 +3472,7 @@ await_statement_walker (tree *stmt, int *do_subtree, void *d) /* We want to initialize the new variable with the expression that contains the await(s) and potentially also needs to have truth_if expressions expanded. */ - tree new_s = build2_loc (sloc, INIT_EXPR, boolean_type_node, - newvar, cond_inner); + tree new_s = cp_build_init_expr (sloc, newvar, cond_inner); finish_expr_stmt (new_s); IF_COND (if_stmt) = newvar; add_stmt (if_stmt); @@ -3656,7 +3656,7 @@ await_statement_walker (tree *stmt, int *do_subtree, void *d) if (TREE_CODE (cond_inner) == CLEANUP_POINT_EXPR) cond_inner = TREE_OPERAND (cond_inner, 0); location_t sloc = EXPR_LOCATION (SWITCH_STMT_COND (sw_stmt)); - tree new_s = build2_loc (sloc, INIT_EXPR, sw_type, newvar, + tree new_s = cp_build_init_expr (sloc, newvar, cond_inner); finish_expr_stmt (new_s); SWITCH_STMT_COND (sw_stmt) = newvar; @@ -4735,7 +4735,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) } tree allocated = build1 (CONVERT_EXPR, coro_frame_ptr, new_fn); - tree r = build2 (INIT_EXPR, TREE_TYPE (coro_fp), coro_fp, allocated); + tree r = cp_build_init_expr (coro_fp, allocated); r = coro_build_cvt_void_expr_stmt (r, fn_start); add_stmt (r); @@ -4796,7 +4796,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) 1, 0,tf_warning_or_error); tree fnf_x = build_class_member_access_expr (deref_fp, fnf_m, NULL_TREE, false, tf_warning_or_error); - r = build2 (INIT_EXPR, boolean_type_node, fnf_x, boolean_true_node); + r = cp_build_init_expr (fnf_x, boolean_true_node); r = coro_build_cvt_void_expr_stmt (r, fn_start); add_stmt (r); @@ -4808,7 +4808,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) /*protect=*/1, /*want_type=*/0, tf_warning_or_error); tree resume_x = build_class_member_access_expr (deref_fp, resume_m, NULL_TREE, false, tf_warning_or_error); - r = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, resume_x, actor_addr); + r = cp_build_init_expr (fn_start, resume_x, actor_addr); finish_expr_stmt (r); tree destroy_addr = build1 (ADDR_EXPR, act_des_fn_ptr, destroy); @@ -4818,7 +4818,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) tree destroy_x = build_class_member_access_expr (deref_fp, destroy_m, NULL_TREE, false, tf_warning_or_error); - r = build2_loc (fn_start, INIT_EXPR, act_des_fn_ptr, destroy_x, destroy_addr); + r = cp_build_init_expr (fn_start, destroy_x, destroy_addr); finish_expr_stmt (r); /* [dcl.fct.def.coroutine] /13 @@ -5011,8 +5011,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) release_tree_vector (arg); } else - r = build2_loc (fn_start, INIT_EXPR, gro_type, - DECL_RESULT (orig), get_ro); + r = cp_build_init_expr (fn_start, DECL_RESULT (orig), get_ro); if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (gro_type)) /* If some part of the initalization code (prior to the await_resume @@ -5067,7 +5066,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer) = build_class_member_access_expr (deref_fp, resume_idx_m, NULL_TREE, false, tf_warning_or_error); r = build_int_cst (short_unsigned_type_node, 0); - r = build2_loc (fn_start, INIT_EXPR, short_unsigned_type_node, resume_idx, r); + r = cp_build_init_expr (fn_start, resume_idx, r); r = coro_build_cvt_void_expr_stmt (r, fn_start); add_stmt (r); diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index cb8bbd8277f..d0e12c9ee17 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -250,6 +250,7 @@ cp_gimplify_init_expr (tree *expr_p) if (TREE_CODE (from) == TARGET_EXPR) if (tree init = TARGET_EXPR_INITIAL (from)) { + gcc_checking_assert (TARGET_EXPR_ELIDING_P (from)); if (target_expr_needs_replace (from)) { /* If this was changed by cp_genericize_target_expr, we need to @@ -745,6 +746,11 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) /* A TARGET_EXPR that expresses direct-initialization should have been elided by cp_gimplify_init_expr. */ gcc_checking_assert (!TARGET_EXPR_DIRECT_INIT_P (*expr_p)); + /* Likewise, but allow extra temps of trivial type so that + gimplify_init_ctor_preeval can materialize subobjects of a CONSTRUCTOR + on the rhs of an assignment, as in constexpr-aggr1.C. */ + gcc_checking_assert (!TARGET_EXPR_ELIDING_P (*expr_p) + || !TREE_ADDRESSABLE (TREE_TYPE (*expr_p))); ret = GS_UNHANDLED; break; @@ -1110,7 +1116,10 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) cp_walk_tree (&init, cp_fold_r, data, NULL); *walk_subtrees = 0; if (TREE_CODE (init) == TARGET_EXPR) - *stmt_p = init; + { + TARGET_EXPR_ELIDING_P (init) = TARGET_EXPR_ELIDING_P (stmt); + *stmt_p = init; + } } break; @@ -2902,7 +2911,7 @@ cp_fold (tree x) loc = EXPR_LOCATION (x); tree s = build_fold_indirect_ref_loc (loc, CALL_EXPR_ARG (x, 0)); - r = build2_loc (loc, INIT_EXPR, TREE_TYPE (s), s, r); + r = cp_build_init_expr (s, r); } x = r; break; diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 07148b9fd4e..82eb0c2f22a 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -7500,7 +7500,7 @@ check_initializer (tree decl, tree init, int flags, vec **cleanups) } if (init && init != error_mark_node) - init_code = build2 (INIT_EXPR, type, decl, init); + init_code = cp_build_init_expr (decl, init); if (init_code && !TREE_SIDE_EFFECTS (init_code) && init_code != error_mark_node) diff --git a/gcc/cp/except.cc b/gcc/cp/except.cc index 048612de400..b8a85ed0572 100644 --- a/gcc/cp/except.cc +++ b/gcc/cp/except.cc @@ -755,7 +755,7 @@ build_throw (location_t loc, tree exp) tree tmp = decay_conversion (exp, tf_warning_or_error); if (tmp == error_mark_node) return error_mark_node; - exp = build2 (INIT_EXPR, temp_type, object, tmp); + exp = cp_build_init_expr (object, tmp); } /* Mark any cleanups from the initialization as MUST_NOT_THROW, since diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc index 0ab0aaabb16..3d5d3904944 100644 --- a/gcc/cp/init.cc +++ b/gcc/cp/init.cc @@ -686,6 +686,8 @@ get_nsdmi (tree member, bool in_ctor, tsubst_flags_t complain) /* Now put it back so C++17 copy elision works. */ init = get_target_expr (init); + set_target_expr_eliding (init); + current_class_ptr = save_ccp; current_class_ref = save_ccr; return init; @@ -1006,7 +1008,7 @@ perform_member_init (tree member, tree init, hash_set &uninitialized) if (TREE_CODE (type) == ARRAY_TYPE) { init = build_vec_init_expr (type, init, tf_warning_or_error); - init = build2 (INIT_EXPR, type, decl, init); + init = cp_build_init_expr (decl, init); finish_expr_stmt (init); } else @@ -1014,7 +1016,7 @@ perform_member_init (tree member, tree init, hash_set &uninitialized) tree value = build_value_init (type, tf_warning_or_error); if (value == error_mark_node) return; - init = build2 (INIT_EXPR, type, decl, value); + init = cp_build_init_expr (decl, value); finish_expr_stmt (init); } } @@ -1025,7 +1027,7 @@ perform_member_init (tree member, tree init, hash_set &uninitialized) { if (init) { - init = build2 (INIT_EXPR, type, decl, TREE_VALUE (init)); + init = cp_build_init_expr (decl, TREE_VALUE (init)); finish_expr_stmt (init); } } @@ -1062,7 +1064,7 @@ perform_member_init (tree member, tree init, hash_set &uninitialized) if (TREE_CODE (type) == ARRAY_TYPE && TYPE_HAS_NONTRIVIAL_DESTRUCTOR (TREE_TYPE (type))) init = build_vec_init_expr (type, init, tf_warning_or_error); - init = build2 (INIT_EXPR, type, decl, init); + init = cp_build_init_expr (decl, init); finish_expr_stmt (init); FOR_EACH_VEC_ELT (*cleanups, i, t) push_cleanup (NULL_TREE, t, false); @@ -1081,7 +1083,7 @@ perform_member_init (tree member, tree init, hash_set &uninitialized) /* Initialize the array only if it's not a flexible array member (i.e., if it has an upper bound). */ init = build_vec_init_expr (type, init, tf_warning_or_error); - init = build2 (INIT_EXPR, type, decl, init); + init = cp_build_init_expr (decl, init); finish_expr_stmt (init); } } @@ -2097,7 +2099,7 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags, complete objects. */ gcc_assert (TREE_CODE (init) == CONSTRUCTOR || true_exp == exp); - init = build2 (INIT_EXPR, TREE_TYPE (exp), exp, init); + init = cp_build_init_expr (exp, init); TREE_SIDE_EFFECTS (init) = 1; finish_expr_stmt (init); return true; @@ -2136,8 +2138,7 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags, TREE_TYPE (*p) = void_type_node; p = &TREE_OPERAND (*p, 0); } - *p = build2 (INIT_EXPR, TREE_TYPE (exp), exp, *p); - TREE_SIDE_EFFECTS (*p) = 1; + *p = cp_build_init_expr (exp, *p); finish_expr_stmt (init); return true; } @@ -2202,7 +2203,7 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags, { tree e = maybe_constant_init (rval, exp); if (TREE_CONSTANT (e)) - rval = build2 (INIT_EXPR, type, exp, e); + rval = cp_build_init_expr (exp, e); } } @@ -2290,7 +2291,7 @@ expand_aggr_init_1 (tree binfo, tree true_exp, tree exp, tree init, int flags, field_size = TYPE_SIZE (CLASSTYPE_AS_BASE (type)); init = build_zero_init_1 (type, NULL_TREE, /*static_storage_p=*/false, field_size); - init = build2 (INIT_EXPR, type, exp, init); + init = cp_build_init_expr (exp, init); finish_expr_stmt (init); } @@ -3678,7 +3679,7 @@ build_new_1 (vec **placement, tree type, tree nelts, tree val = build_value_init (type, complain | tf_no_cleanup); if (val == error_mark_node) return error_mark_node; - init_expr = build2 (INIT_EXPR, type, init_expr, val); + init_expr = cp_build_init_expr (init_expr, val); } else { @@ -4430,7 +4431,7 @@ build_vec_init (tree base, tree maxindex, tree init, if (BRACE_ENCLOSED_INITIALIZER_P (init)) init = digest_init (atype, init, complain); - stmt_expr = build2 (INIT_EXPR, atype, base, init); + stmt_expr = cp_build_init_expr (base, init); return stmt_expr; } @@ -4602,7 +4603,7 @@ build_vec_init (tree base, tree maxindex, tree init, gcc_checking_assert (!target_expr_needs_replace (elt)); if (digested) - one_init = build2 (INIT_EXPR, type, baseref, elt); + one_init = cp_build_init_expr (baseref, elt); else if (tree vi = get_vec_init_expr (elt)) one_init = expand_vec_init_expr (baseref, vi, complain, flags); else if (MAYBE_CLASS_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE) @@ -4623,7 +4624,7 @@ build_vec_init (tree base, tree maxindex, tree init, if (do_static_init) one_init = NULL_TREE; else - one_init = build2 (INIT_EXPR, type, baseref, e); + one_init = cp_build_init_expr (baseref, e); } else { @@ -4805,7 +4806,7 @@ build_vec_init (tree base, tree maxindex, tree init, { elt_init = build_value_init (type, complain); if (elt_init != error_mark_node) - elt_init = build2 (INIT_EXPR, type, to, elt_init); + elt_init = cp_build_init_expr (to, elt_init); } else { diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index 622e1b9802e..c217d7e5aad 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -680,7 +680,7 @@ do_build_copy_constructor (tree fndecl) else if (tree_int_cst_equal (TYPE_SIZE (current_class_type), CLASSTYPE_SIZE (current_class_type))) { - tree t = build2 (INIT_EXPR, void_type_node, current_class_ref, parm); + tree t = cp_build_init_expr (current_class_ref, parm); finish_expr_stmt (t); } else @@ -695,7 +695,7 @@ do_build_copy_constructor (tree fndecl) current_class_ptr, alias_set); tree rhs = build2 (MEM_REF, array_type, TREE_OPERAND (parm, 0), alias_set); - tree t = build2 (INIT_EXPR, void_type_node, lhs, rhs); + tree t = cp_build_init_expr (lhs, rhs); finish_expr_stmt (t); } } diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 39b11eeab8a..7d46c3c2db9 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -2519,6 +2519,10 @@ finish_stmt_expr_expr (tree expr, tree stmt_expr) /* Update for array-to-pointer decay. */ type = TREE_TYPE (expr); + /* This TARGET_EXPR will initialize the outer one added by + finish_stmt_expr. */ + set_target_expr_eliding (expr); + /* Wrap it in a CLEANUP_POINT_EXPR and add it to the list like a normal statement, but don't convert to void or actually add the EXPR_STMT. */ @@ -4668,7 +4672,7 @@ simplify_aggr_init_expr (tree *tp) expand_call{,_inline}. */ cxx_mark_addressable (slot); CALL_EXPR_RETURN_SLOT_OPT (call_expr) = true; - call_expr = build2 (INIT_EXPR, TREE_TYPE (call_expr), slot, call_expr); + call_expr = cp_build_init_expr (slot, call_expr); } else if (style == pcc) { @@ -4687,7 +4691,7 @@ simplify_aggr_init_expr (tree *tp) { tree init = build_zero_init (type, NULL_TREE, /*static_storage_p=*/false); - init = build2 (INIT_EXPR, void_type_node, slot, init); + init = cp_build_init_expr (slot, init); call_expr = build2 (COMPOUND_EXPR, TREE_TYPE (call_expr), init, call_expr); } @@ -4882,7 +4886,7 @@ finalize_nrv_r (tree* tp, int* walk_subtrees, void* data) tree init; if (DECL_INITIAL (dp->var) && DECL_INITIAL (dp->var) != error_mark_node) - init = build2 (INIT_EXPR, void_type_node, dp->result, + init = cp_build_init_expr (dp->result, DECL_INITIAL (dp->var)); else init = build_empty_stmt (EXPR_LOCATION (*tp)); @@ -6426,7 +6430,7 @@ finish_omp_reduction_clause (tree c, bool *need_default_ctor, bool *need_dtor) else init = fold_convert (TREE_TYPE (v), integer_zero_node); OMP_CLAUSE_REDUCTION_INIT (c) - = build2 (INIT_EXPR, TREE_TYPE (v), v, init); + = cp_build_init_expr (v, init); } } } diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 6d968a2af11..3532e44279f 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -533,6 +533,9 @@ build_target_expr (tree decl, tree value, tsubst_flags_t complain) if (t == error_mark_node) return error_mark_node; } + + set_target_expr_eliding (value); + t = build4 (TARGET_EXPR, type, decl, value, t, NULL_TREE); if (location_t eloc = cp_expr_location (value)) SET_EXPR_LOCATION (t, eloc); @@ -3194,6 +3197,7 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_) TARGET_EXPR_IMPLICIT_P (u) = TARGET_EXPR_IMPLICIT_P (t); TARGET_EXPR_LIST_INIT_P (u) = TARGET_EXPR_LIST_INIT_P (t); TARGET_EXPR_DIRECT_INIT_P (u) = TARGET_EXPR_DIRECT_INIT_P (t); + TARGET_EXPR_ELIDING_P (u) = TARGET_EXPR_ELIDING_P (t); /* Map the old variable to the new one. */ splay_tree_insert (target_remap, diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index cecf825f5e6..b4a8e3c205c 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -9294,7 +9294,7 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, if (! same_type_p (TREE_TYPE (rhs), lhstype)) /* Call convert to generate an error; see PR 11063. */ rhs = convert (lhstype, rhs); - result = build2 (INIT_EXPR, lhstype, lhs, rhs); + result = cp_build_init_expr (lhs, rhs); TREE_SIDE_EFFECTS (result) = 1; goto ret; } @@ -9542,6 +9542,8 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode, result = build2_loc (loc, modifycode == NOP_EXPR ? MODIFY_EXPR : INIT_EXPR, lhstype, lhs, newrhs); + if (modifycode == INIT_EXPR) + set_target_expr_eliding (newrhs); TREE_SIDE_EFFECTS (result) = 1; if (!plain_assign) @@ -11105,7 +11107,7 @@ check_return_expr (tree retval, bool *no_warning) /* Actually copy the value returned into the appropriate location. */ if (retval && retval != result) - retval = build2 (INIT_EXPR, TREE_TYPE (result), result, retval); + retval = cp_build_init_expr (result, retval); if (tree set = maybe_set_retval_sentinel ()) retval = build2 (COMPOUND_EXPR, void_type_node, retval, set); diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc index d5236d19b09..26444720cb8 100644 --- a/gcc/cp/typeck2.cc +++ b/gcc/cp/typeck2.cc @@ -649,7 +649,7 @@ split_nonconstant_init_1 (tree dest, tree init, bool last, else { build_init: - code = build2 (INIT_EXPR, inner_type, sub, value); + code = cp_build_init_expr (sub, value); } code = build_stmt (input_location, EXPR_STMT, code); add_stmt (code); @@ -764,7 +764,7 @@ split_nonconstant_init (tree dest, tree init) } else if (init) { - tree ie = build2 (INIT_EXPR, void_type_node, dest, init); + tree ie = cp_build_init_expr (dest, init); code = add_stmt_to_compound (ie, code); } } @@ -773,7 +773,7 @@ split_nonconstant_init (tree dest, tree init) code = build_vec_init (dest, NULL_TREE, init, /*value-init*/false, /*from array*/1, tf_warning_or_error); else - code = build2 (INIT_EXPR, TREE_TYPE (dest), dest, init); + code = cp_build_init_expr (dest, init); return code; } @@ -1464,6 +1464,7 @@ digest_nsdmi_init (tree decl, tree init, tsubst_flags_t complain) && CP_AGGREGATE_TYPE_P (type)) init = reshape_init (type, init, complain); init = digest_init_flags (type, init, flags, complain); + set_target_expr_eliding (init); /* We may have temporary materialization in a NSDMI, if the initializer has something like A{} in it. Digesting the {} could have introduced @@ -1542,6 +1543,7 @@ massage_init_elt (tree type, tree init, int nested, int flags, tree t = fold_non_dependent_init (init, complain); if (TREE_CONSTANT (t)) init = t; + set_target_expr_eliding (init); } return init; } @@ -1771,6 +1773,13 @@ process_init_constructor_record (tree type, tree init, int nested, int flags, { gcc_assert (ce->value); next = massage_init_elt (fldtype, next, nested, flags, complain); + /* We can't actually elide the temporary when initializing a + potentially-overlapping field from a function that returns by + value. */ + if (ce->index + && TREE_CODE (next) == TARGET_EXPR + && unsafe_copy_elision_p (ce->index, next)) + TARGET_EXPR_ELIDING_P (next) = false; ++idx; } } @@ -1804,6 +1813,9 @@ process_init_constructor_record (tree type, tree init, int nested, int flags, a class, just build one up; if it's an array, recurse. */ next = build_constructor (init_list_type_node, NULL); next = massage_init_elt (fldtype, next, nested, flags, complain); + if (TREE_CODE (next) == TARGET_EXPR + && unsafe_copy_elision_p (field, next)) + TARGET_EXPR_ELIDING_P (next) = false; /* Warn when some struct elements are implicitly initialized. */ if ((complain & tf_warning) @@ -2727,3 +2739,41 @@ require_complete_eh_spec_types (tree fntype, tree decl) } } } + +/* Record that any TARGET_EXPR in T are going to be elided in + cp_gimplify_init_expr (or sooner). */ + +void +set_target_expr_eliding (tree t) +{ + if (!t) + return; + switch (TREE_CODE (t)) + { + case TARGET_EXPR: + TARGET_EXPR_ELIDING_P (t) = true; + break; + case COMPOUND_EXPR: + set_target_expr_eliding (TREE_OPERAND (t, 1)); + break; + case COND_EXPR: + set_target_expr_eliding (TREE_OPERAND (t, 1)); + set_target_expr_eliding (TREE_OPERAND (t, 2)); + break; + + default: + break; + } +} + +/* Call the above in the process of building an INIT_EXPR. */ + +tree +cp_build_init_expr (location_t loc, tree target, tree init) +{ + set_target_expr_eliding (init); + tree ie = build2_loc (loc, INIT_EXPR, TREE_TYPE (target), + target, init); + TREE_SIDE_EFFECTS (ie) = true; + return ie; +}