forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			848 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			848 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
// RUN: %clang_cc1 -std=c++11 -fms-extensions -fno-rtti -emit-llvm -o %t.ll -fdump-vtable-layouts %s -triple=i386-pc-win32 >%t
 | 
						|
// RUN: FileCheck %s < %t
 | 
						|
// RUN: FileCheck --check-prefix=MANGLING %s < %t.ll
 | 
						|
 | 
						|
struct Empty { };
 | 
						|
 | 
						|
struct A {
 | 
						|
  virtual void f();
 | 
						|
  virtual void z();  // Useful to check there are no thunks for f() when appropriate.
 | 
						|
};
 | 
						|
 | 
						|
struct B {
 | 
						|
  virtual void g();
 | 
						|
};
 | 
						|
 | 
						|
struct C: virtual A {
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'C' (2 entries)
 | 
						|
  // CHECK-NEXT: 0 | void C::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'C' (1 entry)
 | 
						|
  // CHECK-NEXT: vbtable index 1, vfptr at offset 0
 | 
						|
  // CHECK-NEXT: 0 | void C::f()
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7C@@6B@"
 | 
						|
 | 
						|
  virtual void f() {}
 | 
						|
};
 | 
						|
 | 
						|
C c;
 | 
						|
void use(C *obj) { obj->f(); }
 | 
						|
 | 
						|
struct D: virtual A {
 | 
						|
  // CHECK-LABEL: VFTable for 'D' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void D::h()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'D' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void D::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'D' (2 entries).
 | 
						|
  // CHECK-NEXT: via vfptr at offset 0
 | 
						|
  // CHECK-NEXT: 0 | void D::h()
 | 
						|
  // CHECK-NEXT: via vbtable index 1, vfptr at offset 0
 | 
						|
  // CHECK-NEXT: 0 | void D::f()
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7D@@6B0@@"
 | 
						|
  // MANGLING-DAG: @"??_7D@@6BA@@@"
 | 
						|
 | 
						|
  virtual void f();
 | 
						|
  virtual void h();
 | 
						|
};
 | 
						|
 | 
						|
D d;
 | 
						|
void use(D *obj) { obj->h(); }
 | 
						|
 | 
						|
namespace Test1 {
 | 
						|
 | 
						|
struct X { int x; };
 | 
						|
 | 
						|
// X and A get reordered in the layout since X doesn't have a vfptr while A has.
 | 
						|
struct Y : X, A { };
 | 
						|
// MANGLING-DAG: @"??_7Y@Test1@@6B@"
 | 
						|
 | 
						|
struct Z : virtual Y {
 | 
						|
  Z();
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test1::Y' in 'Test1::Z' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void A::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-NOT: VFTable indices for 'Test1::Z'
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7Z@Test1@@6B@"
 | 
						|
};
 | 
						|
 | 
						|
Z::Z() {}
 | 
						|
}
 | 
						|
 | 
						|
namespace Test2 {
 | 
						|
 | 
						|
struct X: virtual A, virtual B {
 | 
						|
  // CHECK-LABEL: VFTable for 'Test2::X' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void Test2::X::h()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test2::X' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void A::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'B' in 'Test2::X' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void B::g()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'Test2::X' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void Test2::X::h()
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7X@Test2@@6B01@@"
 | 
						|
  // MANGLING-DAG: @"??_7X@Test2@@6BA@@@"
 | 
						|
  // MANGLING-DAG: @"??_7X@Test2@@6BB@@@"
 | 
						|
 | 
						|
  virtual void h();
 | 
						|
};
 | 
						|
 | 
						|
X x;
 | 
						|
void use(X *obj) { obj->h(); }
 | 
						|
}
 | 
						|
 | 
						|
namespace Test3 {
 | 
						|
 | 
						|
struct X : virtual A {
 | 
						|
  // MANGLING-DAG: @"??_7X@Test3@@6B@"
 | 
						|
};
 | 
						|
 | 
						|
struct Y: virtual X {
 | 
						|
  Y();
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test3::X' in 'Test3::Y' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void A::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-NOT: VFTable indices for 'Test3::Y'
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7Y@Test3@@6B@"
 | 
						|
};
 | 
						|
 | 
						|
Y::Y() {}
 | 
						|
}
 | 
						|
 | 
						|
namespace Test4 {
 | 
						|
 | 
						|
struct X: virtual C {
 | 
						|
  X();
 | 
						|
  // This one's interesting. C::f expects (A*) to be passed as 'this' and does
 | 
						|
  // ECX-=4 to cast to (C*). In X, C and A vbases are reordered, so the thunk
 | 
						|
  // should pass a pointer to the end of X in order
 | 
						|
  // for ECX-=4 to point at the C part.
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'C' in 'Test4::X' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void C::f()
 | 
						|
  // CHECK-NEXT:     [this adjustment: 8 non-virtual]
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'void C::f()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [this adjustment: 8 non-virtual]
 | 
						|
 | 
						|
  // CHECK-NOT: VFTable indices for 'Test4::X'
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7X@Test4@@6B@"
 | 
						|
 | 
						|
  // Also check the mangling of the thunk.
 | 
						|
  // MANGLING-DAG: define linkonce_odr dso_local x86_thiscallcc void @"?f@C@@WPPPPPPPI@AEXXZ"
 | 
						|
};
 | 
						|
 | 
						|
X::X() {}
 | 
						|
}
 | 
						|
 | 
						|
namespace Test5 {
 | 
						|
 | 
						|
// New methods are added to the base's vftable.
 | 
						|
struct X : A {
 | 
						|
  // MANGLING-DAG: @"??_7X@Test5@@6B@"
 | 
						|
  virtual void g();
 | 
						|
};
 | 
						|
 | 
						|
struct Y : virtual X {
 | 
						|
  // CHECK-LABEL: VFTable for 'Test5::Y' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void Test5::Y::h()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test5::X' in 'Test5::Y' (3 entries).
 | 
						|
  // CHECK-NEXT: 0 | void A::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
  // CHECK-NEXT: 2 | void Test5::X::g()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'Test5::Y' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void Test5::Y::h()
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7Y@Test5@@6B01@@"
 | 
						|
  // MANGLING-DAG: @"??_7Y@Test5@@6BX@1@@"
 | 
						|
 | 
						|
  virtual void h();
 | 
						|
};
 | 
						|
 | 
						|
Y y;
 | 
						|
void use(Y *obj) { obj->h(); }
 | 
						|
}
 | 
						|
 | 
						|
namespace Test6 {
 | 
						|
 | 
						|
struct X : A, virtual Empty {
 | 
						|
  X();
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test6::X' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void A::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-NOT: VFTable indices for 'Test6::X'
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7X@Test6@@6B@"
 | 
						|
};
 | 
						|
 | 
						|
X::X() {}
 | 
						|
}
 | 
						|
 | 
						|
namespace Test7 {
 | 
						|
 | 
						|
struct X : C {
 | 
						|
  // MANGLING-DAG: @"??_7X@Test7@@6B@"
 | 
						|
};
 | 
						|
 | 
						|
struct Y : virtual X {
 | 
						|
  Y();
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'C' in 'Test7::X' in 'Test7::Y' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void C::f()
 | 
						|
  // CHECK-NEXT:     [this adjustment: 8 non-virtual]
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'void C::f()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [this adjustment: 8 non-virtual]
 | 
						|
 | 
						|
  // CHECK-NOT: VFTable indices for 'Test7::Y'
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7Y@Test7@@6B@"
 | 
						|
};
 | 
						|
 | 
						|
Y::Y() {}
 | 
						|
}
 | 
						|
 | 
						|
namespace Test8 {
 | 
						|
 | 
						|
// This is a typical diamond inheritance with a shared 'A' vbase.
 | 
						|
struct X : D, C {
 | 
						|
  // CHECK-LABEL: VFTable for 'D' in 'Test8::X' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void D::h()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'D' in 'Test8::X' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void Test8::X::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'Test8::X' (1 entry).
 | 
						|
  // CHECK-NEXT: via vbtable index 1, vfptr at offset 0
 | 
						|
  // CHECK-NEXT: 0 | void Test8::X::f()
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7X@Test8@@6BA@@@"
 | 
						|
  // MANGLING-DAG: @"??_7X@Test8@@6BD@@@"
 | 
						|
 | 
						|
  virtual void f();
 | 
						|
};
 | 
						|
 | 
						|
X x;
 | 
						|
void use(X *obj) { obj->f(); }
 | 
						|
 | 
						|
// Another diamond inheritance which led to AST crashes.
 | 
						|
struct Y : virtual A {};
 | 
						|
 | 
						|
struct Z : Y, C {
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test8::Y' in 'Test8::Z' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void Test8::Z::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'Test8::Z' (1 entry).
 | 
						|
  // CHECK-NEXT: via vbtable index 1, vfptr at offset 0
 | 
						|
  // CHECK-NEXT: 0 | void Test8::Z::f()
 | 
						|
  virtual void f();
 | 
						|
};
 | 
						|
Z z;
 | 
						|
void use(Z *obj) { obj->f(); }
 | 
						|
 | 
						|
// Another diamond inheritance which we miscompiled (PR18967).
 | 
						|
struct W : virtual A {
 | 
						|
  virtual void bar();
 | 
						|
};
 | 
						|
 | 
						|
struct T : W, C {
 | 
						|
  // CHECK-LABEL: VFTable for 'Test8::W' in 'Test8::T' (1 entry)
 | 
						|
  // CHECK-NEXT: 0 | void Test8::T::bar()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test8::W' in 'Test8::T' (2 entries)
 | 
						|
  // CHECK-NEXT: 0 | void C::f()
 | 
						|
  // CHECK-NEXT:     [this adjustment: -4 non-virtual]
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'void C::f()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [this adjustment: -4 non-virtual]
 | 
						|
  virtual void bar();
 | 
						|
  int field;
 | 
						|
};
 | 
						|
T t;
 | 
						|
void use(T *obj) { obj->bar(); }
 | 
						|
}
 | 
						|
 | 
						|
namespace Test9 {
 | 
						|
 | 
						|
struct X : A { };
 | 
						|
 | 
						|
struct Y : virtual X {
 | 
						|
  // CHECK-LABEL: VFTable for 'Test9::Y' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void Test9::Y::h()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test9::X' in 'Test9::Y' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void A::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'Test9::Y' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void Test9::Y::h()
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7Y@Test9@@6B01@@"
 | 
						|
  // MANGLING-DAG: @"??_7Y@Test9@@6BX@1@@"
 | 
						|
 | 
						|
  virtual void h();
 | 
						|
};
 | 
						|
 | 
						|
Y y;
 | 
						|
void use(Y *obj) { obj->h(); }
 | 
						|
 | 
						|
struct Z : Y, virtual B {
 | 
						|
  Z();
 | 
						|
  // CHECK-LABEL: VFTable for 'Test9::Y' in 'Test9::Z' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void Test9::Y::h()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void A::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'B' in 'Test9::Z' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void B::g()
 | 
						|
 | 
						|
  // CHECK-NOT: VFTable indices for 'Test9::Z'
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7Z@Test9@@6BX@1@@"
 | 
						|
  // MANGLING-DAG: @"??_7Z@Test9@@6BY@1@@"
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7Z@Test9@@6B@"
 | 
						|
};
 | 
						|
 | 
						|
Z::Z() {}
 | 
						|
 | 
						|
struct W : Z, D, virtual A, virtual B {
 | 
						|
  W();
 | 
						|
  // CHECK-LABEL: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::W' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void Test9::Y::h()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::W' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void A::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'B' in 'Test9::Z' in 'Test9::W' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void B::g()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'D' in 'Test9::W' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void D::h()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'D' in 'Test9::W' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void D::f()
 | 
						|
  // CHECK-NEXT:     [this adjustment: -8 non-virtual]
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'void D::f()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [this adjustment: -8 non-virtual]
 | 
						|
 | 
						|
  // CHECK-NOT: VFTable indices for 'Test9::W'
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7W@Test9@@6BA@@@"
 | 
						|
  // MANGLING-DAG: @"??_7W@Test9@@6BD@@@"
 | 
						|
  // MANGLING-DAG: @"??_7W@Test9@@6BX@1@@"
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7W@Test9@@6B@"
 | 
						|
  // MANGLING-DAG: @"??_7W@Test9@@6BY@1@@"
 | 
						|
};
 | 
						|
 | 
						|
W::W() {}
 | 
						|
 | 
						|
struct T : Z, D, virtual A, virtual B {
 | 
						|
  // CHECK-LABEL: VFTable for 'Test9::Y' in 'Test9::Z' in 'Test9::T' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void Test9::T::h()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test9::X' in 'Test9::Y' in 'Test9::Z' in 'Test9::T' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void Test9::T::f()
 | 
						|
  // CHECK-NEXT: 1 | void Test9::T::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'B' in 'Test9::Z' in 'Test9::T' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void Test9::T::g()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'D' in 'Test9::T' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void Test9::T::h()
 | 
						|
  // CHECK-NEXT:     [this adjustment: -8 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'void Test9::T::h()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [this adjustment: -8 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'D' in 'Test9::T' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void Test9::T::f()
 | 
						|
  // CHECK-NEXT:     [this adjustment: -8 non-virtual]
 | 
						|
  // CHECK-NEXT: 1 | void Test9::T::z()
 | 
						|
  // CHECK-NEXT:     [this adjustment: -8 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'void Test9::T::f()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [this adjustment: -8 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'void Test9::T::z()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [this adjustment: -8 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'Test9::T' (4 entries).
 | 
						|
  // CHECK-NEXT: via vfptr at offset 0
 | 
						|
  // CHECK-NEXT: 0 | void Test9::T::h()
 | 
						|
  // CHECK-NEXT: via vbtable index 1, vfptr at offset 0
 | 
						|
  // CHECK-NEXT: 0 | void Test9::T::f()
 | 
						|
  // CHECK-NEXT: 1 | void Test9::T::z()
 | 
						|
  // CHECK-NEXT: via vbtable index 2, vfptr at offset 0
 | 
						|
  // CHECK-NEXT: 0 | void Test9::T::g()
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7T@Test9@@6BA@@@"
 | 
						|
  // MANGLING-DAG: @"??_7T@Test9@@6BD@@@"
 | 
						|
  // MANGLING-DAG: @"??_7T@Test9@@6BX@1@@"
 | 
						|
 | 
						|
  // MANGLING-DAG: @"??_7T@Test9@@6B@"
 | 
						|
  // MANGLING-DAG: @"??_7T@Test9@@6BY@1@@"
 | 
						|
 | 
						|
  virtual void f();
 | 
						|
  virtual void g();
 | 
						|
  virtual void h();
 | 
						|
  virtual void z();
 | 
						|
};
 | 
						|
 | 
						|
T t;
 | 
						|
void use(T *obj) { obj->f(); }
 | 
						|
}
 | 
						|
 | 
						|
namespace Test10 {
 | 
						|
struct X : virtual C, virtual A {
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'C' in 'Test10::X' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | void Test10::X::f()
 | 
						|
  // CHECK-NEXT: 1 | void A::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'Test10::X' (1 entry).
 | 
						|
  // CHECK-NEXT: via vbtable index 1, vfptr at offset 0
 | 
						|
  // CHECK-NEXT: 0 | void Test10::X::f()
 | 
						|
  virtual void f();
 | 
						|
};
 | 
						|
 | 
						|
void X::f() {}
 | 
						|
X x;
 | 
						|
void use(X *obj) { obj->f(); }
 | 
						|
}
 | 
						|
 | 
						|
namespace Test11 {
 | 
						|
struct X : virtual A {};
 | 
						|
struct Y { virtual void g(); };
 | 
						|
 | 
						|
struct Z : virtual X, Y {
 | 
						|
  // MANGLING-DAG: @"??_7Z@Test11@@6BY@1@@"
 | 
						|
  // MANGLING-DAG: @"??_7Z@Test11@@6BX@1@@"
 | 
						|
};
 | 
						|
 | 
						|
Z z;
 | 
						|
 | 
						|
struct W : virtual X, A {};
 | 
						|
 | 
						|
// Used to crash, PR17748.
 | 
						|
W w;
 | 
						|
}
 | 
						|
 | 
						|
namespace Test12 {
 | 
						|
struct X : B, A { };
 | 
						|
 | 
						|
struct Y : X {
 | 
						|
  virtual void f();  // Overrides A::f.
 | 
						|
};
 | 
						|
 | 
						|
struct Z : virtual Y {
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test12::X' in 'Test12::Y' in 'Test12::Z' (2 entries).
 | 
						|
  // CHECK-NEXT:   0 | void Test12::Y::f()
 | 
						|
  // CHECK-NEXT:   1 | void A::z()
 | 
						|
 | 
						|
  int z;
 | 
						|
  // MANGLING-DAG: @"??_7Z@Test12@@6BA@@@" = {{.*}}@"?f@Y@Test12@@UAEXXZ"
 | 
						|
};
 | 
						|
 | 
						|
struct W : Z {
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'Test12::X' in 'Test12::Y' in 'Test12::Z' in 'Test12::W' (2 entries).
 | 
						|
  // CHECK-NEXT:   0 | void Test12::Y::f()
 | 
						|
  // CHECK-NEXT:   1 | void A::z()
 | 
						|
  W();
 | 
						|
 | 
						|
  int w;
 | 
						|
  // MANGLING-DAG: @"??_7W@Test12@@6BA@@@" = {{.*}}@"?f@Y@Test12@@UAEXXZ"
 | 
						|
};
 | 
						|
 | 
						|
W::W() {}
 | 
						|
}
 | 
						|
 | 
						|
namespace vdtors {
 | 
						|
struct X {
 | 
						|
  virtual ~X();
 | 
						|
  virtual void zzz();
 | 
						|
};
 | 
						|
 | 
						|
struct Y : virtual X {
 | 
						|
  // CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::Y' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | vdtors::Y::~Y() [scalar deleting]
 | 
						|
  // CHECK-NEXT: 1 | void vdtors::X::zzz()
 | 
						|
 | 
						|
  // CHECK-NOT: Thunks for 'vdtors::Y::~Y()'
 | 
						|
  virtual ~Y();
 | 
						|
};
 | 
						|
 | 
						|
Y y;
 | 
						|
void use(Y *obj) { delete obj; }
 | 
						|
 | 
						|
struct Z {
 | 
						|
  virtual void z();
 | 
						|
};
 | 
						|
 | 
						|
struct W : Z, X {
 | 
						|
  // Implicit virtual dtor.
 | 
						|
};
 | 
						|
 | 
						|
struct U : virtual W {
 | 
						|
  // CHECK-LABEL: VFTable for 'vdtors::Z' in 'vdtors::W' in 'vdtors::U' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void vdtors::Z::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::U' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | vdtors::U::~U() [scalar deleting]
 | 
						|
  // CHECK-NEXT:     [this adjustment: -4 non-virtual]
 | 
						|
  // CHECK-NEXT: 1 | void vdtors::X::zzz()
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'vdtors::U::~U()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [this adjustment: -4 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'vdtors::U' (1 entry).
 | 
						|
  // CHECK-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 --
 | 
						|
  // CHECK-NEXT: 0 | vdtors::U::~U() [scalar deleting]
 | 
						|
  virtual ~U();
 | 
						|
};
 | 
						|
 | 
						|
U u;
 | 
						|
void use(U *obj) { delete obj; }
 | 
						|
 | 
						|
struct V : virtual W {
 | 
						|
  // CHECK-LABEL: VFTable for 'vdtors::Z' in 'vdtors::W' in 'vdtors::V' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | void vdtors::Z::z()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::W' in 'vdtors::V' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | vdtors::V::~V() [scalar deleting]
 | 
						|
  // CHECK-NEXT:     [this adjustment: -4 non-virtual]
 | 
						|
  // CHECK-NEXT: 1 | void vdtors::X::zzz()
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'vdtors::V::~V()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [this adjustment: -4 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'vdtors::V' (1 entry).
 | 
						|
  // CHECK-NEXT: -- accessible via vbtable index 1, vfptr at offset 4 --
 | 
						|
  // CHECK-NEXT: 0 | vdtors::V::~V() [scalar deleting]
 | 
						|
};
 | 
						|
 | 
						|
V v;
 | 
						|
void use(V *obj) { delete obj; }
 | 
						|
 | 
						|
struct T : virtual X {
 | 
						|
  virtual ~T();
 | 
						|
};
 | 
						|
 | 
						|
struct P : T, Y {
 | 
						|
  // CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::T' in 'vdtors::P' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | vdtors::P::~P() [scalar deleting]
 | 
						|
  // CHECK-NEXT: 1 | void vdtors::X::zzz()
 | 
						|
 | 
						|
  // CHECK-NOT: Thunks for 'vdtors::P::~P()'
 | 
						|
  virtual ~P();
 | 
						|
};
 | 
						|
 | 
						|
P p;
 | 
						|
void use(P *obj) { delete obj; }
 | 
						|
 | 
						|
struct Q {
 | 
						|
  virtual ~Q();
 | 
						|
};
 | 
						|
 | 
						|
// PR19172: Yet another diamond we miscompiled.
 | 
						|
struct R : virtual Q, X {
 | 
						|
  // CHECK-LABEL: VFTable for 'vdtors::Q' in 'vdtors::R' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting]
 | 
						|
  // CHECK-NEXT:     [this adjustment: -8 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'vdtors::R::~R()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [this adjustment: -8 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'vdtors::X' in 'vdtors::R' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting]
 | 
						|
  // CHECK-NEXT: 1 | void vdtors::X::zzz()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'vdtors::R' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | vdtors::R::~R() [scalar deleting]
 | 
						|
  virtual ~R();
 | 
						|
};
 | 
						|
 | 
						|
R r;
 | 
						|
void use(R *obj) { delete obj; }
 | 
						|
}
 | 
						|
 | 
						|
namespace return_adjustment {
 | 
						|
 | 
						|
struct X : virtual A {
 | 
						|
  virtual void f();
 | 
						|
};
 | 
						|
 | 
						|
struct Y : virtual A, virtual X {
 | 
						|
  virtual void f();
 | 
						|
};
 | 
						|
 | 
						|
struct Z {
 | 
						|
  virtual A* foo();
 | 
						|
};
 | 
						|
 | 
						|
struct W : Z {
 | 
						|
  // CHECK-LABEL: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | return_adjustment::X *return_adjustment::W::foo()
 | 
						|
  // CHECK-NEXT:     [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
 | 
						|
  // CHECK-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'return_adjustment::X *return_adjustment::W::foo()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'return_adjustment::W' (1 entry).
 | 
						|
  // CHECK-NEXT: 1 | return_adjustment::X *return_adjustment::W::foo()
 | 
						|
 | 
						|
  virtual X* foo();
 | 
						|
};
 | 
						|
 | 
						|
W w;
 | 
						|
void use(W *obj) { obj->foo(); }
 | 
						|
 | 
						|
struct T : W {
 | 
						|
  // CHECK-LABEL: VFTable for 'return_adjustment::Z' in 'return_adjustment::W' in 'return_adjustment::T' (3 entries).
 | 
						|
  // CHECK-NEXT: 0 | return_adjustment::Y *return_adjustment::T::foo()
 | 
						|
  // CHECK-NEXT:     [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
 | 
						|
  // CHECK-NEXT: 1 | return_adjustment::Y *return_adjustment::T::foo()
 | 
						|
  // CHECK-NEXT:     [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual]
 | 
						|
  // CHECK-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'return_adjustment::Y *return_adjustment::T::foo()' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | [return adjustment (to type 'struct A *'): vbase #1, 0 non-virtual]
 | 
						|
  // CHECK-NEXT: 1 | [return adjustment (to type 'struct return_adjustment::X *'): vbase #2, 0 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'return_adjustment::T' (1 entry).
 | 
						|
  // CHECK-NEXT: 2 | return_adjustment::Y *return_adjustment::T::foo()
 | 
						|
 | 
						|
  virtual Y* foo();
 | 
						|
};
 | 
						|
 | 
						|
T t;
 | 
						|
void use(T *obj) { obj->foo(); }
 | 
						|
 | 
						|
struct U : virtual A {
 | 
						|
  virtual void g();  // adds a vfptr
 | 
						|
};
 | 
						|
 | 
						|
struct V : Z {
 | 
						|
  // CHECK-LABEL: VFTable for 'return_adjustment::Z' in 'return_adjustment::V' (2 entries).
 | 
						|
  // CHECK-NEXT: 0 | return_adjustment::U *return_adjustment::V::foo()
 | 
						|
  // CHECK-NEXT:     [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual]
 | 
						|
  // CHECK-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo()
 | 
						|
 | 
						|
  // CHECK-LABEL: Thunks for 'return_adjustment::U *return_adjustment::V::foo()' (1 entry).
 | 
						|
  // CHECK-NEXT: 0 | [return adjustment (to type 'struct A *'): vbptr at offset 4, vbase #1, 0 non-virtual]
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable indices for 'return_adjustment::V' (1 entry).
 | 
						|
  // CHECK-NEXT: 1 | return_adjustment::U *return_adjustment::V::foo()
 | 
						|
 | 
						|
  virtual U* foo();
 | 
						|
};
 | 
						|
 | 
						|
V v;
 | 
						|
void use(V *obj) { obj->foo(); }
 | 
						|
}
 | 
						|
 | 
						|
namespace pr17748 {
 | 
						|
struct A {
 | 
						|
  virtual void f() {}
 | 
						|
};
 | 
						|
 | 
						|
struct B : virtual A {
 | 
						|
  B() {}
 | 
						|
};
 | 
						|
 | 
						|
struct C : virtual B, A {
 | 
						|
  C() {}
 | 
						|
};
 | 
						|
C c;
 | 
						|
 | 
						|
// MANGLING-DAG: @"??_7A@pr17748@@6B@"
 | 
						|
// MANGLING-DAG: @"??_7B@pr17748@@6B@"
 | 
						|
// MANGLING-DAG: @"??_7C@pr17748@@6BA@1@@"
 | 
						|
// MANGLING-DAG: @"??_7C@pr17748@@6BB@1@@"
 | 
						|
}
 | 
						|
 | 
						|
namespace pr19066 {
 | 
						|
struct X : virtual B {};
 | 
						|
 | 
						|
struct Y : virtual X, B {
 | 
						|
  Y();
 | 
						|
  // CHECK-LABEL: VFTable for 'B' in 'pr19066::X' in 'pr19066::Y' (1 entry).
 | 
						|
  // CHECK-NEXT:  0 | void B::g()
 | 
						|
 | 
						|
  // CHECK-LABEL: VFTable for 'B' in 'pr19066::Y' (1 entry).
 | 
						|
  // CHECK-NEXT:  0 | void B::g()
 | 
						|
};
 | 
						|
 | 
						|
Y::Y() {}
 | 
						|
}
 | 
						|
 | 
						|
namespace pr19240 {
 | 
						|
struct A {
 | 
						|
  virtual void c();
 | 
						|
};
 | 
						|
 | 
						|
struct B : virtual A {
 | 
						|
  virtual void c();
 | 
						|
};
 | 
						|
 | 
						|
struct C { };
 | 
						|
 | 
						|
struct D : virtual A, virtual C, B {};
 | 
						|
 | 
						|
D obj;
 | 
						|
 | 
						|
// Each MDC only has one vftable.
 | 
						|
 | 
						|
// MANGLING-DAG: @"??_7D@pr19240@@6B@"
 | 
						|
// MANGLING-DAG: @"??_7A@pr19240@@6B@"
 | 
						|
// MANGLING-DAG: @"??_7B@pr19240@@6B@"
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
namespace pr19408 {
 | 
						|
// This test is a non-vtordisp version of the reproducer for PR19408.
 | 
						|
struct X : virtual A {
 | 
						|
  int x;
 | 
						|
};
 | 
						|
 | 
						|
struct Y : X {
 | 
						|
  virtual void f();
 | 
						|
  int y;
 | 
						|
};
 | 
						|
 | 
						|
struct Z : Y {
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'pr19408::X' in 'pr19408::Y' in 'pr19408::Z' (2 entries).
 | 
						|
  // CHECK-NEXT:   0 | void pr19408::Y::f()
 | 
						|
  // CHECK-NEXT:       [this adjustment: -4 non-virtual]
 | 
						|
  // CHECK-NEXT:   1 | void A::z()
 | 
						|
 | 
						|
  Z();
 | 
						|
  int z;
 | 
						|
  // MANGLING-DAG: @"??_7Z@pr19408@@6B@" = {{.*}}@"?f@Y@pr19408@@W3AEXXZ"
 | 
						|
};
 | 
						|
 | 
						|
Z::Z() {}
 | 
						|
 | 
						|
struct W : B, Y {
 | 
						|
  // CHECK-LABEL: VFTable for 'A' in 'pr19408::X' in 'pr19408::Y' in 'pr19408::W' (2 entries).
 | 
						|
  // CHECK-NEXT:   0 | void pr19408::Y::f()
 | 
						|
  // CHECK-NEXT:       [this adjustment: -4 non-virtual]
 | 
						|
  // CHECK-NEXT:   1 | void A::z()
 | 
						|
 | 
						|
  W();
 | 
						|
  int w;
 | 
						|
  // MANGLING-DAG: @"??_7W@pr19408@@6BY@1@@" = {{.*}}@"?f@Y@pr19408@@W3AEXXZ"
 | 
						|
};
 | 
						|
 | 
						|
W::W() {}
 | 
						|
}
 | 
						|
 | 
						|
namespace Test13 {
 | 
						|
struct A {
 | 
						|
  virtual void f();
 | 
						|
};
 | 
						|
struct __declspec(dllexport) B : virtual A {
 | 
						|
  virtual void f() = 0;
 | 
						|
  // MANGLING-DAG: @"??_7B@Test13@@6B@" = weak_odr dllexport unnamed_addr constant { [1 x i8*] } { [1 x i8*] [i8* bitcast (void ()* @_purecall to i8*)] }
 | 
						|
};
 | 
						|
}
 | 
						|
 | 
						|
namespace pr21031_1 {
 | 
						|
// This ordering of base specifiers regressed in r202425.
 | 
						|
struct A { virtual void f(void); };
 | 
						|
struct B : virtual A { virtual void g(void); };
 | 
						|
struct C : virtual A, B { C(); };
 | 
						|
C::C() {}
 | 
						|
 | 
						|
// CHECK-LABEL: VFTable for 'pr21031_1::A' in 'pr21031_1::B' in 'pr21031_1::C' (1 entry)
 | 
						|
// CHECK-NEXT:   0 | void pr21031_1::A::f()
 | 
						|
 | 
						|
// CHECK-LABEL: VFTable for 'pr21031_1::B' in 'pr21031_1::C' (1 entry)
 | 
						|
// CHECK-NEXT:   0 | void pr21031_1::B::g()
 | 
						|
 | 
						|
// MANGLING-DAG: @"??_7C@pr21031_1@@6BB@1@@" = {{.*}} constant { [1 x i8*] }
 | 
						|
// MANGLING-DAG: @"??_7C@pr21031_1@@6B@" = {{.*}} constant { [1 x i8*] }
 | 
						|
}
 | 
						|
 | 
						|
namespace pr21031_2 {
 | 
						|
struct A { virtual void f(void); };
 | 
						|
struct B : virtual A { virtual void g(void); };
 | 
						|
struct C : B, virtual A { C(); };
 | 
						|
C::C() {}
 | 
						|
 | 
						|
// CHECK-LABEL: VFTable for 'pr21031_2::B' in 'pr21031_2::C' (1 entry)
 | 
						|
// CHECK-NEXT:   0 | void pr21031_2::B::g()
 | 
						|
 | 
						|
// CHECK-LABEL: VFTable for 'pr21031_2::A' in 'pr21031_2::B' in 'pr21031_2::C' (1 entry)
 | 
						|
// CHECK-NEXT:   0 | void pr21031_2::A::f()
 | 
						|
 | 
						|
// MANGLING-DAG: @"??_7C@pr21031_2@@6BA@1@@" = {{.*}} constant { [1 x i8*] }
 | 
						|
// MANGLING-DAG: @"??_7C@pr21031_2@@6BB@1@@" = {{.*}} constant { [1 x i8*] }
 | 
						|
}
 | 
						|
 | 
						|
namespace pr21062_1 {
 | 
						|
struct A { virtual void f(); };
 | 
						|
struct B {};
 | 
						|
struct C : virtual B {};
 | 
						|
struct D : virtual C, virtual B, virtual A { D();};
 | 
						|
D::D() {}
 | 
						|
 | 
						|
// CHECK-LABEL: VFTable for 'pr21062_1::A' in 'pr21062_1::D' (1 entry)
 | 
						|
// CHECK-NEXT:   0 | void pr21062_1::A::f()
 | 
						|
 | 
						|
// MANGLING-DAG: @"??_7D@pr21062_1@@6B@" = {{.*}} constant { [1 x i8*] }
 | 
						|
}
 | 
						|
 | 
						|
namespace pr21062_2 {
 | 
						|
struct A { virtual void f(); };
 | 
						|
struct B {};
 | 
						|
struct C : virtual B {};
 | 
						|
struct D : C, virtual B, virtual A { D(); };
 | 
						|
D::D() {}
 | 
						|
 | 
						|
// CHECK-LABEL: VFTable for 'pr21062_2::A' in 'pr21062_2::D' (1 entry)
 | 
						|
// CHECK-NEXT:   0 | void pr21062_2::A::f()
 | 
						|
 | 
						|
// MANGLING-DAG: @"??_7D@pr21062_2@@6B@" = {{.*}} constant { [1 x i8*] }
 | 
						|
}
 | 
						|
 | 
						|
namespace pr21064 {
 | 
						|
struct A {};
 | 
						|
struct B { virtual void f(); };
 | 
						|
struct C : virtual A, virtual B {};
 | 
						|
struct D : virtual A, virtual C { D(); };
 | 
						|
D::D() {}
 | 
						|
// CHECK-LABEL: VFTable for 'pr21064::B' in 'pr21064::C' in 'pr21064::D' (1 entry)
 | 
						|
// CHECK-NEXT:   0 | void pr21064::B::f()
 | 
						|
 | 
						|
// MANGLING-DAG: @"??_7D@pr21064@@6B@" = {{.*}} constant { [1 x i8*] }
 | 
						|
}
 |