forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			83 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			83 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			C++
		
	
	
	
// 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
 |