The bug here was that we were sharing the CONSTRUCTOR between the value
of 'a' and the elements of 'result', so changing 'a' also changed the
value of result[0]. Oops.
Tested x86_64-pc-linux-gnu, applying to trunk and 5.
commit 20c203ae124fd4fb2975eb3fb0c50ce7ade35e69
Author: Jason Merrill <ja...@redhat.com>
Date: Sun Feb 28 23:50:41 2016 -0500
PR c++/69995
* constexpr.c (cxx_eval_store_expression): Unshare init.
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 8d9168c..5e35940 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2925,6 +2925,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
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_expr (init);
if (target == object)
{
/* The hash table might have moved since the get earlier. */
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-array3.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-array3.C
new file mode 100644
index 0000000..8cea41a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-array3.C
@@ -0,0 +1,43 @@
+// PR c++/69995
+// { dg-do compile { target c++14 } }
+
+#define assert(X) static_assert((X),#X)
+
+#define CONSTEXPR constexpr
+
+template <typename T, unsigned long Size>
+struct array {
+ T elems_[Size];
+
+ constexpr T const& operator[](unsigned long n) const
+ { return elems_[n]; }
+
+ constexpr T& operator[](unsigned long n)
+ { return elems_[n]; }
+};
+
+template <typename T>
+CONSTEXPR void my_swap(T& a, T& b) {
+ T tmp = a;
+ a = b;
+ b = tmp;
+}
+
+CONSTEXPR auto rotate2() {
+ array<array<int, 2>, 2> result{};
+ array<int, 2> a{{0, 1}};
+
+ result[0] = a;
+ my_swap(a[0], a[1]);
+ result[1] = a;
+
+ return result;
+}
+
+int main() {
+ CONSTEXPR auto indices = rotate2();
+ assert(indices[0][0] == 0);
+ assert(indices[0][1] == 1);
+ assert(indices[1][0] == 1);
+ assert(indices[1][1] == 0);
+}