When an object is value-initialized, if the type doesn't have a user-provided default constructor, the object is zero-initialized first, and then the synthesized constructor is called. The problem in this PR was that when value-initializing a base in a constructor we were zero-initializing virtual bases of that base even though they had already been initialized properly. The fix is to specify to build_zero_init_1 that we only want to clear the as-base portion of the type.

On the trunk I also tidied up the logic; on release branches I made the minimal change. For the 4.4 branch I also needed to backport the fix for 48035.

Tested x86_64-pc-linux-gnu, applying to 4.4, 4.5, 4.6 and trunk.
commit ae7159a0bd2822557c503cd85d911e0390aaf55d
Author: Jason Merrill <ja...@redhat.com>
Date:   Thu Oct 13 12:59:31 2011 -0400

    	PR c++/50618
    	* init.c (expand_aggr_init_1): Don't zero-initialize virtual
    	bases of a base subobject.

diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 7897fff..8a5bece 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -1588,27 +1588,26 @@ expand_aggr_init_1 (tree binfo, tree true_exp, tree exp, tree init, int flags,
      that's value-initialization.  */
   if (init == void_type_node)
     {
-      /* If there's a user-provided constructor, we just call that.  */
-      if (type_has_user_provided_constructor (type))
-	/* Fall through.  */;
-      /* If there isn't, but we still need to call the constructor,
-	 zero out the object first.  */
-      else if (type_build_ctor_call (type))
+      /* If no user-provided ctor, we need to zero out the object.  */
+      if (!type_has_user_provided_constructor (type))
 	{
-	  init = build_zero_init (type, NULL_TREE, /*static_storage_p=*/false);
+	  tree field_size = NULL_TREE;
+	  if (exp != true_exp
+	      && CLASSTYPE_AS_BASE (type) != type)
+	    /* Don't clobber already initialized virtual bases.  */
+	    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);
 	  finish_expr_stmt (init);
-	  /* And then call the constructor.  */
 	}
+
       /* If we don't need to mess with the constructor at all,
-	 then just zero out the object and we're done.  */
-      else
-	{
-	  init = build2 (INIT_EXPR, type, exp,
-			 build_value_init_noctor (type, complain));
-	  finish_expr_stmt (init);
-	  return;
-	}
+	 then we're done.  */
+      if (! type_build_ctor_call (type))
+	return;
+
+      /* Otherwise fall through and call the constructor.  */
       init = NULL_TREE;
     }
 
diff --git a/gcc/testsuite/g++.dg/init/vbase1.C b/gcc/testsuite/g++.dg/init/vbase1.C
new file mode 100644
index 0000000..bbfd58f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/vbase1.C
@@ -0,0 +1,39 @@
+// PR c++/50618
+// { dg-do run }
+
+struct Base
+{
+    const int text;
+    Base():text(1) {}
+    Base(int aText)
+    : text(aText) {}
+};
+struct SubA : public virtual Base
+{
+protected:
+  int x;
+public:
+  SubA(int aX)
+  : x(aX) {}
+};
+class SubB : public virtual Base
+{};
+struct Diamond : public SubA, public SubB
+{
+    Diamond(int text)
+    : Base(text), SubA(5), SubB() {}
+
+    void printText()
+    {
+        if(text != 2)
+          __builtin_abort();
+        if(x!=5)
+          __builtin_abort();
+    }
+};
+
+int main(int, char**)
+{
+    Diamond x(2);
+    x.printText();
+}
commit fda61bbc8fd29b1df3dbeb576e0dcf806b2fcdf5
Author: Jason Merrill <ja...@redhat.com>
Date:   Thu Oct 13 13:11:31 2011 -0400

    	PR c++/50618
    	* init.c (expand_aggr_init_1): Don't zero-initialize virtual
    	bases of a base subobject.

diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index c4bd635..f85a30b 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -1561,7 +1561,12 @@ expand_aggr_init_1 (tree binfo, tree true_exp, tree exp, tree init, int flags,
 	 zero out the object first.  */
       else if (TYPE_NEEDS_CONSTRUCTING (type))
 	{
-	  init = build_zero_init (type, NULL_TREE, /*static_storage_p=*/false);
+	  tree field_size = NULL_TREE;
+	  if (exp != true_exp && CLASSTYPE_AS_BASE (type) != type)
+	    /* Don't clobber already initialized virtual bases.  */
+	    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);
 	  finish_expr_stmt (init);
 	  /* And then call the constructor.  */
diff --git a/gcc/testsuite/g++.dg/init/vbase1.C b/gcc/testsuite/g++.dg/init/vbase1.C
new file mode 100644
index 0000000..bbfd58f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/vbase1.C
@@ -0,0 +1,39 @@
+// PR c++/50618
+// { dg-do run }
+
+struct Base
+{
+    const int text;
+    Base():text(1) {}
+    Base(int aText)
+    : text(aText) {}
+};
+struct SubA : public virtual Base
+{
+protected:
+  int x;
+public:
+  SubA(int aX)
+  : x(aX) {}
+};
+class SubB : public virtual Base
+{};
+struct Diamond : public SubA, public SubB
+{
+    Diamond(int text)
+    : Base(text), SubA(5), SubB() {}
+
+    void printText()
+    {
+        if(text != 2)
+          __builtin_abort();
+        if(x!=5)
+          __builtin_abort();
+    }
+};
+
+int main(int, char**)
+{
+    Diamond x(2);
+    x.printText();
+}

Reply via email to