aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2019-02-18 20:01:50 -0500
committerJason Merrill <jason@gcc.gnu.org>2019-02-18 20:01:50 -0500
commite8b3c1bc3ba22dcf59b9c743f11d4cb2bc5d7792 (patch)
tree9c3cdaa34468fdc8816877ce803ffca053288deb /gcc
parent44db22fc662edfc4e665f4c523fd75514dfb3be7 (diff)
downloadgcc-e8b3c1bc3ba22dcf59b9c743f11d4cb2bc5d7792.zip
gcc-e8b3c1bc3ba22dcf59b9c743f11d4cb2bc5d7792.tar.gz
gcc-e8b3c1bc3ba22dcf59b9c743f11d4cb2bc5d7792.tar.bz2
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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/ChangeLog4
-rw-r--r--gcc/cp/constexpr.c29
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-89336-1.C35
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-89336-2.C56
4 files changed, 115 insertions, 9 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 33a1ca2..5d909ef 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -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.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index d946a79..d413c6b 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 0000000..93fe165
--- /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 <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, "");
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 0000000..69889ff
--- /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, "");