On Thu, Jan 31, 2019 at 10:17:54AM -0500, Jason Merrill wrote:
> On 1/31/19 9:50 AM, Marek Polacek wrote:
> > On Wed, Jan 30, 2019 at 05:37:13PM -0500, Jason Merrill wrote:
> > > On 1/30/19 4:15 PM, Marek Polacek wrote:
> > > > On Wed, Jan 30, 2019 at 04:11:11PM -0500, Marek Polacek wrote:
> > > > > On Tue, Jan 29, 2019 at 09:40:18PM -0500, Jason Merrill wrote:
> > > > > > On Tue, Jan 29, 2019 at 6:53 PM Marek Polacek <pola...@redhat.com> 
> > > > > > wrote:
> > > > > > > 
> > > > > > > My recent patch for 88815 and 78244 caused 89083, a P1 9 
> > > > > > > regression, which
> > > > > > > happens to be the same problem as 80864 and its many dupes, 
> > > > > > > something I'd
> > > > > > > been meaning to fix for a long time.
> > > > > > > 
> > > > > > > Basically, the problem is repeated reshaping of a constructor, 
> > > > > > > once when
> > > > > > > parsing, and then again when substituting.  With the recent fix, 
> > > > > > > we call
> > > > > > > reshape_init + digest_init in finish_compound_literal even in a 
> > > > > > > template
> > > > > > > if the expression is not instantiation-dependent, and then again 
> > > > > > > when
> > > > > > > tsubst_*.
> > > > > > > 
> > > > > > > For instance, in initlist107.C, when parsing a functional cast, 
> > > > > > > we call
> > > > > > > finish_compound_literal which calls reshape_init, which turns
> > > > > > > 
> > > > > > >     { NON_LVALUE_EXPR<1>, NON_LVALUE_EXPR<2> }
> > > > > > > 
> > > > > > > into
> > > > > > > 
> > > > > > >     { { NON_LVALUE_EXPR<1>, NON_LVALUE_EXPR<2> } }
> > > > > > > 
> > > > > > > and then digest_init turns that into
> > > > > > > 
> > > > > > >     { .x = { 1, 2 } }
> > > > > > > 
> > > > > > > which is a compound literal (TREE_HAS_CONSTRUCTOR set), but the 
> > > > > > > subexpression
> > > > > > > "{ 1, 2 }" isn't.  "{ 1, 2 }" will now have the type int[3], so 
> > > > > > > it's not
> > > > > > > BRACE_ENCLOSED_INITIALIZER_P.
> > > > > > > 
> > > > > > > And then tsubst_* processes "{ .x = { 1, 2 } }".  The case 
> > > > > > > CONSTRUCTOR
> > > > > > > in tsubst_copy_and_build will call finish_compound_literal on a 
> > > > > > > copy of
> > > > > > > "{ 1, 2 }" wrapped in a new { }, because the whole expr has 
> > > > > > > TREE_HAS_CONSTRUCTOR.
> > > > > > > That crashes in reshape_init_r in the
> > > > > > >    6155       if (TREE_CODE (stripped_init) == CONSTRUCTOR)
> > > > > > > block; we have a constructor, it's not COMPOUND_LITERAL_P, and 
> > > > > > > because
> > > > > > > digest_init had given it the type int[3], we hit
> > > > > > >    6172               gcc_assert (BRACE_ENCLOSED_INITIALIZER_P 
> > > > > > > (stripped_init));
> > > > > > > 
> > > > > > > As expand_default_init explains in a comment, a CONSTRUCTOR of 
> > > > > > > the target's type
> > > > > > > is a previously digested initializer, so we should probably do a 
> > > > > > > similar trick
> > > > > > > here.  This fixes all the variants of the problem I've come up 
> > > > > > > with.
> > > > > > > 
> > > > > > > 80864 is a similar case, we reshape when parsing and then second 
> > > > > > > time in
> > > > > > > fold_non_dependent_expr called from store_init_value, because of 
> > > > > > > the 'constexpr'.
> > > > > > > 
> > > > > > > Also update a stale comment.
> > > > > > > 
> > > > > > > Bootstrapped/regtest running on x86_64-linux, ok for trunk and 8 
> > > > > > > after a while?
> > > > > > > 
> > > > > > > 2019-01-29  Marek Polacek  <pola...@redhat.com>
> > > > > > > 
> > > > > > >           PR c++/89083, c++/80864 - ICE with list initialization 
> > > > > > > in template.
> > > > > > >           * decl.c (reshape_init_r): Don't reshape a digested 
> > > > > > > initializer.
> > > > > > > 
> > > > > > >           * g++.dg/cpp0x/initlist107.C: New test.
> > > > > > >           * g++.dg/cpp0x/initlist108.C: New test.
> > > > > > >           * g++.dg/cpp0x/initlist109.C: New test.
> > > > > > > 
> > > > > > > diff --git gcc/cp/decl.c gcc/cp/decl.c
> > > > > > > index 79eeac177b6..da08ecc21aa 100644
> > > > > > > --- gcc/cp/decl.c
> > > > > > > +++ gcc/cp/decl.c
> > > > > > > @@ -6161,11 +6161,17 @@ reshape_init_r (tree type, reshape_iter 
> > > > > > > *d, bool first_initializer_p,
> > > > > > >               ;
> > > > > > >             else if (COMPOUND_LITERAL_P (stripped_init))
> > > > > > >             /* For a nested compound literal, there is no need to 
> > > > > > > reshape since
> > > > > > > -            brace elision is not allowed. Even if we decided to 
> > > > > > > allow it,
> > > > > > > -            we should add a call to reshape_init in 
> > > > > > > finish_compound_literal,
> > > > > > > -            before calling digest_init, so changing this code 
> > > > > > > would still
> > > > > > > -            not be necessary.  */
> > > > > > > +            we called reshape_init in finish_compound_literal, 
> > > > > > > before calling
> > > > > > > +            digest_init.  */
> > > > > > >               gcc_assert (!BRACE_ENCLOSED_INITIALIZER_P 
> > > > > > > (stripped_init));
> > > > > > > +         /* Similarly, a CONSTRUCTOR of the target's type is a 
> > > > > > > previously
> > > > > > > +            digested initializer.  */
> > > > > > > +         else if (same_type_ignoring_top_level_qualifiers_p 
> > > > > > > (type,
> > > > > > > +                                                             
> > > > > > > TREE_TYPE (init)))
> > > > > > 
> > > > > > Hmm, aren't both of these tests true for a dependent compound 
> > > > > > literal,
> > > > > > which won't have been reshaped already?
> > > > > 
> > > > > I'm hoping that can't happen, but it's a good question.  When we have 
> > > > > a
> > > > > dependent compound literal, finish_compound_literal just sets
> > > > > TREE_HAS_CONSTRUCTOR and returns it, so then in tsubst_*, after 
> > > > > substituting
> > > > > each element of the constructor, we call finish_compound_literal.  The
> > > > > constructor is no longer dependent and since we operate on a copy on 
> > > > > which
> > > > > we didn't set TREE_HAS_CONSTRUCTOR, the first condition shouldn't be 
> > > > > true.
> > > > > 
> > > > > And the second condition should also never be true for a compound 
> > > > > literal
> > > > > that hasn't been reshaped, because digest_init is only ever called 
> > > > > after
> > > > > reshape_init (and the comment for digest_init_r says it assumes that
> > > > > reshape_init has already run).
> > > 
> > > And because, as above, tsubst_* builds up a CONSTRUCTOR with
> > > init_list_type_node and feeds that to finish_compound_literal.
> > 
> > Yes.
> > 
> > > I suppose that means we do the same thing for a non-dependent CONSTRUCTOR
> > > that has already been reshaped, but it should be harmless.
> > > 
> > > > > The type of a CONSTRUCTOR can also by changed
> > > > > in tsubst_copy_and_build:
> > > > > 19269         TREE_TYPE (r) = type;
> > > > > but I haven't been able to trigger any problem yet.  Worst comes to 
> > > > > worst this
> > > > > patch changes the ICE to another ICE, but I'm not finding a testcase.
> > > 
> > > I'd expect that's where the { 1, 2 } goes through to produce this issue.
> > 
> > Correct.  There's more to this.  When I debugged 80864 I couldn't understand
> > why r216750 would make any difference here, why the type in the case
> > CONSTRUCTOR was not an init list type.  It turned out that the reason was
> > the new
> >    if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type))
> >      ...
> > 
> > block in cxx_eval_outermost_constant_expr added in r216750, because it does
> > 
> >        if (TREE_CODE (r) == TARGET_EXPR)
> >          /* Avoid creating another CONSTRUCTOR when we expand the
> >             TARGET_EXPR.  */
> >          r = TARGET_EXPR_INITIAL (r);
> > 
> > Before r216750 we'd process the TARGET_EXPR in cxx_eval_constant_expression,
> > and that has
> > 
> > 4422         /* Adjust the type of the result to the type of the temporary. 
> >  */
> > 4423         r = adjust_temp_type (TREE_TYPE (t), r);
> > 
> > whereas now we extract the CONSTRUCTOR from the TARGET_EXPR, and we end up
> > doing
> > 
> > 5176   if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r)))
> > 5177     {
> > 5178       r = adjust_temp_type (type, r);
> > 
> > Now "type" here was obtained by initialized_type, which always uses
> > cv_unqualified.  So while "TREE_TYPE (t)" is const something, "type" is
> > without that const.  And look what adjust_temp_type does:
> > 
> > 1290   if (same_type_p (TREE_TYPE (temp), type))
> > 1291     return temp;
> > 1292   /* Avoid wrapping an aggregate value in a NOP_EXPR.  */
> > 1293   if (TREE_CODE (temp) == CONSTRUCTOR)
> > 1294     return build_constructor (type, CONSTRUCTOR_ELTS (temp));
> > 
> > So if I remember correctly in one case we returned the original ctor with
> > TREE_HAS_CONSTRUCTOR preserved, and in the other case we returned a new ctor
> > without TREE_HAS_CONSTRUCTOR.
> 
> > Now I still think my fix makes sense, but the above is suspicious, too.
> > (Using initialized_type instead of "TREE_TYPE (t)" doesn't fix this bug.)
> 
> Hmm, that does look questionable; adjust_temp_type should probably use
> copy_node (and then change the type) rather than build_constructor. Does
> doing that also fix the bug?  Having that flag would mean COMPOUND_LITERAL_P
> is true, so adding the same_type check shouldn't be necessary.

Ok, so that doesn't help at all -- I guess because we now use initialized_type
without cv-quals, the same_type_p check applies and adjust_temp_type returns
the original expression, so it's not the case that we're losing the
TREE_HAS_CONSTRUCTOR bit here.  

For initlist107.C we don't call adjust_temp_type at all.

> > > Hmm, the PMF and nested compound literal cases above your change look like
> > > they don't do what they're intended to do; they fall through to either
> > > gcc_unreachable or reshape_init_*, contrary to the comments.
> > 
> > Things break if I return in the PMF case, we need to keep it as-is.  I've
> > updated its comment though.  And I've added a new test checking "missing
> > braces" for a PMF.  It matches what clang warns about.
> > 
> > But I merged my new case with the nested compound literal case and that
> > doesn't seem to break anything.
> > 
> > Bootstrapped/regtested on x86_64-linux, ok for trunk?
> > 
> > 2019-01-31  Marek Polacek  <pola...@redhat.com>
> > 
> >     PR c++/89083, c++/80864 - ICE with list initialization in template.
> >     * decl.c (reshape_init_r): Don't reshape a digested initializer.
> >     Return the initializer for COMPOUND_LITERAL_P.
> > 
> >     * g++.dg/cpp0x/initlist107.C: New test.
> >     * g++.dg/cpp0x/initlist108.C: New test.
> >     * g++.dg/cpp0x/initlist109.C: New test.
> >     * g++.dg/cpp0x/initlist110.C: New test.
> >     * g++.dg/cpp0x/initlist111.C: New test.
> >     * g++.dg/cpp0x/initlist112.C: New test.
> >     * g++.dg/init/ptrfn4.C: New test.
> > 
> > diff --git gcc/cp/decl.c gcc/cp/decl.c
> > index 79eeac177b6..de4883ff994 100644
> > --- gcc/cp/decl.c
> > +++ gcc/cp/decl.c
> > @@ -6154,20 +6154,29 @@ reshape_init_r (tree type, reshape_iter *d, bool 
> > first_initializer_p,
> >       {
> >         if (TREE_CODE (stripped_init) == CONSTRUCTOR)
> >     {
> > -     if (TREE_TYPE (init) && TYPE_PTRMEMFUNC_P (TREE_TYPE (init)))
> > -       /* There is no need to reshape pointer-to-member function
> > -          initializers, as they are always constructed correctly
> > -          by the front end.  */
> > -           ;
> > -     else if (COMPOUND_LITERAL_P (stripped_init))
> > +     tree init_type = TREE_TYPE (init);
> > +     if (init_type && TYPE_PTRMEMFUNC_P (init_type))
> > +       /* There is no need to call reshape_init forpointer-to-member
> 
> Missing space.

Fixed.

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

2019-01-31  Marek Polacek  <pola...@redhat.com>

        PR c++/89083, c++/80864 - ICE with list initialization in template.
        * constexpr.c (adjust_temp_type): Use copy_node and change the type
        instead of using build_constructor.
        * decl.c (reshape_init_r): Don't reshape a digested initializer.
        Return the initializer for COMPOUND_LITERAL_P.

        * g++.dg/cpp0x/initlist107.C: New test.
        * g++.dg/cpp0x/initlist108.C: New test.
        * g++.dg/cpp0x/initlist109.C: New test.
        * g++.dg/cpp0x/initlist110.C: New test.
        * g++.dg/cpp0x/initlist111.C: New test.
        * g++.dg/cpp0x/initlist112.C: New test.
        * g++.dg/init/ptrfn4.C: New test.

diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c
index 42681416760..19eb44fc0c0 100644
--- gcc/cp/constexpr.c
+++ gcc/cp/constexpr.c
@@ -1291,7 +1291,12 @@ adjust_temp_type (tree type, tree temp)
     return temp;
   /* Avoid wrapping an aggregate value in a NOP_EXPR.  */
   if (TREE_CODE (temp) == CONSTRUCTOR)
-    return build_constructor (type, CONSTRUCTOR_ELTS (temp));
+    {
+      /* build_constructor wouldn't retain various CONSTRUCTOR flags.  */
+      tree t = copy_node (temp);
+      TREE_TYPE (t) = type;
+      return t;
+    }
   if (TREE_CODE (temp) == EMPTY_CLASS_EXPR)
     return build0 (EMPTY_CLASS_EXPR, type);
   gcc_assert (scalarish_type_p (type));
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 79eeac177b6..65ba812deb6 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -6154,20 +6154,29 @@ reshape_init_r (tree type, reshape_iter *d, bool 
first_initializer_p,
     {
       if (TREE_CODE (stripped_init) == CONSTRUCTOR)
        {
-         if (TREE_TYPE (init) && TYPE_PTRMEMFUNC_P (TREE_TYPE (init)))
-           /* There is no need to reshape pointer-to-member function
-              initializers, as they are always constructed correctly
-              by the front end.  */
-           ;
-         else if (COMPOUND_LITERAL_P (stripped_init))
+         tree init_type = TREE_TYPE (init);
+         if (init_type && TYPE_PTRMEMFUNC_P (init_type))
+           /* There is no need to call reshape_init for pointer-to-member
+              function initializers, as they are always constructed correctly
+              by the front end.  Here we have e.g. {.__pfn=0B, .__delta=0},
+              which is missing outermost braces.  We should warn below, and
+              one of the routines below will wrap it in additional { }.  */;
          /* For a nested compound literal, there is no need to reshape since
-            brace elision is not allowed. Even if we decided to allow it,
-            we should add a call to reshape_init in finish_compound_literal,
-            before calling digest_init, so changing this code would still
-            not be necessary.  */
-           gcc_assert (!BRACE_ENCLOSED_INITIALIZER_P (stripped_init));
+            we called reshape_init in finish_compound_literal, before calling
+            digest_init.  */
+         else if (COMPOUND_LITERAL_P (stripped_init)
+                  /* Similarly, a CONSTRUCTOR of the target's type is a
+                     previously digested initializer.  */
+                  || same_type_ignoring_top_level_qualifiers_p (type,
+                                                                init_type))
+           {
+             ++d->cur;
+             gcc_assert (!BRACE_ENCLOSED_INITIALIZER_P (stripped_init));
+             return init;
+           }
          else
            {
+             /* Something that hasn't been reshaped yet.  */
              ++d->cur;
              gcc_assert (BRACE_ENCLOSED_INITIALIZER_P (stripped_init));
              return reshape_init (type, init, complain);
diff --git gcc/testsuite/g++.dg/cpp0x/initlist107.C 
gcc/testsuite/g++.dg/cpp0x/initlist107.C
new file mode 100644
index 00000000000..9acfe7cb267
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/initlist107.C
@@ -0,0 +1,24 @@
+// PR c++/89083
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wmissing-braces" }
+
+struct A { int x[3]; };
+
+template<class T>
+decltype(A{1, 2}, T()) fn1(T t) // { dg-warning "missing braces" }
+{
+  return t;
+}
+
+template<class T>
+decltype(A{{1, 2}}, T()) fn2(T t)
+{
+  return t;
+}
+
+void
+f()
+{
+  fn1(1);
+  fn2(1);
+}
diff --git gcc/testsuite/g++.dg/cpp0x/initlist108.C 
gcc/testsuite/g++.dg/cpp0x/initlist108.C
new file mode 100644
index 00000000000..e2839787fa5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/initlist108.C
@@ -0,0 +1,34 @@
+// PR c++/80864
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wmissing-braces" }
+
+struct S {
+  char c[1];
+};
+
+template <typename T>
+void
+fn ()
+{
+   constexpr S s1 = S{};
+   constexpr S s2 = S{{}};
+   constexpr S s3 = S{{{}}};
+   constexpr S s4 = {};
+   constexpr S s5 = {{}};
+   constexpr S s6 = {{{}}};
+   constexpr S s7{{}};
+   constexpr S s8{S{}};
+   constexpr S s9{S{{}}};
+   constexpr S s10{S{{{}}}};
+   constexpr S s11 = S();
+   constexpr S s12 = S({});
+   constexpr S s13 = S({{}});
+   constexpr S s14 = {{}};
+   constexpr S s15 = {{{}}};
+}
+
+void
+foo ()
+{
+  fn<int>();
+}
diff --git gcc/testsuite/g++.dg/cpp0x/initlist109.C 
gcc/testsuite/g++.dg/cpp0x/initlist109.C
new file mode 100644
index 00000000000..1351a2d57ce
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/initlist109.C
@@ -0,0 +1,15 @@
+// PR c++/80864
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wmissing-braces" }
+
+struct S {};
+struct A { S s[1]; };
+
+template <typename>
+struct R { static constexpr auto h = A{S{}}; }; // { dg-warning "missing 
braces" }
+
+template <typename>
+struct R2 { static constexpr auto h = A{{S{}}}; };
+
+A foo = R<int>::h;
+A foo2 = R2<int>::h;
diff --git gcc/testsuite/g++.dg/cpp0x/initlist110.C 
gcc/testsuite/g++.dg/cpp0x/initlist110.C
new file mode 100644
index 00000000000..7bb229cbc7e
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/initlist110.C
@@ -0,0 +1,32 @@
+// PR c++/89083
+// { dg-do compile { target c++11 } }
+
+struct C { int a[3]; int i; };
+struct B { C c[3]; };
+struct A { B b[3]; };
+
+template<class T, int N>
+decltype(A{N, N}, T()) fn1(T t)
+{
+  return t;
+}
+
+template<class T, int N>
+decltype(A{{{N, N, N}, {N + 1}}}, T()) fn2(T t)
+{
+  return t;
+}
+
+template<class T, int N, int M>
+decltype(A{{N + M}}, T()) fn3(T t)
+{
+  return t;
+}
+
+void
+f()
+{
+  fn1<int, 10>(1);
+  fn2<int, 10>(1);
+  fn3<int, 10, 20>(1);
+}
diff --git gcc/testsuite/g++.dg/cpp0x/initlist111.C 
gcc/testsuite/g++.dg/cpp0x/initlist111.C
new file mode 100644
index 00000000000..7f96115e618
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/initlist111.C
@@ -0,0 +1,32 @@
+// PR c++/80864
+// { dg-do compile { target c++11 } }
+
+struct S {
+  int c[3];
+};
+
+template <typename T, int N>
+void
+fn ()
+{
+   constexpr S s1 = S{N};
+   constexpr S s2 = S{{N, N}};
+   constexpr S s3 = S{N, N};
+   constexpr S s4 = {N};
+   constexpr S s5 = {{N}};
+   constexpr S s6 = {N, N};
+   constexpr S s7{{N}};
+   constexpr S s8{S{N}};
+   constexpr S s9{S{{N}}};
+   constexpr S s10{S{{N}}};
+   constexpr S s11 = S({N});
+   constexpr S s12 = S({{N}});
+   constexpr S s13 = {{N}};
+   constexpr S s14 = {{N, N, N}};
+}
+
+void
+foo ()
+{
+  fn<int, 10>();
+}
diff --git gcc/testsuite/g++.dg/cpp0x/initlist112.C 
gcc/testsuite/g++.dg/cpp0x/initlist112.C
new file mode 100644
index 00000000000..cd97098eec5
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/initlist112.C
@@ -0,0 +1,14 @@
+// PR c++/80864
+// { dg-do compile { target c++11 } }
+
+struct S {int a[2]; };
+struct A { S s[1]; };
+
+template <typename, int N>
+struct R { static constexpr auto h = A{S{N}}; };
+
+template <typename, int N>
+struct R2 { static constexpr auto h = A{S{{N, N}}}; };
+
+A foo = R<int, 10>::h;
+A foo2 = R2<int, 10>::h;
diff --git gcc/testsuite/g++.dg/init/ptrfn4.C gcc/testsuite/g++.dg/init/ptrfn4.C
new file mode 100644
index 00000000000..e1635c86483
--- /dev/null
+++ gcc/testsuite/g++.dg/init/ptrfn4.C
@@ -0,0 +1,19 @@
+// { dg-do compile }
+// { dg-options "-Wmissing-braces" }
+
+struct S { };
+typedef void (S::*fptr1) (int);
+
+struct A {
+  fptr1 f;
+};
+
+A a[] =
+{
+ (fptr1) 0,
+}; // { dg-warning "missing braces around initializer" }
+
+A a2[] =
+{
+ { (fptr1) 0 }
+};

Reply via email to