Make our marking of virtual members functions in a class be

deterministic and work properly with templates. Once a class that
needs a vtable has been defined, we now do one if two things:

  - If the class has no key function, we place the class on a list of
    classes whose virtual functions will need to be "marked" at the
    end of the translation unit. The delay until the end of the
    translation unit is needed because we might see template
    specializations of these virtual functions.
  - If the class has a key function, we do nothing; when the key
    function is defined, the class will be placed on the
    aforementioned list.

At the end of the translation unit, we "mark" all of the virtual
functions of the classes on the list as used, possibly causing
template instantiation and other classes to be added to the
list. This gets LLVM's lib/Support/CommandLine.cpp compiling again.

llvm-svn: 92821
This commit is contained in:
Douglas Gregor 2010-01-06 04:44:19 +00:00
parent 991eb3f6c0
commit 0a0f04dcb9
7 changed files with 66 additions and 69 deletions

View File

@ -2172,11 +2172,11 @@ public:
void MarkBaseAndMemberDestructorsReferenced(CXXDestructorDecl *Destructor); void MarkBaseAndMemberDestructorsReferenced(CXXDestructorDecl *Destructor);
/// ClassesWithUnmarkedVirtualMembers - Contains record decls whose virtual /// ClassesWithUnmarkedVirtualMembers - Contains record decls whose virtual
/// members might need to be marked as referenced. This is either done when /// members need to be marked as referenced at the end of the translation
/// the key function definition is emitted (this is handled by by /// unit. It will contain polymorphic classes that do not have a key
/// MaybeMarkVirtualMembersReferenced), or at the end of the translation unit /// function or have a key function that has been defined.
/// (done by ProcessPendingClassesWithUnmarkedVirtualMembers). llvm::SmallVector<std::pair<CXXRecordDecl *, SourceLocation>, 4>
std::map<CXXRecordDecl *, SourceLocation> ClassesWithUnmarkedVirtualMembers; ClassesWithUnmarkedVirtualMembers;
/// MaybeMarkVirtualMembersReferenced - If the passed in method is the /// MaybeMarkVirtualMembersReferenced - If the passed in method is the
/// key function of the record decl, will mark virtual member functions as /// key function of the record decl, will mark virtual member functions as

View File

@ -5098,6 +5098,16 @@ void Sema::ActOnTagFinishDefinition(Scope *S, DeclPtrTy TagD,
// Exit this scope of this tag's definition. // Exit this scope of this tag's definition.
PopDeclContext(); PopDeclContext();
// If this is a polymorphic C++ class without a key function, we'll
// have to mark all of the virtual members to allow emission of a vtable
// in this translation unit.
if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Tag)) {
if (!Record->isDependentContext() && Record->isDynamicClass() &&
!Context.getKeyFunction(Record))
ClassesWithUnmarkedVirtualMembers.push_back(std::make_pair(Record,
RBraceLoc));
}
// Notify the consumer that we've defined a tag. // Notify the consumer that we've defined a tag.
Consumer.HandleTagDeclDefinition(Tag); Consumer.HandleTagDeclDefinition(Tag);
} }

View File

@ -2054,7 +2054,7 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) {
if (!Record->isDependentType()) if (!Record->isDependentType())
AddImplicitlyDeclaredMembersToClass(Record); AddImplicitlyDeclaredMembersToClass(Record);
if (Record->isInvalidDecl()) if (Record->isInvalidDecl())
return; return;
@ -5693,67 +5693,30 @@ void Sema::MaybeMarkVirtualMembersReferenced(SourceLocation Loc,
if (!RD->isDynamicClass()) if (!RD->isDynamicClass())
return; return;
if (!MD->isOutOfLine()) { // Only out-of-line definitions matter.
// The only inline functions we care about are constructors. We also defer if (!MD->isOutOfLine())
// marking the virtual members as referenced until we've reached the end return;
// of the translation unit. We do this because we need to know the key
// function of the class in order to determine the key function. const CXXMethodDecl *KeyFunction = Context.getKeyFunction(RD);
if (isa<CXXConstructorDecl>(MD)) if (!KeyFunction || KeyFunction->getCanonicalDecl() != MD->getCanonicalDecl())
ClassesWithUnmarkedVirtualMembers.insert(std::make_pair(RD, Loc));
return; return;
}
switch (RD->getTemplateSpecializationKind()) { // We will need to mark all of the virtual members as referenced to build the
case TSK_Undeclared: // vtable.
case TSK_ExplicitSpecialization: { ClassesWithUnmarkedVirtualMembers.push_back(std::make_pair(RD, Loc));
const CXXMethodDecl *KeyFunction = Context.getKeyFunction(RD);
if (!KeyFunction) {
// This record does not have a key function, so we assume that the vtable
// will be emitted when it's used by the constructor.
if (!isa<CXXConstructorDecl>(MD))
return;
} else if (KeyFunction->getCanonicalDecl() != MD->getCanonicalDecl()) {
// We don't have the right key function.
return;
}
break;
}
case TSK_ImplicitInstantiation:
case TSK_ExplicitInstantiationDeclaration:
case TSK_ExplicitInstantiationDefinition:
// Always mark the virtual members of an instantiated template.
break;
}
// Mark the members as referenced.
MarkVirtualMembersReferenced(Loc, RD);
ClassesWithUnmarkedVirtualMembers.erase(RD);
} }
bool Sema::ProcessPendingClassesWithUnmarkedVirtualMembers() { bool Sema::ProcessPendingClassesWithUnmarkedVirtualMembers() {
if (ClassesWithUnmarkedVirtualMembers.empty()) if (ClassesWithUnmarkedVirtualMembers.empty())
return false; return false;
for (std::map<CXXRecordDecl *, SourceLocation>::iterator i = while (!ClassesWithUnmarkedVirtualMembers.empty()) {
ClassesWithUnmarkedVirtualMembers.begin(), CXXRecordDecl *RD = ClassesWithUnmarkedVirtualMembers.back().first;
e = ClassesWithUnmarkedVirtualMembers.end(); i != e; ++i) { SourceLocation Loc = ClassesWithUnmarkedVirtualMembers.back().second;
CXXRecordDecl *RD = i->first; ClassesWithUnmarkedVirtualMembers.pop_back();
const CXXMethodDecl *KeyFunction = Context.getKeyFunction(RD);
if (KeyFunction) {
// We know that the class has a key function. If the key function was
// declared in this translation unit, then it the class decl would not
// have been in the ClassesWithUnmarkedVirtualMembers map.
continue;
}
SourceLocation Loc = i->second;
MarkVirtualMembersReferenced(Loc, RD); MarkVirtualMembersReferenced(Loc, RD);
} }
ClassesWithUnmarkedVirtualMembers.clear();
return true; return true;
} }

View File

@ -1060,6 +1060,13 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
// Exit the scope of this instantiation. // Exit the scope of this instantiation.
CurContext = PreviousContext; CurContext = PreviousContext;
// If this is a polymorphic C++ class without a key function, we'll
// have to mark all of the virtual members to allow emission of a vtable
// in this translation unit.
if (Instantiation->isDynamicClass() && !Context.getKeyFunction(Instantiation))
ClassesWithUnmarkedVirtualMembers.push_back(std::make_pair(Instantiation,
PointOfInstantiation));
if (!Invalid) if (!Invalid)
Consumer.HandleTagDeclDefinition(Instantiation); Consumer.HandleTagDeclDefinition(Instantiation);

View File

@ -15,15 +15,15 @@ void B::f() { // expected-note {{implicit default destructor for 'struct B' firs
struct C : A { // expected-error {{no suitable member 'operator delete' in 'C'}} struct C : A { // expected-error {{no suitable member 'operator delete' in 'C'}}
C(); C();
void operator delete(void *, int); // expected-note {{'operator delete' declared here}} void operator delete(void *, int); // expected-note {{'operator delete' declared here}}
}; }; // expected-note {{implicit default destructor for 'struct C' first required here}}
C::C() { } // expected-note {{implicit default destructor for 'struct C' first required here}} C::C() { }
struct D : A { // expected-error {{no suitable member 'operator delete' in 'D'}} struct D : A { // expected-error {{no suitable member 'operator delete' in 'D'}}
void operator delete(void *, int); // expected-note {{'operator delete' declared here}} void operator delete(void *, int); // expected-note {{'operator delete' declared here}}
}; }; // expected-note {{implicit default destructor for 'struct D' first required here}}
void f() { void f() {
new D; // expected-note {{implicit default destructor for 'struct D' first required here}} new D;
} }

View File

@ -4,19 +4,15 @@ struct A {
}; };
struct B : A { // expected-error {{no suitable member 'operator delete' in 'B'}} struct B : A { // expected-error {{no suitable member 'operator delete' in 'B'}}
B() { } // expected-note {{implicit default destructor for 'struct B' first required here}} B() { }
void operator delete(void *, int); // expected-note {{'operator delete' declared here}} void operator delete(void *, int); // expected-note {{'operator delete' declared here}}
}; }; // expected-note {{implicit default destructor for 'struct B' first required here}}
struct C : A { // expected-error {{no suitable member 'operator delete' in 'C'}} struct C : A { // expected-error {{no suitable member 'operator delete' in 'C'}}
void operator delete(void *, int); // expected-note {{'operator delete' declared here}} void operator delete(void *, int); // expected-note {{'operator delete' declared here}}
}; }; // expected-note {{implicit default destructor for 'struct C' first required here}}
void f() { void f() {
// new B should mark the constructor as used, which then marks
// all the virtual members as used, because B has no key function.
(void)new B; (void)new B;
(void)new C;
// Same here, except that C has an implicit constructor.
(void)new C; // expected-note {{implicit default destructor for 'struct C' first required here}}
} }

View File

@ -3,14 +3,19 @@
namespace PR5557 { namespace PR5557 {
template <class T> struct A { template <class T> struct A {
A(); A();
virtual void anchor(); // expected-note{{instantiation}}
virtual int a(T x); virtual int a(T x);
}; };
template<class T> A<T>::A() {} template<class T> A<T>::A() {}
template<class T> void A<T>::anchor() { }
template<class T> int A<T>::a(T x) { template<class T> int A<T>::a(T x) {
return *x; // expected-error{{requires pointer operand}} return *x; // expected-error{{requires pointer operand}}
} }
A<int> x; // expected-note{{instantiation}} void f(A<int> x) {
x.anchor();
}
template<typename T> template<typename T>
struct X { struct X {
@ -20,3 +25,19 @@ struct X {
template<> template<>
void X<int>::f() { } void X<int>::f() { }
} }
template<typename T>
struct Base {
virtual ~Base() {
int *ptr = 0;
T t = ptr; // expected-error{{cannot initialize}}
}
};
template<typename T>
struct Derived : Base<T> {
virtual void foo() { }
};
template struct Derived<int>; // expected-note{{instantiation}}