Skip to content

Commit a271686

Browse files
committed
PR30937: don't devirtualize if we find that the callee is a pure virtual
function. In that case, there is no requirement that the callee is actually defined, and the code may in fact be valid and have defined behavior if the virtual call is unreachable. llvm-svn: 286534
1 parent b07c0d7 commit a271686

File tree

2 files changed

+54
-23
lines changed

2 files changed

+54
-23
lines changed

clang/lib/CodeGen/CGClass.cpp

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2851,41 +2851,38 @@ CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base,
28512851
return false;
28522852

28532853
// If the member function is marked 'final', we know that it can't be
2854-
// overridden and can therefore devirtualize it.
2854+
// overridden and can therefore devirtualize it unless it's pure virtual.
28552855
if (MD->hasAttr<FinalAttr>())
2856-
return true;
2856+
return !MD->isPure();
28572857

28582858
// If the base expression (after skipping derived-to-base conversions) is a
28592859
// class prvalue, then we can devirtualize.
28602860
Base = Base->getBestDynamicClassTypeExpr();
28612861
if (Base->isRValue() && Base->getType()->isRecordType())
28622862
return true;
28632863

2864-
// If the most derived class is marked final, we know that no subclass can
2865-
// override this member function and so we can devirtualize it. For example:
2866-
//
2867-
// struct A { virtual void f(); }
2868-
// struct B final : A { };
2869-
//
2870-
// void f(B *b) {
2871-
// b->f();
2872-
// }
2873-
//
2874-
if (const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType()) {
2875-
if (BestDynamicDecl->hasAttr<FinalAttr>())
2876-
return true;
2864+
// If we don't even know what we would call, we can't devirtualize.
2865+
const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType();
2866+
if (!BestDynamicDecl)
2867+
return false;
28772868

2878-
// There may be a method corresponding to MD in a derived class. If that
2879-
// method is marked final, we can devirtualize it.
2880-
const CXXMethodDecl *DevirtualizedMethod =
2881-
MD->getCorrespondingMethodInClass(BestDynamicDecl);
2882-
if (DevirtualizedMethod->hasAttr<FinalAttr>())
2883-
return true;
2884-
}
2869+
// There may be a method corresponding to MD in a derived class.
2870+
const CXXMethodDecl *DevirtualizedMethod =
2871+
MD->getCorrespondingMethodInClass(BestDynamicDecl);
2872+
2873+
// If that method is pure virtual, we can't devirtualize. If this code is
2874+
// reached, the result would be UB, not a direct call to the derived class
2875+
// function, and we can't assume the derived class function is defined.
2876+
if (DevirtualizedMethod->isPure())
2877+
return false;
2878+
2879+
// If that method is marked final, we can devirtualize it.
2880+
if (DevirtualizedMethod->hasAttr<FinalAttr>())
2881+
return true;
28852882

28862883
// Similarly, if the class itself is marked 'final' it can't be overridden
28872884
// and we can therefore devirtualize the member function call.
2888-
if (MD->getParent()->hasAttr<FinalAttr>())
2885+
if (BestDynamicDecl->hasAttr<FinalAttr>())
28892886
return true;
28902887

28912888
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Base)) {

clang/test/CodeGenCXX/devirtualize-virtual-function-calls.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,37 @@ namespace test4 {
161161
p->fish.eat();
162162
}
163163
}
164+
165+
// Do not devirtualize to pure virtual function calls.
166+
namespace test5 {
167+
struct X {
168+
virtual void f() = 0;
169+
};
170+
struct Y {};
171+
// CHECK-LABEL: define {{.*}} @_ZN5test51f
172+
void f(Y &y, X Y::*p) {
173+
// CHECK-NOT: call {{.*}} @_ZN5test51X1fEv
174+
// CHECK: call void %
175+
(y.*p).f();
176+
};
177+
178+
struct Z final {
179+
virtual void f() = 0;
180+
};
181+
// CHECK-LABEL: define {{.*}} @_ZN5test51g
182+
void g(Z &z) {
183+
// CHECK-NOT: call {{.*}} @_ZN5test51Z1fEv
184+
// CHECK: call void %
185+
z.f();
186+
}
187+
188+
struct Q {
189+
virtual void f() final = 0;
190+
};
191+
// CHECK-LABEL: define {{.*}} @_ZN5test51h
192+
void h(Q &q) {
193+
// CHECK-NOT: call {{.*}} @_ZN5test51Q1fEv
194+
// CHECK: call void %
195+
q.f();
196+
}
197+
}

0 commit comments

Comments
 (0)