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(E->getDecl())) - if (Method->isVirtual()) + if (Method->isVirtual() && !(Method->hasAttr() || + Method->getParent()->hasAttr())) 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(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 + 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 *s = new S; + s->foo1(); + } +}