forked from OSchip/llvm-project
Fix wrong devirtualization when the final overrider in one base class
overrides the final overrider in a different base class.
This commit is contained in:
parent
b074acb82f
commit
aade5fbbfe
|
@ -758,6 +758,8 @@ CXXRecordDecl::getFinalOverriders(CXXFinalOverriderMap &FinalOverriders) const {
|
|||
return false;
|
||||
};
|
||||
|
||||
// FIXME: IsHidden reads from Overriding from the middle of a remove_if
|
||||
// over the same sequence! Is this guaranteed to work?
|
||||
Overriding.erase(
|
||||
std::remove_if(Overriding.begin(), Overriding.end(), IsHidden),
|
||||
Overriding.end());
|
||||
|
|
|
@ -2038,17 +2038,36 @@ CXXMethodDecl::getCorrespondingMethodInClass(const CXXRecordDecl *RD,
|
|||
if (auto *MD = getCorrespondingMethodDeclaredInClass(RD, MayBeBase))
|
||||
return MD;
|
||||
|
||||
llvm::SmallVector<CXXMethodDecl*, 4> FinalOverriders;
|
||||
auto AddFinalOverrider = [&](CXXMethodDecl *D) {
|
||||
// If this function is overridden by a candidate final overrider, it is not
|
||||
// a final overrider.
|
||||
for (CXXMethodDecl *OtherD : FinalOverriders) {
|
||||
if (declaresSameEntity(D, OtherD) || recursivelyOverrides(OtherD, D))
|
||||
return;
|
||||
}
|
||||
|
||||
// Other candidate final overriders might be overridden by this function.
|
||||
FinalOverriders.erase(
|
||||
std::remove_if(FinalOverriders.begin(), FinalOverriders.end(),
|
||||
[&](CXXMethodDecl *OtherD) {
|
||||
return recursivelyOverrides(D, OtherD);
|
||||
}),
|
||||
FinalOverriders.end());
|
||||
|
||||
FinalOverriders.push_back(D);
|
||||
};
|
||||
|
||||
for (const auto &I : RD->bases()) {
|
||||
const RecordType *RT = I.getType()->getAs<RecordType>();
|
||||
if (!RT)
|
||||
continue;
|
||||
const auto *Base = cast<CXXRecordDecl>(RT->getDecl());
|
||||
CXXMethodDecl *T = this->getCorrespondingMethodInClass(Base);
|
||||
if (T)
|
||||
return T;
|
||||
if (CXXMethodDecl *D = this->getCorrespondingMethodInClass(Base))
|
||||
AddFinalOverrider(D);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return FinalOverriders.size() == 1 ? FinalOverriders.front() : nullptr;
|
||||
}
|
||||
|
||||
CXXMethodDecl *CXXMethodDecl::Create(ASTContext &C, CXXRecordDecl *RD,
|
||||
|
@ -2105,6 +2124,11 @@ CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base,
|
|||
CXXMethodDecl *DevirtualizedMethod =
|
||||
getCorrespondingMethodInClass(BestDynamicDecl);
|
||||
|
||||
// If there final overrider in the dynamic type is ambiguous, we can't
|
||||
// devirtualize this call.
|
||||
if (!DevirtualizedMethod)
|
||||
return nullptr;
|
||||
|
||||
// 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.
|
||||
|
|
|
@ -255,6 +255,49 @@ namespace Test10 {
|
|||
}
|
||||
}
|
||||
|
||||
namespace TestVBase {
|
||||
struct A { virtual void f(); };
|
||||
struct B : virtual A {};
|
||||
struct C : virtual A { void f() override; };
|
||||
|
||||
extern struct BC final : B, C {} &bc;
|
||||
extern struct BCusingA final : B, C { using A::f; } &bc_using_a;
|
||||
extern struct BCusingB final : B, C { using B::f; } &bc_using_b;
|
||||
extern struct BCusingC final : B, C { using C::f; } &bc_using_c;
|
||||
|
||||
extern struct CB final : C, B {} &cb;
|
||||
extern struct CBusingA final : C, B { using A::f; } &cb_using_a;
|
||||
extern struct CBusingB final : C, B { using B::f; } &cb_using_b;
|
||||
extern struct CBusingC final : C, B { using C::f; } &cb_using_c;
|
||||
|
||||
// CHECK-LABEL: @_ZN9TestVBase4testEv(
|
||||
void test() {
|
||||
// FIXME: The 'using A' case can be devirtualized to call A's virtual
|
||||
// adjustment thunk for C::f.
|
||||
// FIXME: The 'using B' case can be devirtualized, but requires us to emit
|
||||
// a derived-to-base or base-to-derived conversion as part of
|
||||
// devirtualization.
|
||||
|
||||
// CHECK: call void @_ZN9TestVBase1C1fEv(
|
||||
bc.f();
|
||||
// CHECK: call void %
|
||||
bc_using_a.f();
|
||||
// CHECK: call void %
|
||||
bc_using_b.f();
|
||||
// CHECK: call void @_ZN9TestVBase1C1fEv(
|
||||
bc_using_c.f();
|
||||
|
||||
// CHECK: call void @_ZN9TestVBase1C1fEv(
|
||||
cb.f();
|
||||
// CHECK: call void %
|
||||
cb_using_a.f();
|
||||
// CHECK: call void %
|
||||
cb_using_b.f();
|
||||
// CHECK: call void @_ZN9TestVBase1C1fEv(
|
||||
cb_using_c.f();
|
||||
}
|
||||
}
|
||||
|
||||
namespace Test11 {
|
||||
// Check that the definitions of Derived's operators are emitted.
|
||||
|
||||
|
|
Loading…
Reference in New Issue