blob: 4a0a76dbf2c5c7f84d5a2a08091aa2c2d752126f [file] [log] [blame]
// RUN: %clang_cc1 %s -fno-rtti -cxx-abi microsoft -triple=i386-pc-win32 -emit-llvm -fdump-vtable-layouts -o - > %t 2>&1
// RUN: FileCheck --check-prefix=EMITS-VFTABLE %s < %t
// RUN: FileCheck --check-prefix=NO-VFTABLE %s < %t
// RUN: FileCheck --check-prefix=CHECK-A %s < %t
// RUN: FileCheck --check-prefix=CHECK-B %s < %t
// RUN: FileCheck --check-prefix=CHECK-C %s < %t
// RUN: FileCheck --check-prefix=CHECK-D %s < %t
// RUN: FileCheck --check-prefix=CHECK-E %s < %t
// RUN: FileCheck --check-prefix=CHECK-F %s < %t
// RUN: FileCheck --check-prefix=CHECK-G %s < %t
// RUN: FileCheck --check-prefix=CHECK-I %s < %t
// RUN: FileCheck --check-prefix=CHECK-J %s < %t
// RUN: FileCheck --check-prefix=CHECK-K %s < %t
// RUN: FileCheck --check-prefix=CHECK-L %s < %t
// RUN: FileCheck --check-prefix=CHECK-M %s < %t
// RUN: FileCheck --check-prefix=CHECK-N %s < %t
struct A {
// CHECK-A: VFTable for 'A' (3 entries)
// CHECK-A-NEXT: 0 | void A::f()
// CHECK-A-NEXT: 1 | void A::g()
// CHECK-A-NEXT: 2 | void A::h()
// CHECK-A: VFTable indices for 'A' (3 entries)
// CHECK-A-NEXT: 0 | void A::f()
// CHECK-A-NEXT: 1 | void A::g()
// CHECK-A-NEXT: 2 | void A::h()
virtual void f();
virtual void g();
virtual void h();
int ia;
};
A a;
// EMITS-VFTABLE-DAG: @"\01??_7A@@6B@" = linkonce_odr unnamed_addr constant [3 x i8*]
struct B : A {
// CHECK-B: VFTable for 'A' in 'B' (5 entries)
// CHECK-B-NEXT: 0 | void B::f()
// CHECK-B-NEXT: 1 | void A::g()
// CHECK-B-NEXT: 2 | void A::h()
// CHECK-B-NEXT: 3 | void B::i()
// CHECK-B-NEXT: 4 | void B::j()
// CHECK-B: VFTable indices for 'B' (3 entries)
// CHECK-B-NEXT: 0 | void B::f()
// CHECK-B-NEXT: 3 | void B::i()
// CHECK-B-NEXT: 4 | void B::j()
virtual void f(); // overrides A::f()
virtual void i();
virtual void j();
};
B b;
// EMITS-VFTABLE-DAG: @"\01??_7B@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
struct C {
// CHECK-C: VFTable for 'C' (2 entries)
// CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-C-NEXT: 1 | void C::f()
// CHECK-C: VFTable indices for 'C' (2 entries).
// CHECK-C-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-C-NEXT: 1 | void C::f()
virtual ~C();
virtual void f();
};
void C::f() {}
// NO-VFTABLE-NOT: @"\01??_7C@@6B@"
struct D {
// CHECK-D: VFTable for 'D' (2 entries)
// CHECK-D-NEXT: 0 | void D::f()
// CHECK-D-NEXT: 1 | D::~D() [scalar deleting]
// CHECK-D: VFTable indices for 'D' (2 entries)
// CHECK-D-NEXT: 0 | void D::f()
// CHECK-D-NEXT: 1 | D::~D() [scalar deleting]
virtual void f();
virtual ~D();
};
D d;
// EMITS-VFTABLE-DAG: @"\01??_7D@@6B@" = linkonce_odr unnamed_addr constant [2 x i8*]
struct E : A {
// CHECK-E: VFTable for 'A' in 'E' (5 entries)
// CHECK-E-NEXT: 0 | void A::f()
// CHECK-E-NEXT: 1 | void A::g()
// CHECK-E-NEXT: 2 | void A::h()
// CHECK-E-NEXT: 3 | E::~E() [scalar deleting]
// CHECK-E-NEXT: 4 | void E::i()
// CHECK-E: VFTable indices for 'E' (2 entries).
// CHECK-E-NEXT: 3 | E::~E() [scalar deleting]
// CHECK-E-NEXT: 4 | void E::i()
// ~E would be the key method, but it isn't used, and MS ABI has no key
// methods.
virtual ~E();
virtual void i();
};
void E::i() {}
// NO-VFTABLE-NOT: @"\01??_7E@@6B@"
struct F : A {
// CHECK-F: VFTable for 'A' in 'F' (5 entries)
// CHECK-F-NEXT: 0 | void A::f()
// CHECK-F-NEXT: 1 | void A::g()
// CHECK-F-NEXT: 2 | void A::h()
// CHECK-F-NEXT: 3 | void F::i()
// CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
// CHECK-F: VFTable indices for 'F' (2 entries).
// CHECK-F-NEXT: 3 | void F::i()
// CHECK-F-NEXT: 4 | F::~F() [scalar deleting]
virtual void i();
virtual ~F();
};
F f;
// EMITS-VFTABLE-DAG: @"\01??_7F@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
struct G : E {
// CHECK-G: VFTable for 'A' in 'E' in 'G' (6 entries)
// CHECK-G-NEXT: 0 | void G::f()
// CHECK-G-NEXT: 1 | void A::g()
// CHECK-G-NEXT: 2 | void A::h()
// CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
// CHECK-G-NEXT: 4 | void E::i()
// CHECK-G-NEXT: 5 | void G::j()
// CHECK-G: VFTable indices for 'G' (3 entries).
// CHECK-G-NEXT: 0 | void G::f()
// CHECK-G-NEXT: 3 | G::~G() [scalar deleting]
// CHECK-G-NEXT: 5 | void G::j()
virtual void f(); // overrides A::f()
virtual ~G();
virtual void j();
};
void G::j() {}
// NO-VFTABLE-NOT: @"\01??_7G@@6B@"
// Test that the usual Itanium-style key method does not emit a vtable.
struct H {
virtual void f();
};
void H::f() {}
// NO-VFTABLE-NOT: @"\01??_7H@@6B@"
struct Empty { };
struct I : Empty {
// CHECK-I: VFTable for 'I' (2 entries)
// CHECK-I-NEXT: 0 | void I::f()
// CHECK-I-NEXT: 1 | void I::g()
virtual void f();
virtual void g();
};
I i;
struct J {
// CHECK-J: VFTable for 'J' (6 entries)
// CHECK-J-NEXT: 0 | void J::foo(long)
// CHECK-J-NEXT: 1 | void J::foo(int)
// CHECK-J-NEXT: 2 | void J::foo(short)
// CHECK-J-NEXT: 3 | void J::bar(long)
// CHECK-J-NEXT: 4 | void J::bar(int)
// CHECK-J-NEXT: 5 | void J::bar(short)
virtual void foo(short);
virtual void bar(short);
virtual void foo(int);
virtual void bar(int);
virtual void foo(long);
virtual void bar(long);
};
J j;
struct K : J {
// CHECK-K: VFTable for 'J' in 'K' (9 entries)
// CHECK-K-NEXT: 0 | void J::foo(long)
// CHECK-K-NEXT: 1 | void J::foo(int)
// CHECK-K-NEXT: 2 | void J::foo(short)
// CHECK-K-NEXT: 3 | void J::bar(long)
// CHECK-K-NEXT: 4 | void J::bar(int)
// CHECK-K-NEXT: 5 | void J::bar(short)
// CHECK-K-NEXT: 6 | void K::bar(double)
// CHECK-K-NEXT: 7 | void K::bar(float)
// CHECK-K-NEXT: 8 | void K::foo(float)
virtual void bar(float);
virtual void foo(float);
virtual void bar(double);
};
K k;
struct L : J {
// CHECK-L: VFTable for 'J' in 'L' (9 entries)
// CHECK-L-NEXT: 0 | void J::foo(long)
// CHECK-L-NEXT: 1 | void L::foo(int)
// CHECK-L-NEXT: 2 | void J::foo(short)
// CHECK-L-NEXT: 3 | void J::bar(long)
// CHECK-L-NEXT: 4 | void J::bar(int)
// CHECK-L-NEXT: 5 | void J::bar(short)
// CHECK-L-NEXT: 6 | void L::foo(float)
// CHECK-L-NEXT: 7 | void L::bar(double)
// CHECK-L-NEXT: 8 | void L::bar(float)
// This case is interesting. Since the J::foo(int) override is the first method in
// the class, foo(float) precedes the bar(double) and bar(float) in the vftable.
virtual void foo(int);
virtual void bar(float);
virtual void foo(float);
virtual void bar(double);
};
L l;
struct M : J {
// CHECK-M: VFTable for 'J' in 'M' (11 entries)
// CHECK-M-NEXT: 0 | void J::foo(long)
// CHECK-M-NEXT: 1 | void M::foo(int)
// CHECK-M-NEXT: 2 | void J::foo(short)
// CHECK-M-NEXT: 3 | void J::bar(long)
// CHECK-M-NEXT: 4 | void J::bar(int)
// CHECK-M-NEXT: 5 | void J::bar(short)
// CHECK-M-NEXT: 6 | void M::foo(float)
// CHECK-M-NEXT: 7 | void M::spam(long)
// CHECK-M-NEXT: 8 | void M::spam(int)
// CHECK-M-NEXT: 9 | void M::bar(double)
// CHECK-M-NEXT: 10 | void M::bar(float)
virtual void foo(int);
virtual void spam(int);
virtual void bar(float);
virtual void bar(double);
virtual void foo(float);
virtual void spam(long);
};
M m;
struct N {
// CHECK-N: VFTable for 'N' (4 entries)
// CHECK-N-NEXT: 0 | void N::operator+(int)
// CHECK-N-NEXT: 1 | void N::operator+(short)
// CHECK-N-NEXT: 2 | void N::operator*(int)
// CHECK-N-NEXT: 3 | void N::operator*(short)
virtual void operator+(short);
virtual void operator*(short);
virtual void operator+(int);
virtual void operator*(int);
};
N n;