+2016-11-14 Jakub Jelinek <jakub@redhat.com>
+ Jason Merrill <jason@redhat.com>
+
+ Implement P0217R3 - C++17 structured bindings
+ * g++.dg/cpp1z/decomp1.C: New test.
+ * g++.dg/cpp1z/decomp2.C: New test.
+ * g++.dg/cpp1z/decomp3.C: New test.
+ * g++.dg/cpp1z/decomp4.C: New test.
+ * g++.dg/cpp1z/decomp5.C: New test.
+ * g++.dg/cpp1z/decomp6.C: New test.
+ * g++.dg/cpp1z/decomp7.C: New test.
+ * g++.dg/cpp1z/decomp8.C: New test.
+ * g++.dg/cpp1z/decomp9.C: New test.
+ * g++.dg/cpp1z/decomp10.C: New test.
+
2016-11-13 Kugan Vivekanandarajah <kuganv@linaro.org>
* g++.dg/torture/pr78268.C: New test.
--- /dev/null
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+int a[2] = { 1, 2 };
+struct S { int a; signed char b; float c; } s = { 6, 7, 8.0f };
+
+int
+main ()
+{
+ auto & [ c, d ] = a; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto [ e, f ] = a; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto [ g, h, i ] = s; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto & [ j, k, l ] = s; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ c++;
+ d++;
+ e += 6;
+ f += 7;
+ g++;
+ h++;
+ j += 10;
+ k += 11;
+ if (c != 2 || &c != &a[0]
+ || d != 3 || &d != &a[1]
+ || e != 7 || &e == &a[0]
+ || f != 9 || &f == &a[1]
+ || g != 7 || &g == &s.a
+ || h != 8 || &h == &s.b
+ || i != 8.0f || &i == &s.c
+ || j != 16 || &j != &s.a
+ || k != 18 || &k != &s.b
+ || l != 8.0f || &l != &s.c
+ || a[0] != 2 || a[1] != 3
+ || s.a != 16 || s.b != 18 || s.c != 8.0f)
+ __builtin_abort ();
+}
--- /dev/null
+// { dg-options -std=c++1z }
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+struct A1 { int i,j; } a1;
+template<> struct std::tuple_size<A1> { };
+void f1() { auto [ x ] = a1; } // { dg-error "decomposes into 2" }
+
+struct A2 { int i,j; } a2;
+template<> struct std::tuple_size<A2> { enum { value = 5 }; };
+void f2() { auto [ x ] = a2; } // { dg-error "decomposes into 5" }
+
+struct A3 { int i,j; } a3;
+template<> struct std::tuple_size<A3> { enum { value = 1 }; };
+void f3() { auto [ x ] = a3; } // { dg-error "get" }
+
+struct A3a { int i,j; int get(); } a3a;
+template<> struct std::tuple_size<A3a> { enum { value = 1 }; };
+void f3a() { auto [ x ] = a3a; } // { dg-error "get<0>" }
+
+struct A3b { int i,j; } a3b;
+int get(A3b&&);
+template<> struct std::tuple_size<A3b> { enum { value = 1 }; };
+void f3b() { auto [ x ] = a3b; } // { dg-error "get<0>" }
+
+struct A4 {
+ int ar[3];
+ template <int I> int& get() { return ar[I]; }
+} a4;
+template<> struct std::tuple_size<A4> { enum { value = 3 }; };
+template <int I>
+void f4() { auto [ x, y, z ] = a4; } // { dg-error "tuple_element" }
+
+struct A5 { } a5;
+template <int I> int& get(A5&& a);
+template<> struct std::tuple_size<A5> { enum { value = 3 }; };
+template <int I>
+void f5() { auto [ x, y, z ] = a5; } // { dg-error "tuple_element" }
+
+struct A6 { } a6;
+template <int I> int& get(A6&& a);
+template<> struct std::tuple_size<A6> { enum { value = 3 }; };
+template<> struct std::tuple_element<0, A6> { };
+template <int I>
+void f6() { auto [ x, y, z ] = a6; } // { dg-error "no type named .type" }
--- /dev/null
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+typedef int V __attribute__((vector_size (4 * sizeof (int))));
+V a = (V) { 1, 2, 3, 4 };
+__complex__ double b = 5.0 + 6.0i;
+__complex__ int c = 7 + 8i;
+
+int
+main ()
+{
+ auto & [ d, e, f, g ] = a; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto [ h, i, j, k ] = a; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto [ l, m ] = b; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto & [ n, o ] = b; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto & [ p, q ] = c; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto [ r, s ] = c; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ d += 10;
+ e += 11;
+ f += 12;
+ g += 13;
+ h += 14;
+ i += 15;
+ j += 16;
+ k += 17;
+ l = l * 2.;
+ m = m * 3.;
+ n = n * 3.;
+ o = o * 2.;
+ p += 18;
+ q += 19;
+ r += 22;
+ s += 23;
+ if (d != 11 || &d != &a[0]
+ || e != 13 || &e != &a[1]
+ || f != 15 || &f != &a[2]
+ || g != 17 || &g != &a[3]
+ || h != 15 || &h == &a[0]
+ || i != 17 || &i == &a[1]
+ || j != 19 || &j == &a[2]
+ || k != 21 || &k == &a[3]
+ || l != 10.0 || &l == &__real__ b
+ || m != 18.0 || &m == &__imag__ b
+ || n != 15.0 || &n != &__real__ b
+ || o != 12.0 || &o != &__imag__ b
+ || p != 25 || &p != &__real__ c
+ || q != 27 || &q != &__imag__ c
+ || r != 29 || &r == &__real__ c
+ || s != 31 || &s == &__imag__ c
+ || a[0] != 11 || a[1] != 13 || a[2] != 15 || a[3] != 17
+ || b != 15.0 + 12.0i
+ || c != 25 + 27i)
+ __builtin_abort ();
+}
--- /dev/null
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+struct A { int a, b; float c; };
+A &bar ();
+struct B { int d; };
+B baz ();
+
+void
+test (A &b, B c)
+{
+ int && [ d ] = c; // { dg-error "decomposition declaration cannot be declared with type 'int'" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ char & [ e, f, ff ] { b }; // { dg-error "decomposition declaration cannot be declared with type 'char'" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ auto&[g,h,i]=b; // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } }
+ decltype (auto) [ j ] = c; // { dg-error "decomposition declaration cannot be declared with type 'decltype.auto.'" "" { target c++14 } }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ // { dg-error "expected primary-expression before 'decltype'" "" { target c++11_down } .-2 }
+ auto & & && & [ m, n, o ] = b; // { dg-error "multiple ref-qualifiers" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ constexpr auto [ p ] = c; // { dg-error "decomposition declaration cannot be declared 'constexpr'" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ friend auto [ q ] = c; // { dg-error "'friend' used outside of class" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ typedef auto [ r ] = c; // { dg-error "decomposition declaration cannot be declared 'typedef'" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ inline auto [ s ] = c; // { dg-error "decomposition declaration cannot be declared 'inline'" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ __restrict auto [ t ] = c; // { dg-error "invalid use of 'restrict'" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ long long auto [ u ] = c; // { dg-error "'long long' invalid for 'decomposition'" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ virtual auto [ v ] = c; // { dg-error "'virtual' outside class declaration" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ explicit auto [ w ] = c; // { dg-error "'explicit' outside class declaration" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ static auto [ x ] = c; // { dg-error "decomposition declaration cannot be declared 'static'" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ extern auto [ y ] { c }; // { dg-error "decomposition declaration cannot be declared 'extern'" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+}
+
+void
+test2 (auto & [ p ] = bar ()) // { dg-error "'p' was not declared in this scope" }
+{
+}
+
+int arr[4];
+
+void
+test3 (A &b, B c)
+{
+ auto [ d, e, f ] = arr; // { dg-error "only 3 names provided while 'int .4.' decomposes into 4 elements" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ auto & [ g, h, i, j, k ] = arr; // { dg-error "5 names provided while 'int .4.' decomposes into 4 elements" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ auto [ l, m ] = b; // { dg-error "only 2 names provided while 'A' decomposes into 3 elements" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ auto & [ n, o, p, q ] = b; // { dg-error "4 names provided while 'A' decomposes into 3 elements" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ auto [] { c }; // { dg-error "empty decomposition declaration" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ auto [ r, s ] = c; // { dg-error "2 names provided while 'B' decomposes into 1 elements" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+}
--- /dev/null
+// { dg-do compile { target c++11 } }
+// { dg-options "" }
+
+struct A { int a; struct { int b; }; };
+struct B { int a; union { int c; long d; }; };
+struct C { int a; private: int b; };
+struct D { int a; private: static int b; };
+struct E { protected: int a; };
+struct F { int a; };
+struct G : public F { int b; };
+struct H { int b; };
+struct I : public F, H {};
+
+void
+test (A &a, B &b, C &c, D &d, E &e, F &f, G &g, H &h, I &i)
+{
+ auto [ j ] = a; // { dg-error "cannot decompose class type 'A' because it has an anonymous struct member" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ auto [ k ] { b }; // { dg-error "cannot decompose class type 'B' because it has an anonymous union member" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ auto [ l ] = c; // { dg-error "cannot decompose non-public member 'C::b' of 'C'" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ auto [ m ] = d; // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } }
+ auto [ n ] { e }; // { dg-error "cannot decompose non-public member 'E::a' of 'E'" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ auto [ o ] { f }; // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } }
+ auto & [ p ] { g }; // { dg-error "cannot decompose class type 'G': both it and its base class 'F' have non-static data members" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+ auto [ q ] { h }; // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } }
+ auto [ r ] { i }; // { dg-error "cannot decompose class type 'I': its base classes 'F' and 'H' have non-static data members" }
+ // { dg-warning "decomposition declaration only available with -std=c..1z or -std=gnu..1z" "" { target c++14_down } .-1 }
+}
--- /dev/null
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+struct A { int i; long long j; } a[64];
+
+int
+main ()
+{
+ int i = 0;
+ for (auto &x : a)
+ {
+ x.i = i;
+ x.j = 2 * i++;
+ }
+ for (auto & [ x, y ] : a) // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ {
+ x += 2;
+ y += 3;
+ }
+ i = 0;
+ for (const auto [ u, v ] : a) // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ {
+ if (u != i + 2 || v != 2 * i++ + 3)
+ __builtin_abort ();
+ }
+ i = 0;
+ for (auto [ x, y ] : a) // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ {
+ x += 4;
+ y += 5;
+ if (x != i + 6 || y != 2 * i++ + 8)
+ __builtin_abort ();
+ }
+ i = 0;
+ for (const auto x : a)
+ {
+ if (x.i != i + 2 || x.j != 2 * i++ + 3)
+ __builtin_abort ();
+ }
+}
--- /dev/null
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+int ccnt, dcnt, cccnt, tccnt;
+
+struct A
+{
+ A () : a (6) { ccnt++; }
+ ~A () { dcnt++; }
+ explicit A (const A &x) : a (x.a) { cccnt++; }
+ template <typename T>
+ A (const T &x) : a (x.a) { tccnt++; }
+ int a;
+};
+
+int
+main ()
+{
+ if (ccnt || dcnt || cccnt || tccnt)
+ __builtin_abort ();
+ {
+ A a[6];
+ if (ccnt != 6 || dcnt || cccnt || tccnt)
+ __builtin_abort ();
+ {
+ auto [b,c,d,e,f,g] = a; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ if (ccnt != 6 || dcnt || cccnt || tccnt != 6)
+ __builtin_abort ();
+ b.a++;
+ c.a += 2;
+ f.a += 3;
+ if (b.a != 7 || c.a != 8 || d.a != 6 || e.a != 6 || f.a != 9 || g.a != 6)
+ __builtin_abort ();
+ if (&b == &a[0] || &c == &a[1] || &d == &a[2] || &e == &a[3] || &f == &a[4] || &g == &a[5])
+ __builtin_abort ();
+ {
+ auto&[ h, i, j, k, l, m ] = a; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ if (ccnt != 6 || dcnt || cccnt || tccnt != 6)
+ __builtin_abort ();
+ j.a += 4;
+ k.a += 5;
+ m.a += 6;
+ if (a[0].a != 6 || a[1].a != 6 || a[2].a != 10 || a[3].a != 11 || a[4].a != 6 || a[5].a != 12)
+ __builtin_abort ();
+ if (&h != &a[0] || &i != &a[1] || &j != &a[2] || &k != &a[3] || &l != &a[4] || &m != &a[5])
+ __builtin_abort ();
+ }
+ if (ccnt != 6 || dcnt || cccnt || tccnt != 6)
+ __builtin_abort ();
+ }
+ if (ccnt != 6 || dcnt != 6 || cccnt || tccnt != 6)
+ __builtin_abort ();
+ }
+ if (ccnt != 6 || dcnt != 12 || cccnt || tccnt != 6)
+ __builtin_abort ();
+
+ {
+ A a[6];
+ if (ccnt != 12 || dcnt != 12 || cccnt || tccnt != 6)
+ __builtin_abort ();
+ {
+ auto [b,c,d,e,f,g] { a }; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ if (ccnt != 12 || dcnt != 12 || cccnt != 6 || tccnt != 6)
+ __builtin_abort ();
+ b.a++;
+ c.a += 2;
+ f.a += 3;
+ if (b.a != 7 || c.a != 8 || d.a != 6 || e.a != 6 || f.a != 9 || g.a != 6)
+ __builtin_abort ();
+ if (&b == &a[0] || &c == &a[1] || &d == &a[2] || &e == &a[3] || &f == &a[4] || &g == &a[5])
+ __builtin_abort ();
+ {
+ auto&[ h, i, j, k, l, m ] {a}; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ if (ccnt != 12 || dcnt != 12 || cccnt != 6 || tccnt != 6)
+ __builtin_abort ();
+ j.a += 4;
+ k.a += 5;
+ m.a += 6;
+ if (a[0].a != 6 || a[1].a != 6 || a[2].a != 10 || a[3].a != 11 || a[4].a != 6 || a[5].a != 12)
+ __builtin_abort ();
+ if (&h != &a[0] || &i != &a[1] || &j != &a[2] || &k != &a[3] || &l != &a[4] || &m != &a[5])
+ __builtin_abort ();
+ }
+ if (ccnt != 12 || dcnt != 12 || cccnt != 6 || tccnt != 6)
+ __builtin_abort ();
+ }
+ if (ccnt != 12 || dcnt != 18 || cccnt != 6 || tccnt != 6)
+ __builtin_abort ();
+ }
+ if (ccnt != 12 || dcnt != 24 || cccnt != 6 || tccnt != 6)
+ __builtin_abort ();
+}
--- /dev/null
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+int a[2] = { 1, 2 };
+int b[2] = { 4, 5 };
+struct S { int a; signed char b; float c; } sa = { 6, 7, 8.0f };
+S sb = { 9, 10, 11.0f };
+
+template <typename T, typename U>
+void
+foo (T &x, U &y)
+{
+ auto & [ c, d ] = a; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto [ e, f ] = a; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto [ g, h, i ] = sa; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto & [ j, k, l ] = sa; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto & [ m, n ] = x; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto [ o, p ] = x; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto [ q, r, s ] = y; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ auto & [ t, u, v ] = y; // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ c += 1;
+ e += 2;
+ g += 3;
+ j += 4;
+ m += 5;
+ o += 6;
+ q += 7;
+ t += 8;
+ if (c != 2 || &c != &a[0]
+ || d != 2 || &d != &a[1]
+ || e != 3 || &e == &a[0]
+ || f != 2 || &f == &a[1]
+ || g != 9 || &g == &sa.a
+ || h != 7 || &h == &sa.b
+ || i != 8.0f || &i == &sa.c
+ || j != 10 || &j != &sa.a
+ || k != 7 || &k != &sa.b
+ || l != 8.0f || &l != &sa.c
+ || m != 9 || &m != &b[0]
+ || n != 5 || &n != &b[1]
+ || o != 10 || &o == &b[0]
+ || p != 5 || &p == &b[1]
+ || q != 16 || &q == &sb.a
+ || r != 10 || &r == &sb.b
+ || s != 11.0f || &s == &sb.c
+ || t != 17 || &t != &sb.a
+ || u != 10 || &u != &sb.b
+ || v != 11.0f || &v != &sb.c
+ || a[0] != 2 || a[1] != 2
+ || sa.a != 10 || sa.b != 7 || sa.c != 8.0f
+ || b[0] != 9 || b[1] != 5
+ || sb.a != 17 || sb.b != 10 || sb.c != 11.0f)
+ __builtin_abort ();
+}
+
+int
+main ()
+{
+ foo (b, sb);
+}
--- /dev/null
+// { dg-do run { target c++11 } }
+// { dg-options "" }
+
+struct A { int i; long long j; } a[64];
+A b[32];
+
+template <typename T>
+void
+foo (T &b)
+{
+ int i = 0;
+ for (auto &x : a)
+ {
+ x.i = i;
+ x.j = 2 * i++;
+ }
+ for (auto & [ x, y ] : a) // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ {
+ x += 2;
+ y += 3;
+ }
+ i = 0;
+ for (const auto [ u, v ] : a) // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ {
+ if (u != i + 2 || v != 2 * i++ + 3)
+ __builtin_abort ();
+ }
+ i = 0;
+ for (auto [ x, y ] : a) // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ {
+ x += 4;
+ y += 5;
+ if (x != i + 6 || y != 2 * i++ + 8)
+ __builtin_abort ();
+ }
+ i = 0;
+ for (const auto x : a)
+ {
+ if (x.i != i + 2 || x.j != 2 * i++ + 3)
+ __builtin_abort ();
+ }
+ i = 0;
+ for (auto &x : b)
+ {
+ x.i = i;
+ x.j = 2 * i++;
+ }
+ for (auto & [ x, y ] : b) // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ {
+ x -= 2;
+ y -= 3;
+ }
+ i = 0;
+ for (const auto [ u, v ] : b) // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ {
+ if (u != i - 2 || v != 2 * i++ - 3)
+ __builtin_abort ();
+ }
+ i = 0;
+ for (auto [ x, y ] : b) // { dg-warning "decomposition declaration only available with" "" { target c++14_down } }
+ {
+ x -= 4;
+ y -= 5;
+ if (x != i - 6 || y != 2 * i++ - 8)
+ __builtin_abort ();
+ }
+ i = 0;
+ for (const auto x : b)
+ {
+ if (x.i != i - 2 || x.j != 2 * i++ - 3)
+ __builtin_abort ();
+ }
+}
+
+int
+main ()
+{
+ foo (b);
+ for (int i = 0; i < 64; i++)
+ {
+ if (a[i].i != i + 2 || a[i].j != 2 * i + 3)
+ __builtin_abort ();
+ if (i >= 32)
+ continue;
+ if (b[i].i != i - 2 || b[i].j != 2 * i - 3)
+ __builtin_abort ();
+ }
+}
--- /dev/null
+// { dg-do run }
+// { dg-options -std=c++1z }
+
+#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
+
+namespace std {
+ template<typename T> struct tuple_size;
+ template<int, typename> struct tuple_element;
+}
+
+struct A {
+ int i;
+ template <int I> int& get() { return i; }
+};
+
+template<> struct std::tuple_size<A> { static const int value = 2; };
+template<int I> struct std::tuple_element<I,A> { using type = int; };
+
+struct B {
+ int i;
+};
+template <int I> int& get(B&& b) { return b.i; }
+template <int I> int& get(B& b) { return b.i; }
+
+template<> struct std::tuple_size<B> { static const int value = 2; };
+template<int I> struct std::tuple_element<I,B> { using type = int; };
+
+int main()
+{
+ {
+ A a = { 42 };
+ auto& [ x, y ] = a;
+ assert (&x == &y && &x == &a.i && x == 42);
+
+ auto [ x2, y2 ] = a;
+ assert (&x2 == &y2 && &x2 != &a.i && x2 == 42);
+ }
+
+ {
+ B b = { 42 };
+ auto& [ x, y ] = b;
+ assert (&x == &y && &x == &b.i && x == 42);
+
+ auto [ x2, y2 ] = b;
+ assert (&x2 == &y2 && &x2 != &b.i && x2 == 42);
+ }
+}