Whereas it is UB in terms of the standard to delete an array of objects via pointer whose static type doesn't match its dynamic type, MSVC supports an extension allowing to do it. Aside from array deletion not working correctly in the mentioned case, currently not having this extension implemented causes clang to generate code that is not compatible with the code generated by MSVC, because clang always puts scalar deleting destructor to the vftable. This PR aims to resolve these problems. Fixes https://github.com/llvm/llvm-project/issues/19772
219 lines
7.7 KiB
C++
219 lines
7.7 KiB
C++
// RUN: %clang_cc1 -fno-rtti %s -emit-llvm -o %t -triple=i386-pc-win32 -fdump-vtable-layouts 2>&1 | FileCheck --check-prefix=VFTABLES %s
|
|
// RUN: FileCheck --check-prefix=GLOBALS %s < %t
|
|
// RUN: FileCheck --check-prefix=CODEGEN %s < %t
|
|
|
|
namespace test1 {
|
|
|
|
// Some covariant types.
|
|
struct A { int a; };
|
|
struct B { int b; };
|
|
struct C : A, B { int c; };
|
|
struct D : C { int d; };
|
|
struct E : D { int e; };
|
|
|
|
// One base class and two overrides, all with covariant return types.
|
|
struct H { virtual B *foo(); };
|
|
struct I : H { virtual C *foo(); };
|
|
struct J : I { virtual D *foo(); J(); };
|
|
struct K : J { virtual E *foo(); K(); };
|
|
|
|
J::J() {}
|
|
|
|
// VFTABLES-LABEL: VFTable for 'test1::H' in 'test1::I' in 'test1::J' (3 entries).
|
|
// VFTABLES-NEXT: 0 | D *test1::J::foo()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct test1::B *'): 4 non-virtual]
|
|
// VFTABLES-NEXT: 1 | D *test1::J::foo()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct test1::C *'): 0 non-virtual]
|
|
// VFTABLES-NEXT: 2 | D *test1::J::foo()
|
|
|
|
// GLOBALS-LABEL: @"??_7J@test1@@6B@" = linkonce_odr unnamed_addr constant { [3 x ptr] }
|
|
// GLOBALS: @"?foo@J@test1@@QAEPAUB@2@XZ"
|
|
// GLOBALS: @"?foo@J@test1@@QAEPAUC@2@XZ"
|
|
// GLOBALS: @"?foo@J@test1@@UAEPAUD@2@XZ"
|
|
|
|
K::K() {}
|
|
|
|
// VFTABLES-LABEL: VFTable for 'test1::H' in 'test1::I' in 'test1::J' in 'test1::K' (4 entries).
|
|
// VFTABLES-NEXT: 0 | E *test1::K::foo()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct test1::B *'): 4 non-virtual]
|
|
// VFTABLES-NEXT: 1 | E *test1::K::foo()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct test1::C *'): 0 non-virtual]
|
|
// VFTABLES-NEXT: 2 | E *test1::K::foo()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct test1::D *'): 0 non-virtual]
|
|
// VFTABLES-NEXT: 3 | E *test1::K::foo()
|
|
|
|
// Only B to C requires adjustment, but we get 3 thunks in K's vftable, two of
|
|
// which are trivial.
|
|
// GLOBALS-LABEL: @"??_7K@test1@@6B@" = linkonce_odr unnamed_addr constant { [4 x ptr] }
|
|
// GLOBALS: @"?foo@K@test1@@QAEPAUB@2@XZ"
|
|
// GLOBALS: @"?foo@K@test1@@QAEPAUC@2@XZ"
|
|
// GLOBALS: @"?foo@K@test1@@QAEPAUD@2@XZ"
|
|
// GLOBALS: @"?foo@K@test1@@UAEPAUE@2@XZ"
|
|
|
|
// This thunk has a return adjustment.
|
|
// CODEGEN-LABEL: define {{.*}} @"?foo@K@test1@@QAEPAUB@2@XZ"
|
|
// CODEGEN: call {{.*}} @"?foo@K@test1@@UAEPAUE@2@XZ"
|
|
// CODEGEN: icmp {{.*}}, null
|
|
// CODEGEN: getelementptr
|
|
// CODEGEN: ret
|
|
|
|
// These two don't.
|
|
// CODEGEN-LABEL: define {{.*}} @"?foo@K@test1@@QAEPAUC@2@XZ"
|
|
// CODEGEN: call {{.*}} @"?foo@K@test1@@UAEPAUE@2@XZ"
|
|
// CODEGEN-NEXT: ret
|
|
|
|
// CODEGEN-LABEL: define {{.*}} @"?foo@K@test1@@QAEPAUD@2@XZ"
|
|
// CODEGEN: call {{.*}} @"?foo@K@test1@@UAEPAUE@2@XZ"
|
|
// CODEGEN-NEXT: ret
|
|
|
|
}
|
|
|
|
namespace test2 {
|
|
|
|
// Covariant types. D* is not trivially convertible to C*.
|
|
struct A { int a; };
|
|
struct B { int b; };
|
|
struct C : B { int c; };
|
|
struct D : A, C { int d; };
|
|
struct E : D { int e; };
|
|
|
|
// J's foo will require an adjusting thunk, and K will require a trivial thunk.
|
|
struct H { virtual B *foo(); };
|
|
struct I : H { virtual C *foo(); };
|
|
struct J : I { virtual D *foo(); J(); };
|
|
struct K : J { virtual E *foo(); K(); };
|
|
|
|
J::J() {}
|
|
|
|
// VFTABLES-LABEL: VFTable for 'test2::H' in 'test2::I' in 'test2::J' (2 entries).
|
|
// VFTABLES-NEXT: 0 | D *test2::J::foo()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct test2::B *'): 4 non-virtual]
|
|
// VFTABLES-NEXT: 1 | D *test2::J::foo()
|
|
|
|
// GLOBALS-LABEL: @"??_7J@test2@@6B@" = linkonce_odr unnamed_addr constant { [2 x ptr] }
|
|
|
|
K::K() {}
|
|
|
|
// VFTABLES-LABEL: VFTable for 'test2::H' in 'test2::I' in 'test2::J' in 'test2::K' (3 entries).
|
|
// VFTABLES-NEXT: 0 | E *test2::K::foo()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct test2::B *'): 4 non-virtual]
|
|
// VFTABLES-NEXT: 1 | E *test2::K::foo()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct test2::D *'): 0 non-virtual]
|
|
// VFTABLES-NEXT: 2 | E *test2::K::foo()
|
|
|
|
// GLOBALS-LABEL: @"??_7K@test2@@6B@" = linkonce_odr unnamed_addr constant { [3 x ptr] }
|
|
|
|
}
|
|
|
|
namespace pr20479 {
|
|
struct A {
|
|
virtual A *f();
|
|
};
|
|
|
|
struct B : virtual A {
|
|
virtual B *f();
|
|
};
|
|
|
|
struct C : virtual A, B {
|
|
// VFTABLES-LABEL: VFTable for 'pr20479::A' in 'pr20479::B' in 'pr20479::C' (2 entries).
|
|
// VFTABLES-NEXT: 0 | B *pr20479::B::f()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct pr20479::A *'): vbase #1, 0 non-virtual]
|
|
// VFTABLES-NEXT: 1 | B *pr20479::B::f()
|
|
C();
|
|
};
|
|
|
|
C::C() {}
|
|
|
|
// GLOBALS-LABEL: @"??_7C@pr20479@@6B@" = linkonce_odr unnamed_addr constant { [2 x ptr] }
|
|
// GLOBALS: @"?f@B@pr20479@@QAEPAUA@2@XZ"
|
|
// GLOBALS: @"?f@B@pr20479@@UAEPAU12@XZ"
|
|
}
|
|
|
|
namespace pr21073 {
|
|
struct A {
|
|
virtual A *f();
|
|
};
|
|
|
|
struct B : virtual A {
|
|
virtual B *f();
|
|
};
|
|
|
|
struct C : virtual A, virtual B {
|
|
// VFTABLES-LABEL: VFTable for 'pr21073::A' in 'pr21073::B' in 'pr21073::C' (2 entries).
|
|
// VFTABLES-NEXT: 0 | B *pr21073::B::f()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct pr21073::A *'): vbase #1, 0 non-virtual]
|
|
// VFTABLES-NEXT: [this adjustment: 8 non-virtual]
|
|
// VFTABLES-NEXT: 1 | B *pr21073::B::f()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct pr21073::B *'): 0 non-virtual]
|
|
// VFTABLES-NEXT: [this adjustment: 8 non-virtual]
|
|
C();
|
|
};
|
|
|
|
C::C() {}
|
|
|
|
// GLOBALS-LABEL: @"??_7C@pr21073@@6B@" = linkonce_odr unnamed_addr constant { [2 x ptr] }
|
|
// GLOBALS: @"?f@B@pr21073@@WPPPPPPPI@AEPAUA@2@XZ"
|
|
// GLOBALS: @"?f@B@pr21073@@WPPPPPPPI@AEPAU12@XZ"
|
|
}
|
|
|
|
namespace pr21073_2 {
|
|
struct A { virtual A *foo(); };
|
|
struct B : virtual A {};
|
|
struct C : virtual A { virtual C *foo(); };
|
|
struct D : B, C { D(); };
|
|
D::D() {}
|
|
|
|
// VFTABLES-LABEL: VFTable for 'pr21073_2::A' in 'pr21073_2::C' in 'pr21073_2::D' (2 entries)
|
|
// VFTABLES-NEXT: 0 | C *pr21073_2::C::foo()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct pr21073_2::A *'): vbase #1, 0 non-virtual]
|
|
// VFTABLES-NEXT: 1 | C *pr21073_2::C::foo()
|
|
|
|
// GLOBALS-LABEL: @"??_7D@pr21073_2@@6B@" = {{.*}} constant { [2 x ptr] }
|
|
// GLOBALS: @"?foo@C@pr21073_2@@QAEPAUA@2@XZ"
|
|
// GLOBALS: @"?foo@C@pr21073_2@@UAEPAU12@XZ"
|
|
}
|
|
|
|
namespace test3 {
|
|
struct A { virtual A *fn(); };
|
|
struct B : virtual A { virtual B *fn(); };
|
|
struct X : virtual B {};
|
|
struct Y : virtual B {};
|
|
struct C : X, Y {};
|
|
struct D : virtual B, virtual A, C {
|
|
D *fn();
|
|
D();
|
|
};
|
|
D::D() {}
|
|
|
|
// VFTABLES-LABEL: VFTable for 'test3::A' in 'test3::B' in 'test3::X' in 'test3::C' in 'test3::D' (3 entries).
|
|
// VFTABLES-NEXT: 0 | D *test3::D::fn()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct test3::A *'): vbase #1, 0 non-virtual]
|
|
// VFTABLES-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
|
// VFTABLES-NEXT: 1 | D *test3::D::fn()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct test3::B *'): vbase #2, 0 non-virtual]
|
|
// VFTABLES-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
|
// VFTABLES-NEXT: 2 | D *test3::D::fn()
|
|
// VFTABLES-NEXT: [return adjustment (to type 'struct test3::D *'): 0 non-virtual]
|
|
// VFTABLES-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
|
|
|
// GLOBALS-LABEL: @"??_7D@test3@@6B@" = {{.*}} constant { [3 x ptr] }
|
|
// GLOBALS: @"?fn@D@test3@@$4PPPPPPPM@A@AEPAUA@2@XZ"
|
|
// GLOBALS: @"?fn@D@test3@@$4PPPPPPPM@A@AEPAUB@2@XZ"
|
|
// GLOBALS: @"?fn@D@test3@@$4PPPPPPPM@A@AEPAU12@XZ"
|
|
}
|
|
|
|
namespace pr34302 {
|
|
// C::f is lives in the vftable inside its virtual B subobject. In the MS ABI,
|
|
// covariant return type virtual methods extend vftables from virtual bases,
|
|
// even though that can make it impossible to implement certain diamond
|
|
// hierarchies correctly.
|
|
struct A { virtual ~A(); };
|
|
struct B : A { virtual B *f(); };
|
|
struct C : virtual B { C *f(); };
|
|
C c;
|
|
// VFTABLES-LABEL: VFTable indices for 'pr34302::C' (2 entries).
|
|
// VFTABLES-NEXT: -- accessible via vbtable index 1, vfptr at offset 0 --
|
|
// VFTABLES-NEXT: 0 | pr34302::C::~C() [vector deleting]
|
|
// VFTABLES-NEXT: 2 | C *pr34302::C::f()
|
|
}
|