1 | // RUN: %clang_cc1 -std=c++11 -fms-extensions -fno-rtti -emit-llvm -o %t.ll -fdump-vtable-layouts %s -triple=i386-pc-win32 >%t |
2 | // RUN: FileCheck %s < %t |
3 | // RUN: FileCheck --check-prefix=MANGLING %s < %t.ll |
4 | |
5 | struct Empty { }; |
6 | |
7 | struct A { |
8 | virtual void f(); |
9 | virtual void z(); // Useful to check there are no thunks for f() when appropriate. |
10 | }; |
11 | |
12 | struct B { |
13 | virtual void g(); |
14 | }; |
15 | |
16 | struct C: virtual A { |
17 | // CHECK-LABEL: VFTable for 'A' in 'C' (2 entries) |
18 | // CHECK-NEXT: 0 | void C::f() |
19 | // CHECK-NEXT: 1 | void A::z() |
20 | |
21 | // CHECK-LABEL: VFTable indices for 'C' (1 entry) |
22 | // CHECK-NEXT: vbtable index 1, vfptr at offset 0 |
23 | // CHECK-NEXT: 0 | void C::f() |
24 | |
25 | // MANGLING-DAG: @"??_7C@@6B@" |
26 | |
27 | virtual void f() {} |
28 | }; |
29 | |
30 | C c; |
31 | void use(C *obj) { obj->f(); } |
32 | |
33 | struct D: virtual A { |
34 | // CHECK-LABEL: VFTable for 'D' (1 entry). |
35 | // CHECK-NEXT: 0 | void D::h() |
36 | |
37 | // CHECK-LABEL: VFTable for 'A' in 'D' (2 entries). |
38 | // CHECK-NEXT: 0 | void D::f() |
39 | // CHECK-NEXT: 1 | void A::z() |
40 | |
41 | // CHECK-LABEL: VFTable indices for 'D' (2 entries). |
42 | // CHECK-NEXT: via vfptr at offset 0 |
43 | // CHECK-NEXT: 0 | void D::h() |
44 | // CHECK-NEXT: via vbtable index 1, vfptr at offset 0 |
45 | // CHECK-NEXT: 0 | void D::f() |
46 | |
47 | // MANGLING-DAG: @"??_7D@@6B0@@" |
48 | // MANGLING-DAG: @"??_7D@@6BA@@@" |
49 | |
50 | virtual void f(); |
51 | virtual void h(); |
52 | }; |
53 | |
54 | D d; |
55 | void use(D *obj) { obj->h(); } |
56 | |
57 | namespace Test1 { |
58 | |
59 | struct X { int x; }; |
60 | |
61 | // X and A get reordered in the layout since X doesn't have a vfptr while A has. |
62 | struct Y : X, A { }; |
63 | // MANGLING-DAG: @"??_7Y@Test1@@6B@" |
64 | |
65 | struct Z : virtual Y { |
66 | Z(); |
67 | // CHECK-LABEL: VFTable for 'A' in 'Test1::Y' in 'Test1::Z' (2 entries). |
68 | // CHECK-NEXT: 0 | void A::f() |
69 | // CHECK-NEXT: 1 | void A::z() |
70 | |
71 | // CHECK-NOT: VFTable indices for 'Test1::Z' |
72 | |
73 | // MANGLING-DAG: @"??_7Z@Test1@@6B@" |
74 | }; |
75 | |
76 | Z::Z() {} |
77 | } |
78 | |
79 | namespace Test2 { |
80 | |
81 | struct X: virtual A, virtual B { |
82 | // CHECK-LABEL: VFTable for 'Test2::X' (1 entry). |
83 | // CHECK-NEXT: 0 | void Test2::X::h() |
84 | |
85 | // CHECK-LABEL: VFTable for 'A' in 'Test2::X' (2 entries). |
86 | // CHECK-NEXT: 0 | void A::f() |
87 | // CHECK-NEXT: 1 | void A::z() |
88 | |
89 | // CHECK-LABEL: VFTable for 'B' in 'Test2::X' (1 entry). |
90 | // CHECK-NEXT: 0 | void B::g() |
91 | |
92 | // CHECK-LABEL: VFTable indices for 'Test2::X' (1 entry). |
93 | // CHECK-NEXT: 0 | void Test2::X::h() |
94 | |
95 | // MANGLING-DAG: @"??_7X@Test2@@6B01@@" |
96 | // MANGLING-DAG: @"??_7X@Test2@@6BA@@@" |
97 | // MANGLING-DAG: @"??_7X@Test2@@6BB@@@" |
98 | |
99 | virtual void h(); |
100 | }; |
101 | |
102 | X x; |
103 | void use(X *obj) { obj->h(); } |
104 | } |
105 | |
106 | namespace Test3 { |
107 | |
108 | struct X : virtual A { |
109 | // MANGLING-DAG: @"??_7X@Test3@@6B@" |
110 | }; |
111 | |
112 | struct Y: virtual X { |
113 | Y(); |
114 | // CHECK-LABEL: VFTable for 'A' in 'Test3::X' in 'Test3::Y' (2 entries). |
115 | // CHECK-NEXT: 0 | void A::f() |
116 | // CHECK-NEXT: 1 | void A::z() |
117 | |
118 | // CHECK-NOT: VFTable indices for 'Test3::Y' |
119 | |
120 | // MANGLING-DAG: @"??_7Y@Test3@@6B@" |
121 | }; |
122 | |
123 | Y::Y() {} |
124 | } |
125 | |
126 | namespace Test4 { |
127 | |
128 | struct X: virtual C { |
129 | X(); |
130 | // This one's interesting. C::f expects (A*) to be passed as 'this' and does |
131 | // ECX-=4 to cast to (C*). In X, C and A vbases are reordered, so the thunk |
132 | // should pass a pointer to the end of X in order |
133 | // for ECX-=4 to point at the C part. |
134 | |
135 | // CHECK-LABEL: VFTable for 'A' in 'C' in 'Test4::X' (2 entries). |
136 | // CHECK-NEXT: 0 | void C::f() |
137 | // CHECK-NEXT: [this adjustment: 8 non-virtual] |
138 | // CHECK-NEXT: 1 | void A::z() |
139 | |
140 | // CHECK-LABEL: Thunks for 'void C::f()' (1 entry). |
141 | // CHECK-NEXT: 0 | [this adjustment: 8 non-virtual] |
142 | |
143 | // CHECK-NOT: VFTable indices for 'Test4::X' |
144 | |
145 | // MANGLING-DAG: @"??_7X@Test4@@6B@" |
146 | |
147 | // Also check the mangling of the thunk. |
148 | // MANGLING-DAG: define linkonce_odr dso_local x86_thiscallcc void @"?f@C@@WPPPPPPPI@AEXXZ" |
149 | }; |
150 | |
151 | X::X() {} |
152 | } |
153 | |
154 | namespace Test5 { |
155 | |
156 | // New methods are added to the base's vftable. |
157 | struct X : A { |
158 | // MANGLING-DAG: @"??_7X@Test5@@6B@" |
159 | virtual void g(); |
160 | }; |
161 | |
162 | struct Y : virtual X { |
163 | // CHECK-LABEL: VFTable for 'Test5::Y' (1 entry). |
164 | // CHECK-NEXT: 0 | void Test5::Y::h() |
165 | |
166 | // CHECK-LABEL: VFTable for 'A' in 'Test5::X' in 'Test5::Y' (3 entries). |
167 | // CHECK-NEXT: 0 | void A::f() |
168 | // CHECK-NEXT: 1 | void A::z() |
169 | // CHECK-NEXT: 2 | void Test5::X::g() |
170 | |
171 | // CHECK-LABEL: VFTable indices for 'Test5::Y' (1 entry). |
172 | // CHECK-NEXT: 0 | void Test5::Y::h() |
173 | |
174 | // MANGLING-DAG: @"??_7Y@Test5@@6B01@@" |
175 | // MANGLING-DAG: @"??_7Y@Test5@@6BX@1@@" |
176 | |
177 | virtual void h(); |
178 | }; |
179 | |
180 | Y y; |
181 | void use(Y *obj) { obj->h(); } |
182 | } |
183 | |
184 | namespace Test6 { |
185 | |
186 | struct X : A, virtual Empty { |
187 | X(); |
188 | // CHECK-LABEL: VFTable for 'A' in 'Test6::X' (2 entries). |
189 | // CHECK-NEXT: 0 | void A::f() |
190 | // CHECK-NEXT: 1 | void A::z() |
191 | |
192 | // CHECK-NOT: VFTable indices for 'Test6::X' |
193 | |
194 | // MANGLING-DAG: @"??_7X@Test6@@6B@" |
195 | }; |
196 | |
197 | X::X() {} |
198 | } |
199 | |
200 | namespace Test7 { |
201 | |
202 | struct X : C { |
203 | // MANGLING-DAG: @"??_7X@Test7@@6B@" |
204 | }; |
205 | |
206 | struct Y : virtual X { |
207 | Y(); |
208 | // CHECK-LABEL: VFTable for 'A' in 'C' in 'Test7::X' in 'Test7::Y' (2 entries). |
209 | // CHECK-NEXT: 0 | void C::f() |
210 | // CHECK-NEXT: [this adjustment: 8 non-virtual] |
211 | // CHECK-NEXT: 1 | void A::z() |
212 | |
213 | // CHECK-LABEL: Thunks for 'void C::f()' (1 entry). |
214 | // CHECK-NEXT: 0 | [this adjustment: 8 non-virtual] |
215 | |
216 | // CHECK-NOT: VFTable indices for 'Test7::Y' |
217 | |
218 | // MANGLING-DAG: @"??_7Y@Test7@@6B@" |
219 | }; |
220 | |
221 | Y::Y() {} |
222 | } |
223 | |
224 | namespace Test8 { |
225 | |
226 | // This is a typical diamond inheritance with a shared 'A' vbase. |
227 | struct X : D, C { |
228 | // CHECK-LABEL: VFTable for 'D' in 'Test8::X' (1 entry). |
229 | // CHECK-NEXT: 0 | void D::h() |
230 | |
231 | // CHECK-LABEL: VFTable for 'A' in 'D' in 'Test8::X' (2 entries). |
232 | // CHECK-NEXT: 0 | void Test8::X::f() |
233 | // CHECK-NEXT: 1 | void A::z() |
234 | |
235 | // CHECK-LABEL: VFTable indices for 'Test8::X' (1 entry). |
236 | // CHECK-NEXT: via vbtable index 1, vfptr at offset 0 |
237 | // CHECK-NEXT: 0 | void Test8::X::f() |
238 | |
239 | // MANGLING-DAG: @"??_7X@Test8@@6BA@@@" |
240 | // MANGLING-DAG: @"??_7X@Test8@@6BD@@@" |
241 | |
242 | virtual void f(); |
243 | }; |
244 | |
245 | X x; |
246 | void use(X *obj) { obj->f(); } |
247 | |
248 | // Another diamond inheritance which led to AST crashes. |
249 | struct Y : virtual A {}; |
250 | |
251 | struct Z : Y, C { |
252 | // CHECK-LABEL: VFTable for 'A' in 'Test8::Y' in 'Test8::Z' (2 entries). |
253 | // CHECK-NEXT: 0 | void Test8::Z::f() |
254 | // CHECK-NEXT: 1 | void A::z() |
255 | |
256 | // CHECK-LABEL: VFTable indices for 'Test8::Z' (1 entry). |
257 | // CHECK-NEXT: via vbtable index 1, vfptr at offset 0 |
258 | // CHECK-NEXT: 0 | void Test8::Z::f() |
259 | virtual void f(); |
260 | }; |
261 | Z z; |
262 | void use(Z *obj) { obj->f(); } |
263 | |
264 | // Another diamond inheritance which we miscompiled (PR18967). |
265 | struct W : virtual A { |
266 | virtual void bar(); |
267 | }; |
268 | |
269 | struct T : W, C { |
270 | // CHECK-LABEL: VFTable for 'Test8::W' in 'Test8::T' (1 entry) |
271 | // CHECK-NEXT: 0 | void Test8::T::bar() |
272 | |
273 | // CHECK-LABEL: VFTable for 'A' in 'Test8::W' in 'Test8::T' (2 entries) |
274 | // CHECK-NEXT: 0 | void C::f() |
275 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
276 | // CHECK-NEXT: 1 | void A::z() |
277 | |
278 | // CHECK-LABEL: Thunks for 'void C::f()' (1 entry). |
279 | // CHECK-NEXT: 0 | [this adjustment: -4 non-virtual] |
280 | virtual void bar(); |
281 | int field; |
282 | }; |
283 | T t; |
284 | void use(T *obj) { obj->bar(); } |
285 | } |
286 | |
287 | namespace Test9 { |
288 | |
289 | struct X : A { }; |
290 | |
291 | struct Y : virtual X { |
292 | // CHECK-LABEL: VFTable for 'Test9::Y' (1 entry). |
293 | // CHECK-NEXT: 0 | void Test9::Y::h() |
294 | |
295 | // CHECK-LABEL: VFTable for 'A' in 'Test9::X' in 'Test9::Y' (2 entries). |
296 | // CHECK-NEXT: 0 | void A::f() |
297 | // CHECK-NEXT: 1 | void A::z() |
298 | |
299 | // CHECK-LABEL: VFTable indices for 'Test9::Y' (1 entry). |
300 | // CHECK-NEXT: 0 | void Test9::Y::h() |
301 | |
302 | // MANGLING-DAG: @"??_7Y@Test9@@6B01@@" |
303 | // MANGLING-DAG: @"??_7Y@Test9@@6BX@1@@" |
304 | |
305 | virtual void h(); |
306 | }; |
307 | |
308 | Y y; |
309 | void use(Y *obj) { obj->h(); } |
310 | |
311 | struct Z : Y, virtual B { |
312 | Z(); |
313 | // CHECK-LABEL: VFTable for 'Test9::Y' in 'Test9::Z' (1 entry). |
314 | // CHECK-NEXT: 0 | void Test9::Y::h() |
315 | |
316 | // CHECK-LABEL: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' (2 entries). |
317 | // CHECK-NEXT: 0 | void A::f() |
318 | // CHECK-NEXT: 1 | void A::z() |
319 | |
320 | // CHECK-LABEL: VFTable for 'B' in 'Test9::Z' (1 entry). |
321 | // CHECK-NEXT: 0 | void B::g() |
322 | |
323 | // CHECK-NOT: VFTable indices for 'Test9::Z' |
324 | |
325 | // MANGLING-DAG: @"??_7Z@Test9@@6BX@1@@" |
326 | // MANGLING-DAG: @"??_7Z@Test9@@6BY@1@@" |
327 | |
328 | // MANGLING-DAG: @"??_7Z@Test9@@6B@" |
329 | }; |
330 | |
331 | Z::Z() {} |
332 | |
333 | struct W : Z, D, virtual A, virtual B { |
334 | W(); |
335 | // CHECK-LABEL: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::W' (1 entry). |
336 | // CHECK-NEXT: 0 | void Test9::Y::h() |
337 | |
338 | // CHECK-LABEL: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::W' (2 entries). |
339 | // CHECK-NEXT: 0 | void A::f() |
340 | // CHECK-NEXT: 1 | void A::z() |
341 | |
342 | // CHECK-LABEL: VFTable for 'B' in 'Test9::Z' in 'Test9::W' (1 entry). |
343 | // CHECK-NEXT: 0 | void B::g() |
344 | |
345 | // CHECK-LABEL: VFTable for 'D' in 'Test9::W' (1 entry). |
346 | // CHECK-NEXT: 0 | void D::h() |
347 | |
348 | // CHECK-LABEL: VFTable for 'A' in 'D' in 'Test9::W' (2 entries). |
349 | // CHECK-NEXT: 0 | void D::f() |
350 | // CHECK-NEXT: [this adjustment: -8 non-virtual] |
351 | // CHECK-NEXT: 1 | void A::z() |
352 | |
353 | // CHECK-LABEL: Thunks for 'void D::f()' (1 entry). |
354 | // CHECK-NEXT: 0 | [this adjustment: -8 non-virtual] |
355 | |
356 | // CHECK-NOT: VFTable indices for 'Test9::W' |
357 | |
358 | // MANGLING-DAG: @"??_7W@Test9@@6BA@@@" |
359 | // MANGLING-DAG: @"??_7W@Test9@@6BD@@@" |
360 | // MANGLING-DAG: @"??_7W@Test9@@6BX@1@@" |
361 | |
362 | // MANGLING-DAG: @"??_7W@Test9@@6B@" |
363 | // MANGLING-DAG: @"??_7W@Test9@@6BY@1@@" |
364 | }; |
365 | |
366 | W::W() {} |
367 | |
368 | struct T : Z, D, virtual A, virtual B { |
369 | // CHECK-LABEL: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::T' (1 entry). |
370 | // CHECK-NEXT: 0 | void Test9::T::h() |
371 | |
372 | // CHECK-LABEL: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::T' (2 entries). |
373 | // CHECK-NEXT: 0 | void Test9::T::f() |
374 | // CHECK-NEXT: 1 | void Test9::T::z() |
375 | |
376 | // CHECK-LABEL: VFTable for 'B' in 'Test9::Z' in 'Test9::T' (1 entry). |
377 | // CHECK-NEXT: 0 | void Test9::T::g() |
378 | |
379 | // CHECK-LABEL: VFTable for 'D' in 'Test9::T' (1 entry). |
380 | // CHECK-NEXT: 0 | void Test9::T::h() |
381 | // CHECK-NEXT: [this adjustment: -8 non-virtual] |
382 | |
383 | // CHECK-LABEL: Thunks for 'void Test9::T::h()' (1 entry). |
384 | // CHECK-NEXT: 0 | [this adjustment: -8 non-virtual] |
385 | |
386 | // CHECK-LABEL: VFTable for 'A' in 'D' in 'Test9::T' (2 entries). |
387 | // CHECK-NEXT: 0 | void Test9::T::f() |
388 | // CHECK-NEXT: [this adjustment: -8 non-virtual] |
389 | // CHECK-NEXT: 1 | void Test9::T::z() |
390 | // CHECK-NEXT: [this adjustment: -8 non-virtual] |
391 | |
392 | // CHECK-LABEL: Thunks for 'void Test9::T::f()' (1 entry). |
393 | // CHECK-NEXT: 0 | [this adjustment: -8 non-virtual] |
394 | |
395 | // CHECK-LABEL: Thunks for 'void Test9::T::z()' (1 entry). |
396 | // CHECK-NEXT: 0 | [this adjustment: -8 non-virtual] |
397 | |
398 | // CHECK-LABEL: VFTable indices for 'Test9::T' (4 entries). |
399 | // CHECK-NEXT: via vfptr at offset 0 |
400 | // CHECK-NEXT: 0 | void Test9::T::h() |
401 | // CHECK-NEXT: via vbtable index 1, vfptr at offset 0 |
402 | // CHECK-NEXT: 0 | void Test9::T::f() |
403 | // CHECK-NEXT: 1 | void Test9::T::z() |
404 | // CHECK-NEXT: via vbtable index 2, vfptr at offset 0 |
405 | // CHECK-NEXT: 0 | void Test9::T::g() |
406 | |
407 | // MANGLING-DAG: @"??_7T@Test9@@6BA@@@" |
408 | // MANGLING-DAG: @"??_7T@Test9@@6BD@@@" |
409 | // MANGLING-DAG: @"??_7T@Test9@@6BX@1@@" |
410 | |
411 | // MANGLING-DAG: @"??_7T@Test9@@6B@" |
412 | // MANGLING-DAG: @"??_7T@Test9@@6BY@1@@" |
413 | |
414 | virtual void f(); |
415 | virtual void g(); |
416 | virtual void h(); |
417 | virtual void z(); |
418 | }; |
419 | |
420 | T t; |
421 | void use(T *obj) { obj->f(); } |
422 | } |
423 | |
424 | namespace Test10 { |
425 | struct X : virtual C, virtual A { |
426 | // CHECK-LABEL: VFTable for 'A' in 'C' in 'Test10::X' (2 entries). |
427 | // CHECK-NEXT: 0 | void Test10::X::f() |
428 | // CHECK-NEXT: 1 | void A::z() |
429 | |
430 | // CHECK-LABEL: VFTable indices for 'Test10::X' (1 entry). |
431 | // CHECK-NEXT: via vbtable index 1, vfptr at offset 0 |
432 | // CHECK-NEXT: 0 | void Test10::X::f() |
433 | virtual void f(); |
434 | }; |
435 | |
436 | void X::f() {} |
437 | X x; |
438 | void use(X *obj) { obj->f(); } |
439 | } |
440 | |
441 | namespace Test11 { |
442 | struct X : virtual A {}; |
443 | struct Y { virtual void g(); }; |
444 | |
445 | struct Z : virtual X, Y { |
446 | // MANGLING-DAG: @"??_7Z@Test11@@6BY@1@@" |
447 | // MANGLING-DAG: @"??_7Z@Test11@@6BX@1@@" |
448 | }; |
449 | |
450 | Z z; |
451 | |
452 | struct W : virtual X, A {}; |
453 | |
454 | // Used to crash, PR17748. |
455 | W w; |
456 | } |
457 | |
458 | namespace Test12 { |
459 | struct X : B, A { }; |
460 | |
461 | struct Y : X { |
462 | virtual void f(); // Overrides A::f. |
463 | }; |
464 | |
465 | struct Z : virtual Y { |
466 | // CHECK-LABEL: VFTable for 'A' in 'Test12::X' in 'Test12::Y' in 'Test12::Z' (2 entries). |
467 | // CHECK-NEXT: 0 | void Test12::Y::f() |
468 | // CHECK-NEXT: 1 | void A::z() |
469 | |
470 | int z; |
471 | // MANGLING-DAG: @"??_7Z@Test12@@6BA@@@" = {{.*}}@"?f@Y@Test12@@UAEXXZ" |
472 | }; |
473 | |
474 | struct W : Z { |
475 | // CHECK-LABEL: VFTable for 'A' in 'Test12::X' in 'Test12::Y' in 'Test12::Z' in 'Test12::W' (2 entries). |
476 | // CHECK-NEXT: 0 | void Test12::Y::f() |
477 | // CHECK-NEXT: 1 | void A::z() |
478 | W(); |
479 | |
480 | int w; |
481 | // MANGLING-DAG: @"??_7W@Test12@@6BA@@@" = {{.*}}@"?f@Y@Test12@@UAEXXZ" |
482 | }; |
483 | |
484 | W::W() {} |
485 | } |
486 | |
487 | namespace vdtors { |
488 | struct X { |
489 | virtual ~X(); |
490 | virtual void zzz(); |
491 | }; |
492 | |
493 | struct Y : virtual X { |
494 | // CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::Y' (2 entries). |
495 | // CHECK-NEXT: 0 | vdtors::Y::~Y() [scalar deleting] |
496 | // CHECK-NEXT: 1 | void vdtors::X::zzz() |
497 | |
498 | // CHECK-NOT: Thunks for 'vdtors::Y::~Y()' |
499 | virtual ~Y(); |
500 | }; |
501 | |
502 | Y y; |
503 | void use(Y *obj) { delete obj; } |
504 | |
505 | struct Z { |
506 | virtual void z(); |
507 | }; |
508 | |
509 | struct W : Z, X { |
510 | // Implicit virtual dtor. |
511 | }; |
512 | |
513 | struct U : virtual W { |
514 | // CHECK-LABEL: VFTable for 'vdtors::Z' in 'vdtors::W' in 'vdtors::U' (1 entry). |
515 | // CHECK-NEXT: 0 | void vdtors::Z::z() |
516 | |
517 | // CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::U' (2 entries). |
518 | // CHECK-NEXT: 0 | vdtors::U::~U() [scalar deleting] |
519 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
520 | // CHECK-NEXT: 1 | void vdtors::X::zzz() |
521 | |
522 | // CHECK-LABEL: Thunks for 'vdtors::U::~U()' (1 entry). |
523 | // CHECK-NEXT: 0 | [this adjustment: -4 non-virtual] |
524 | |
525 | // CHECK-LABEL: VFTable indices for 'vdtors::U' (1 entry). |
526 | // CHECK-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 -- |
527 | // CHECK-NEXT: 0 | vdtors::U::~U() [scalar deleting] |
528 | virtual ~U(); |
529 | }; |
530 | |
531 | U u; |
532 | void use(U *obj) { delete obj; } |
533 | |
534 | struct V : virtual W { |
535 | // CHECK-LABEL: VFTable for 'vdtors::Z' in 'vdtors::W' in 'vdtors::V' (1 entry). |
536 | // CHECK-NEXT: 0 | void vdtors::Z::z() |
537 | |
538 | // CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::V' (2 entries). |
539 | // CHECK-NEXT: 0 | vdtors::V::~V() [scalar deleting] |
540 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
541 | // CHECK-NEXT: 1 | void vdtors::X::zzz() |
542 | |
543 | // CHECK-LABEL: Thunks for 'vdtors::V::~V()' (1 entry). |
544 | // CHECK-NEXT: 0 | [this adjustment: -4 non-virtual] |
545 | |
546 | // CHECK-LABEL: VFTable indices for 'vdtors::V' (1 entry). |
547 | // CHECK-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 -- |
548 | // CHECK-NEXT: 0 | vdtors::V::~V() [scalar deleting] |
549 | }; |
550 | |
551 | V v; |
552 | void use(V *obj) { delete obj; } |
553 | |
554 | struct T : virtual X { |
555 | virtual ~T(); |
556 | }; |
557 | |
558 | struct P : T, Y { |
559 | // CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::T' in 'vdtors::P' (2 entries). |
560 | // CHECK-NEXT: 0 | vdtors::P::~P() [scalar deleting] |
561 | // CHECK-NEXT: 1 | void vdtors::X::zzz() |
562 | |
563 | // CHECK-NOT: Thunks for 'vdtors::P::~P()' |
564 | virtual ~P(); |
565 | }; |
566 | |
567 | P p; |
568 | void use(P *obj) { delete obj; } |
569 | |
570 | struct Q { |
571 | virtual ~Q(); |
572 | }; |
573 | |
574 | // PR19172: Yet another diamond we miscompiled. |
575 | struct R : virtual Q, X { |
576 | // CHECK-LABEL: VFTable for 'vdtors::Q' in 'vdtors::R' (1 entry). |
577 | // CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting] |
578 | // CHECK-NEXT: [this adjustment: -8 non-virtual] |
579 | |
580 | // CHECK-LABEL: Thunks for 'vdtors::R::~R()' (1 entry). |
581 | // CHECK-NEXT: 0 | [this adjustment: -8 non-virtual] |
582 | |
583 | // CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::R' (2 entries). |
584 | // CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting] |
585 | // CHECK-NEXT: 1 | void vdtors::X::zzz() |
586 | |
587 | // CHECK-LABEL: VFTable indices for 'vdtors::R' (1 entry). |
588 | // CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting] |
589 | virtual ~R(); |
590 | }; |
591 | |
592 | R r; |
593 | void use(R *obj) { delete obj; } |
594 | } |
595 | |
596 | namespace return_adjustment { |
597 | |
598 | struct X : virtual A { |
599 | virtual void f(); |
600 | }; |
601 | |
602 | struct Y : virtual A, virtual X { |
603 | virtual void f(); |
604 | }; |
605 | |
606 | struct Z { |
607 | virtual A* foo(); |
608 | }; |
609 | |
610 | struct W : Z { |
611 | // CHECK-LABEL: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' (2 entries). |
612 | // CHECK-NEXT: 0 | return_adjustment::X *return_adjustment::W::foo() |
613 | // CHECK-NEXT: [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual] |
614 | // CHECK-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo() |
615 | |
616 | // CHECK-LABEL: Thunks for 'return_adjustment::X *return_adjustment::W::foo()' (1 entry). |
617 | // CHECK-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual] |
618 | |
619 | // CHECK-LABEL: VFTable indices for 'return_adjustment::W' (1 entry). |
620 | // CHECK-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo() |
621 | |
622 | virtual X* foo(); |
623 | }; |
624 | |
625 | W w; |
626 | void use(W *obj) { obj->foo(); } |
627 | |
628 | struct T : W { |
629 | // CHECK-LABEL: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' in 'return_adjustment::T' (3 entries). |
630 | // CHECK-NEXT: 0 | return_adjustment::Y *return_adjustment::T::foo() |
631 | // CHECK-NEXT: [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual] |
632 | // CHECK-NEXT: 1 | return_adjustment::Y *return_adjustment::T::foo() |
633 | // CHECK-NEXT: [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual] |
634 | // CHECK-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo() |
635 | |
636 | // CHECK-LABEL: Thunks for 'return_adjustment::Y *return_adjustment::T::foo()' (2 entries). |
637 | // CHECK-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual] |
638 | // CHECK-NEXT: 1 | [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual] |
639 | |
640 | // CHECK-LABEL: VFTable indices for 'return_adjustment::T' (1 entry). |
641 | // CHECK-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo() |
642 | |
643 | virtual Y* foo(); |
644 | }; |
645 | |
646 | T t; |
647 | void use(T *obj) { obj->foo(); } |
648 | |
649 | struct U : virtual A { |
650 | virtual void g(); // adds a vfptr |
651 | }; |
652 | |
653 | struct V : Z { |
654 | // CHECK-LABEL: VFTable for 'return_adjustment::Z' in 'return_adjustment::V' (2 entries). |
655 | // CHECK-NEXT: 0 | return_adjustment::U *return_adjustment::V::foo() |
656 | // CHECK-NEXT: [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual] |
657 | // CHECK-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo() |
658 | |
659 | // CHECK-LABEL: Thunks for 'return_adjustment::U *return_adjustment::V::foo()' (1 entry). |
660 | // CHECK-NEXT: 0 | [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual] |
661 | |
662 | // CHECK-LABEL: VFTable indices for 'return_adjustment::V' (1 entry). |
663 | // CHECK-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo() |
664 | |
665 | virtual U* foo(); |
666 | }; |
667 | |
668 | V v; |
669 | void use(V *obj) { obj->foo(); } |
670 | } |
671 | |
672 | namespace pr17748 { |
673 | struct A { |
674 | virtual void f() {} |
675 | }; |
676 | |
677 | struct B : virtual A { |
678 | B() {} |
679 | }; |
680 | |
681 | struct C : virtual B, A { |
682 | C() {} |
683 | }; |
684 | C c; |
685 | |
686 | // MANGLING-DAG: @"??_7A@pr17748@@6B@" |
687 | // MANGLING-DAG: @"??_7B@pr17748@@6B@" |
688 | // MANGLING-DAG: @"??_7C@pr17748@@6BA@1@@" |
689 | // MANGLING-DAG: @"??_7C@pr17748@@6BB@1@@" |
690 | } |
691 | |
692 | namespace pr19066 { |
693 | struct X : virtual B {}; |
694 | |
695 | struct Y : virtual X, B { |
696 | Y(); |
697 | // CHECK-LABEL: VFTable for 'B' in 'pr19066::X' in 'pr19066::Y' (1 entry). |
698 | // CHECK-NEXT: 0 | void B::g() |
699 | |
700 | // CHECK-LABEL: VFTable for 'B' in 'pr19066::Y' (1 entry). |
701 | // CHECK-NEXT: 0 | void B::g() |
702 | }; |
703 | |
704 | Y::Y() {} |
705 | } |
706 | |
707 | namespace pr19240 { |
708 | struct A { |
709 | virtual void c(); |
710 | }; |
711 | |
712 | struct B : virtual A { |
713 | virtual void c(); |
714 | }; |
715 | |
716 | struct C { }; |
717 | |
718 | struct D : virtual A, virtual C, B {}; |
719 | |
720 | D obj; |
721 | |
722 | // Each MDC only has one vftable. |
723 | |
724 | // MANGLING-DAG: @"??_7D@pr19240@@6B@" |
725 | // MANGLING-DAG: @"??_7A@pr19240@@6B@" |
726 | // MANGLING-DAG: @"??_7B@pr19240@@6B@" |
727 | |
728 | } |
729 | |
730 | namespace pr19408 { |
731 | // This test is a non-vtordisp version of the reproducer for PR19408. |
732 | struct X : virtual A { |
733 | int x; |
734 | }; |
735 | |
736 | struct Y : X { |
737 | virtual void f(); |
738 | int y; |
739 | }; |
740 | |
741 | struct Z : Y { |
742 | // CHECK-LABEL: VFTable for 'A' in 'pr19408::X' in 'pr19408::Y' in 'pr19408::Z' (2 entries). |
743 | // CHECK-NEXT: 0 | void pr19408::Y::f() |
744 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
745 | // CHECK-NEXT: 1 | void A::z() |
746 | |
747 | Z(); |
748 | int z; |
749 | // MANGLING-DAG: @"??_7Z@pr19408@@6B@" = {{.*}}@"?f@Y@pr19408@@W3AEXXZ" |
750 | }; |
751 | |
752 | Z::Z() {} |
753 | |
754 | struct W : B, Y { |
755 | // CHECK-LABEL: VFTable for 'A' in 'pr19408::X' in 'pr19408::Y' in 'pr19408::W' (2 entries). |
756 | // CHECK-NEXT: 0 | void pr19408::Y::f() |
757 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
758 | // CHECK-NEXT: 1 | void A::z() |
759 | |
760 | W(); |
761 | int w; |
762 | // MANGLING-DAG: @"??_7W@pr19408@@6BY@1@@" = {{.*}}@"?f@Y@pr19408@@W3AEXXZ" |
763 | }; |
764 | |
765 | W::W() {} |
766 | } |
767 | |
768 | namespace Test13 { |
769 | struct A { |
770 | virtual void f(); |
771 | }; |
772 | struct __declspec(dllexport) B : virtual A { |
773 | virtual void f() = 0; |
774 | // MANGLING-DAG: @"??_7B@Test13@@6B@" = weak_odr dllexport unnamed_addr constant { [1 x i8*] } { [1 x i8*] [i8* bitcast (void ()* @_purecall to i8*)] } |
775 | }; |
776 | } |
777 | |
778 | namespace pr21031_1 { |
779 | // This ordering of base specifiers regressed in r202425. |
780 | struct A { virtual void f(void); }; |
781 | struct B : virtual A { virtual void g(void); }; |
782 | struct C : virtual A, B { C(); }; |
783 | C::C() {} |
784 | |
785 | // CHECK-LABEL: VFTable for 'pr21031_1::A' in 'pr21031_1::B' in 'pr21031_1::C' (1 entry) |
786 | // CHECK-NEXT: 0 | void pr21031_1::A::f() |
787 | |
788 | // CHECK-LABEL: VFTable for 'pr21031_1::B' in 'pr21031_1::C' (1 entry) |
789 | // CHECK-NEXT: 0 | void pr21031_1::B::g() |
790 | |
791 | // MANGLING-DAG: @"??_7C@pr21031_1@@6BB@1@@" = {{.*}} constant { [1 x i8*] } |
792 | // MANGLING-DAG: @"??_7C@pr21031_1@@6B@" = {{.*}} constant { [1 x i8*] } |
793 | } |
794 | |
795 | namespace pr21031_2 { |
796 | struct A { virtual void f(void); }; |
797 | struct B : virtual A { virtual void g(void); }; |
798 | struct C : B, virtual A { C(); }; |
799 | C::C() {} |
800 | |
801 | // CHECK-LABEL: VFTable for 'pr21031_2::B' in 'pr21031_2::C' (1 entry) |
802 | // CHECK-NEXT: 0 | void pr21031_2::B::g() |
803 | |
804 | // CHECK-LABEL: VFTable for 'pr21031_2::A' in 'pr21031_2::B' in 'pr21031_2::C' (1 entry) |
805 | // CHECK-NEXT: 0 | void pr21031_2::A::f() |
806 | |
807 | // MANGLING-DAG: @"??_7C@pr21031_2@@6BA@1@@" = {{.*}} constant { [1 x i8*] } |
808 | // MANGLING-DAG: @"??_7C@pr21031_2@@6BB@1@@" = {{.*}} constant { [1 x i8*] } |
809 | } |
810 | |
811 | namespace pr21062_1 { |
812 | struct A { virtual void f(); }; |
813 | struct B {}; |
814 | struct C : virtual B {}; |
815 | struct D : virtual C, virtual B, virtual A { D();}; |
816 | D::D() {} |
817 | |
818 | // CHECK-LABEL: VFTable for 'pr21062_1::A' in 'pr21062_1::D' (1 entry) |
819 | // CHECK-NEXT: 0 | void pr21062_1::A::f() |
820 | |
821 | // MANGLING-DAG: @"??_7D@pr21062_1@@6B@" = {{.*}} constant { [1 x i8*] } |
822 | } |
823 | |
824 | namespace pr21062_2 { |
825 | struct A { virtual void f(); }; |
826 | struct B {}; |
827 | struct C : virtual B {}; |
828 | struct D : C, virtual B, virtual A { D(); }; |
829 | D::D() {} |
830 | |
831 | // CHECK-LABEL: VFTable for 'pr21062_2::A' in 'pr21062_2::D' (1 entry) |
832 | // CHECK-NEXT: 0 | void pr21062_2::A::f() |
833 | |
834 | // MANGLING-DAG: @"??_7D@pr21062_2@@6B@" = {{.*}} constant { [1 x i8*] } |
835 | } |
836 | |
837 | namespace pr21064 { |
838 | struct A {}; |
839 | struct B { virtual void f(); }; |
840 | struct C : virtual A, virtual B {}; |
841 | struct D : virtual A, virtual C { D(); }; |
842 | D::D() {} |
843 | // CHECK-LABEL: VFTable for 'pr21064::B' in 'pr21064::C' in 'pr21064::D' (1 entry) |
844 | // CHECK-NEXT: 0 | void pr21064::B::f() |
845 | |
846 | // MANGLING-DAG: @"??_7D@pr21064@@6B@" = {{.*}} constant { [1 x i8*] } |
847 | } |
848 | |