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