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 | |
8 | void no_tuple_size_1() { auto [x, y] = A(); } // ok, decompose elementwise |
9 | |
10 | namespace std { template<typename T> struct tuple_size; } |
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> {}; |
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; }; |
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 | |
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 | |
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); // expected-note 2 {{no known conversion}} |
40 | |
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 | |
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 | |
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]; // expected-note 2 {{no known conversion}} |
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 | |
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 | |
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 { |
142 | // FIXME: Emit a note here explaining why this was ignored. |
143 | template<int> struct get {}; |
144 | }; |
145 | template<> struct std::tuple_size<D> { static const int value = 1; }; |
146 | template<> struct std::tuple_element<0, D> { typedef D::get<0> type; }; |
147 | void member_get_class_template() { |
148 | auto [d] = D(); // expected-error {{no matching function for call to 'get'}} expected-note {{in implicit init}} |
149 | } |
150 | |
151 | struct E { |
152 | // FIXME: Emit a note here explaining why this was ignored. |
153 | int get(); |
154 | }; |
155 | template<> struct std::tuple_size<E> { static const int value = 1; }; |
156 | template<> struct std::tuple_element<0, E> { typedef int type; }; |
157 | void member_get_non_template() { |
158 | // FIXME: This diagnostic is not very good. |
159 | auto [e] = E(); // expected-error {{no matching function for call to 'get'}} expected-note {{in implicit init}} |
160 | } |
161 | |
162 | namespace ADL { |
163 | struct X {}; |
164 | }; |
165 | template<int> int get(ADL::X); |
166 | template<> struct std::tuple_size<ADL::X> { static const int value = 1; }; |
167 | template<> struct std::tuple_element<0, ADL::X> { typedef int type; }; |
168 | void adl_only_bad() { |
169 | auto [x] = ADL::X(); // expected-error {{undeclared identifier 'get'}} expected-note {{in implicit init}} |
170 | } |
171 | |
172 | template<typename ElemType, typename GetTypeLV, typename GetTypeRV> |
173 | struct wrap { |
174 | template<size_t> GetTypeLV get() &; |
175 | template<size_t> GetTypeRV get() &&; |
176 | }; |
177 | template<typename ET, typename GTL, typename GTR> |
178 | struct std::tuple_size<wrap<ET, GTL, GTR>> { |
179 | static const int value = 1; |
180 | }; |
181 | template<typename ET, typename GTL, typename GTR> |
182 | struct std::tuple_element<0, wrap<ET, GTL, GTR>> { |
183 | using type = ET; |
184 | }; |
185 | |
186 | template<typename T> T &lvalue(); |
187 | |
188 | void test_value_category() { |
189 | // If the declared variable is an lvalue reference, the operand to get is an |
190 | // lvalue. Otherwise it's an xvalue. |
191 | { auto [a] = wrap<int, void, int>(); } |
192 | { auto &[a] = lvalue<wrap<int, int, void>>(); } |
193 | { auto &&[a] = wrap<int, void, int>(); } |
194 | // If the initializer (call to get) is an lvalue, the binding is an lvalue |
195 | // reference to the element type. Otherwise it's an rvalue reference to the |
196 | // element type. |
197 | { auto [a] = wrap<int, void, int&>(); } |
198 | { auto [a] = wrap<int&, void, int&>(); } |
199 | { auto [a] = wrap<int&&, void, int&>(); } // ok, reference collapse to int& |
200 | |
201 | { auto [a] = wrap<int, void, int&&>(); } |
202 | { auto [a] = wrap<int&, void, int&&>(); } // expected-error {{non-const lvalue reference to type 'int' cannot bind}} expected-note {{in implicit}} |
203 | { auto [a] = wrap<const int&, void, int&&>(); } |
204 | { auto [a] = wrap<int&&, void, int&&>(); } |
205 | |
206 | { auto [a] = wrap<int, void, float&>(); } // expected-error {{cannot bind}} expected-note {{implicit}} |
207 | { auto [a] = wrap<const int, void, float&>(); } // ok, const int &a can bind to float |
208 | { auto [a] = wrap<int, void, float>(); } // ok, int &&a can bind to float |
209 | } |
210 | |
211 | namespace constant { |
212 | struct Q {}; |
213 | template<int N> constexpr int get(Q &&) { return N * N; } |
214 | } |
215 | template<> struct std::tuple_size<constant::Q> { static const int value = 3; }; |
216 | template<int N> struct std::tuple_element<N, constant::Q> { typedef int type; }; |
217 | namespace constant { |
218 | Q q; |
219 | // This creates and lifetime-extends a temporary to hold the result of each get() call. |
220 | auto [a, b, c] = q; // expected-note {{temporary}} |
221 | static_assert(a == 0); // expected-error {{constant expression}} expected-note {{temporary}} |
222 | |
223 | constexpr bool f() { |
224 | auto [a, b, c] = q; |
225 | return a == 0 && b == 1 && c == 4; |
226 | } |
227 | static_assert(f()); |
228 | |
229 | constexpr int g() { |
230 | int *p = nullptr; |
231 | { |
232 | auto [a, b, c] = q; |
233 | p = &c; |
234 | } |
235 | return *p; // expected-note {{read of object outside its lifetime}} |
236 | } |
237 | static_assert(g() == 4); // expected-error {{constant}} expected-note {{in call to 'g()'}} |
238 | } |
239 | |
240 | // P0961R1 |
241 | struct InvalidMemberGet { |
242 | int get(); |
243 | template <class T> int get(); |
244 | struct get {}; |
245 | }; |
246 | template <> struct std::tuple_size<InvalidMemberGet> { static constexpr size_t value = 1; }; |
247 | template <> struct std::tuple_element<0, InvalidMemberGet> { typedef float type; }; |
248 | template <size_t> float get(InvalidMemberGet) { return 0; } |
249 | int f() { |
250 | InvalidMemberGet img; |
251 | auto [x] = img; |
252 | typedef decltype(x) same_as_float; |
253 | typedef float same_as_float; |
254 | } |
255 | |
256 | struct ValidMemberGet { |
257 | int get(); |
258 | template <class T> int get() { return 0; } |
259 | template <size_t N> float get() { return 0; } |
260 | }; |
261 | template <> struct std::tuple_size<ValidMemberGet> { static constexpr size_t value = 1; }; |
262 | template <> struct std::tuple_element<0, ValidMemberGet> { typedef float type; }; |
263 | // Don't use this one; we should use the member get. |
264 | template <size_t N> int get(ValidMemberGet) { static_assert(N && false, ""); } |
265 | int f2() { |
266 | ValidMemberGet img; |
267 | auto [x] = img; |
268 | typedef decltype(x) same_as_float; |
269 | typedef float same_as_float; |
270 | } |
271 | |
272 | struct Base1 { |
273 | int get(); // expected-note{{member found by ambiguous name lookup}} |
274 | }; |
275 | struct Base2 { |
276 | template<int> int get(); // expected-note{{member found by ambiguous name lookup}} |
277 | }; |
278 | struct Derived : Base1, Base2 {}; |
279 | |
280 | template <> struct std::tuple_size<Derived> { static constexpr size_t value = 1; }; |
281 | template <> struct std::tuple_element<0, Derived> { typedef int type; }; |
282 | |
283 | auto [x] = Derived(); // expected-error{{member 'get' found in multiple base classes of different types}} |
284 | |
285 | struct Base { |
286 | template<int> int get(); |
287 | }; |
288 | struct UsingGet : Base { |
289 | using Base::get; |
290 | }; |
291 | |
292 | template <> struct std::tuple_size<UsingGet> { |
293 | static constexpr size_t value = 1; |
294 | }; |
295 | template <> struct std::tuple_element<0, UsingGet> { typedef int type; }; |
296 | |
297 | auto [y] = UsingGet(); |
298 | |