Patchwork C++ PATCH for c++/50930 (unnecessary copy ctor calls with list-init)

login
register
mail settings
Submitter Jason Merrill
Date Nov. 2, 2011, 9:14 p.m.
Message ID <4EB1B29B.3020807@redhat.com>
Download mbox | patch
Permalink /patch/123351/
State New
Headers show

Comments

Jason Merrill - Nov. 2, 2011, 9:14 p.m.
C++11 list-initialization doesn't involve a second copy constructor 
call, even copy-list-initialization.  We were building one incorrectly, 
which causes problems if the copy ctor is deleted.

Tested x86_64-pc-linux-gnu, applying to trunk.

Patch

commit 0b34e91f284e47ac4e9240954d10242c9feb6e61
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Nov 1 17:46:44 2011 -0400

    	PR c++/50930
    	* init.c (build_aggr_init): Don't set LOOKUP_ONLYCONVERTING
    	if the initializer has TARGET_EXPR_DIRECT_INIT_P.
    	(expand_default_init): An initializer with TARGET_EXPR_DIRECT_INIT_P
    	or TARGET_EXPR_LIST_INIT_P doesn't need more processing.
    	* tree.c (bot_manip): Propagate TARGET_EXPR_IMPLICIT_P,
    	TARGET_EXPR_LIST_INIT_P, TARGET_EXPR_DIRECT_INIT_P.
    	* call.c (convert_like_real): Set TARGET_EXPR_DIRECT_INIT_P
    	as appropriate on list-value-initialization.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 439a1fe..ce8933a 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5655,10 +5655,14 @@  convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	    && CONSTRUCTOR_NELTS (expr) == 0
 	    && TYPE_HAS_DEFAULT_CONSTRUCTOR (totype))
 	  {
+	    bool direct = CONSTRUCTOR_IS_DIRECT_INIT (expr);
 	    expr = build_value_init (totype, complain);
 	    expr = get_target_expr_sfinae (expr, complain);
 	    if (expr != error_mark_node)
-	      TARGET_EXPR_LIST_INIT_P (expr) = true;
+	      {
+		TARGET_EXPR_LIST_INIT_P (expr) = true;
+		TARGET_EXPR_DIRECT_INIT_P (expr) = direct;
+	      }
 	    return expr;
 	  }
 
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 6b57eb6..ec7ba0e 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -1377,6 +1377,8 @@  build_aggr_init (tree exp, tree init, int flags, tsubst_flags_t complain)
   TREE_THIS_VOLATILE (exp) = 0;
 
   if (init && TREE_CODE (init) != TREE_LIST
+      && !(TREE_CODE (init) == TARGET_EXPR
+	   && TARGET_EXPR_DIRECT_INIT_P (init))
       && !(BRACE_ENCLOSED_INITIALIZER_P (init)
 	   && CONSTRUCTOR_IS_DIRECT_INIT (init)))
     flags |= LOOKUP_ONLYCONVERTING;
@@ -1459,10 +1461,28 @@  expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
 
   if (init && BRACE_ENCLOSED_INITIALIZER_P (init)
       && CP_AGGREGATE_TYPE_P (type))
+    /* A brace-enclosed initializer for an aggregate.  In C++0x this can
+       happen for direct-initialization, too.  */
+    init = digest_init (type, init, complain);
+
+  /* A CONSTRUCTOR of the target's type is a previously digested
+     initializer, whether that happened just above or in
+     cp_parser_late_parsing_nsdmi.
+
+     A TARGET_EXPR with TARGET_EXPR_DIRECT_INIT_P or TARGET_EXPR_LIST_INIT_P
+     set represents the whole initialization, so we shouldn't build up
+     another ctor call.  */
+  if (init
+      && (TREE_CODE (init) == CONSTRUCTOR
+	  || (TREE_CODE (init) == TARGET_EXPR
+	      && (TARGET_EXPR_DIRECT_INIT_P (init)
+		  || TARGET_EXPR_LIST_INIT_P (init))))
+      && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init), type))
     {
-      /* A brace-enclosed initializer for an aggregate.  In C++0x this can
-	 happen for direct-initialization, too.  */
-      init = digest_init (type, init, complain);
+      /* Early initialization via a TARGET_EXPR only works for
+	 complete objects.  */
+      gcc_assert (TREE_CODE (init) == CONSTRUCTOR || true_exp == exp);
+
       init = build2 (INIT_EXPR, TREE_TYPE (exp), exp, init);
       TREE_SIDE_EFFECTS (init) = 1;
       finish_expr_stmt (init);
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 707f2c8..dc9fc95 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -1889,6 +1889,10 @@  bot_manip (tree* tp, int* walk_subtrees, void* data)
 	u = build_target_expr_with_type (TREE_OPERAND (t, 1), TREE_TYPE (t),
 					 tf_warning_or_error);
 
+      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);
+
       /* Map the old variable to the new one.  */
       splay_tree_insert (target_remap,
 			 (splay_tree_key) TREE_OPERAND (t, 0),
diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi-list2.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi-list2.C
new file mode 100644
index 0000000..a6321ff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi-list2.C
@@ -0,0 +1,32 @@ 
+// PR c++/50930
+// { dg-options -std=c++0x }
+
+struct nmc {
+ nmc() = default;
+ nmc(nmc&&) = delete; // line 3
+};
+
+struct A { // line 6
+ nmc n{};
+ nmc n2 = {};
+} a; // line 8
+
+// ------
+
+struct lock_t {
+  int lock[4];
+};
+
+struct pthread_mutex_t {
+  volatile lock_t __spinlock;
+};
+
+struct mutex {
+  pthread_mutex_t m = { };
+  mutex() = default;
+};
+
+int main()
+{
+  mutex mx;
+}