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}}}}};
+}

Reply via email to