diff options
Diffstat (limited to 'test/CodeGenCXX/msabi-ctor-abstract-vbase.cpp')
-rw-r--r-- | test/CodeGenCXX/msabi-ctor-abstract-vbase.cpp | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/test/CodeGenCXX/msabi-ctor-abstract-vbase.cpp b/test/CodeGenCXX/msabi-ctor-abstract-vbase.cpp new file mode 100644 index 0000000000..c1a7e6b66b --- /dev/null +++ b/test/CodeGenCXX/msabi-ctor-abstract-vbase.cpp @@ -0,0 +1,82 @@ +// RUN: %clang_cc1 -triple x86_64-windows-msvc %s -emit-llvm -fexceptions -o - | FileCheck %s + +// PR41065: As background, when constructing a complete object, virtual bases +// are constructed first. If an exception is thrown while constructing a +// subobject later, those virtual bases are destroyed, so sema references the +// relevant constructors and destructors of every base class in case they are +// needed. +// +// However, an abstract class can never be used to construct a complete object. +// In the Itanium C++ ABI, this works out nicely, because we never end up +// emitting the "complete" constructor variant, only the "base" constructor +// variant, which can be called by constructors of derived classes. For various +// reasons, Sema does not mark ctors and dtors of virtual bases referenced when +// the constructor of an abstract class is emitted. +// +// In the Microsoft ABI, there are no complete/base variants, so before PR41065 +// was fixed, the constructor of an abstract class could reference special +// members of a virtual base without marking them referenced. This could lead to +// unresolved symbol errors at link time. +// +// The fix is to implement the same optimization as Sema: If the class is +// abstract, don't bother initializing its virtual bases. The "is this class the +// most derived class" check in the constructor will never pass, and the virtual +// base constructor calls are always dead. Skip them. + +struct A { + A(); + virtual void f() = 0; + virtual ~A(); +}; + +// B has an implicit inline dtor, but is still abstract. +struct B : A { + B(int n); + int n; +}; + +// Still abstract +struct C : virtual B { + C(int n); + //void f() override; +}; + +// Not abstract, D::D calls C::C and B::B. +struct D : C { + D(int n); + void f() override; +}; + +void may_throw(); +C::C(int n) : B(n) { may_throw(); } + +// No branches, no constructor calls before may_throw(); +// +// CHECK-LABEL: define dso_local %struct.C* @"??0C@@QEAA@H@Z"(%struct.C* returned %this, i32 %n, i32 %is_most_derived) +// CHECK-NOT: br i1 +// CHECK-NOT: {{call.*@"\?0}} +// CHECK: call void @"?may_throw@@YAXXZ"() +// no cleanups + + +D::D(int n) : C(n), B(n) { may_throw(); } + +// Conditionally construct (and destroy) vbase B, unconditionally C. +// +// CHECK-LABEL: define dso_local %struct.D* @"??0D@@QEAA@H@Z"(%struct.D* returned %this, i32 %n, i32 %is_most_derived) +// CHECK: icmp ne i32 {{.*}}, 0 +// CHECK: br i1 +// CHECK: call %struct.B* @"??0B@@QEAA@H@Z" +// CHECK: br label +// CHECK: invoke %struct.C* @"??0C@@QEAA@H@Z" +// CHECK: invoke void @"?may_throw@@YAXXZ"() +// CHECK: cleanuppad +// CHECK: call void @"??1C@@UEAA@XZ" +// CHECK: cleanupret +// +// CHECK: cleanuppad +// CHECK: icmp ne i32 {{.*}}, 0 +// CHECK: br i1 +// CHECK: call void @"??1B@@UEAA@XZ" +// CHECK: br label +// CHECK: cleanupret |