forked from OSchip/llvm-project
Fix PR17382 - properly group virtual method overloads in the vftable
llvm-svn: 192067
This commit is contained in:
parent
78527050c2
commit
20df98c0d0
|
@ -2730,6 +2730,38 @@ FindDirectlyOverriddenMethodInBases(const CXXMethodDecl *MD,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void GroupNewVirtualOverloads(
|
||||
const CXXRecordDecl *RD,
|
||||
SmallVector<const CXXMethodDecl *, 10> &VirtualMethods) {
|
||||
// Put the virtual methods into VirtualMethods in the proper order:
|
||||
// 1) Group overloads by declaration name. New groups are added to the
|
||||
// vftable in the order of their first declarations in this class
|
||||
// (including overrides).
|
||||
// 2) In each group, new overloads appear in the reverse order of declaration.
|
||||
typedef SmallVector<const CXXMethodDecl *, 1> MethodGroup;
|
||||
SmallVector<MethodGroup, 10> Groups;
|
||||
typedef llvm::DenseMap<DeclarationName, unsigned> VisitedGroupIndicesTy;
|
||||
VisitedGroupIndicesTy VisitedGroupIndices;
|
||||
for (CXXRecordDecl::method_iterator I = RD->method_begin(),
|
||||
E = RD->method_end(); I != E; ++I) {
|
||||
const CXXMethodDecl *MD = *I;
|
||||
if (!MD->isVirtual())
|
||||
continue;
|
||||
|
||||
VisitedGroupIndicesTy::iterator J;
|
||||
bool Inserted;
|
||||
llvm::tie(J, Inserted) = VisitedGroupIndices.insert(
|
||||
std::make_pair(MD->getDeclName(), Groups.size()));
|
||||
if (Inserted)
|
||||
Groups.push_back(MethodGroup(1, MD));
|
||||
else
|
||||
Groups[J->second].push_back(MD);
|
||||
}
|
||||
|
||||
for (unsigned I = 0, E = Groups.size(); I != E; ++I)
|
||||
VirtualMethods.append(Groups[I].rbegin(), Groups[I].rend());
|
||||
}
|
||||
|
||||
void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
|
||||
const CXXRecordDecl *LastVBase,
|
||||
BasesSetVectorTy &VisitedBases) {
|
||||
|
@ -2766,6 +2798,10 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
|
|||
llvm_unreachable("Found a duplicate primary base!");
|
||||
}
|
||||
|
||||
SmallVector<const CXXMethodDecl*, 10> VirtualMethods;
|
||||
// Put virtual methods in the proper order.
|
||||
GroupNewVirtualOverloads(RD, VirtualMethods);
|
||||
|
||||
// Now go through all virtual member functions and add them to the current
|
||||
// vftable. This is done by
|
||||
// - replacing overridden methods in their existing slots, as long as they
|
||||
|
@ -2774,12 +2810,8 @@ void VFTableBuilder::AddMethods(BaseSubobject Base, unsigned BaseDepth,
|
|||
// sub-bases;
|
||||
// - adding new slots for methods that require Return adjustment.
|
||||
// We keep track of the methods visited in the sub-bases in MethodInfoMap.
|
||||
for (CXXRecordDecl::method_iterator I = RD->method_begin(),
|
||||
E = RD->method_end(); I != E; ++I) {
|
||||
const CXXMethodDecl *MD = *I;
|
||||
|
||||
if (!MD->isVirtual())
|
||||
continue;
|
||||
for (unsigned I = 0, E = VirtualMethods.size(); I != E; ++I) {
|
||||
const CXXMethodDecl *MD = VirtualMethods[I];
|
||||
|
||||
FinalOverriders::OverriderInfo Overrider =
|
||||
Overriders.getOverrider(MD, Base.getBaseOffset());
|
||||
|
|
|
@ -9,6 +9,11 @@
|
|||
// RUN: FileCheck --check-prefix=CHECK-F %s < %t
|
||||
// RUN: FileCheck --check-prefix=CHECK-G %s < %t
|
||||
// RUN: FileCheck --check-prefix=CHECK-I %s < %t
|
||||
// RUN: FileCheck --check-prefix=CHECK-J %s < %t
|
||||
// RUN: FileCheck --check-prefix=CHECK-K %s < %t
|
||||
// RUN: FileCheck --check-prefix=CHECK-L %s < %t
|
||||
// RUN: FileCheck --check-prefix=CHECK-M %s < %t
|
||||
// RUN: FileCheck --check-prefix=CHECK-N %s < %t
|
||||
|
||||
struct A {
|
||||
// CHECK-A: VFTable for 'A' (3 entries)
|
||||
|
@ -149,3 +154,99 @@ struct I : Empty {
|
|||
};
|
||||
|
||||
I i;
|
||||
|
||||
struct J {
|
||||
// CHECK-J: VFTable for 'J' (6 entries)
|
||||
// CHECK-J-NEXT: 0 | void J::foo(long)
|
||||
// CHECK-J-NEXT: 1 | void J::foo(int)
|
||||
// CHECK-J-NEXT: 2 | void J::foo(short)
|
||||
// CHECK-J-NEXT: 3 | void J::bar(long)
|
||||
// CHECK-J-NEXT: 4 | void J::bar(int)
|
||||
// CHECK-J-NEXT: 5 | void J::bar(short)
|
||||
virtual void foo(short);
|
||||
virtual void bar(short);
|
||||
virtual void foo(int);
|
||||
virtual void bar(int);
|
||||
virtual void foo(long);
|
||||
virtual void bar(long);
|
||||
};
|
||||
|
||||
J j;
|
||||
|
||||
struct K : J {
|
||||
// CHECK-K: VFTable for 'J' in 'K' (9 entries)
|
||||
// CHECK-K-NEXT: 0 | void J::foo(long)
|
||||
// CHECK-K-NEXT: 1 | void J::foo(int)
|
||||
// CHECK-K-NEXT: 2 | void J::foo(short)
|
||||
// CHECK-K-NEXT: 3 | void J::bar(long)
|
||||
// CHECK-K-NEXT: 4 | void J::bar(int)
|
||||
// CHECK-K-NEXT: 5 | void J::bar(short)
|
||||
// CHECK-K-NEXT: 6 | void K::bar(double)
|
||||
// CHECK-K-NEXT: 7 | void K::bar(float)
|
||||
// CHECK-K-NEXT: 8 | void K::foo(float)
|
||||
virtual void bar(float);
|
||||
virtual void foo(float);
|
||||
virtual void bar(double);
|
||||
};
|
||||
|
||||
K k;
|
||||
|
||||
struct L : J {
|
||||
// CHECK-L: VFTable for 'J' in 'L' (9 entries)
|
||||
// CHECK-L-NEXT: 0 | void J::foo(long)
|
||||
// CHECK-L-NEXT: 1 | void L::foo(int)
|
||||
// CHECK-L-NEXT: 2 | void J::foo(short)
|
||||
// CHECK-L-NEXT: 3 | void J::bar(long)
|
||||
// CHECK-L-NEXT: 4 | void J::bar(int)
|
||||
// CHECK-L-NEXT: 5 | void J::bar(short)
|
||||
// CHECK-L-NEXT: 6 | void L::foo(float)
|
||||
// CHECK-L-NEXT: 7 | void L::bar(double)
|
||||
// CHECK-L-NEXT: 8 | void L::bar(float)
|
||||
|
||||
// This case is interesting. Since the J::foo(int) override is the first method in
|
||||
// the class, foo(float) precedes the bar(double) and bar(float) in the vftable.
|
||||
virtual void foo(int);
|
||||
virtual void bar(float);
|
||||
virtual void foo(float);
|
||||
virtual void bar(double);
|
||||
};
|
||||
|
||||
L l;
|
||||
|
||||
struct M : J {
|
||||
// CHECK-M: VFTable for 'J' in 'M' (11 entries)
|
||||
// CHECK-M-NEXT: 0 | void J::foo(long)
|
||||
// CHECK-M-NEXT: 1 | void M::foo(int)
|
||||
// CHECK-M-NEXT: 2 | void J::foo(short)
|
||||
// CHECK-M-NEXT: 3 | void J::bar(long)
|
||||
// CHECK-M-NEXT: 4 | void J::bar(int)
|
||||
// CHECK-M-NEXT: 5 | void J::bar(short)
|
||||
// CHECK-M-NEXT: 6 | void M::foo(float)
|
||||
// CHECK-M-NEXT: 7 | void M::spam(long)
|
||||
// CHECK-M-NEXT: 8 | void M::spam(int)
|
||||
// CHECK-M-NEXT: 9 | void M::bar(double)
|
||||
// CHECK-M-NEXT: 10 | void M::bar(float)
|
||||
|
||||
virtual void foo(int);
|
||||
virtual void spam(int);
|
||||
virtual void bar(float);
|
||||
virtual void bar(double);
|
||||
virtual void foo(float);
|
||||
virtual void spam(long);
|
||||
};
|
||||
|
||||
M m;
|
||||
|
||||
struct N {
|
||||
// CHECK-N: VFTable for 'N' (4 entries)
|
||||
// CHECK-N-NEXT: 0 | void N::operator+(int)
|
||||
// CHECK-N-NEXT: 1 | void N::operator+(short)
|
||||
// CHECK-N-NEXT: 2 | void N::operator*(int)
|
||||
// CHECK-N-NEXT: 3 | void N::operator*(short)
|
||||
virtual void operator+(short);
|
||||
virtual void operator*(short);
|
||||
virtual void operator+(int);
|
||||
virtual void operator*(int);
|
||||
};
|
||||
|
||||
N n;
|
||||
|
|
Loading…
Reference in New Issue