1 // RUN: %clang_cc1 -std=c++1z -verify %s
2
3 using size_t = decltype(sizeof(0));
4
5 struct A { int x, y; };
6 struct B { int x, y; };
7
no_tuple_size_1()8 void no_tuple_size_1() { auto [x, y] = A(); } // ok, decompose elementwise
9
10 namespace std { template<typename T> struct tuple_size; }
no_tuple_size_2()11 void no_tuple_size_2() { auto [x, y] = A(); } // ok, decompose elementwise
12
13 struct Bad1 { int a, b; };
14 template<> struct std::tuple_size<Bad1> {};
no_tuple_size_3()15 void no_tuple_size_3() { auto [x, y] = Bad1(); } // expected-error {{cannot decompose this type; 'std::tuple_size<Bad1>::value' is not a valid integral constant expression}}
16
17 struct Bad2 {};
18 template<> struct std::tuple_size<Bad2> { const int value = 5; };
no_tuple_size_4()19 void no_tuple_size_4() { auto [x, y] = Bad2(); } // expected-error {{cannot decompose this type; 'std::tuple_size<Bad2>::value' is not a valid integral constant expression}}
20
21 template<> struct std::tuple_size<A> { static const int value = 3; };
22 template<> struct std::tuple_size<B> { enum { value = 3 }; };
23
no_get_1()24 void no_get_1() {
25 {
26 auto [a0, a1] = A(); // expected-error {{decomposes into 3 elements}}
27 auto [b0, b1] = B(); // expected-error {{decomposes into 3 elements}}
28 }
29 auto [a0, a1, a2] = A(); // expected-error {{undeclared identifier 'get'}} expected-note {{in implicit initialization of binding declaration 'a0'}}
30 }
31
32 int get(A);
33
no_get_2()34 void no_get_2() {
35 // FIXME: This diagnostic is not great.
36 auto [a0, a1, a2] = A(); // expected-error {{undeclared identifier 'get'}} expected-note {{in implicit initialization of binding declaration 'a0'}}
37 }
38
39 template<int> float &get(A);
40
no_tuple_element_1()41 void no_tuple_element_1() {
42 auto [a0, a1, a2] = A(); // expected-error-re {{'std::tuple_element<0U{{L*}}, A>::type' does not name a type}} expected-note {{in implicit}}
43 }
44
45 namespace std { template<size_t, typename> struct tuple_element; } // expected-note 2{{here}}
46
no_tuple_element_2()47 void no_tuple_element_2() {
48 auto [a0, a1, a2] = A(); // expected-error {{implicit instantiation of undefined template 'std::tuple_element<0, A>'}} expected-note {{in implicit}}
49 }
50
51 template<> struct std::tuple_element<0, A> { typedef float type; };
52
no_tuple_element_3()53 void no_tuple_element_3() {
54 auto [a0, a1, a2] = A(); // expected-error {{implicit instantiation of undefined template 'std::tuple_element<1, A>'}} expected-note {{in implicit}}
55 }
56
57 template<> struct std::tuple_element<1, A> { typedef float &type; };
58 template<> struct std::tuple_element<2, A> { typedef const float &type; };
59
60 template<int N> auto get(B) -> int (&)[N + 1];
61 template<int N> struct std::tuple_element<N, B> { typedef int type[N +1 ]; };
62
63 template<typename T> struct std::tuple_size<const T> : std::tuple_size<T> {};
64 template<size_t N, typename T> struct std::tuple_element<N, const T> {
65 typedef const typename std::tuple_element<N, T>::type type;
66 };
67
referenced_type()68 void referenced_type() {
69 auto [a0, a1, a2] = A();
70 auto [b0, b1, b2] = B();
71
72 A a;
73 B b;
74 auto &[ar0, ar1, ar2] = a;
75 auto &[br0, br1, br2] = b;
76
77 auto &&[arr0, arr1, arr2] = A();
78 auto &&[brr0, brr1, brr2] = B();
79
80 const auto &[acr0, acr1, acr2] = A();
81 const auto &[bcr0, bcr1, bcr2] = B();
82
83
84 using Float = float;
85 using Float = decltype(a0);
86 using Float = decltype(ar0);
87 using Float = decltype(arr0);
88
89 using ConstFloat = const float;
90 using ConstFloat = decltype(acr0);
91
92 using FloatRef = float&;
93 using FloatRef = decltype(a1);
94 using FloatRef = decltype(ar1);
95 using FloatRef = decltype(arr1);
96 using FloatRef = decltype(acr1);
97
98 using ConstFloatRef = const float&;
99 using ConstFloatRef = decltype(a2);
100 using ConstFloatRef = decltype(ar2);
101 using ConstFloatRef = decltype(arr2);
102 using ConstFloatRef = decltype(acr2);
103
104
105 using Int1 = int[1];
106 using Int1 = decltype(b0);
107 using Int1 = decltype(br0);
108 using Int1 = decltype(brr0);
109
110 using ConstInt1 = const int[1];
111 using ConstInt1 = decltype(bcr0);
112
113 using Int2 = int[2];
114 using Int2 = decltype(b1);
115 using Int2 = decltype(br1);
116 using Int2 = decltype(brr1);
117
118 using ConstInt2 = const int[2];
119 using ConstInt2 = decltype(bcr1);
120
121 using Int3 = int[3];
122 using Int3 = decltype(b2);
123 using Int3 = decltype(br2);
124 using Int3 = decltype(brr2);
125
126 using ConstInt3 = const int[3];
127 using ConstInt3 = decltype(bcr2);
128 }
129
130 struct C { template<int> int get(); };
131 template<> struct std::tuple_size<C> { static const int value = 1; };
132 template<> struct std::tuple_element<0, C> { typedef int type; };
133
member_get()134 int member_get() {
135 auto [c] = C();
136 using T = int;
137 using T = decltype(c);
138 return c;
139 }
140
141 struct D { template<int> struct get {}; }; // expected-note {{declared here}}
142 template<> struct std::tuple_size<D> { static const int value = 1; };
143 template<> struct std::tuple_element<0, D> { typedef D::get<0> type; };
member_get_class_template()144 void member_get_class_template() {
145 auto [d] = D(); // expected-error {{cannot refer to member 'get' in 'D' with '.'}} expected-note {{in implicit init}}
146 }
147
148 struct E { int get(); };
149 template<> struct std::tuple_size<E> { static const int value = 1; };
150 template<> struct std::tuple_element<0, E> { typedef int type; };
member_get_non_template()151 void member_get_non_template() {
152 // FIXME: This diagnostic is not very good.
153 auto [e] = E(); // expected-error {{no member named 'get'}} expected-note {{in implicit init}}
154 }
155
156 namespace ADL {
157 struct X {};
158 };
159 template<int> int get(ADL::X);
160 template<> struct std::tuple_size<ADL::X> { static const int value = 1; };
161 template<> struct std::tuple_element<0, ADL::X> { typedef int type; };
adl_only_bad()162 void adl_only_bad() {
163 auto [x] = ADL::X(); // expected-error {{undeclared identifier 'get'}} expected-note {{in implicit init}}
164 }
165
166 template<typename ElemType, typename GetTypeLV, typename GetTypeRV>
167 struct wrap {
168 template<size_t> GetTypeLV get() &;
169 template<size_t> GetTypeRV get() &&;
170 };
171 template<typename ET, typename GTL, typename GTR>
172 struct std::tuple_size<wrap<ET, GTL, GTR>> {
173 static const int value = 1;
174 };
175 template<typename ET, typename GTL, typename GTR>
176 struct std::tuple_element<0, wrap<ET, GTL, GTR>> {
177 using type = ET;
178 };
179
180 template<typename T> T &lvalue();
181
test_value_category()182 void test_value_category() {
183 // If the declared variable is an lvalue reference, the operand to get is an
184 // lvalue. Otherwise it's an xvalue.
185 { auto [a] = wrap<int, void, int>(); }
186 { auto &[a] = lvalue<wrap<int, int, void>>(); }
187 { auto &&[a] = wrap<int, void, int>(); }
188 // If the initializer (call to get) is an lvalue, the binding is an lvalue
189 // reference to the element type. Otherwise it's an rvalue reference to the
190 // element type.
191 { auto [a] = wrap<int, void, int&>(); }
192 { auto [a] = wrap<int&, void, int&>(); }
193 { auto [a] = wrap<int&&, void, int&>(); } // ok, reference collapse to int&
194
195 { auto [a] = wrap<int, void, int&&>(); }
196 { auto [a] = wrap<int&, void, int&&>(); } // expected-error {{non-const lvalue reference to type 'int' cannot bind}} expected-note {{in implicit}}
197 { auto [a] = wrap<const int&, void, int&&>(); }
198 { auto [a] = wrap<int&&, void, int&&>(); }
199
200 { auto [a] = wrap<int, void, float&>(); } // expected-error {{cannot bind}} expected-note {{implicit}}
201 { auto [a] = wrap<const int, void, float&>(); } // ok, const int &a can bind to float
202 { auto [a] = wrap<int, void, float>(); } // ok, int &&a can bind to float
203 }
204
205 namespace constant {
206 struct Q {};
get(Q &&)207 template<int N> constexpr int get(Q &&) { return N * N; }
208 }
209 template<> struct std::tuple_size<constant::Q> { static const int value = 3; };
210 template<int N> struct std::tuple_element<N, constant::Q> { typedef int type; };
211 namespace constant {
212 Q q;
213 // This creates and lifetime-extends a temporary to hold the result of each get() call.
214 auto [a, b, c] = q; // expected-note {{temporary}}
215 static_assert(a == 0); // expected-error {{constant expression}} expected-note {{temporary}}
216
f()217 constexpr bool f() {
218 auto [a, b, c] = q;
219 return a == 0 && b == 1 && c == 4;
220 }
221 static_assert(f());
222
g()223 constexpr int g() {
224 int *p = nullptr;
225 {
226 auto [a, b, c] = q;
227 p = &c;
228 }
229 return *p; // expected-note {{read of object outside its lifetime}}
230 }
231 static_assert(g() == 4); // expected-error {{constant}} expected-note {{in call to 'g()'}}
232 }
233