public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-3175] c++: track whether we expect a TARGET_EXPR to be elided
@ 2022-10-08  0:28 Jason Merrill
  0 siblings, 0 replies; only message in thread
From: Jason Merrill @ 2022-10-08  0:28 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:6ffbf87ca66f4ed9cd79cff675fabe2109e46e85

commit r13-3175-g6ffbf87ca66f4ed9cd79cff675fabe2109e46e85
Author: Jason Merrill <jason@redhat.com>
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<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)
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<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);
 
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<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)
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<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
 	{
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;
+}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-10-08  0:28 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-08  0:28 [gcc r13-3175] c++: track whether we expect a TARGET_EXPR to be elided Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).