On Thu, Oct 22, 2020 at 05:37:06PM -0400, Jason Merrill via Gcc-patches wrote: > On 10/15/20 6:18 PM, Marek Polacek wrote: > > The code added in r10-6437 caused us to create a CONSTRUCTOR when we're > > {}-initializing an aggregate. Then we pass this new CONSTRUCTOR down to > > cxx_eval_constant_expression which, if the CONSTRUCTOR isn't TREE_CONSTANT > > or reduced_constant_expression_p, calls cxx_eval_bare_aggregate. In > > this case the CONSTRUCTOR wasn't reduced_constant_expression_p because > > for r_c_e_p a CONST_DECL isn't good enough so it returns false. So we > > go to cxx_eval_bare_aggregate where we crash, because ctx->ctor wasn't > > set up properly. So my fix is to do so. Since we're value-initializing, > > I'm not setting CONSTRUCTOR_NO_CLEARING. > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/10? > > > > gcc/cp/ChangeLog: > > > > PR c++/96241 > > * constexpr.c (cxx_eval_array_reference): Set up ctx->ctor if we > > are initializing an aggregate. > > > > gcc/testsuite/ChangeLog: > > > > PR c++/96241 > > * g++.dg/cpp1y/constexpr-96241.C: New test. > > --- > > gcc/cp/constexpr.c | 4 ++ > > gcc/testsuite/g++.dg/cpp1y/constexpr-96241.C | 47 ++++++++++++++++++++ > > 2 files changed, 51 insertions(+) > > create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-96241.C > > > > diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c > > index a118f8a810b..5193047670c 100644 > > --- a/gcc/cp/constexpr.c > > +++ b/gcc/cp/constexpr.c > > @@ -3657,10 +3657,14 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, > > tree t, > > initializer, it's initialized from {}. But use build_value_init > > directly for non-aggregates to avoid creating a garbage CONSTRUCTOR. > > */ > > tree val; > > + constexpr_ctx new_ctx; > > if (CP_AGGREGATE_TYPE_P (elem_type)) > > { > > tree empty_ctor = build_constructor (init_list_type_node, NULL); > > val = digest_init (elem_type, empty_ctor, tf_warning_or_error); > > + new_ctx = *ctx; > > + new_ctx.ctor = build_constructor (elem_type, NULL); > > + ctx = &new_ctx; > > In the case that 'val' is reduced_constant_expression_p, this results in a > garbage constructor. Let's try to free the new CONSTRUCTOR if it isn't > returned from cxx_eval_constant_expression.
Good point indeed. Here's a patch with that fixed. A new test is included; it's something that crashed when I freed the wrong CONSTRUCTOR, but our testsuite didn't catch it. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- The code added in r10-6437 caused us to create a CONSTRUCTOR when we're {}-initializing an aggregate. Then we pass this new CONSTRUCTOR down to cxx_eval_constant_expression which, if the CONSTRUCTOR isn't TREE_CONSTANT or reduced_constant_expression_p, calls cxx_eval_bare_aggregate. In this case the CONSTRUCTOR wasn't reduced_constant_expression_p because for r_c_e_p a CONST_DECL isn't good enough so it returns false. So we go to cxx_eval_bare_aggregate where we crash, because ctx->ctor wasn't set up properly. So my fix is to do so. Since we're value-initializing, I'm not setting CONSTRUCTOR_NO_CLEARING. To avoid keeping a garbage constructor around, I call free_constructor in case the evaluation did not use it. gcc/cp/ChangeLog: PR c++/96241 * constexpr.c (cxx_eval_array_reference): Set up ctx->ctor if we are initializing an aggregate. Call free_constructor on the new CONSTRUCTOR if it isn't returned from cxx_eval_constant_expression. gcc/testsuite/ChangeLog: PR c++/96241 * g++.dg/cpp0x/constexpr-96241.C: New test. * g++.dg/cpp1y/constexpr-96241.C: New test. --- gcc/cp/constexpr.c | 11 ++++- gcc/testsuite/g++.dg/cpp0x/constexpr-96241.C | 18 ++++++++ gcc/testsuite/g++.dg/cpp1y/constexpr-96241.C | 47 ++++++++++++++++++++ 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-96241.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-96241.C diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 4707d29f3bc..7ebdd308dcd 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -3657,15 +3657,22 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, initializer, it's initialized from {}. But use build_value_init directly for non-aggregates to avoid creating a garbage CONSTRUCTOR. */ tree val; + constexpr_ctx new_ctx; if (CP_AGGREGATE_TYPE_P (elem_type)) { tree empty_ctor = build_constructor (init_list_type_node, NULL); val = digest_init (elem_type, empty_ctor, tf_warning_or_error); + new_ctx = *ctx; + new_ctx.ctor = build_constructor (elem_type, NULL); + ctx = &new_ctx; } else val = build_value_init (elem_type, tf_warning_or_error); - return cxx_eval_constant_expression (ctx, val, lval, non_constant_p, - overflow_p); + t = cxx_eval_constant_expression (ctx, val, lval, non_constant_p, + overflow_p); + if (CP_AGGREGATE_TYPE_P (elem_type) && t != ctx->ctor) + free_constructor (ctx->ctor); + return t; } /* Subroutine of cxx_eval_constant_expression. diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-96241.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-96241.C new file mode 100644 index 00000000000..599405302cb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-96241.C @@ -0,0 +1,18 @@ +// PR c++/96241 +// { dg-do compile { target c++11 } } + +template <typename T, T...> struct S {}; +template <typename T, T t> using U = S<T, __integer_pack(t)...>; +template <long... N> using f = S<unsigned long, N...>; +template <long N> using V = U<unsigned long, N>; +template <int N> struct A { typedef int type[N]; }; +template <int N> struct B { typename A<N>::type k; }; +template <typename T, int N, unsigned long... P> +constexpr B<N> bar(T (&arr)[N], f<P...>) { + return {arr[P]...}; +} +template <typename T, int N> constexpr B<N> foo(T (&arr)[N]) { + return bar(arr, V<N>{}); +} +constexpr char arr[2]{}; +B<2> b = foo(arr); diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-96241.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-96241.C new file mode 100644 index 00000000000..107f2b09de9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-96241.C @@ -0,0 +1,47 @@ +// PR c++/96241 +// { dg-do compile { target c++14 } } + +#define assert(expr) static_assert (expr, #expr) + +enum E { o }; + +struct S { + int e = o; +}; + +using T = S[3]; + +constexpr struct S s[1][1][1] = { }; +assert (0 == s[0][0][0].e); + +constexpr int +fn0 () +{ + return T{}[0].e; +} +assert(fn0 () == 0); + +constexpr int +fn1 () +{ + S d[1]; + int x = d[0].e; + return x; +} +assert(fn1 () == 0); + +constexpr int +fn2 () +{ + S d[1]; + return d[0].e; +} +assert(fn2 () == 0); + +constexpr int +fn3 () +{ + struct X { int e = o; } d[1]{}; + return d[0].e; +} +assert(fn3 () == 0); base-commit: d835608f05471e749533c756ccdd9e166b819b68 -- 2.26.2