ahatanak created this revision. Herald added a subscriber: Prazek. The test case I added used to fail because of a linker error.
Linkage failed because Sema::MarkDeclRefReferenced would prevent the virtual method definition from being emitted by setting OdrUse=false and then code-gen would devirtualized the virtual call because its class is marked final. This patch fixes the bug. rdar://problem/27455779 https://reviews.llvm.org/D34301 Files: lib/Sema/SemaExpr.cpp test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp Index: test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp =================================================================== --- test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp +++ test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp @@ -241,3 +241,40 @@ return static_cast<A *>(b)->f(); } } + +namespace Test11 { + // Check that the definition of Derived::operator() is emitted. + + // CHECK-LABEL: define linkonce_odr void @_ZN6Test111SIiE4foo1Ev( + // CHECK: call void @_ZN6Test111SIiE7DerivedclEv( + // CHECK: define linkonce_odr void @_ZN6Test111SIiE7DerivedclEv( + class Base { + public: + virtual void operator()() { + } + }; + + template<class T> + struct S { + class Derived final : public Base { + public: + void operator()() override { + } + }; + + Derived *ptr = nullptr; + + void foo1() { + if (ptr) { + // This call gets devirtualized. If the definition of + // Derived::operator() is not emitted, there will be a linker error. + (*ptr)(); + } + } + }; + + void foo2() { + S<int> *s = new S<int>; + s->foo1(); + } +} Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -14711,7 +14711,8 @@ // if it's a qualified reference. bool OdrUse = true; if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(E->getDecl())) - if (Method->isVirtual()) + if (Method->isVirtual() && !(Method->hasAttr<FinalAttr>() || + Method->getParent()->hasAttr<FinalAttr>())) OdrUse = false; MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse); }
Index: test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp =================================================================== --- test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp +++ test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp @@ -241,3 +241,40 @@ return static_cast<A *>(b)->f(); } } + +namespace Test11 { + // Check that the definition of Derived::operator() is emitted. + + // CHECK-LABEL: define linkonce_odr void @_ZN6Test111SIiE4foo1Ev( + // CHECK: call void @_ZN6Test111SIiE7DerivedclEv( + // CHECK: define linkonce_odr void @_ZN6Test111SIiE7DerivedclEv( + class Base { + public: + virtual void operator()() { + } + }; + + template<class T> + struct S { + class Derived final : public Base { + public: + void operator()() override { + } + }; + + Derived *ptr = nullptr; + + void foo1() { + if (ptr) { + // This call gets devirtualized. If the definition of + // Derived::operator() is not emitted, there will be a linker error. + (*ptr)(); + } + } + }; + + void foo2() { + S<int> *s = new S<int>; + s->foo1(); + } +} Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -14711,7 +14711,8 @@ // if it's a qualified reference. bool OdrUse = true; if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(E->getDecl())) - if (Method->isVirtual()) + if (Method->isVirtual() && !(Method->hasAttr<FinalAttr>() || + Method->getParent()->hasAttr<FinalAttr>())) OdrUse = false; MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits