From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2122) id 3652E385843E; Thu, 9 Mar 2023 21:26:50 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3652E385843E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1678397210; bh=8GRHED9Jf3XFeYerK9EAYLjIBi+VPA8Li+Hv0Br2hio=; h=From:To:Subject:Date:From; b=pydKCKv5/+PrDTTWWNmWhVanlSmr0gppOtNyAFFlovAQssJEJoKPCXshdqOS2G4rV HSgZSAGKOWakzm1mVva8d6GMAV5egcvc5CcbCm1hLNXofVX0/8oRqHhZRUrRFwVeXy UETEtxrYTOIfVCJIWxS6CcDK+W3i5BVB1qEwjFRE= 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-6566] c++: allocator temps in list of arrays [PR108773] X-Act-Checkin: gcc X-Git-Author: Jason Merrill X-Git-Refname: refs/heads/trunk X-Git-Oldrev: 4214bdb1d77ebee04d12f66c831730ed67fedf55 X-Git-Newrev: e0324e2629e25a90c13c68b4eef1e47b091970c3 Message-Id: <20230309212650.3652E385843E@sourceware.org> Date: Thu, 9 Mar 2023 21:26:50 +0000 (GMT) List-Id: https://gcc.gnu.org/g:e0324e2629e25a90c13c68b4eef1e47b091970c3 commit r13-6566-ge0324e2629e25a90c13c68b4eef1e47b091970c3 Author: Jason Merrill Date: Mon Mar 6 22:57:57 2023 -0500 c++: allocator temps in list of arrays [PR108773] 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. Diff: --- 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(-) 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 &temps = *static_cast *>(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, return a pointer to the temp. + std::allocator, 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 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 + +namespace std { +struct __new_allocator {}; +struct allocator : __new_allocator {}; +template +struct basic_string { + basic_string(const T *, allocator = allocator()); + ~basic_string(); +}; +using string = basic_string; +template +struct array { + T _M_elems; +}; +template +struct list { + list &operator=(initializer_list); +}; +} +struct RGWSyncTraceManager { + std::list> 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 + +namespace std { +template struct array { + _Tp _M_elems[_Nm]; +}; +template 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> stuff; +void foo() { + stuff = {{"", ""}, {"", ""}}; +}