This is a crash in digest_init_r -- we encounter /* "If T is a class type and the initializer list has a single element of type cv U, where U is T or a class derived from T, the object is initialized from that element." */ if (flag_checking && cxx_dialect >= cxx11 && BRACE_ENCLOSED_INITIALIZER_P (stripped_init) && CONSTRUCTOR_NELTS (stripped_init) == 1 && ((CLASS_TYPE_P (type) && !CLASSTYPE_NON_AGGREGATE (type)) || VECTOR_TYPE_P (type))) { 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 (); }
As the comment suggests, we have code to fix this up in reshape_init_r: /* "If T is a class type and the initializer list has a single element of type cv U, where U is T or a class derived from T, the object is initialized from that element." Even if T is an aggregate. */ if (cxx_dialect >= cxx11 && (CLASS_TYPE_P (type) || VECTOR_TYPE_P (type)) && first_initializer_p && d->end - d->cur == 1 && reference_related_p (type, TREE_TYPE (init))) { d->cur++; return init; } but in this case this didn't work, because reshape_init_class always creates a fresh CONSTRUCTOR. As of C++17, aggregates can have bases, so we called reshape_init_class on the initializer for the public base, and returned a constructor whose single element was of a reference-related type. We can check for this case and fix it up, as in the below. I came up with testcases that test both cases of "where U is T *or* a class derived from T". Bootstrapped/regtested on x86_64-linux, ok for trunk/8? 2019-03-21 Marek Polacek <pola...@redhat.com> PR c++/89214 - ICE when initializing aggregates with bases. * decl.c (reshape_init_class): Adjust commentary. If the initializer list has a single element of the same or derived type, return the element itself, not wrapped in a constructor. * g++.dg/cpp1z/aggr-base8.C: New test. * g++.dg/cpp1z/aggr-base9.C: New test. diff --git gcc/cp/decl.c gcc/cp/decl.c index c8435e29491..817e33e40d2 100644 --- gcc/cp/decl.c +++ gcc/cp/decl.c @@ -5873,7 +5873,7 @@ reshape_init_vector (tree type, reshape_iter *d, tsubst_flags_t complain) } /* Subroutine of reshape_init_r, processes the initializers for classes - or union. Parameters are the same of reshape_init_r. */ + or union. Parameters are the same of reshape_init_r. */ static tree reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p, @@ -5884,7 +5884,8 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p, gcc_assert (CLASS_TYPE_P (type)); - /* The initializer for a class is always a CONSTRUCTOR. */ + /* The initializer for a class is always a CONSTRUCTOR. Unless it + has a single element of the same or derived type, see below. */ new_init = build_constructor (init_list_type_node, NULL); field = next_initializable_field (TYPE_FIELDS (type)); @@ -5978,6 +5979,19 @@ reshape_init_class (tree type, reshape_iter *d, bool first_initializer_p, field = next_initializable_field (DECL_CHAIN (field)); } + /* "If T is a class type and the initializer list has a single + element of type cv U, where U is T or a class derived from T, + the object is initialized from that element." But this function + always builds up a new CONSTRUCTOR, so undo that here. */ + if (cxx_dialect >= cxx11 + && BRACE_ENCLOSED_INITIALIZER_P (new_init) + && CONSTRUCTOR_NELTS (new_init) == 1) + { + tree elt = CONSTRUCTOR_ELT (new_init, 0)->value; + if (reference_related_p (type, TREE_TYPE (elt))) + return elt; + } + return new_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..10cff027c39 --- /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}}}; + B b11 = {{B{{42}}}}; + B b12 = {{B{{{42}}}}}; + + 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}}}; + B bb11{{B{{42}}}}; + B bb12{{B{{{42}}}}}; +} diff --git gcc/testsuite/g++.dg/cpp1z/aggr-base9.C gcc/testsuite/g++.dg/cpp1z/aggr-base9.C new file mode 100644 index 00000000000..a3fa747efa4 --- /dev/null +++ gcc/testsuite/g++.dg/cpp1z/aggr-base9.C @@ -0,0 +1,40 @@ +// 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 d3 = {{{42}}}; + + D d4 = D{42}; + D d5 = D{{42}}; + D d6 = D{{{42}}}; + + D d7 = {D{42}}; + D d8 = {D{{42}}}; + D d9 = {D{{{42}}}}; + + D d10 = {{D{42}}}; + D d11 = {{D{{42}}}}; + D d12 = {{D{{{42}}}}}; + + D dd1{42}; + D dd2{{42}}; + D dd3{{{42}}}; + + D dd7{D{42}}; + D dd8{D{{42}}}; + D dd9{D{{{42}}}}; + + D dd10{{D{42}}}; + D dd11{{D{{42}}}}; + D dd12{{D{{{42}}}}}; +}