diff mbox series

[pushed] c++: allocator temps in list of arrays [PR108773]

Message ID 20230309212619.2329010-1-jason@redhat.com
State New
Headers show
Series [pushed] c++: allocator temps in list of arrays [PR108773] | expand

Commit Message

Jason Merrill March 9, 2023, 9:26 p.m. UTC
Tested x86_64-pc-linux-gnu, applying to trunk.

-- 8< --

The optimization to reuse the same allocator temporary for all string
constructor calls was breaking on this testcase, because the temps were
already in the argument to build_vec_init, and replacing them with
references to one slot got confused with calls at multiple levels (for the
initializer_list backing array, and then again for the array member of the
std::array).  Fixed by reusing the whole TARGET_EXPR instead of pulling out
the slot; gimplification ensures that it's only initialized once.

I also moved the check for initializing a std:: class down into the tree
walk, and handle multiple temps within a single array element
initialization.

	PR c++/108773

gcc/cp/ChangeLog:

	* init.cc (find_allocator_temps_r): New.
	(combine_allocator_temps): Replace find_allocator_temp.
	(build_vec_init): Adjust.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp0x/initlist-array18.C: New test.
	* g++.dg/cpp0x/initlist-array19.C: New test.
---
 gcc/cp/init.cc                                | 78 ++++++++++++++-----
 gcc/testsuite/g++.dg/cpp0x/initlist-array18.C | 30 +++++++
 gcc/testsuite/g++.dg/cpp0x/initlist-array19.C | 23 ++++++
 3 files changed, 110 insertions(+), 21 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/initlist-array18.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/initlist-array19.C


base-commit: afe1f0c251d0429069c2414d4f3f14042efc174f
diff mbox series

Patch

diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc
index 52e96fbe590..1b7d3d8fe3e 100644
--- a/gcc/cp/init.cc
+++ b/gcc/cp/init.cc
@@ -4330,8 +4330,54 @@  find_temps_r (tree *tp, int *walk_subtrees, void *data)
   return NULL_TREE;
 }
 
+/* walk_tree callback to collect temporaries in an expression that
+   are allocator arguments to standard library classes.  */
+
+static tree
+find_allocator_temps_r (tree *tp, int *walk_subtrees, void *data)
+{
+  vec<tree*> &temps = *static_cast<auto_vec<tree*> *>(data);
+  tree t = *tp;
+  if (TYPE_P (t))
+    {
+      *walk_subtrees = 0;
+      return NULL_TREE;
+    }
+
+  /* If this is a call to a constructor for a std:: class, look for
+     a reference-to-allocator argument.  */
+  tree fn = cp_get_callee_fndecl_nofold (t);
+  if (fn && DECL_CONSTRUCTOR_P (fn)
+      && decl_in_std_namespace_p (TYPE_NAME (DECL_CONTEXT (fn))))
+    {
+      int nargs = call_expr_nargs (t);
+      for (int i = 1; i < nargs; ++i)
+	{
+	  tree arg = get_nth_callarg (t, i);
+	  tree atype = TREE_TYPE (arg);
+	  if (TREE_CODE (atype) == REFERENCE_TYPE
+	      && is_std_allocator (TREE_TYPE (atype)))
+	    {
+	      STRIP_NOPS (arg);
+	      if (TREE_CODE (arg) == ADDR_EXPR)
+		{
+		  tree *ap = &TREE_OPERAND (arg, 0);
+		  if (TREE_CODE (*ap) == TARGET_EXPR)
+		    temps.safe_push (ap);
+		}
+	    }
+	}
+    }
+
+  return NULL_TREE;
+}
+
 /* If INIT initializes a standard library class, and involves a temporary
-   std::allocator<T>, return a pointer to the temp.
+   std::allocator<T>, use ALLOC_OBJ for all such temporaries.
+
+   Note that this can clobber the input to build_vec_init; no unsharing is
+   done.  To make this safe we use the TARGET_EXPR in all places rather than
+   pulling out the TARGET_EXPR_SLOT.
 
    Used by build_vec_init when initializing an array of e.g. strings to reuse
    the same temporary allocator for all of the strings.  We can do this because
@@ -4341,22 +4387,18 @@  find_temps_r (tree *tp, int *walk_subtrees, void *data)
    ??? Add an attribute to allow users to assert the same property for other
    classes, i.e. one object of the type is interchangeable with any other?  */
 
-static tree*
-find_allocator_temp (tree init)
+static void
+combine_allocator_temps (tree &init, tree &alloc_obj)
 {
-  if (TREE_CODE (init) == EXPR_STMT)
-    init = EXPR_STMT_EXPR (init);
-  if (TREE_CODE (init) == CONVERT_EXPR)
-    init = TREE_OPERAND (init, 0);
-  tree type = TREE_TYPE (init);
-  if (!CLASS_TYPE_P (type) || !decl_in_std_namespace_p (TYPE_NAME (type)))
-    return NULL;
   auto_vec<tree*> temps;
-  cp_walk_tree_without_duplicates (&init, find_temps_r, &temps);
+  cp_walk_tree_without_duplicates (&init, find_allocator_temps_r, &temps);
   for (tree *p : temps)
-    if (is_std_allocator (TREE_TYPE (*p)))
-      return p;
-  return NULL;
+    {
+      if (!alloc_obj)
+	alloc_obj = *p;
+      else
+	*p = alloc_obj;
+    }
 }
 
 /* `build_vec_init' returns tree structure that performs
@@ -4694,13 +4736,7 @@  build_vec_init (tree base, tree maxindex, tree init,
 	  if (one_init)
 	    {
 	      /* Only create one std::allocator temporary.  */
-	      if (tree *this_alloc = find_allocator_temp (one_init))
-		{
-		  if (alloc_obj)
-		    *this_alloc = alloc_obj;
-		  else
-		    alloc_obj = TARGET_EXPR_SLOT (*this_alloc);
-		}
+	      combine_allocator_temps (one_init, alloc_obj);
 	      finish_expr_stmt (one_init);
 	    }
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-array18.C b/gcc/testsuite/g++.dg/cpp0x/initlist-array18.C
new file mode 100644
index 00000000000..8c0f316789d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist-array18.C
@@ -0,0 +1,30 @@ 
+// PR c++/108773
+// { dg-do compile { target c++11 } }
+
+#include <initializer_list>
+
+namespace std {
+struct __new_allocator {};
+struct allocator : __new_allocator {};
+template <typename T>
+struct basic_string {
+  basic_string(const T *, allocator = allocator());
+  ~basic_string();
+};
+using string = basic_string<char>;
+template <typename T>
+struct array {
+  T _M_elems;
+};
+template <typename T>
+struct list {
+  list &operator=(initializer_list<T>);
+};
+}
+struct RGWSyncTraceManager {
+  std::list<std::array<std::string>> admin_commands;
+  void hook_to_admin_command();
+};
+void RGWSyncTraceManager::hook_to_admin_command() {
+  admin_commands = {{""}, {""}};
+}
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist-array19.C b/gcc/testsuite/g++.dg/cpp0x/initlist-array19.C
new file mode 100644
index 00000000000..1ede5d77339
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist-array19.C
@@ -0,0 +1,23 @@ 
+// PR c++/108773
+// { dg-do compile { target c++20 } }
+
+#include <initializer_list>
+
+namespace std {
+template <typename _Tp, int _Nm> struct array {
+  _Tp _M_elems[_Nm];
+};
+template <typename _Tp> struct list { void operator=(initializer_list<_Tp>); };
+struct allocator {};
+struct basic_string {
+  int _M_p;
+  constexpr basic_string() {}
+  basic_string(const char *, const allocator & = allocator());
+  ~basic_string();
+};
+} // namespace std
+
+std::list<std::array<std::basic_string, 3>> stuff;
+void foo() {
+  stuff = {{"", ""}, {"", ""}};
+}