]> granicus.if.org Git - clang/commitdiff
PR30937: don't devirtualize if we find that the callee is a pure virtual
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 11 Nov 2016 01:01:31 +0000 (01:01 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 11 Nov 2016 01:01:31 +0000 (01:01 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@286534 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGClass.cpp
test/CodeGenCXX/devirtualize-virtual-function-calls.cpp

index 2f5855f89be00d304cd86a210a828a85fa0ffcf2..635824e950352036996d0d6345affd3fa36cbfb1 100644 (file)
@@ -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<FinalAttr>())
-    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<FinalAttr>())
-      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<FinalAttr>())
-      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<FinalAttr>())
+    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<FinalAttr>())
+  if (BestDynamicDecl->hasAttr<FinalAttr>())
     return true;
 
   if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Base)) {
index f2dc97897251cc8e079ffba62ccbcacea5a4d8c4..b4c39f3fcff58d26c50c9e171c778b6c81b8f4e1 100644 (file)
@@ -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();
+  }
+}