Commit e8b3c1bc by Jason Merrill Committed by Jason Merrill

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
parent 44db22fc
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);
/* 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)
/* Don't share a CONSTRUCTOR that might be changed later. */
init = unshare_constructor (init);
if (*valp && TREE_CODE (*valp) == CONSTRUCTOR
&& TREE_CODE (init) == CONSTRUCTOR)
{
/* An outer ctx->ctor might be pointing to *valp, so replace
its contents. */
......
// 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, "");
// 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, "");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment