Home | History | Annotate | Download | only in CodeGenCXX
      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: @"\01?f@A@simple@@$4PPPPPPPM@A@AEXXZ"
     71 
     72   virtual ~A();
     73   // MANGLING-DAG: @"\01??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z"
     74 };
     75 
     76 A a;
     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.
    102   B() { use_somewhere_else(this); }
    103 
    104   virtual void f();
    105   // MANGLING-DAG: @"\01?f@B@simple@@$4PPPPPPPE@A@AEXXZ"
    106 
    107   // Has an implicit destructor.
    108   // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z"
    109   // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z"
    110 };
    111 
    112 B b;
    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: @"\01?f@C@simple@@$4PPPPPPPA@3AEXXZ"
    151   // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPE@A@AEXXZ"
    152   virtual ~C();
    153   // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z"
    154   // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z"
    155   // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z"
    156 };
    157 
    158 C c;
    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: @"\01?f@B@simple@@$4PPPPPPPE@3AEXXZ"
    171 };
    172 
    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: @"\01?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 
    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: @"\01?g@F@simple@@$4PPPPPPPM@3AEXXZ"{{.*}}@"\01??_EG@simple@@$4PPPPPPPM@A@AEPAXI@Z"
    217   // MANGLING-DAG: @"\01?f@E@simple@@UAEXXZ"{{.*}}@"\01??_EG@simple@@$4PPPPPPPE@7AEPAXI@Z"
    218 };
    219 
    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: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
    252 
    253   virtual ~A();
    254   // vtordisp{4294967292,0}
    255   // MANGLING-DAG: @"\01??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z"
    256 };
    257 
    258 A a;
    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: @"\01??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z"
    277 };
    278 
    279 B b;
    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: @"\01?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ"
    294   int x;
    295   virtual ~C();
    296   // MANGLING-DAG: @"\01??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z"
    297 };
    298 
    299 C c;
    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: @"\01?f@D@extended@@$R477PPPPPPPM@M@AEXXZ"
    320 
    321   virtual ~E();
    322   // MANGLING-DAG: @"\01??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z"
    323 };
    324 
    325 E e;
    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: @"\01?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ"
    340   int x;
    341   virtual ~F();
    342   // MANGLING-DAG: @"\01??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z"
    343 };
    344 
    345 F f;
    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: @"\01??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z"
    369 };
    370 
    371 G g;
    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: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
    389   // MANGLING-DAG: @"\01??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z"
    390 };
    391 
    392 H h;
    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: @"\01?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ"
    411   int a;
    412   virtual ~A();
    413 };
    414 
    415 A a;
    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: @"\01?f@C@pr19408@@$4PPPPPPPM@3AEXXZ"
    446   D();
    447   int d;
    448 };
    449 
    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: @"\01?prot@B@access@@$2PPPPPPPM@A@AEXXZ"
    467 private:
    468   virtual void priv();
    469   // MANGLING-DAG: @"\01?priv@B@access@@$0PPPPPPPM@A@AEXXZ"
    470 };
    471 
    472 B b;
    473 
    474 struct C : virtual B {
    475   virtual ~C();
    476 
    477   // MANGLING-DAG: @"\01?prot@B@access@@$R277PPPPPPPM@7AEXXZ"
    478   // MANGLING-DAG: @"\01?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 {
    499   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: @"\01??_7X@pr19505@@6BB@1@@" = {{.*}}@"\01?f@B@pr19505@@UAEXXZ"
    507 } x;
    508 
    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();
    526   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: @"\01??_7X@pr19506@@6BB@1@@" = {{.*}}@"\01?f@B@pr19506@@UAEXXZ"
    534 } x;
    535 
    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: @"\01??_7X@pr19519@@6B@" = {{.*}}@"\01?g@C@pr19519@@$4PPPPPPPM@3AEXXZ"
    566 };
    567 
    568 X::X() {}
    569 }
    570