[clang] Handle __declspec() attributes in using
[llvm-project.git] / clang / test / CodeGenCXX / microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp
blobc5ce69f5cbcacec57e634b14a6a4154011c69352
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
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
8 struct V1 {
9 virtual void f();
10 virtual ~V1();
13 struct V2 {
14 virtual void f();
15 virtual ~V2();
16 int v;
19 struct Z {
20 virtual void g();
21 virtual ~Z();
22 int x;
25 struct V3 : Z, V2 {
28 struct V4 : Z, V1, V2 {
29 int y;
32 void use_somewhere_else(void*);
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 // };
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.
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
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]
63 // CHECK-LABEL: Thunks for 'simple::A::~A()' (1 entry).
64 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
66 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
67 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
69 virtual void f();
70 // MANGLING-DAG: @"?f@A@simple@@$4PPPPPPPM@A@AEXXZ"
72 virtual ~A();
73 // MANGLING-DAG: @"??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z"
76 A a;
77 void use(A *obj) { obj->f(); }
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]
85 // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
86 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
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]
94 // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
95 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
97 // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry).
98 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
100 // FIXME: The vtordisp thunk should only get emitted for a constructor
101 // if "this" leaves scope.
102 B() { use_somewhere_else(this); }
104 virtual void f();
105 // MANGLING-DAG: @"?f@B@simple@@$4PPPPPPPE@A@AEXXZ"
107 // Has an implicit destructor.
108 // MANGLING-DAG: @"??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z"
109 // MANGLING-DAG: @"??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z"
112 B b;
113 void use(B *obj) { obj->f(); }
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]
121 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
122 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
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]
130 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
131 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
133 // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry).
134 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
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]
142 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
143 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -12 non-virtual]
145 // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry).
146 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -4 non-virtual]
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"
158 C c;
159 void use(C *obj) { obj->f(); }
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;
170 // MANGLING-DAG: @"?f@B@simple@@$4PPPPPPPE@3AEXXZ"
173 D::D() {}
175 struct E : V3 {
176 virtual void f();
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]
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]
191 F();
192 virtual void g(); // Force a vtordisp.
193 int f;
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
199 F::F() {}
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]
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]
213 G();
214 int g;
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"
220 G::G() {}
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.
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
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]
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]
250 // `vtordispex{8,8,4294967292,8}'
251 // MANGLING-DAG: @"?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
253 virtual ~A();
254 // vtordisp{4294967292,0}
255 // MANGLING-DAG: @"??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z"
258 A a;
259 void use(A *obj) { delete obj; }
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.
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]
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]
275 // vtordisp{4294967292,0}
276 // MANGLING-DAG: @"??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z"
279 B b;
280 void use(B *obj) { delete obj; }
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]
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]
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"
299 C c;
300 void use(C *obj) { delete obj; }
302 struct D : virtual V2 {
303 virtual void f();
304 virtual ~D();
305 int x;
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]
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]
318 // `vtordispex{8,8,4294967292,12}'
319 // MANGLING-DAG: @"?f@D@extended@@$R477PPPPPPPM@M@AEXXZ"
321 virtual ~E();
322 // MANGLING-DAG: @"??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z"
325 E e;
326 void use(E *obj) { delete obj; }
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]
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]
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"
345 F f;
346 void use(F *obj) { delete obj; }
348 struct G : virtual simple::A {
349 // CHECK-LABEL: VFTable for 'extended::G' (1 entry).
350 // CHECK-NEXT: 0 | void extended::G::g()
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]
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]
363 // Emits a G's own vfptr, thus moving the vbptr in the layout.
364 virtual void g();
366 virtual ~G();
367 // vtordisp{4294967292,0}
368 // MANGLING-DAG: @"??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z"
371 G g;
372 void use(G *obj) { obj->g(); }
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]
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]
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]
388 // MANGLING-DAG: @"?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
389 // MANGLING-DAG: @"??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z"
392 H h;
393 void use(H *obj) { delete obj; }
396 namespace pr17738 {
397 // These classes should have vtordispex thunks but MSVS CL miscompiles them.
398 // Just do the right thing.
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]
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]
410 // MANGLING-DAG: @"?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ"
411 int a;
412 virtual ~A();
415 A a;
416 void use(A *obj) { delete obj; }
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;
429 struct B : virtual A {
430 B();
431 int b;
434 struct C : B {
435 C();
436 virtual void f();
437 int c;
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]
445 // MANGLING-DAG: @"?f@C@pr19408@@$4PPPPPPPM@3AEXXZ"
446 D();
447 int d;
450 D::D() {}
453 namespace access {
454 struct A {
455 virtual ~A();
456 protected:
457 virtual void prot();
458 private:
459 virtual void priv();
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"
472 B b;
474 struct C : virtual B {
475 virtual ~C();
477 // MANGLING-DAG: @"?prot@B@access@@$R277PPPPPPPM@7AEXXZ"
478 // MANGLING-DAG: @"?priv@B@access@@$R077PPPPPPPM@7AEXXZ"
481 C c;
484 namespace pr19505 {
485 struct A {
486 virtual void f();
487 virtual void z();
490 struct B : A {
491 virtual void f();
494 struct C : A, B {
495 virtual void g();
498 struct X : B, virtual C {
499 X() {}
500 virtual void g();
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()
506 // MANGLING-DAG: @"??_7X@pr19505@@6BB@1@@" = {{.*}}@"?f@B@pr19505@@UAEXXZ"
507 } x;
509 void build_vftable(X *obj) { obj->g(); }
512 namespace pr19506 {
513 struct A {
514 virtual void f();
515 virtual void g();
518 struct B : A {
519 virtual void f();
522 struct C : B {};
524 struct X : C, virtual B {
525 virtual void g();
526 X() {}
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]
533 // MANGLING-DAG: @"??_7X@pr19506@@6BB@1@@" = {{.*}}@"?f@B@pr19506@@UAEXXZ"
534 } x;
536 void build_vftable(X *obj) { obj->g(); }
539 namespace pr19519 {
540 // VS2013 CL miscompiles this, just make sure we don't regress.
542 struct A {
543 virtual void f();
544 virtual void g();
547 struct B : virtual A {
548 virtual void f();
549 B();
552 struct C : virtual A {
553 virtual void g();
556 struct X : B, C {
557 X();
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]
565 // MANGLING-DAG: @"??_7X@pr19519@@6B@" = {{.*}}@"?g@C@pr19519@@$4PPPPPPPM@3AEXXZ"
568 X::X() {}