PR c++/89336 - multiple stores in constexpr stmt.
authorJason Merrill <jason@redhat.com>
Tue, 19 Feb 2019 01:01:50 +0000 (20:01 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Tue, 19 Feb 2019 01:01:50 +0000 (20:01 -0500)
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

gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/testsuite/g++.dg/cpp1y/constexpr-89336-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/constexpr-89336-2.C [new file with mode: 0644]

index 33a1ca24c9fd8ebb7a2c192030a7837d5aab37ec..5d909ef62ec73f99394c863dfe1cf8903e364a26 100644 (file)
@@ -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.
 
index d946a797999b7df31934a2aca962e4e32ce2c3e0..d413c6b9b275b299045664a62467e37b039449ed 100644 (file)
@@ -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 (file)
index 0000000..93fe165
--- /dev/null
@@ -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 (file)
index 0000000..69889ff
--- /dev/null
@@ -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, "");