1 // RUN: %clang_cc1 -fno-rtti -emit-llvm -o %t.ll -fdump-vtable-layouts %s -triple=i386-pc-win32 >%t
2 
3 // RUN: FileCheck --check-prefix=VTABLE-C %s < %t
4 // RUN: FileCheck --check-prefix=VTABLE-D %s < %t
5 // RUN: FileCheck --check-prefix=TEST1 %s < %t
6 // RUN: FileCheck --check-prefix=TEST2 %s < %t
7 // RUN: FileCheck --check-prefix=TEST3 %s < %t
8 // RUN: FileCheck --check-prefix=TEST4 %s < %t
9 // RUN: FileCheck --check-prefix=TEST5 %s < %t
10 // RUN: FileCheck --check-prefix=TEST6 %s < %t
11 // RUN: FileCheck --check-prefix=TEST7 %s < %t
12 // RUN: FileCheck --check-prefix=TEST8-X %s < %t
13 // RUN: FileCheck --check-prefix=TEST8-Z %s < %t
14 // RUN: FileCheck --check-prefix=TEST8-T %s < %t
15 // RUN: FileCheck --check-prefix=TEST9-Y %s < %t
16 // RUN: FileCheck --check-prefix=TEST9-Z %s < %t
17 // RUN: FileCheck --check-prefix=TEST9-W %s < %t
18 // RUN: FileCheck --check-prefix=TEST9-T %s < %t
19 // RUN: FileCheck --check-prefix=TEST10 %s < %t
20 // RUN: FileCheck --check-prefix=VDTORS-Y %s < %t
21 // RUN: FileCheck --check-prefix=VDTORS-U %s < %t
22 // RUN: FileCheck --check-prefix=VDTORS-V %s < %t
23 // RUN: FileCheck --check-prefix=VDTORS-P %s < %t
24 // RUN: FileCheck --check-prefix=RET-W %s < %t
25 // RUN: FileCheck --check-prefix=RET-T %s < %t
26 // RUN: FileCheck --check-prefix=RET-V %s < %t
27 
28 // RUN: FileCheck --check-prefix=MANGLING %s < %t.ll
29 
30 struct Empty { };
31 
32 struct A {
33   virtual void f();
34   virtual void z();  // Useful to check there are no thunks for f() when appropriate.
35 };
36 
37 struct B {
38   virtual void g();
39 };
40 
41 struct C: virtual A {
42   // VTABLE-C: VFTable for 'A' in 'C' (2 entries)
43   // VTABLE-C-NEXT: 0 | void C::f()
44   // VTABLE-C-NEXT: 1 | void A::z()
45 
46   // VTABLE-C: VFTable indices for 'C' (1 entry)
47   // VTABLE-C-NEXT: vbtable index 1, vfptr at offset 0
48   // VTABLE-C-NEXT: 0 | void C::f()
49 
50   // MANGLING-DAG: @"\01??_7C@@6B@"
51 
52   virtual void f() {}
53 };
54 
55 C c;
56 
57 struct D: virtual A {
58   // VTABLE-D: VFTable for 'D' (1 entry).
59   // VTABLE-D-NEXT: 0 | void D::h()
60 
61   // VTABLE-D: VFTable for 'A' in 'D' (2 entries).
62   // VTABLE-D-NEXT: 0 | void D::f()
63   // VTABLE-D-NEXT: 1 | void A::z()
64 
65   // VTABLE-D: VFTable indices for 'D' (2 entries).
66   // VTABLE-D-NEXT: via vfptr at offset 0
67   // VTABLE-D-NEXT: 0 | void D::h()
68   // VTABLE-D-NEXT: via vbtable index 1, vfptr at offset 0
69   // VTABLE-D-NEXT: 0 | void D::f()
70 
71   // MANGLING-DAG: @"\01??_7D@@6B0@@"
72   // MANGLING-DAG: @"\01??_7D@@6BA@@@"
73 
74   virtual void f();
75   virtual void h();
76 };
77 
78 void D::h() {}
79 D d;
80 
81 namespace Test1 {
82 
83 struct X { int x; };
84 
85 // X and A get reordered in the layout since X doesn't have a vfptr while A has.
86 struct Y : X, A { };
87 // MANGLING-DAG: @"\01??_7Y@Test1@@6B@"
88 
89 struct Z : virtual Y {
90   // TEST1: VFTable for 'A' in 'Test1::Y' in 'Test1::Z' (2 entries).
91   // TEST1-NEXT: 0 | void A::f()
92   // TEST1-NEXT: 1 | void A::z()
93 
94   // TEST1-NOT: VFTable indices for 'Test1::Z'
95 
96   // MANGLING-DAG: @"\01??_7Z@Test1@@6B@"
97 };
98 
99 Z z;
100 }
101 
102 namespace Test2 {
103 
104 struct X: virtual A, virtual B {
105   // TEST2: VFTable for 'Test2::X' (1 entry).
106   // TEST2-NEXT: 0 | void Test2::X::h()
107 
108   // TEST2: VFTable for 'A' in 'Test2::X' (2 entries).
109   // TEST2-NEXT: 0 | void A::f()
110   // TEST2-NEXT: 1 | void A::z()
111 
112   // TEST2: VFTable for 'B' in 'Test2::X' (1 entry).
113   // TEST2-NEXT: 0 | void B::g()
114 
115   // TEST2: VFTable indices for 'Test2::X' (1 entry).
116   // TEST2-NEXT: 0 | void Test2::X::h()
117 
118   // MANGLING-DAG: @"\01??_7X@Test2@@6B01@@"
119   // MANGLING-DAG: @"\01??_7X@Test2@@6BA@@@"
120   // MANGLING-DAG: @"\01??_7X@Test2@@6BB@@@"
121 
122   virtual void h();
123 };
124 
125 X x;
126 }
127 
128 namespace Test3 {
129 
130 struct X : virtual A {
131   // MANGLING-DAG: @"\01??_7X@Test3@@6B@"
132 };
133 
134 struct Y: virtual X {
135   // TEST3: VFTable for 'A' in 'Test3::X' in 'Test3::Y' (2 entries).
136   // TEST3-NEXT: 0 | void A::f()
137   // TEST3-NEXT: 1 | void A::z()
138 
139   // TEST3-NOT: VFTable indices for 'Test3::Y'
140 
141   // MANGLING-DAG: @"\01??_7Y@Test3@@6B@"
142 };
143 
144 Y y;
145 }
146 
147 namespace Test4 {
148 
149 struct X: virtual C {
150   // This one's interesting. C::f expects (A*) to be passed as 'this' and does
151   // ECX-=4 to cast to (C*). In X, C and A vbases are reordered, so the thunk
152   // should pass a pointer to the end of X in order
153   // for ECX-=4 to point at the C part.
154 
155   // TEST4: VFTable for 'A' in 'C' in 'Test4::X' (2 entries).
156   // TEST4-NEXT: 0 | void C::f()
157   // TEST4-NEXT:     [this adjustment: 8 non-virtual]
158   // TEST4-NEXT: 1 | void A::z()
159 
160   // TEST4: Thunks for 'void C::f()' (1 entry).
161   // TEST4-NEXT: 0 | [this adjustment: 8 non-virtual]
162 
163   // TEST4-NOT: VFTable indices for 'Test4::X'
164 
165   // MANGLING-DAG: @"\01??_7X@Test4@@6B@"
166 
167   // Also check the mangling of the thunk.
168   // MANGLING-DAG: define weak x86_thiscallcc void @"\01?f@C@@WPPPPPPPI@AEXXZ"
169 };
170 
171 X x;
172 }
173 
174 namespace Test5 {
175 
176 // New methods are added to the base's vftable.
177 struct X : A {
178   // MANGLING-DAG: @"\01??_7X@Test5@@6B@"
179   virtual void g();
180 };
181 
182 struct Y : virtual X {
183   // TEST5: VFTable for 'Test5::Y' (1 entry).
184   // TEST5-NEXT: 0 | void Test5::Y::h()
185 
186   // TEST5: VFTable for 'A' in 'Test5::X' in 'Test5::Y' (3 entries).
187   // TEST5-NEXT: 0 | void A::f()
188   // TEST5-NEXT: 1 | void A::z()
189   // TEST5-NEXT: 2 | void Test5::X::g()
190 
191   // TEST5: VFTable indices for 'Test5::Y' (1 entry).
192   // TEST5-NEXT: 0 | void Test5::Y::h()
193 
194   // MANGLING-DAG: @"\01??_7Y@Test5@@6B01@@"
195   // MANGLING-DAG: @"\01??_7Y@Test5@@6BX@1@@"
196 
197   virtual void h();
198 };
199 
200 Y y;
201 }
202 
203 namespace Test6 {
204 
205 struct X : A, virtual Empty {
206   // TEST6: VFTable for 'A' in 'Test6::X' (2 entries).
207   // TEST6-NEXT: 0 | void A::f()
208   // TEST6-NEXT: 1 | void A::z()
209 
210   // TEST6-NOT: VFTable indices for 'Test6::X'
211 
212   // MANGLING-DAG: @"\01??_7X@Test6@@6B@"
213 };
214 
215 X x;
216 }
217 
218 namespace Test7 {
219 
220 struct X : C {
221   // MANGLING-DAG: @"\01??_7X@Test7@@6B@"
222 };
223 
224 struct Y : virtual X {
225   // TEST7: VFTable for 'A' in 'C' in 'Test7::X' in 'Test7::Y' (2 entries).
226   // TEST7-NEXT: 0 | void C::f()
227   // TEST7-NEXT:     [this adjustment: 8 non-virtual]
228   // TEST7-NEXT: 1 | void A::z()
229 
230   // TEST7: Thunks for 'void C::f()' (1 entry).
231   // TEST7-NEXT: 0 | [this adjustment: 8 non-virtual]
232 
233   // TEST7-NOT: VFTable indices for 'Test7::Y'
234 
235   // MANGLING-DAG: @"\01??_7Y@Test7@@6B@"
236 };
237 
238 Y y;
239 }
240 
241 namespace Test8 {
242 
243 // This is a typical diamond inheritance with a shared 'A' vbase.
244 struct X : D, C {
245   // TEST8-X: VFTable for 'D' in 'Test8::X' (1 entry).
246   // TEST8-X-NEXT: 0 | void D::h()
247 
248   // TEST8-X: VFTable for 'A' in 'D' in 'Test8::X' (2 entries).
249   // TEST8-X-NEXT: 0 | void Test8::X::f()
250   // TEST8-X-NEXT: 1 | void A::z()
251 
252   // TEST8-X: VFTable indices for 'Test8::X' (1 entry).
253   // TEST8-X-NEXT: via vbtable index 1, vfptr at offset 0
254   // TEST8-X-NEXT: 0 | void Test8::X::f()
255 
256   // MANGLING-DAG: @"\01??_7X@Test8@@6BA@@@"
257   // MANGLING-DAG: @"\01??_7X@Test8@@6BD@@@"
258 
259   virtual void f();
260 };
261 
262 X x;
263 
264 // Another diamond inheritance which led to AST crashes.
265 struct Y : virtual A {};
266 
267 struct Z : Y, C {
268   // TEST8-Z: VFTable for 'A' in 'Test8::Y' in 'Test8::Z' (2 entries).
269   // TEST8-Z-NEXT: 0 | void Test8::Z::f()
270   // TEST8-Z-NEXT: 1 | void A::z()
271 
272   // TEST8-Z: VFTable indices for 'Test8::Z' (1 entry).
273   // TEST8-Z-NEXT: via vbtable index 1, vfptr at offset 0
274   // TEST8-Z-NEXT: 0 | void Test8::Z::f()
275   virtual void f();
276 };
277 Z z;
278 
279 // Another diamond inheritance which we miscompiled (PR18967).
280 struct W : virtual A {
281   virtual void bar();
282 };
283 
284 struct T : W, C {
285   // TEST8-T: VFTable for 'Test8::W' in 'Test8::T' (1 entry)
286   // TEST8-T-NEXT: 0 | void Test8::T::bar()
287 
288   // TEST8-T: VFTable for 'A' in 'Test8::W' in 'Test8::T' (2 entries)
289   // TEST8-T-NEXT: 0 | void C::f()
290   // TEST8-T-NEXT:     [this adjustment: -4 non-virtual]
291   // TEST8-T-NEXT: 1 | void A::z()
292 
293   // TEST8-T: Thunks for 'void C::f()' (1 entry).
294   // TEST8-T-NEXT: 0 | [this adjustment: -4 non-virtual]
295   virtual void bar();
296   int field;
297 };
298 T t;
299 }
300 
301 namespace Test9 {
302 
303 struct X : A { };
304 
305 struct Y : virtual X {
306   // TEST9-Y: VFTable for 'Test9::Y' (1 entry).
307   // TEST9-Y-NEXT: 0 | void Test9::Y::h()
308 
309   // TEST9-Y: VFTable for 'A' in 'Test9::X' in 'Test9::Y' (2 entries).
310   // TEST9-Y-NEXT: 0 | void A::f()
311   // TEST9-Y-NEXT: 1 | void A::z()
312 
313   // TEST9-Y: VFTable indices for 'Test9::Y' (1 entry).
314   // TEST9-Y-NEXT: 0 | void Test9::Y::h()
315 
316   // MANGLING-DAG: @"\01??_7Y@Test9@@6B01@@"
317   // MANGLING-DAG: @"\01??_7Y@Test9@@6BX@1@@"
318 
319   virtual void h();
320 };
321 
322 Y y;
323 
324 struct Z : Y, virtual B {
325   // TEST9-Z: VFTable for 'Test9::Y' in 'Test9::Z' (1 entry).
326   // TEST9-Z-NEXT: 0 | void Test9::Y::h()
327 
328   // TEST9-Z: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' (2 entries).
329   // TEST9-Z-NEXT: 0 | void A::f()
330   // TEST9-Z-NEXT: 1 | void A::z()
331 
332   // TEST9-Z: VFTable for 'B' in 'Test9::Z' (1 entry).
333   // TEST9-Z-NEXT: 0 | void B::g()
334 
335   // TEST9-Z-NOT: VFTable indices for 'Test9::Z'
336 
337   // MANGLING-DAG: @"\01??_7Z@Test9@@6BX@1@@"
338   // MANGLING-DAG: @"\01??_7Z@Test9@@6BY@1@@"
339 
340   // MANGLING-DAG: @"\01??_7Z@Test9@@6B@"
341 };
342 
343 Z z;
344 
345 struct W : Z, D, virtual A, virtual B {
346   // TEST9-W: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::W' (1 entry).
347   // TEST9-W-NEXT: 0 | void Test9::Y::h()
348 
349   // TEST9-W: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::W' (2 entries).
350   // TEST9-W-NEXT: 0 | void A::f()
351   // TEST9-W-NEXT: 1 | void A::z()
352 
353   // TEST9-W: VFTable for 'B' in 'Test9::Z' in 'Test9::W' (1 entry).
354   // TEST9-W-NEXT: 0 | void B::g()
355 
356   // TEST9-W: VFTable for 'D' in 'Test9::W' (1 entry).
357   // TEST9-W-NEXT: 0 | void D::h()
358 
359   // TEST9-W: VFTable for 'A' in 'D' in 'Test9::W' (2 entries).
360   // TEST9-W-NEXT: 0 | void D::f()
361   // TEST9-W-NEXT:     [this adjustment: -8 non-virtual]
362   // TEST9-W-NEXT: 1 | void A::z()
363 
364   // TEST9-W: Thunks for 'void D::f()' (1 entry).
365   // TEST9-W-NEXT: 0 | [this adjustment: -8 non-virtual]
366 
367   // TEST9-W-NOT: VFTable indices for 'Test9::W'
368 
369   // MANGLING-DAG: @"\01??_7W@Test9@@6BA@@@"
370   // MANGLING-DAG: @"\01??_7W@Test9@@6BD@@@"
371   // MANGLING-DAG: @"\01??_7W@Test9@@6BX@1@@"
372 
373   // MANGLING-DAG: @"\01??_7W@Test9@@6B@"
374   // MANGLING-DAG: @"\01??_7W@Test9@@6BY@1@@"
375 };
376 
377 W w;
378 
379 struct T : Z, D, virtual A, virtual B {
380   // TEST9-T: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::T' (1 entry).
381   // TEST9-T-NEXT: 0 | void Test9::T::h()
382 
383   // TEST9-T: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::T' (2 entries).
384   // TEST9-T-NEXT: 0 | void Test9::T::f()
385   // TEST9-T-NEXT: 1 | void Test9::T::z()
386 
387   // TEST9-T: VFTable for 'B' in 'Test9::Z' in 'Test9::T' (1 entry).
388   // TEST9-T-NEXT: 0 | void Test9::T::g()
389 
390   // TEST9-T: VFTable for 'D' in 'Test9::T' (1 entry).
391   // TEST9-T-NEXT: 0 | void Test9::T::h()
392   // TEST9-T-NEXT:     [this adjustment: -8 non-virtual]
393 
394   // TEST9-T: Thunks for 'void Test9::T::h()' (1 entry).
395   // TEST9-T-NEXT: 0 | [this adjustment: -8 non-virtual]
396 
397   // TEST9-T: VFTable for 'A' in 'D' in 'Test9::T' (2 entries).
398   // TEST9-T-NEXT: 0 | void Test9::T::f()
399   // TEST9-T-NEXT:     [this adjustment: -8 non-virtual]
400   // TEST9-T-NEXT: 1 | void Test9::T::z()
401   // TEST9-T-NEXT:     [this adjustment: -8 non-virtual]
402 
403   // TEST9-T: Thunks for 'void Test9::T::f()' (1 entry).
404   // TEST9-T-NEXT: 0 | [this adjustment: -8 non-virtual]
405 
406   // TEST9-T: Thunks for 'void Test9::T::z()' (1 entry).
407   // TEST9-T-NEXT: 0 | [this adjustment: -8 non-virtual]
408 
409   // TEST9-T: VFTable indices for 'Test9::T' (4 entries).
410   // TEST9-T-NEXT: via vfptr at offset 0
411   // TEST9-T-NEXT: 0 | void Test9::T::h()
412   // TEST9-T-NEXT: via vbtable index 1, vfptr at offset 0
413   // TEST9-T-NEXT: 0 | void Test9::T::f()
414   // TEST9-T-NEXT: 1 | void Test9::T::z()
415   // TEST9-T-NEXT: via vbtable index 2, vfptr at offset 0
416   // TEST9-T-NEXT: 0 | void Test9::T::g()
417 
418   // MANGLING-DAG: @"\01??_7T@Test9@@6BA@@@"
419   // MANGLING-DAG: @"\01??_7T@Test9@@6BD@@@"
420   // MANGLING-DAG: @"\01??_7T@Test9@@6BX@1@@"
421 
422   // MANGLING-DAG: @"\01??_7T@Test9@@6B@"
423   // MANGLING-DAG: @"\01??_7T@Test9@@6BY@1@@"
424 
425   virtual void f();
426   virtual void g();
427   virtual void h();
428   virtual void z();
429 };
430 
431 T t;
432 }
433 
434 namespace Test10 {
435 struct X : virtual C, virtual A {
436   // TEST10: VFTable for 'A' in 'C' in 'Test10::X' (2 entries).
437   // TEST10-NEXT: 0 | void Test10::X::f()
438   // TEST10-NEXT: 1 | void A::z()
439 
440   // TEST10: VFTable indices for 'Test10::X' (1 entry).
441   // TEST10-NEXT: via vbtable index 1, vfptr at offset 0
442   // TEST10-NEXT: 0 | void Test10::X::f()
443   virtual void f();
444 };
445 
446 void X::f() {}
447 X x;
448 }
449 
450 namespace Test11 {
451 struct X : virtual A {};
452 struct Y { virtual void g(); };
453 
454 struct Z : virtual X, Y {
455   // MANGLING-DAG: @"\01??_7Z@Test11@@6BY@1@@"
456   // MANGLING-DAG: @"\01??_7Z@Test11@@6BX@1@@"
457 };
458 
459 Z z;
460 
461 struct W : virtual X, A {};
462 
463 // PR17748 FIXME this one hits UNREACHABLE:
464 // W w;
465 }
466 
467 namespace vdtors {
468 struct X {
469   virtual ~X();
470   virtual void zzz();
471 };
472 
473 struct Y : virtual X {
474   // VDTORS-Y: VFTable for 'vdtors::X' in 'vdtors::Y' (2 entries).
475   // VDTORS-Y-NEXT: 0 | vdtors::Y::~Y() [scalar deleting]
476   // VDTORS-Y-NEXT: 1 | void vdtors::X::zzz()
477 
478   // VDTORS-Y-NOT: Thunks for 'vdtors::Y::~Y()'
479   virtual ~Y();
480 };
481 
482 Y y;
483 
484 struct Z {
485   virtual void z();
486 };
487 
488 struct W : Z, X {
489   // Implicit virtual dtor.
490 };
491 
492 struct U : virtual W {
493   // VDTORS-U: VFTable for 'vdtors::Z' in 'vdtors::W' in 'vdtors::U' (1 entry).
494   // VDTORS-U-NEXT: 0 | void vdtors::Z::z()
495 
496   // VDTORS-U: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::U' (2 entries).
497   // VDTORS-U-NEXT: 0 | vdtors::U::~U() [scalar deleting]
498   // VDTORS-U-NEXT:     [this adjustment: -4 non-virtual]
499   // VDTORS-U-NEXT: 1 | void vdtors::X::zzz()
500 
501   // VDTORS-U: Thunks for 'vdtors::U::~U()' (1 entry).
502   // VDTORS-U-NEXT: 0 | [this adjustment: -4 non-virtual]
503 
504   // VDTORS-U: VFTable indices for 'vdtors::U' (1 entry).
505   // VDTORS-U-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 --
506   // VDTORS-U-NEXT: 0 | vdtors::U::~U() [scalar deleting]
507   virtual ~U();
508 };
509 
510 U u;
511 
512 struct V : virtual W {
513   // VDTORS-V: VFTable for 'vdtors::Z' in 'vdtors::W' in 'vdtors::V' (1 entry).
514   // VDTORS-V-NEXT: 0 | void vdtors::Z::z()
515 
516   // VDTORS-V: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::V' (2 entries).
517   // VDTORS-V-NEXT: 0 | vdtors::V::~V() [scalar deleting]
518   // VDTORS-V-NEXT:     [this adjustment: -4 non-virtual]
519   // VDTORS-V-NEXT: 1 | void vdtors::X::zzz()
520 
521   // VDTORS-V: Thunks for 'vdtors::V::~V()' (1 entry).
522   // VDTORS-V-NEXT: 0 | [this adjustment: -4 non-virtual]
523 
524   // VDTORS-V: VFTable indices for 'vdtors::V' (1 entry).
525   // VDTORS-V-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 --
526   // VDTORS-V-NEXT: 0 | vdtors::V::~V() [scalar deleting]
527 };
528 
529 V v;
530 
531 struct T : virtual X {
532   virtual ~T();
533 };
534 
535 struct P : T, Y {
536   // VDTORS-P: VFTable for 'vdtors::X' in 'vdtors::T' in 'vdtors::P' (2 entries).
537   // VDTORS-P-NEXT: 0 | vdtors::P::~P() [scalar deleting]
538   // VDTORS-P-NEXT: 1 | void vdtors::X::zzz()
539 
540   // VDTORS-P-NOT: Thunks for 'vdtors::P::~P()'
541   virtual ~P();
542 };
543 
544 P p;
545 
546 }
547 
548 namespace return_adjustment {
549 
550 struct X : virtual A {
551   virtual void f();
552 };
553 
554 struct Y : virtual A, virtual X {
555   virtual void f();
556 };
557 
558 struct Z {
559   virtual A* foo();
560 };
561 
562 struct W : Z {
563   // RET-W: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' (2 entries).
564   // RET-W-NEXT: 0 | return_adjustment::X *return_adjustment::W::foo()
565   // RET-W-NEXT:     [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
566   // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
567 
568   // RET-W: Thunks for 'return_adjustment::X *return_adjustment::W::foo()' (1 entry).
569   // RET-W-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
570 
571   // RET-W: VFTable indices for 'return_adjustment::W' (1 entry).
572   // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
573 
574   virtual X* foo();
575 };
576 
577 W y;
578 
579 struct T : W {
580   // RET-T: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' in 'return_adjustment::T' (3 entries).
581   // RET-T-NEXT: 0 | return_adjustment::Y *return_adjustment::T::foo()
582   // RET-T-NEXT:     [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
583   // RET-T-NEXT: 1 | return_adjustment::Y *return_adjustment::T::foo()
584   // RET-T-NEXT:     [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual]
585   // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
586 
587   // RET-T: Thunks for 'return_adjustment::Y *return_adjustment::T::foo()' (2 entries).
588   // RET-T-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
589   // RET-T-NEXT: 1 | [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual]
590 
591   // RET-T: VFTable indices for 'return_adjustment::T' (1 entry).
592   // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
593 
594   virtual Y* foo();
595 };
596 
597 T t;
598 
599 struct U : virtual A {
600   virtual void g();  // adds a vfptr
601 };
602 
603 struct V : Z {
604   // RET-V: VFTable for 'return_adjustment::Z' in 'return_adjustment::V' (2 entries).
605   // RET-V-NEXT: 0 | return_adjustment::U *return_adjustment::V::foo()
606   // RET-V-NEXT:     [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual]
607   // RET-V-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo()
608 
609   // RET-V: Thunks for 'return_adjustment::U *return_adjustment::V::foo()' (1 entry).
610   // RET-V-NEXT: 0 | [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual]
611 
612   // RET-V: VFTable indices for 'return_adjustment::V' (1 entry).
613   // RET-V-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo()
614 
615   virtual U* foo();
616 };
617 
618 V v;
619 }
620 
621 namespace pr17748 {
622 struct A {
623   virtual void f() {}
624 };
625 
626 struct B : virtual A {
627   B() {}
628 };
629 
630 struct C : virtual B, A {
631   C() {}
632 };
633 C c;
634 
635 // MANGLING-DAG: @"\01??_7A@pr17748@@6B@"
636 // MANGLING-DAG: @"\01??_7B@pr17748@@6B@"
637 // MANGLING-DAG: @"\01??_7C@pr17748@@6BA@1@@"
638 // MANGLING-DAG: @"\01??_7C@pr17748@@6BB@1@@"
639 }
640