diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 2f5855f89be0..635824e95035 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2851,9 +2851,9 @@ CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base, return false; // If the member function is marked 'final', we know that it can't be - // overridden and can therefore devirtualize it. + // overridden and can therefore devirtualize it unless it's pure virtual. if (MD->hasAttr()) - return true; + return !MD->isPure(); // If the base expression (after skipping derived-to-base conversions) is a // class prvalue, then we can devirtualize. @@ -2861,31 +2861,28 @@ CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base, if (Base->isRValue() && Base->getType()->isRecordType()) return true; - // If the most derived class is marked final, we know that no subclass can - // override this member function and so we can devirtualize it. For example: - // - // struct A { virtual void f(); } - // struct B final : A { }; - // - // void f(B *b) { - // b->f(); - // } - // - if (const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType()) { - if (BestDynamicDecl->hasAttr()) - return true; + // If we don't even know what we would call, we can't devirtualize. + const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType(); + if (!BestDynamicDecl) + return false; - // There may be a method corresponding to MD in a derived class. If that - // method is marked final, we can devirtualize it. - const CXXMethodDecl *DevirtualizedMethod = - MD->getCorrespondingMethodInClass(BestDynamicDecl); - if (DevirtualizedMethod->hasAttr()) - return true; - } + // There may be a method corresponding to MD in a derived class. + const CXXMethodDecl *DevirtualizedMethod = + MD->getCorrespondingMethodInClass(BestDynamicDecl); + + // If that method is pure virtual, we can't devirtualize. If this code is + // reached, the result would be UB, not a direct call to the derived class + // function, and we can't assume the derived class function is defined. + if (DevirtualizedMethod->isPure()) + return false; + + // If that method is marked final, we can devirtualize it. + if (DevirtualizedMethod->hasAttr()) + return true; // Similarly, if the class itself is marked 'final' it can't be overridden // and we can therefore devirtualize the member function call. - if (MD->getParent()->hasAttr()) + if (BestDynamicDecl->hasAttr()) return true; if (const DeclRefExpr *DRE = dyn_cast(Base)) { diff --git a/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp b/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp index f2dc97897251..b4c39f3fcff5 100644 --- a/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp +++ b/clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp @@ -161,3 +161,37 @@ namespace test4 { p->fish.eat(); } } + +// Do not devirtualize to pure virtual function calls. +namespace test5 { + struct X { + virtual void f() = 0; + }; + struct Y {}; + // CHECK-LABEL: define {{.*}} @_ZN5test51f + void f(Y &y, X Y::*p) { + // CHECK-NOT: call {{.*}} @_ZN5test51X1fEv + // CHECK: call void % + (y.*p).f(); + }; + + struct Z final { + virtual void f() = 0; + }; + // CHECK-LABEL: define {{.*}} @_ZN5test51g + void g(Z &z) { + // CHECK-NOT: call {{.*}} @_ZN5test51Z1fEv + // CHECK: call void % + z.f(); + } + + struct Q { + virtual void f() final = 0; + }; + // CHECK-LABEL: define {{.*}} @_ZN5test51h + void h(Q &q) { + // CHECK-NOT: call {{.*}} @_ZN5test51Q1fEv + // CHECK: call void % + q.f(); + } +}