reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   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