c++: track whether we expect a TARGET_EXPR to be elided
authorJason Merrill <jason@redhat.com>
Sat, 17 Sep 2022 10:04:05 +0000 (12:04 +0200)
committerJason Merrill <jason@redhat.com>
Sat, 8 Oct 2022 00:25:51 +0000 (20:25 -0400)
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.

12 files changed:
gcc/cp/call.cc
gcc/cp/coroutines.cc
gcc/cp/cp-gimplify.cc
gcc/cp/cp-tree.h
gcc/cp/decl.cc
gcc/cp/except.cc
gcc/cp/init.cc
gcc/cp/method.cc
gcc/cp/semantics.cc
gcc/cp/tree.cc
gcc/cp/typeck.cc
gcc/cp/typeck2.cc

index 7771d80..70ec964 100644 (file)
@@ -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<tree, va_gc> **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<tree, va_gc> **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<tree, va_gc> **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)
index 60b8466..01a3e83 100644 (file)
@@ -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<tree, va_gc> *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<tree> *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<tree> *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<tree>& 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);
 
index cb8bbd8..d0e12c9 100644 (file)
@@ -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;
index 469eb2f..ab6f85a 100644 (file)
@@ -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);
index 07148b9..82eb0c2 100644 (file)
@@ -7500,7 +7500,7 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **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)
index 048612d..b8a85ed 100644 (file)
@@ -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
index 0ab0aaa..3d5d390 100644 (file)
@@ -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<tree> &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<tree> &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<tree> &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<tree> &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<tree> &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<tree, va_gc> **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
        {
index 622e1b9..c217d7e 100644 (file)
@@ -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);
        }
     }
index 39b11ee..7d46c3c 100644 (file)
@@ -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);
                }
            }
        }
index 6d968a2..3532e44 100644 (file)
@@ -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,
index cecf825..b4a8e3c 100644 (file)
@@ -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);
index d5236d1..2644472 100644 (file)
@@ -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;
+}