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 <pola...@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" } +}