forked from OSchip/llvm-project
Synthesizing the definition of an implicit member is an AST modification, so notify any mutation listeners of it. This fixes a crasher in chained PCH, where an implicit destructor in a PCH gets a definition in a chained PCH, which is then lost. However, any further use of the destructor would cause its definition to be regenerated in the final file, hiding the bug.
llvm-svn: 130103
This commit is contained in:
parent
f830df4e9d
commit
ab238a7d18
|
@ -89,7 +89,7 @@ public:
|
|||
|
||||
/// \brief If the consumer is interested in entities getting modified after
|
||||
/// their initial creation, it should return a pointer to
|
||||
/// a GetASTMutationListener here.
|
||||
/// an ASTMutationListener here.
|
||||
virtual ASTMutationListener *GetASTMutationListener() { return 0; }
|
||||
|
||||
/// \brief If the consumer is interested in entities being deserialized from
|
||||
|
|
|
@ -48,6 +48,9 @@ public:
|
|||
/// template declaration.
|
||||
virtual void AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD,
|
||||
const FunctionDecl *D) {}
|
||||
|
||||
/// \brief An implicit member got a definition.
|
||||
virtual void CompletedImplicitDefinition(const FunctionDecl *D) {}
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
|
|
@ -43,6 +43,7 @@ namespace clang {
|
|||
class ADLResult;
|
||||
class ASTConsumer;
|
||||
class ASTContext;
|
||||
class ASTMutationListener;
|
||||
class ArrayType;
|
||||
class AttributeList;
|
||||
class BlockDecl;
|
||||
|
@ -657,6 +658,7 @@ public:
|
|||
Preprocessor &getPreprocessor() const { return PP; }
|
||||
ASTContext &getASTContext() const { return Context; }
|
||||
ASTConsumer &getASTConsumer() const { return Consumer; }
|
||||
ASTMutationListener *getASTMutationListener() const;
|
||||
|
||||
/// \brief Helper class that creates diagnostics with optional
|
||||
/// template instantiation stacks.
|
||||
|
|
|
@ -588,6 +588,7 @@ public:
|
|||
const ClassTemplateSpecializationDecl *D);
|
||||
virtual void AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD,
|
||||
const FunctionDecl *D);
|
||||
virtual void CompletedImplicitDefinition(const FunctionDecl *D);
|
||||
};
|
||||
|
||||
/// \brief AST and semantic-analysis consumer that generates a
|
||||
|
|
|
@ -97,6 +97,7 @@ public:
|
|||
const ClassTemplateSpecializationDecl *D);
|
||||
virtual void AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD,
|
||||
const FunctionDecl *D);
|
||||
virtual void CompletedImplicitDefinition(const FunctionDecl *D);
|
||||
private:
|
||||
std::vector<ASTMutationListener*> Listeners;
|
||||
};
|
||||
|
@ -132,6 +133,11 @@ void MultiplexASTMutationListener::AddedCXXTemplateSpecialization(
|
|||
for (size_t i = 0, e = Listeners.size(); i != e; ++i)
|
||||
Listeners[i]->AddedCXXTemplateSpecialization(TD, D);
|
||||
}
|
||||
void MultiplexASTMutationListener::CompletedImplicitDefinition(
|
||||
const FunctionDecl *D) {
|
||||
for (size_t i = 0, e = Listeners.size(); i != e; ++i)
|
||||
Listeners[i]->CompletedImplicitDefinition(D);
|
||||
}
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
|
|
|
@ -201,6 +201,10 @@ Sema::~Sema() {
|
|||
ExternalSema->ForgetSema();
|
||||
}
|
||||
|
||||
ASTMutationListener *Sema::getASTMutationListener() const {
|
||||
return getASTConsumer().GetASTMutationListener();
|
||||
}
|
||||
|
||||
/// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast.
|
||||
/// If there is already an implicit cast, merge into the existing one.
|
||||
/// The result is of the given category.
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "clang/Sema/Lookup.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/ASTMutationListener.h"
|
||||
#include "clang/AST/CharUnits.h"
|
||||
#include "clang/AST/CXXInheritance.h"
|
||||
#include "clang/AST/DeclVisitor.h"
|
||||
|
@ -4969,6 +4970,10 @@ void Sema::DefineImplicitDefaultConstructor(SourceLocation CurrentLocation,
|
|||
|
||||
Constructor->setUsed();
|
||||
MarkVTableUsed(CurrentLocation, ClassDecl);
|
||||
|
||||
if (ASTMutationListener *L = getASTMutationListener()) {
|
||||
L->CompletedImplicitDefinition(Constructor);
|
||||
}
|
||||
}
|
||||
|
||||
void Sema::DeclareInheritedConstructors(CXXRecordDecl *ClassDecl) {
|
||||
|
@ -5254,6 +5259,10 @@ void Sema::DefineImplicitDestructor(SourceLocation CurrentLocation,
|
|||
|
||||
Destructor->setUsed();
|
||||
MarkVTableUsed(CurrentLocation, ClassDecl);
|
||||
|
||||
if (ASTMutationListener *L = getASTMutationListener()) {
|
||||
L->CompletedImplicitDefinition(Destructor);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Builds a statement that copies the given entity from \p From to
|
||||
|
@ -5913,6 +5922,10 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
|
|||
/*isStmtExpr=*/false);
|
||||
assert(!Body.isInvalid() && "Compound statement creation cannot fail");
|
||||
CopyAssignOperator->setBody(Body.takeAs<Stmt>());
|
||||
|
||||
if (ASTMutationListener *L = getASTMutationListener()) {
|
||||
L->CompletedImplicitDefinition(CopyAssignOperator);
|
||||
}
|
||||
}
|
||||
|
||||
CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor(
|
||||
|
@ -6113,6 +6126,10 @@ void Sema::DefineImplicitCopyConstructor(SourceLocation CurrentLocation,
|
|||
}
|
||||
|
||||
CopyConstructor->setUsed();
|
||||
|
||||
if (ASTMutationListener *L = getASTMutationListener()) {
|
||||
L->CompletedImplicitDefinition(CopyConstructor);
|
||||
}
|
||||
}
|
||||
|
||||
ExprResult
|
||||
|
|
|
@ -3955,4 +3955,13 @@ void ASTWriter::AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD,
|
|||
AddDeclRef(D, Record);
|
||||
}
|
||||
|
||||
void ASTWriter::CompletedImplicitDefinition(const FunctionDecl *D) {
|
||||
if (D->getPCHLevel() == 0)
|
||||
return; // Declaration not imported from PCH.
|
||||
|
||||
// Implicit decl from a PCH was defined.
|
||||
// FIXME: Should implicit definition be a separate FunctionDecl?
|
||||
RewriteDecl(D);
|
||||
}
|
||||
|
||||
ASTSerializationListener::~ASTSerializationListener() { }
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
// no PCH
|
||||
// RUN: %clang_cc1 -emit-llvm -o /dev/null -include %s -include %s %s
|
||||
// with PCH
|
||||
// RUN: %clang_cc1 -emit-llvm -o /dev/null -chain-include %s -chain-include %s %s
|
||||
#if !defined(PASS1)
|
||||
#define PASS1
|
||||
|
||||
// A base with a virtual dtor.
|
||||
struct A {
|
||||
virtual ~A();
|
||||
};
|
||||
|
||||
// A derived class with an implicit virtual dtor.
|
||||
struct B : A {
|
||||
// Key function to suppress vtable definition.
|
||||
virtual void virt();
|
||||
};
|
||||
|
||||
#elif !defined(PASS2)
|
||||
#define PASS2
|
||||
|
||||
// Further derived class that requires ~B().
|
||||
// Causes definition of ~B(), but it was lost when saving PCH.
|
||||
struct C : B {
|
||||
C();
|
||||
~C() {}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
void foo() {
|
||||
// Variable that requires ~C().
|
||||
C c;
|
||||
}
|
||||
|
||||
// VTable placement would again cause definition of ~B(), hiding the bug,
|
||||
// if not for B::virt(), which suppresses the placement.
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue