1 // RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o %t.ll -triple=i386-pc-win32 > %t
2 // RUN: FileCheck %s < %t
3 // RUN: FileCheck --check-prefix=MANGLING %s < %t.ll
4 
5 // For now, just make sure x86_64 doesn't crash.
6 // RUN: %clang_cc1 -fno-rtti -emit-llvm-only -fdump-vtable-layouts %s -triple=x86_64-pc-win32 > /dev/null
7 
8 struct V1 {
9   virtual void f();
10   virtual ~V1();
11 };
12 
13 struct V2 {
14   virtual void f();
15   virtual ~V2();
16   int v;
17 };
18 
19 struct Z {
20   virtual void g();
21   virtual ~Z();
22   int x;
23 };
24 
25 struct V3 : Z, V2 {
26 };
27 
28 struct V4 : Z, V1, V2 {
29   int y;
30 };
31 
32 void use_somewhere_else(void*);
33 
34 namespace simple {
35 // In case of a single-layer virtual inheritance, the "this" adjustment for a
36 // virtual method is done statically:
37 //   struct A {
38 //     virtual void f();  // Expects "(A*)this" in ECX
39 //   };
40 //   struct B : virtual A {
41 //     virtual void f();  // Expects "(char*)(B*)this + 12" in ECX
42 //     virtual ~B();      // Might call f()
43 //   };
44 //
45 // If a class overrides a virtual function of its base and has a non-trivial
46 // ctor/dtor that call(s) the virtual function (or may escape "this" to some
47 // code that might call it), a virtual adjustment might be needed in case the
48 // current class layout and the most derived class layout are different.
49 // This is done using vtordisp thunks.
50 //
51 // A simple vtordisp{x,y} thunk for Method@Class is something like:
52 //   sub  ecx, [ecx+x]  // apply the vtordisp adjustment
53 //   sub  ecx, y        // apply the subobject adjustment, if needed.
54 //   jmp Method@Class
55 
56 struct A : virtual V1 {
57   // CHECK-LABEL: VFTable for 'V1' in 'simple::A' (2 entries).
58   // CHECK-NEXT: 0 | void simple::A::f()
59   // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
60   // CHECK-NEXT: 1 | simple::A::~A() [scalar deleting]
61   // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
62 
63   // CHECK-LABEL: Thunks for 'simple::A::~A()' (1 entry).
64   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
65 
66   // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
67   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
68 
69   virtual void f();
70   // MANGLING-DAG: @"?f@A@simple@@$4PPPPPPPM@A@AEXXZ"
71 
72   virtual ~A();
73   // MANGLING-DAG: @"??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z"
74 };
75 
76 A a;
use(A * obj)77 void use(A *obj) { obj->f(); }
78 
79 struct B : virtual V3 {
80   // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries).
81   // CHECK-NEXT: 0 | void Z::g()
82   // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting]
83   // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
84 
85   // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
86   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
87 
88   // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries).
89   // CHECK-NEXT: 0 | void simple::B::f()
90   // CHECK-NEXT:     [this adjustment: vtordisp at -12, 0 non-virtual]
91   // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting]
92   // CHECK-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual]
93 
94   // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
95   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
96 
97   // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry).
98   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
99 
100   // FIXME: The vtordisp thunk should only get emitted for a constructor
101   // if "this" leaves scope.
Bsimple::B102   B() { use_somewhere_else(this); }
103 
104   virtual void f();
105   // MANGLING-DAG: @"?f@B@simple@@$4PPPPPPPE@A@AEXXZ"
106 
107   // Has an implicit destructor.
108   // MANGLING-DAG: @"??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z"
109   // MANGLING-DAG: @"??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z"
110 };
111 
112 B b;
use(B * obj)113 void use(B *obj) { obj->f(); }
114 
115 struct C : virtual V4 {
116   // CHECK-LABEL: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries).
117   // CHECK-NEXT: 0 | void Z::g()
118   // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
119   // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
120 
121   // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
122   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
123 
124   // CHECK-LABEL: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries).
125   // CHECK-NEXT: 0 | void simple::C::f()
126   // CHECK-NEXT:     [this adjustment: vtordisp at -12, 0 non-virtual]
127   // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
128   // CHECK-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual]
129 
130   // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
131   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
132 
133   // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry).
134   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
135 
136   // CHECK-LABEL: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries).
137   // CHECK-NEXT: 0 | void simple::C::f()
138   // CHECK-NEXT:     [this adjustment: vtordisp at -16, -4 non-virtual]
139   // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
140   // CHECK-NEXT:     [this adjustment: vtordisp at -16, -12 non-virtual]
141 
142   // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
143   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -12 non-virtual]
144 
145   // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry).
146   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -4 non-virtual]
147 
148   int x;
149   virtual void f();
150   // MANGLING-DAG: @"?f@C@simple@@$4PPPPPPPA@3AEXXZ"
151   // MANGLING-DAG: @"?f@C@simple@@$4PPPPPPPE@A@AEXXZ"
152   virtual ~C();
153   // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z"
154   // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z"
155   // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z"
156 };
157 
158 C c;
use(C * obj)159 void use(C *obj) { obj->f(); }
160 
161 class D : B {
162   // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'simple::D' (2 entries).
163   // CHECK-NEXT: 0 | void simple::B::f()
164   // CHECK-NEXT:     [this adjustment: vtordisp at -12, -4 non-virtual]
165   // CHECK-NEXT: 1 | simple::D::~D() [scalar deleting]
166   // CHECK-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual]
167   D();
168   int z;
169 
170   // MANGLING-DAG: @"?f@B@simple@@$4PPPPPPPE@3AEXXZ"
171 };
172 
D()173 D::D() {}
174 
175 struct E : V3 {
176   virtual void f();
177 };
178 
179 struct F : virtual E {
180   // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' (2 entries).
181   // CHECK-NEXT:   0 | void simple::F::g()
182   // CHECK-NEXT:       [this adjustment: vtordisp at -4, 0 non-virtual]
183   // CHECK-NEXT:   1 | simple::F::~F() [scalar deleting]
184   // CHECK-NEXT:       [this adjustment: vtordisp at -4, 0 non-virtual]
185 
186   // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' (2 entries).
187   // CHECK-NEXT:   0 | void simple::E::f()
188   // CHECK-NEXT:   1 | simple::F::~F() [scalar deleting]
189   // CHECK-NEXT:       [this adjustment: vtordisp at -12, -8 non-virtual]
190 
191   F();
192   virtual void g();  // Force a vtordisp.
193   int f;
194 
195   // MANGLING-DAG: @"?g@F@simple@@$4PPPPPPPM@A@AEXXZ"{{.*}}??_EF@simple@@$4PPPPPPPM@A@AEPAXI@Z
196   // MANGLING-DAG: ?f@E@simple@@UAEXXZ{{.*}}??_EF@simple@@$4PPPPPPPE@7AEPAXI@Z
197 };
198 
F()199 F::F() {}
200 
201 struct G : F {
202   // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries).
203   // CHECK-NEXT:   0 | void simple::F::g()
204   // CHECK-NEXT:       [this adjustment: vtordisp at -4, -4 non-virtual]
205   // CHECK-NEXT:   1 | simple::G::~G() [scalar deleting]
206   // CHECK-NEXT:       [this adjustment: vtordisp at -4, 0 non-virtual]
207 
208   // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries).
209   // CHECK-NEXT:   0 | void simple::E::f()
210   // CHECK-NEXT:   1 | simple::G::~G() [scalar deleting]
211   // CHECK-NEXT:       [this adjustment: vtordisp at -12, -8 non-virtual]
212 
213   G();
214   int g;
215 
216   // MANGLING-DAG: @"?g@F@simple@@$4PPPPPPPM@3AEXXZ"{{.*}}@"??_EG@simple@@$4PPPPPPPM@A@AEPAXI@Z"
217   // MANGLING-DAG: @"?f@E@simple@@UAEXXZ"{{.*}}@"??_EG@simple@@$4PPPPPPPE@7AEPAXI@Z"
218 };
219 
G()220 G::G() {}
221 }
222 
223 namespace extended {
224 // If a virtual function requires vtordisp adjustment and the final overrider
225 // is defined in another virtual base of the most derived class,
226 // we need to know two vbase offsets.
227 // In this case, we should use the extended form of vtordisp thunks, called
228 // vtordispex thunks.
229 //
230 // vtordispex{x,y,z,w} thunk for Method@Class is something like:
231 //   sub  ecx, [ecx+z]  // apply the vtordisp adjustment
232 //   sub  ecx, x        // jump to the vbptr of the most derived class
233 //   mov  eax, [ecx]    // load the vbtable address
234 //   add  ecx, [eax+y]  // lookup the final overrider's vbase offset
235 //   add  ecx, w        // apphy the subobject offset if needed
236 //   jmp Method@Class
237 
238 struct A : virtual simple::A {
239   // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' (2 entries).
240   // CHECK-NEXT: 0 | void simple::A::f()
241   // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
242   // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
243   // CHECK-NEXT: 1 | extended::A::~A() [scalar deleting]
244   // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
245 
246   // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
247   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
248   // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
249 
250   // `vtordispex{8,8,4294967292,8}'
251   // MANGLING-DAG: @"?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
252 
253   virtual ~A();
254   // vtordisp{4294967292,0}
255   // MANGLING-DAG: @"??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z"
256 };
257 
258 A a;
use(A * obj)259 void use(A *obj) { delete obj; }
260 
261 struct B : virtual simple::A {
262   // This class has an implicit dtor.  Vdtors don't require vtordispex thunks
263   // as the most derived class always has an implicit dtor,
264   // which is a final overrider.
265 
266   // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries).
267   //  ...
268   // CHECK: 1 | extended::B::~B() [scalar deleting]
269   // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
270 
271   // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
272   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
273   // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
274 
275   // vtordisp{4294967292,0}
276   // MANGLING-DAG: @"??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z"
277 };
278 
279 B b;
use(B * obj)280 void use(B *obj) { delete obj; }
281 
282 struct C : virtual simple::A {
283   // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::C' (2 entries).
284   // CHECK-NEXT: 0 | void simple::A::f()
285   // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 12 to the left,
286   // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
287 
288   // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
289   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 12 to the left,
290   // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
291 
292   // `vtordispex{12,8,4294967292,8}'
293   // MANGLING-DAG: @"?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ"
294   int x;
295   virtual ~C();
296   // MANGLING-DAG: @"??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z"
297 };
298 
299 C c;
use(C * obj)300 void use(C *obj) { delete obj; }
301 
302 struct D : virtual V2 {
303   virtual void f();
304   virtual ~D();
305   int x;
306 };
307 
308 struct E : virtual D {
309   // CHECK-LABEL: VFTable for 'V2' in 'extended::D' in 'extended::E' (2 entries).
310   // CHECK-NEXT: 0 | void extended::D::f()
311   // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
312   // CHECK-NEXT:      vboffset at 8 in the vbtable, 12 non-virtual]
313 
314   // CHECK-LABEL: Thunks for 'void extended::D::f()' (1 entry).
315   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
316   // CHECK-NEXT:      vboffset at 8 in the vbtable, 12 non-virtual]
317 
318   // `vtordispex{8,8,4294967292,12}'
319   // MANGLING-DAG: @"?f@D@extended@@$R477PPPPPPPM@M@AEXXZ"
320 
321   virtual ~E();
322   // MANGLING-DAG: @"??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z"
323 };
324 
325 E e;
use(E * obj)326 void use(E *obj) { delete obj; }
327 
328 struct F : virtual Z, virtual D {
329   // CHECK-LABEL: VFTable for 'V2' in 'extended::D' in 'extended::F' (2 entries).
330   // CHECK-NEXT: 0 | void extended::D::f()
331   // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 20 to the left,
332   // CHECK-NEXT:      vboffset at 12 in the vbtable, 12 non-virtual]
333 
334   // CHECK-LABEL: Thunks for 'void extended::D::f()' (1 entry).
335   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 20 to the left,
336   // CHECK-NEXT:      vboffset at 12 in the vbtable, 12 non-virtual]
337 
338   // `vtordispex{20,12,4294967292,12}'
339   // MANGLING-DAG: @"?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ"
340   int x;
341   virtual ~F();
342   // MANGLING-DAG: @"??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z"
343 };
344 
345 F f;
use(F * obj)346 void use(F *obj) { delete obj; }
347 
348 struct G : virtual simple::A {
349   // CHECK-LABEL: VFTable for 'extended::G' (1 entry).
350   // CHECK-NEXT: 0 | void extended::G::g()
351 
352   // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::G' (2 entries).
353   // CHECK-NEXT: 0 | void simple::A::f()
354   // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
355   // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
356   // CHECK-NEXT: 1 | extended::G::~G() [scalar deleting]
357   // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
358 
359   // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
360   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
361   // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
362 
363   // Emits a G's own vfptr, thus moving the vbptr in the layout.
364   virtual void g();
365 
366   virtual ~G();
367   // vtordisp{4294967292,0}
368   // MANGLING-DAG: @"??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z"
369 };
370 
371 G g;
use(G * obj)372 void use(G *obj) { obj->g(); }
373 
374 struct H : Z, A {
375   // CHECK-LABEL: VFTable for 'Z' in 'extended::H' (2 entries).
376   // CHECK-NEXT: 0 | void Z::g()
377   // CHECK-NEXT: 1 | extended::H::~H() [scalar deleting]
378 
379   // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries).
380   // CHECK-NEXT: 0 | void simple::A::f()
381   // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
382   // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
383 
384   // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
385   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
386   // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
387 
388   // MANGLING-DAG: @"?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
389   // MANGLING-DAG: @"??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z"
390 };
391 
392 H h;
use(H * obj)393 void use(H *obj) { delete obj; }
394 }
395 
396 namespace pr17738 {
397 // These classes should have vtordispex thunks but MSVS CL miscompiles them.
398 // Just do the right thing.
399 
400 struct A : virtual simple::B {
401   // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'pr17738::A' (2 entries).
402   // CHECK-NEXT: 0 | void simple::B::f()
403   // CHECK-NEXT:     [this adjustment: vtordisp at -12, vbptr at 20 to the left,
404   // CHECK-NEXT:      vboffset at 8 in the vbtable, 16 non-virtual]
405 
406   // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry).
407   // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, vbptr at 20 to the left,
408   // CHECK-NEXT:      vboffset at 8 in the vbtable, 16 non-virtual]
409 
410   // MANGLING-DAG: @"?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ"
411   int a;
412   virtual ~A();
413 };
414 
415 A a;
use(A * obj)416 void use(A *obj) { delete obj; }
417 }
418 
419 namespace pr19408 {
420 // In this test, the vptr used to vcall D::f() is located in the A vbase.
421 // The offset of A in different in C and D, so the D vtordisp thunk should
422 // adjust "this" so C::f gets the right value.
423 struct A {
424   A();
425   virtual void f();
426   int a;
427 };
428 
429 struct B : virtual A {
430   B();
431   int b;
432 };
433 
434 struct C : B {
435   C();
436   virtual void f();
437   int c;
438 };
439 
440 struct D : C {
441   // CHECK-LABEL: VFTable for 'pr19408::A' in 'pr19408::B' in 'pr19408::C' in 'pr19408::D' (1 entry).
442   // CHECK-NEXT:   0 | void pr19408::C::f()
443   // CHECK-NEXT:       [this adjustment: vtordisp at -4, -4 non-virtual]
444 
445   // MANGLING-DAG: @"?f@C@pr19408@@$4PPPPPPPM@3AEXXZ"
446   D();
447   int d;
448 };
449 
D()450 D::D() {}
451 }
452 
453 namespace access {
454 struct A {
455   virtual ~A();
456 protected:
457   virtual void prot();
458 private:
459   virtual void priv();
460 };
461 
462 struct B : virtual A {
463   virtual ~B();
464 protected:
465   virtual void prot();
466   // MANGLING-DAG: @"?prot@B@access@@$2PPPPPPPM@A@AEXXZ"
467 private:
468   virtual void priv();
469   // MANGLING-DAG: @"?priv@B@access@@$0PPPPPPPM@A@AEXXZ"
470 };
471 
472 B b;
473 
474 struct C : virtual B {
475   virtual ~C();
476 
477   // MANGLING-DAG: @"?prot@B@access@@$R277PPPPPPPM@7AEXXZ"
478   // MANGLING-DAG: @"?priv@B@access@@$R077PPPPPPPM@7AEXXZ"
479 };
480 
481 C c;
482 }
483 
484 namespace pr19505 {
485 struct A {
486   virtual void f();
487   virtual void z();
488 };
489 
490 struct B : A {
491   virtual void f();
492 };
493 
494 struct C : A, B {
495   virtual void g();
496 };
497 
498 struct X : B, virtual C {
Xpr19505::X499   X() {}
500   virtual void g();
501 
502   // CHECK-LABEL: VFTable for 'pr19505::A' in 'pr19505::B' in 'pr19505::C' in 'pr19505::X' (2 entries).
503   // CHECK-NEXT:   0 | void pr19505::B::f()
504   // CHECK-NEXT:   1 | void pr19505::A::z()
505 
506   // MANGLING-DAG: @"??_7X@pr19505@@6BB@1@@" = {{.*}}@"?f@B@pr19505@@UAEXXZ"
507 } x;
508 
build_vftable(X * obj)509 void build_vftable(X *obj) { obj->g(); }
510 }
511 
512 namespace pr19506 {
513 struct A {
514   virtual void f();
515   virtual void g();
516 };
517 
518 struct B : A {
519   virtual void f();
520 };
521 
522 struct C : B {};
523 
524 struct X : C, virtual B {
525   virtual void g();
Xpr19506::X526   X() {}
527 
528   // CHECK-LABEL: VFTable for 'pr19506::A' in 'pr19506::B' in 'pr19506::X' (2 entries).
529   // CHECK-NEXT:   0 | void pr19506::B::f()
530   // CHECK-NEXT:   1 | void pr19506::X::g()
531   // CHECK-NEXT:       [this adjustment: vtordisp at -4, -12 non-virtual]
532 
533   // MANGLING-DAG: @"??_7X@pr19506@@6BB@1@@" = {{.*}}@"?f@B@pr19506@@UAEXXZ"
534 } x;
535 
build_vftable(X * obj)536 void build_vftable(X *obj) { obj->g(); }
537 }
538 
539 namespace pr19519 {
540 // VS2013 CL miscompiles this, just make sure we don't regress.
541 
542 struct A {
543   virtual void f();
544   virtual void g();
545 };
546 
547 struct B : virtual A {
548   virtual void f();
549   B();
550 };
551 
552 struct C : virtual A {
553   virtual void g();
554 };
555 
556 struct X : B, C {
557   X();
558 
559   // CHECK-LABEL: VFTable for 'pr19519::A' in 'pr19519::B' in 'pr19519::X' (2 entries).
560   // CHECK-NEXT:   0 | void pr19519::B::f()
561   // CHECK-NEXT:       [this adjustment: vtordisp at -4, -4 non-virtual]
562   // CHECK-NEXT:   1 | void pr19519::C::g()
563   // CHECK-NEXT:       [this adjustment: vtordisp at -4, -4 non-virtual]
564 
565   // MANGLING-DAG: @"??_7X@pr19519@@6B@" = {{.*}}@"?g@C@pr19519@@$4PPPPPPPM@3AEXXZ"
566 };
567 
X()568 X::X() {}
569 }
570