forked from OSchip/llvm-project
[Sema] Mark a virtual CXXMethodDecl as used if a call to it can be
devirtualized. The code to detect devirtualized calls is already in IRGen, so move the code to lib/AST and make it a shared utility between Sema and IRGen. This commit fixes a linkage error I was seeing when compiling the following code: $ cat test1.cpp struct Base { virtual void operator()() {} }; template<class T> struct Derived final : Base { void operator()() override {} }; Derived<int> *d; int main() { if (d) (*d)(); return 0; } rdar://problem/33195657 Differential Revision: https://reviews.llvm.org/D34301 llvm-svn: 307883
This commit is contained in:
parent
fa5183b028
commit
2246167362
|
@ -1886,6 +1886,19 @@ public:
|
||||||
return (CD->begin_overridden_methods() != CD->end_overridden_methods());
|
return (CD->begin_overridden_methods() != CD->end_overridden_methods());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If it's possible to devirtualize a call to this method, return the called
|
||||||
|
/// function. Otherwise, return null.
|
||||||
|
|
||||||
|
/// \param Base The object on which this virtual function is called.
|
||||||
|
/// \param IsAppleKext True if we are compiling for Apple kext.
|
||||||
|
CXXMethodDecl *getDevirtualizedMethod(const Expr *Base, bool IsAppleKext);
|
||||||
|
|
||||||
|
const CXXMethodDecl *getDevirtualizedMethod(const Expr *Base,
|
||||||
|
bool IsAppleKext) const {
|
||||||
|
return const_cast<CXXMethodDecl *>(this)->getDevirtualizedMethod(
|
||||||
|
Base, IsAppleKext);
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Determine whether this is a usual deallocation function
|
/// \brief Determine whether this is a usual deallocation function
|
||||||
/// (C++ [basic.stc.dynamic.deallocation]p2), which is an overloaded
|
/// (C++ [basic.stc.dynamic.deallocation]p2), which is an overloaded
|
||||||
/// delete or delete[] operator with a particular signature.
|
/// delete or delete[] operator with a particular signature.
|
||||||
|
|
|
@ -3944,7 +3944,7 @@ public:
|
||||||
void MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
|
void MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
|
||||||
bool MightBeOdrUse = true);
|
bool MightBeOdrUse = true);
|
||||||
void MarkVariableReferenced(SourceLocation Loc, VarDecl *Var);
|
void MarkVariableReferenced(SourceLocation Loc, VarDecl *Var);
|
||||||
void MarkDeclRefReferenced(DeclRefExpr *E);
|
void MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base = nullptr);
|
||||||
void MarkMemberReferenced(MemberExpr *E);
|
void MarkMemberReferenced(MemberExpr *E);
|
||||||
|
|
||||||
void UpdateMarkingForLValueToRValue(Expr *E);
|
void UpdateMarkingForLValueToRValue(Expr *E);
|
||||||
|
|
|
@ -1605,6 +1605,84 @@ CXXMethodDecl *CXXMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
|
||||||
SC_None, false, false, SourceLocation());
|
SC_None, false, false, SourceLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base,
|
||||||
|
bool IsAppleKext) {
|
||||||
|
assert(isVirtual() && "this method is expected to be virtual");
|
||||||
|
|
||||||
|
// When building with -fapple-kext, all calls must go through the vtable since
|
||||||
|
// the kernel linker can do runtime patching of vtables.
|
||||||
|
if (IsAppleKext)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// If the member function is marked 'final', we know that it can't be
|
||||||
|
// overridden and can therefore devirtualize it unless it's pure virtual.
|
||||||
|
if (hasAttr<FinalAttr>())
|
||||||
|
return isPure() ? nullptr : this;
|
||||||
|
|
||||||
|
// If Base is unknown, we cannot devirtualize.
|
||||||
|
if (!Base)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// If the base expression (after skipping derived-to-base conversions) is a
|
||||||
|
// class prvalue, then we can devirtualize.
|
||||||
|
Base = Base->getBestDynamicClassTypeExpr();
|
||||||
|
if (Base->isRValue() && Base->getType()->isRecordType())
|
||||||
|
return this;
|
||||||
|
|
||||||
|
// If we don't even know what we would call, we can't devirtualize.
|
||||||
|
const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType();
|
||||||
|
if (!BestDynamicDecl)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// There may be a method corresponding to MD in a derived class.
|
||||||
|
CXXMethodDecl *DevirtualizedMethod =
|
||||||
|
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 nullptr;
|
||||||
|
|
||||||
|
// If that method is marked final, we can devirtualize it.
|
||||||
|
if (DevirtualizedMethod->hasAttr<FinalAttr>())
|
||||||
|
return DevirtualizedMethod;
|
||||||
|
|
||||||
|
// Similarly, if the class itself is marked 'final' it can't be overridden
|
||||||
|
// and we can therefore devirtualize the member function call.
|
||||||
|
if (BestDynamicDecl->hasAttr<FinalAttr>())
|
||||||
|
return DevirtualizedMethod;
|
||||||
|
|
||||||
|
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Base)) {
|
||||||
|
if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl()))
|
||||||
|
if (VD->getType()->isRecordType())
|
||||||
|
// This is a record decl. We know the type and can devirtualize it.
|
||||||
|
return DevirtualizedMethod;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Base))
|
||||||
|
if (const ValueDecl *VD = dyn_cast<ValueDecl>(ME->getMemberDecl()))
|
||||||
|
return VD->getType()->isRecordType() ? DevirtualizedMethod : nullptr;
|
||||||
|
|
||||||
|
// Likewise for calls on an object accessed by a (non-reference) pointer to
|
||||||
|
// member access.
|
||||||
|
if (auto *BO = dyn_cast<BinaryOperator>(Base)) {
|
||||||
|
if (BO->isPtrMemOp()) {
|
||||||
|
auto *MPT = BO->getRHS()->getType()->castAs<MemberPointerType>();
|
||||||
|
if (MPT->getPointeeType()->isRecordType())
|
||||||
|
return DevirtualizedMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't devirtualize the call.
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool CXXMethodDecl::isUsualDeallocationFunction() const {
|
bool CXXMethodDecl::isUsualDeallocationFunction() const {
|
||||||
if (getOverloadedOperator() != OO_Delete &&
|
if (getOverloadedOperator() != OO_Delete &&
|
||||||
getOverloadedOperator() != OO_Array_Delete)
|
getOverloadedOperator() != OO_Array_Delete)
|
||||||
|
|
|
@ -2716,88 +2716,6 @@ llvm::Value *CodeGenFunction::EmitVTableTypeCheckedLoad(
|
||||||
cast<llvm::PointerType>(VTable->getType())->getElementType());
|
cast<llvm::PointerType>(VTable->getType())->getElementType());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
CodeGenFunction::CanDevirtualizeMemberFunctionCall(const Expr *Base,
|
|
||||||
const CXXMethodDecl *MD) {
|
|
||||||
// When building with -fapple-kext, all calls must go through the vtable since
|
|
||||||
// the kernel linker can do runtime patching of vtables.
|
|
||||||
if (getLangOpts().AppleKext)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If the member function is marked 'final', we know that it can't be
|
|
||||||
// overridden and can therefore devirtualize it unless it's pure virtual.
|
|
||||||
if (MD->hasAttr<FinalAttr>())
|
|
||||||
return !MD->isPure();
|
|
||||||
|
|
||||||
// If the base expression (after skipping derived-to-base conversions) is a
|
|
||||||
// class prvalue, then we can devirtualize.
|
|
||||||
Base = Base->getBestDynamicClassTypeExpr();
|
|
||||||
if (Base->isRValue() && Base->getType()->isRecordType())
|
|
||||||
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.
|
|
||||||
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 (BestDynamicDecl->hasAttr<FinalAttr>())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Base)) {
|
|
||||||
if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
|
|
||||||
// This is a record decl. We know the type and can devirtualize it.
|
|
||||||
return VD->getType()->isRecordType();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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. However, we avoid
|
|
||||||
// devirtualizing a call to a template function that we could instantiate
|
|
||||||
// implicitly, but have not decided to do so. This is needed because if this
|
|
||||||
// function does not get instantiated, the devirtualization will create a
|
|
||||||
// direct call to a function whose body may not exist. In contrast, calls to
|
|
||||||
// template functions that are not defined in this TU are allowed to be
|
|
||||||
// devirtualized under assumption that it is user responsibility to
|
|
||||||
// instantiate them in some other TU.
|
|
||||||
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Base))
|
|
||||||
if (const ValueDecl *VD = dyn_cast<ValueDecl>(ME->getMemberDecl()))
|
|
||||||
return VD->getType()->isRecordType() &&
|
|
||||||
(MD->instantiationIsPending() || MD->isDefined() ||
|
|
||||||
!MD->isImplicitlyInstantiable());
|
|
||||||
|
|
||||||
// Likewise for calls on an object accessed by a (non-reference) pointer to
|
|
||||||
// member access.
|
|
||||||
if (auto *BO = dyn_cast<BinaryOperator>(Base)) {
|
|
||||||
if (BO->isPtrMemOp()) {
|
|
||||||
auto *MPT = BO->getRHS()->getType()->castAs<MemberPointerType>();
|
|
||||||
if (MPT->getPointeeType()->isRecordType())
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can't devirtualize the call.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CodeGenFunction::EmitForwardingCallToLambda(
|
void CodeGenFunction::EmitForwardingCallToLambda(
|
||||||
const CXXMethodDecl *callOperator,
|
const CXXMethodDecl *callOperator,
|
||||||
CallArgList &callArgs) {
|
CallArgList &callArgs) {
|
||||||
|
|
|
@ -199,7 +199,8 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
|
||||||
bool CanUseVirtualCall = MD->isVirtual() && !HasQualifier;
|
bool CanUseVirtualCall = MD->isVirtual() && !HasQualifier;
|
||||||
|
|
||||||
const CXXMethodDecl *DevirtualizedMethod = nullptr;
|
const CXXMethodDecl *DevirtualizedMethod = nullptr;
|
||||||
if (CanUseVirtualCall && CanDevirtualizeMemberFunctionCall(Base, MD)) {
|
if (CanUseVirtualCall &&
|
||||||
|
MD->getDevirtualizedMethod(Base, getLangOpts().AppleKext)) {
|
||||||
const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType();
|
const CXXRecordDecl *BestDynamicDecl = Base->getBestDynamicClassType();
|
||||||
DevirtualizedMethod = MD->getCorrespondingMethodInClass(BestDynamicDecl);
|
DevirtualizedMethod = MD->getCorrespondingMethodInClass(BestDynamicDecl);
|
||||||
assert(DevirtualizedMethod);
|
assert(DevirtualizedMethod);
|
||||||
|
|
|
@ -1752,11 +1752,6 @@ public:
|
||||||
llvm::Value *EmitVTableTypeCheckedLoad(const CXXRecordDecl *RD, llvm::Value *VTable,
|
llvm::Value *EmitVTableTypeCheckedLoad(const CXXRecordDecl *RD, llvm::Value *VTable,
|
||||||
uint64_t VTableByteOffset);
|
uint64_t VTableByteOffset);
|
||||||
|
|
||||||
/// CanDevirtualizeMemberFunctionCalls - Checks whether virtual calls on given
|
|
||||||
/// expr can be devirtualized.
|
|
||||||
bool CanDevirtualizeMemberFunctionCall(const Expr *Base,
|
|
||||||
const CXXMethodDecl *MD);
|
|
||||||
|
|
||||||
/// EnterDtorCleanups - Enter the cleanups necessary to complete the
|
/// EnterDtorCleanups - Enter the cleanups necessary to complete the
|
||||||
/// given phase of destruction for a destructor. The end result
|
/// given phase of destruction for a destructor. The end result
|
||||||
/// should call destructors on members and base classes in reverse
|
/// should call destructors on members and base classes in reverse
|
||||||
|
|
|
@ -14665,24 +14665,24 @@ static void MarkExprReferenced(Sema &SemaRef, SourceLocation Loc,
|
||||||
ME->performsVirtualDispatch(SemaRef.getLangOpts());
|
ME->performsVirtualDispatch(SemaRef.getLangOpts());
|
||||||
if (!IsVirtualCall)
|
if (!IsVirtualCall)
|
||||||
return;
|
return;
|
||||||
const Expr *Base = ME->getBase();
|
|
||||||
const CXXRecordDecl *MostDerivedClassDecl = Base->getBestDynamicClassType();
|
// If it's possible to devirtualize the call, mark the called function
|
||||||
if (!MostDerivedClassDecl)
|
// referenced.
|
||||||
return;
|
CXXMethodDecl *DM = MD->getDevirtualizedMethod(
|
||||||
CXXMethodDecl *DM = MD->getCorrespondingMethodInClass(MostDerivedClassDecl);
|
ME->getBase(), SemaRef.getLangOpts().AppleKext);
|
||||||
if (!DM || DM->isPure())
|
if (DM)
|
||||||
return;
|
SemaRef.MarkAnyDeclReferenced(Loc, DM, MightBeOdrUse);
|
||||||
SemaRef.MarkAnyDeclReferenced(Loc, DM, MightBeOdrUse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Perform reference-marking and odr-use handling for a DeclRefExpr.
|
/// \brief Perform reference-marking and odr-use handling for a DeclRefExpr.
|
||||||
void Sema::MarkDeclRefReferenced(DeclRefExpr *E) {
|
void Sema::MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base) {
|
||||||
// TODO: update this with DR# once a defect report is filed.
|
// TODO: update this with DR# once a defect report is filed.
|
||||||
// C++11 defect. The address of a pure member should not be an ODR use, even
|
// C++11 defect. The address of a pure member should not be an ODR use, even
|
||||||
// if it's a qualified reference.
|
// if it's a qualified reference.
|
||||||
bool OdrUse = true;
|
bool OdrUse = true;
|
||||||
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(E->getDecl()))
|
if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(E->getDecl()))
|
||||||
if (Method->isVirtual())
|
if (Method->isVirtual() &&
|
||||||
|
!Method->getDevirtualizedMethod(Base, getLangOpts().AppleKext))
|
||||||
OdrUse = false;
|
OdrUse = false;
|
||||||
MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse);
|
MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse);
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ static bool functionHasPassObjectSizeParams(const FunctionDecl *FD) {
|
||||||
/// A convenience routine for creating a decayed reference to a function.
|
/// A convenience routine for creating a decayed reference to a function.
|
||||||
static ExprResult
|
static ExprResult
|
||||||
CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl,
|
CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl,
|
||||||
bool HadMultipleCandidates,
|
const Expr *Base, bool HadMultipleCandidates,
|
||||||
SourceLocation Loc = SourceLocation(),
|
SourceLocation Loc = SourceLocation(),
|
||||||
const DeclarationNameLoc &LocInfo = DeclarationNameLoc()){
|
const DeclarationNameLoc &LocInfo = DeclarationNameLoc()){
|
||||||
if (S.DiagnoseUseOfDecl(FoundDecl, Loc))
|
if (S.DiagnoseUseOfDecl(FoundDecl, Loc))
|
||||||
|
@ -68,7 +68,7 @@ CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl,
|
||||||
if (HadMultipleCandidates)
|
if (HadMultipleCandidates)
|
||||||
DRE->setHadMultipleCandidates(true);
|
DRE->setHadMultipleCandidates(true);
|
||||||
|
|
||||||
S.MarkDeclRefReferenced(DRE);
|
S.MarkDeclRefReferenced(DRE, Base);
|
||||||
return S.ImpCastExprToType(DRE, S.Context.getPointerType(DRE->getType()),
|
return S.ImpCastExprToType(DRE, S.Context.getPointerType(DRE->getType()),
|
||||||
CK_FunctionToPointerDecay);
|
CK_FunctionToPointerDecay);
|
||||||
}
|
}
|
||||||
|
@ -11946,6 +11946,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
|
||||||
FunctionDecl *FnDecl = Best->Function;
|
FunctionDecl *FnDecl = Best->Function;
|
||||||
|
|
||||||
if (FnDecl) {
|
if (FnDecl) {
|
||||||
|
Expr *Base = nullptr;
|
||||||
// We matched an overloaded operator. Build a call to that
|
// We matched an overloaded operator. Build a call to that
|
||||||
// operator.
|
// operator.
|
||||||
|
|
||||||
|
@ -11958,7 +11959,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
|
||||||
Best->FoundDecl, Method);
|
Best->FoundDecl, Method);
|
||||||
if (InputRes.isInvalid())
|
if (InputRes.isInvalid())
|
||||||
return ExprError();
|
return ExprError();
|
||||||
Input = InputRes.get();
|
Base = Input = InputRes.get();
|
||||||
} else {
|
} else {
|
||||||
// Convert the arguments.
|
// Convert the arguments.
|
||||||
ExprResult InputInit
|
ExprResult InputInit
|
||||||
|
@ -11974,7 +11975,8 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, UnaryOperatorKind Opc,
|
||||||
|
|
||||||
// Build the actual expression node.
|
// Build the actual expression node.
|
||||||
ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl, Best->FoundDecl,
|
ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl, Best->FoundDecl,
|
||||||
HadMultipleCandidates, OpLoc);
|
Base, HadMultipleCandidates,
|
||||||
|
OpLoc);
|
||||||
if (FnExpr.isInvalid())
|
if (FnExpr.isInvalid())
|
||||||
return ExprError();
|
return ExprError();
|
||||||
|
|
||||||
|
@ -12159,6 +12161,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
|
||||||
FunctionDecl *FnDecl = Best->Function;
|
FunctionDecl *FnDecl = Best->Function;
|
||||||
|
|
||||||
if (FnDecl) {
|
if (FnDecl) {
|
||||||
|
Expr *Base = nullptr;
|
||||||
// We matched an overloaded operator. Build a call to that
|
// We matched an overloaded operator. Build a call to that
|
||||||
// operator.
|
// operator.
|
||||||
|
|
||||||
|
@ -12180,7 +12183,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
|
||||||
Best->FoundDecl, Method);
|
Best->FoundDecl, Method);
|
||||||
if (Arg0.isInvalid())
|
if (Arg0.isInvalid())
|
||||||
return ExprError();
|
return ExprError();
|
||||||
Args[0] = Arg0.getAs<Expr>();
|
Base = Args[0] = Arg0.getAs<Expr>();
|
||||||
Args[1] = RHS = Arg1.getAs<Expr>();
|
Args[1] = RHS = Arg1.getAs<Expr>();
|
||||||
} else {
|
} else {
|
||||||
// Convert the arguments.
|
// Convert the arguments.
|
||||||
|
@ -12204,7 +12207,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
|
||||||
|
|
||||||
// Build the actual expression node.
|
// Build the actual expression node.
|
||||||
ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl,
|
ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl,
|
||||||
Best->FoundDecl,
|
Best->FoundDecl, Base,
|
||||||
HadMultipleCandidates, OpLoc);
|
HadMultipleCandidates, OpLoc);
|
||||||
if (FnExpr.isInvalid())
|
if (FnExpr.isInvalid())
|
||||||
return ExprError();
|
return ExprError();
|
||||||
|
@ -12426,6 +12429,7 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
|
||||||
OpLocInfo.setCXXOperatorNameRange(SourceRange(LLoc, RLoc));
|
OpLocInfo.setCXXOperatorNameRange(SourceRange(LLoc, RLoc));
|
||||||
ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl,
|
ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl,
|
||||||
Best->FoundDecl,
|
Best->FoundDecl,
|
||||||
|
Base,
|
||||||
HadMultipleCandidates,
|
HadMultipleCandidates,
|
||||||
OpLocInfo.getLoc(),
|
OpLocInfo.getLoc(),
|
||||||
OpLocInfo.getInfo());
|
OpLocInfo.getInfo());
|
||||||
|
@ -12984,7 +12988,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj,
|
||||||
Context.DeclarationNames.getCXXOperatorName(OO_Call), LParenLoc);
|
Context.DeclarationNames.getCXXOperatorName(OO_Call), LParenLoc);
|
||||||
OpLocInfo.setCXXOperatorNameRange(SourceRange(LParenLoc, RParenLoc));
|
OpLocInfo.setCXXOperatorNameRange(SourceRange(LParenLoc, RParenLoc));
|
||||||
ExprResult NewFn = CreateFunctionRefExpr(*this, Method, Best->FoundDecl,
|
ExprResult NewFn = CreateFunctionRefExpr(*this, Method, Best->FoundDecl,
|
||||||
HadMultipleCandidates,
|
Obj, HadMultipleCandidates,
|
||||||
OpLocInfo.getLoc(),
|
OpLocInfo.getLoc(),
|
||||||
OpLocInfo.getInfo());
|
OpLocInfo.getInfo());
|
||||||
if (NewFn.isInvalid())
|
if (NewFn.isInvalid())
|
||||||
|
@ -13175,7 +13179,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
|
||||||
|
|
||||||
// Build the operator call.
|
// Build the operator call.
|
||||||
ExprResult FnExpr = CreateFunctionRefExpr(*this, Method, Best->FoundDecl,
|
ExprResult FnExpr = CreateFunctionRefExpr(*this, Method, Best->FoundDecl,
|
||||||
HadMultipleCandidates, OpLoc);
|
Base, HadMultipleCandidates, OpLoc);
|
||||||
if (FnExpr.isInvalid())
|
if (FnExpr.isInvalid())
|
||||||
return ExprError();
|
return ExprError();
|
||||||
|
|
||||||
|
@ -13234,7 +13238,7 @@ ExprResult Sema::BuildLiteralOperatorCall(LookupResult &R,
|
||||||
|
|
||||||
FunctionDecl *FD = Best->Function;
|
FunctionDecl *FD = Best->Function;
|
||||||
ExprResult Fn = CreateFunctionRefExpr(*this, FD, Best->FoundDecl,
|
ExprResult Fn = CreateFunctionRefExpr(*this, FD, Best->FoundDecl,
|
||||||
HadMultipleCandidates,
|
nullptr, HadMultipleCandidates,
|
||||||
SuffixInfo.getLoc(),
|
SuffixInfo.getLoc(),
|
||||||
SuffixInfo.getInfo());
|
SuffixInfo.getInfo());
|
||||||
if (Fn.isInvalid())
|
if (Fn.isInvalid())
|
||||||
|
|
|
@ -21,7 +21,7 @@ public:
|
||||||
struct Wrapper {
|
struct Wrapper {
|
||||||
TmplWithArray<bool, 10> data;
|
TmplWithArray<bool, 10> data;
|
||||||
bool indexIt(int a) {
|
bool indexIt(int a) {
|
||||||
if (a > 6) return data[a] ; // Should not devirtualize
|
if (a > 6) return data[a] ; // Should devirtualize
|
||||||
if (a > 4) return data.func1(a); // Should devirtualize
|
if (a > 4) return data.func1(a); // Should devirtualize
|
||||||
return data.func2(a); // Should devirtualize
|
return data.func2(a); // Should devirtualize
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ bool stuff(int p)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// CHECK-NOT: call {{.*}} @_ZN13TmplWithArrayIbLi10EEixEi
|
// CHECK-DAG: call {{.*}} @_ZN13TmplWithArrayIbLi10EEixEi
|
||||||
// CHECK-DAG: call {{.*}} @_ZN13TmplWithArrayIbLi10EE5func1Ei
|
// CHECK-DAG: call {{.*}} @_ZN13TmplWithArrayIbLi10EE5func1Ei
|
||||||
// CHECK-DAG: call {{.*}} @_ZN13TmplWithArrayIbLi10EE5func2Ei
|
// CHECK-DAG: call {{.*}} @_ZN13TmplWithArrayIbLi10EE5func2Ei
|
||||||
|
|
||||||
|
|
|
@ -241,3 +241,53 @@ namespace Test10 {
|
||||||
return static_cast<A *>(b)->f();
|
return static_cast<A *>(b)->f();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Test11 {
|
||||||
|
// Check that the definitions of Derived's operators are emitted.
|
||||||
|
|
||||||
|
// CHECK-LABEL: define linkonce_odr void @_ZN6Test111SIiE4foo1Ev(
|
||||||
|
// CHECK: call void @_ZN6Test111SIiE7DerivedclEv(
|
||||||
|
// CHECK: call zeroext i1 @_ZN6Test111SIiE7DerivedeqERKNS_4BaseE(
|
||||||
|
// CHECK: call zeroext i1 @_ZN6Test111SIiE7DerivedntEv(
|
||||||
|
// CHECK: call dereferenceable(4) %"class.Test11::Base"* @_ZN6Test111SIiE7DerivedixEi(
|
||||||
|
// CHECK: define linkonce_odr void @_ZN6Test111SIiE7DerivedclEv(
|
||||||
|
// CHECK: define linkonce_odr zeroext i1 @_ZN6Test111SIiE7DerivedeqERKNS_4BaseE(
|
||||||
|
// CHECK: define linkonce_odr zeroext i1 @_ZN6Test111SIiE7DerivedntEv(
|
||||||
|
// CHECK: define linkonce_odr dereferenceable(4) %"class.Test11::Base"* @_ZN6Test111SIiE7DerivedixEi(
|
||||||
|
class Base {
|
||||||
|
public:
|
||||||
|
virtual void operator()() {}
|
||||||
|
virtual bool operator==(const Base &other) { return false; }
|
||||||
|
virtual bool operator!() { return false; }
|
||||||
|
virtual Base &operator[](int i) { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct S {
|
||||||
|
class Derived final : public Base {
|
||||||
|
public:
|
||||||
|
void operator()() override {}
|
||||||
|
bool operator==(const Base &other) override { return true; }
|
||||||
|
bool operator!() override { return true; }
|
||||||
|
Base &operator[](int i) override { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
Derived *ptr = nullptr, *ptr2 = nullptr;
|
||||||
|
|
||||||
|
void foo1() {
|
||||||
|
if (ptr && ptr2) {
|
||||||
|
// These calls get devirtualized. Linkage fails if the definitions of
|
||||||
|
// the called functions are not emitted.
|
||||||
|
(*ptr)();
|
||||||
|
(void)(*ptr == *ptr2);
|
||||||
|
(void)(!(*ptr));
|
||||||
|
(void)((*ptr)[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void foo2() {
|
||||||
|
S<int> *s = new S<int>;
|
||||||
|
s->foo1();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -275,9 +275,8 @@ struct C {
|
||||||
virtual D& operator=(const D&);
|
virtual D& operator=(const D&);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cannot emit D's vtable available_externally, because we cannot create
|
// Can emit D's vtable available_externally.
|
||||||
// a reference to the inline virtual D::operator= function.
|
// CHECK-TEST11: @_ZTVN6Test111DE = available_externally unnamed_addr constant
|
||||||
// CHECK-TEST11: @_ZTVN6Test111DE = external unnamed_addr constant
|
|
||||||
struct D : C {
|
struct D : C {
|
||||||
virtual void key();
|
virtual void key();
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue