Index: include/clang/AST/Decl.h =================================================================== --- include/clang/AST/Decl.h +++ include/clang/AST/Decl.h @@ -1599,6 +1599,7 @@ unsigned HasImplicitReturnZero : 1; unsigned IsLateTemplateParsed : 1; unsigned IsConstexpr : 1; + unsigned IsMarkedForPendingInstantiation : 1; /// \brief Indicates if the function uses __try. unsigned UsesSEHTry : 1; @@ -1691,7 +1692,8 @@ HasWrittenPrototype(true), IsDeleted(false), IsTrivial(false), IsDefaulted(false), IsExplicitlyDefaulted(false), HasImplicitReturnZero(false), IsLateTemplateParsed(false), - IsConstexpr(isConstexprSpecified), UsesSEHTry(false), + IsConstexpr(isConstexprSpecified), + IsMarkedForPendingInstantiation(false), UsesSEHTry(false), HasSkippedBody(false), EndRangeLoc(NameInfo.getEndLoc()), TemplateOrSpecialization(), DNLoc(NameInfo.getInfo()) {} @@ -1872,6 +1874,15 @@ bool isConstexpr() const { return IsConstexpr; } void setConstexpr(bool IC) { IsConstexpr = IC; } + /// \brief Whether this function has been put on the pending instatiations + /// list. + bool isMarkedForPendingInstantiation() const { + return IsMarkedForPendingInstantiation; + } + void setMarkedForPendingInstantiation(bool IM = true) { + IsMarkedForPendingInstantiation = IM; + } + /// \brief Indicates the function uses __try. bool usesSEHTry() const { return UsesSEHTry; } void setUsesSEHTry(bool UST) { UsesSEHTry = UST; } Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -2874,10 +2874,16 @@ // We can devirtualize calls on an object accessed by a class member access // expression, since by C++11 [basic.life]p6 we know that it can't refer to - // a derived class object constructed in the same location. + // a derived class object constructed in the same location. However, we avoid + // devirtualizing a call to a function that can be instantiated implicitly, + // unless we know that we will be instantiating it in this TU. If this + // function does not get instantiated, the devirtualization will create a + // direct call to a function whose body may not exist. if (const MemberExpr *ME = dyn_cast(Base)) if (const ValueDecl *VD = dyn_cast(ME->getMemberDecl())) - return VD->getType()->isRecordType(); + return VD->getType()->isRecordType() && + (MD->isMarkedForPendingInstantiation() || + !MD->isImplicitlyInstantiable()); // We can always devirtualize calls on temporary object expressions. if (isa(Base)) Index: lib/Sema/Sema.cpp =================================================================== --- lib/Sema/Sema.cpp +++ lib/Sema/Sema.cpp @@ -680,6 +680,9 @@ // Load pending instantiations from the external source. SmallVector Pending; ExternalSource->ReadPendingInstantiations(Pending); + for (auto PII : Pending) + if (FunctionDecl *Func = dyn_cast(PII.first)) + Func->setMarkedForPendingInstantiation(); PendingInstantiations.insert(PendingInstantiations.begin(), Pending.begin(), Pending.end()); } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -13082,6 +13082,7 @@ // call to such a function. InstantiateFunctionDefinition(PointOfInstantiation, Func); else { + Func->setMarkedForPendingInstantiation(); PendingInstantiations.push_back(std::make_pair(Func, PointOfInstantiation)); // Notify the consumer that a function was implicitly instantiated. Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3531,6 +3531,7 @@ // Postpone late parsed template instantiations. if (PatternDecl->isLateTemplateParsed() && !LateTemplateParser) { + Function->setMarkedForPendingInstantiation(); PendingInstantiations.push_back( std::make_pair(Function, PointOfInstantiation)); return; @@ -3582,6 +3583,7 @@ } else if (Function->getTemplateSpecializationKind() == TSK_ExplicitInstantiationDefinition) { assert(!Recursive); + Function->setMarkedForPendingInstantiation(); PendingInstantiations.push_back( std::make_pair(Function, PointOfInstantiation)); } else if (Function->getTemplateSpecializationKind() Index: test/CodeGen/no-devirt.cpp =================================================================== --- test/CodeGen/no-devirt.cpp +++ test/CodeGen/no-devirt.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 %s -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %s +template < typename T, int N = 0 > class TmplWithArray { +public: + virtual T& operator [] (int idx); + virtual T& func1 (int idx); + virtual T& func2 (int idx); + T ar[N+1]; +}; +class Wrapper { + TmplWithArray data; + bool indexIt(int a); +}; +bool Wrapper::indexIt(int a) +{ + if (a > 6) return data[a] ; // Should not devirtualize + if (a > 4) return data.func1(a); // Should devirtualize + return data.func2(a); // Should devirtualize +} +template T& TmplWithArray::operator[](int idx) { + return ar[idx]; +} +template T& TmplWithArray::func1(int idx) { + return ar[idx]; +} +// CHECK-NOT: call {{.*}} @_ZN13TmplWithArrayIbLi10EEixEi +// CHECK-DAG: call {{.*}} @_ZN13TmplWithArrayIbLi10EE5func1Ei +// CHECK-DAG: call {{.*}} @_ZN13TmplWithArrayIbLi10EE5func2Ei