PR c++/89336 - multiple stores in constexpr stmt.
If we evaluate the RHS in the context of the LHS, that evaluation might change the LHS in ways that mess with being able to store the value later. So for assignment or scalar values, evaluate the RHS first. * constexpr.c (cxx_eval_store_expression): Preevaluate scalar or assigned value. From-SVN: r269003
This commit is contained in:
parent
44db22fc66
commit
e8b3c1bc3b
4 changed files with 115 additions and 9 deletions
|
@ -1,5 +1,9 @@
|
|||
2019-02-18 Jason Merrill <jason@redhat.com>
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
35
gcc/testsuite/g++.dg/cpp1y/constexpr-89336-1.C
Normal file
35
gcc/testsuite/g++.dg/cpp1y/constexpr-89336-1.C
Normal file
|
@ -0,0 +1,35 @@
|
|||
// PR c++/89336
|
||||
// { dg-do compile { target c++14 } }
|
||||
|
||||
template <typename T, int N> 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<int, 16>
|
||||
foo ()
|
||||
{
|
||||
A<int, 16> 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, "");
|
56
gcc/testsuite/g++.dg/cpp1y/constexpr-89336-2.C
Normal file
56
gcc/testsuite/g++.dg/cpp1y/constexpr-89336-2.C
Normal file
|
@ -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, "");
|
Loading…
Add table
Reference in a new issue