diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 33a1ca24c9f..5d909ef62ec 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,9 @@ 2019-02-18 Jason Merrill + PR c++/89336 - multiple stores in constexpr stmt. + * constexpr.c (cxx_eval_store_expression): Preevaluate scalar or + assigned value. + * pt.c (check_explicit_specialization): If the declarator is a template-id, only check whether the arguments are dependent. diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index d946a797999..d413c6b9b27 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -3634,6 +3634,18 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, maybe_simplify_trivial_copy (target, init); tree type = TREE_TYPE (target); + bool preeval = SCALAR_TYPE_P (type) || TREE_CODE (t) == MODIFY_EXPR; + if (preeval) + { + /* Evaluate the value to be stored without knowing what object it will be + stored in, so that any side-effects happen first. */ + if (!SCALAR_TYPE_P (type)) + new_ctx.ctor = new_ctx.object = NULL_TREE; + init = cxx_eval_constant_expression (&new_ctx, init, false, + non_constant_p, overflow_p); + if (*non_constant_p) + return t; + } target = cxx_eval_constant_expression (ctx, target, true, non_constant_p, overflow_p); @@ -3834,7 +3846,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, } release_tree_vector (refs); - if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type)) + if (!preeval) { /* Create a new CONSTRUCTOR in case evaluation of the initializer wants to modify it. */ @@ -3843,21 +3855,20 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, *valp = build_constructor (type, NULL); CONSTRUCTOR_NO_CLEARING (*valp) = no_zero_init; } - else if (TREE_CODE (*valp) == PTRMEM_CST) - *valp = cplus_expand_constant (*valp); new_ctx.ctor = *valp; new_ctx.object = target; + init = cxx_eval_constant_expression (&new_ctx, init, false, + non_constant_p, overflow_p); + if (target == object) + /* The hash table might have moved since the get earlier. */ + valp = ctx->values->get (object); } - init = cxx_eval_constant_expression (&new_ctx, init, false, - non_constant_p, overflow_p); /* Don't share a CONSTRUCTOR that might be changed later. */ init = unshare_constructor (init); - if (target == object) - /* The hash table might have moved since the get earlier. */ - valp = ctx->values->get (object); - if (TREE_CODE (init) == CONSTRUCTOR) + if (*valp && TREE_CODE (*valp) == CONSTRUCTOR + && TREE_CODE (init) == CONSTRUCTOR) { /* An outer ctx->ctor might be pointing to *valp, so replace its contents. */ diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89336-1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89336-1.C new file mode 100644 index 00000000000..93fe16551a1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89336-1.C @@ -0,0 +1,35 @@ +// PR c++/89336 +// { dg-do compile { target c++14 } } + +template struct A { + T a[N]; + constexpr T &operator[] (int x) { return a[x]; } + constexpr const T &operator[] (int x) const { return a[x]; } +}; + +constexpr A +foo () +{ + A r{}; + for (int i = 0; i < 6; ++i) + r[i + 8] = r[i] = i + 1; + return r; +} + +constexpr auto x = foo (); +static_assert (x[0] == 1, ""); +static_assert (x[1] == 2, ""); +static_assert (x[2] == 3, ""); +static_assert (x[3] == 4, ""); +static_assert (x[4] == 5, ""); +static_assert (x[5] == 6, ""); +static_assert (x[6] == 0, ""); +static_assert (x[7] == 0, ""); +static_assert (x[8] == 1, ""); +static_assert (x[9] == 2, ""); +static_assert (x[10] == 3, ""); +static_assert (x[11] == 4, ""); +static_assert (x[12] == 5, ""); +static_assert (x[13] == 6, ""); +static_assert (x[14] == 0, ""); +static_assert (x[15] == 0, ""); diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-89336-2.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-89336-2.C new file mode 100644 index 00000000000..69889ffb2b4 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-89336-2.C @@ -0,0 +1,56 @@ +// PR c++/89336 +// { dg-do compile { target c++14 } } + +constexpr int +foo () +{ + int a[16] = {}; + int r = 0; + a[15] = a[14] = a[13] = a[12] = a[11] = a[10] = a[9] = a[8] + = a[7] = a[6] = a[5] = a[4] = a[3] = a[2] = a[1] = a[0] = 5; + for (int i = 0; i < 16; ++i) + r += a[i]; + return r; +} + +static_assert (foo () == 16 * 5, ""); + +struct A { int a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p; }; + +constexpr int +bar () +{ + A a {}; + a.p = a.o = a.n = a.m = a.l = a.k = a.j = a.i + = a.h = a.g = a.f = a.e = a.d = a.c = a.b = a.a = 8; + return a.a + a.b + a.c + a.d + a.e + a.f + a.g + a.h + + a.i + a.j + a.k + a.l + a.m + a.n + a.o + a.p; +} + +static_assert (bar () == 16 * 8, ""); + +constexpr int +baz () +{ + int a[16] = {}; + int r = 0; + a[0] = a[1] = a[2] = a[3] = a[4] = a[5] = a[6] = a[7] + = a[8] = a[9] = a[10] = a[11] = a[12] = a[13] = a[14] = a[15] = 7; + for (int i = 0; i < 16; ++i) + r += a[i]; + return r; +} + +static_assert (baz () == 16 * 7, ""); + +constexpr int +qux () +{ + A a {}; + a.a = a.b = a.c = a.d = a.e = a.f = a.g = a.h + = a.i = a.j = a.k = a.l = a.m = a.n = a.o = a.p = 6; + return a.a + a.b + a.c + a.d + a.e + a.f + a.g + a.h + + a.i + a.j + a.k + a.l + a.m + a.n + a.o + a.p; +} + +static_assert (qux () == 16 * 6, "");