1 // RUN: %clang_cc1 -fno-rtti -emit-llvm -o %t.ll -fdump-vtable-layouts %s -cxx-abi microsoft -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 vdtors { 434 struct X { 435 virtual ~X(); 436 virtual void zzz(); 437 }; 438 439 struct Y : virtual X { 440 // VDTORS-Y: VFTable for 'vdtors::X' in 'vdtors::Y' (2 entries). 441 // VDTORS-Y-NEXT: 0 | vdtors::Y::~Y() [scalar deleting] 442 // VDTORS-Y-NEXT: 1 | void vdtors::X::zzz() 443 444 // VDTORS-Y-NOT: Thunks for 'vdtors::Y::~Y()' 445 virtual ~Y(); 446 }; 447 448 Y y; 449 450 struct Z { 451 virtual void z(); 452 }; 453 454 struct W : Z, X { 455 // Implicit virtual dtor. 456 }; 457 458 struct U : virtual W { 459 // VDTORS-U: VFTable for 'vdtors::Z' in 'vdtors::W' in 'vdtors::U' (1 entries). 460 // VDTORS-U-NEXT: 0 | void vdtors::Z::z() 461 462 // VDTORS-U: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::U' (2 entries). 463 // VDTORS-U-NEXT: 0 | vdtors::U::~U() [scalar deleting] 464 // VDTORS-U-NEXT: [this adjustment: -4 non-virtual] 465 // VDTORS-U-NEXT: 1 | void vdtors::X::zzz() 466 467 // VDTORS-U: Thunks for 'vdtors::W::~W()' (1 entry). 468 // VDTORS-U-NEXT: 0 | [this adjustment: -4 non-virtual] 469 470 // VDTORS-U: VFTable indices for 'vdtors::U' (1 entries). 471 // VDTORS-U-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 -- 472 // VDTORS-U-NEXT: 0 | vdtors::U::~U() [scalar deleting] 473 virtual ~U(); 474 }; 475 476 U u; 477 478 struct V : virtual W { 479 // VDTORS-V: VFTable for 'vdtors::Z' in 'vdtors::W' in 'vdtors::V' (1 entries). 480 // VDTORS-V-NEXT: 0 | void vdtors::Z::z() 481 482 // VDTORS-V: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::V' (2 entries). 483 // VDTORS-V-NEXT: 0 | vdtors::V::~V() [scalar deleting] 484 // VDTORS-V-NEXT: [this adjustment: -4 non-virtual] 485 // VDTORS-V-NEXT: 1 | void vdtors::X::zzz() 486 487 // VDTORS-V: Thunks for 'vdtors::W::~W()' (1 entry). 488 // VDTORS-V-NEXT: 0 | [this adjustment: -4 non-virtual] 489 490 // VDTORS-V: VFTable indices for 'vdtors::V' (1 entries). 491 // VDTORS-V-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 -- 492 // VDTORS-V-NEXT: 0 | vdtors::V::~V() [scalar deleting] 493 }; 494 495 V v; 496 497 struct T : virtual X { 498 virtual ~T(); 499 }; 500 501 struct P : T, Y { 502 // VDTORS-P: VFTable for 'vdtors::X' in 'vdtors::T' in 'vdtors::P' (2 entries). 503 // VDTORS-P-NEXT: 0 | vdtors::P::~P() [scalar deleting] 504 // VDTORS-P-NEXT: 1 | void vdtors::X::zzz() 505 506 // VDTORS-P-NOT: Thunks for 'vdtors::P::~P()' 507 virtual ~P(); 508 }; 509 510 P p; 511 512 } 513 514 namespace return_adjustment { 515 516 struct X : virtual A { 517 virtual void f(); 518 }; 519 520 struct Y : virtual A, virtual X { 521 virtual void f(); 522 }; 523 524 struct Z { 525 virtual A* foo(); 526 }; 527 528 struct W : Z { 529 // RET-W: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' (2 entries). 530 // RET-W-NEXT: 0 | return_adjustment::X *return_adjustment::W::foo() 531 // RET-W-NEXT: [return adjustment: vbase #1, 0 non-virtual] 532 // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo() 533 534 // RET-W: VFTable indices for 'return_adjustment::W' (1 entries). 535 // RET-W-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo() 536 537 virtual X* foo(); 538 }; 539 540 W y; 541 542 struct T : W { 543 // RET-T: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' in 'return_adjustment::T' (3 entries). 544 // RET-T-NEXT: 0 | return_adjustment::Y *return_adjustment::T::foo() 545 // RET-T-NEXT: [return adjustment: vbase #1, 0 non-virtual] 546 // RET-T-NEXT: 1 | return_adjustment::Y *return_adjustment::T::foo() 547 // RET-T-NEXT: [return adjustment: vbase #2, 0 non-virtual] 548 // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo() 549 550 // RET-T: VFTable indices for 'return_adjustment::T' (1 entries). 551 // RET-T-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo() 552 553 virtual Y* foo(); 554 }; 555 556 T t; 557 558 struct U : virtual A { 559 virtual void g(); // adds a vfptr 560 }; 561 562 struct V : Z { 563 // RET-V: VFTable for 'return_adjustment::Z' in 'return_adjustment::V' (2 entries). 564 // RET-V-NEXT: 0 | return_adjustment::U *return_adjustment::V::foo() 565 // RET-V-NEXT: [return adjustment: vbptr at offset 4, vbase #1, 0 non-virtual] 566 // RET-V-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo() 567 568 // RET-V: VFTable indices for 'return_adjustment::V' (1 entries). 569 // RET-V-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo() 570 571 virtual U* foo(); 572 }; 573 574 V v; 575 } 576