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). 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. The following patch is the same but adds tests with dependent compound literals for good measure. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2019-01-30 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. * g++.dg/cpp0x/initlist110.C: New test. * g++.dg/cpp0x/initlist111.C: New test. * g++.dg/cpp0x/initlist112.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))) + { + ++d->cur; + return init; + } else { ++d->cur; 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;