This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: C++ PATCH for c++/89214 - ICE when initializing aggregates with bases


On Fri, Mar 22, 2019 at 04:20:43PM -0400, Jason Merrill wrote:
> On 3/22/19 4:12 PM, Jason Merrill wrote:
> > On 3/22/19 2:14 PM, Marek Polacek wrote:
> > > On Fri, Mar 22, 2019 at 10:48:32AM -0400, Jason Merrill wrote:
> > 
> > > > > > +  B b10 = {{B{42}}};
> > > > > > +  B b11 = {{B{{42}}}};
> > > > > > +  B b12 = {{B{{{42}}}}};
> > > > 
> > > > These look ill-formed to me: too many braces around the B value.
> > > > 
> > > > Looks like the original testcase had the same problem.  So I
> > > > think this is
> > > > ice-on-invalid.
> > > 
> > > Are you sure?  clang/icc/gcc8/msvc compile it.  I thought this was a
> > > case of
> > > aggregate initialization, where we have a nested braced-init-list:
> > > http://eel.is/c++draft/dcl.init.aggr#4.2
> > > "If an initializer is itself an initializer list, the element is
> > > list-initialized, which will result in a recursive application of
> > > the rules in
> > > this subclause if the element is an aggregate."
> > 
> > Yes.  Since the first element of the outer init-list is also an
> > init-list, we initialize the first element of D from the inner
> > init-list.  The first element of D is the B base, so we're initializing
> > a B from {D{42}}.
> > 
> > Ah, and D is derived from B, so the B base is copy-initialized from the
> > (B base subobject of) the D temporary.  So that's why it's different for
> > a base class.
> > 
> > > So originally, when calling reshape_init, we have
> > > 
> > >   {{ TARGET_EXPR<> }} of type D
> > > 
> > > we recurse on it -- it's a class so we call reshape_init_class. 
> > > Then we call
> > > reshape_init_r on a field D.2070 for the base (type B), the ctor is
> > > { TARGET_EXPR<> }.
> > > Here we elide the braces, so we return just the TARGET_EXPR as a
> > > field_init
> > > for D.2070, but then we're back in reshape_init_class and append the
> > > TARGET_EXPR to new_init constructor:
> > > 5969       CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (new_init),
> > > field, field_init);
> > > so we end up with the undesirable form.
> > 
> > I don't think it's undesirable; the temporary initializes the base, not
> > the complete object.  It seems that the assert in digest_init_r is wrong
> > for C++17.
> > 
> > It seems a bit questionable that adding a layer of braces silently
> > causes slicing; I'll raise this with the committee.

Thanks for that.

> I think let's warn instead of aborting if the first element of the aggregate
> is a base class.

Okay.  I guess we might raise it to an error later, if it becomes invalid.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2019-03-25  Marek Polacek  <polacek@redhat.com>

	PR c++/89214 - ICE when initializing aggregates with bases.
	* typeck2.c (digest_init_r): Warn about object slicing instead of
	crashing.

	* g++.dg/cpp1z/aggr-base8.C: New test.
	* g++.dg/cpp1z/aggr-base9.C: New test.

diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index e50d6ed83c3..7f242ba93da 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -1209,8 +1209,29 @@ digest_init_r (tree type, tree init, int nested, int flags,
     {
       tree elt = CONSTRUCTOR_ELT (stripped_init, 0)->value;
       if (reference_related_p (type, TREE_TYPE (elt)))
-	/* We should have fixed this in reshape_init.  */
-	gcc_unreachable ();
+	{
+	  /* In C++17, aggregates can have bases, thus participate in
+	     aggregate initialization.  In the following case:
+
+	       struct B { int c; };
+	       struct D : B { };
+	       D d{{D{{42}}}};
+
+	    there's an extra set of braces, so the D temporary initializes
+	    the first element of d, which is the B base subobject.  The base
+	    of type B is copy-initialized from the D temporary, causing
+	    object slicing.  */
+	  tree field = next_initializable_field (TYPE_FIELDS (type));
+	  if (field && DECL_FIELD_IS_BASE (field))
+	    {
+	      if (warning_at (loc, 0, "initializing a base class of type %qT "
+			      "results in object slicing", TREE_TYPE (field)))
+		inform (loc, "remove %<{ }%> around initializer");
+	    }
+	  else
+	    /* We should have fixed this in reshape_init.  */
+	    gcc_unreachable ();
+	}
     }
 
   if (BRACE_ENCLOSED_INITIALIZER_P (stripped_init)
diff --git gcc/testsuite/g++.dg/cpp1z/aggr-base8.C gcc/testsuite/g++.dg/cpp1z/aggr-base8.C
new file mode 100644
index 00000000000..8b495a80cb3
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp1z/aggr-base8.C
@@ -0,0 +1,48 @@
+// PR c++/89214
+// { dg-do compile { target c++17 } }
+
+struct A
+{
+  A (int);
+};
+
+struct BB
+{
+  A a;
+};
+
+struct B : BB
+{
+};
+
+void
+foo ()
+{
+  B b1 = {42};
+  B b2 = {{42}};
+  B b3 = {{{42}}};
+
+  B b4 = B{42};
+  B b5 = B{{42}};
+  B b6 = B{{{42}}};
+
+  B b7 = {B{42}};
+  B b8 = {B{{42}}};
+  B b9 = {B{{{42}}}};
+
+  B b10 = {{B{42}}}; // { dg-warning "initializing a base class of type .BB. results in object slicing" }
+  B b11 = {{B{{42}}}}; // { dg-warning "initializing a base class of type .BB. results in object slicing" }
+  B b12 = {{B{{{42}}}}}; // { dg-warning "initializing a base class of type .BB. results in object slicing" }
+
+  B bb1{42};
+  B bb2{{42}};
+  B bb3{{{42}}};
+
+  B bb7{B{42}};
+  B bb8{B{{42}}};
+  B bb9{B{{{42}}}};
+
+  B bb10{{B{42}}}; // { dg-warning "initializing a base class of type .BB. results in object slicing" }
+  B bb11{{B{{42}}}}; // { dg-warning "initializing a base class of type .BB. results in object slicing" }
+  B bb12{{B{{{42}}}}}; // { dg-warning "initializing a base class of type .BB. results in object slicing" }
+}
diff --git gcc/testsuite/g++.dg/cpp1z/aggr-base9.C gcc/testsuite/g++.dg/cpp1z/aggr-base9.C
new file mode 100644
index 00000000000..56aa59cb64a
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp1z/aggr-base9.C
@@ -0,0 +1,33 @@
+// PR c++/89214
+// { dg-do compile { target c++17 } }
+
+struct B {
+  int c;
+};
+
+struct D : B { };
+
+void
+foo ()
+{
+  D d1 = {42};
+  D d2 = {{42}};
+  
+  D d4 = D{42};
+  D d5 = D{{42}};
+ 
+  D d7 = {D{42}};
+  D d8 = {D{{42}}};
+
+  D d10 = {{D{42}}}; // { dg-warning "initializing a base class of type .B. results in object slicing" }
+  D d11 = {{D{{42}}}}; // { dg-warning "initializing a base class of type .B. results in object slicing" }
+
+  D dd1{42};
+  D dd2{{42}};
+  
+  D dd7{D{42}};
+  D dd8{D{{42}}};
+
+  D dd10{{D{42}}}; // { dg-warning "initializing a base class of type .B. results in object slicing" }
+  D dd11{{D{{42}}}}; // { dg-warning "initializing a base class of type .B. results in object slicing" }
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]