forked from OSchip/llvm-project
Don't eagerly load all conversion operators when loading a class declaration
from a PCH/module. llvm-svn: 189646
This commit is contained in:
parent
8568e576b0
commit
a4ba74c5f5
|
@ -22,12 +22,21 @@ namespace clang {
|
|||
|
||||
/// \brief An UnresolvedSet-like class which uses the ASTContext's allocator.
|
||||
class ASTUnresolvedSet {
|
||||
typedef ASTVector<DeclAccessPair> DeclsTy;
|
||||
struct DeclsTy : ASTVector<DeclAccessPair> {
|
||||
DeclsTy() {}
|
||||
DeclsTy(ASTContext &C, unsigned N) : ASTVector<DeclAccessPair>(C, N) {}
|
||||
|
||||
bool isLazy() const { return getTag(); }
|
||||
void setLazy(bool Lazy) { setTag(Lazy); }
|
||||
};
|
||||
|
||||
DeclsTy Decls;
|
||||
|
||||
ASTUnresolvedSet(const ASTUnresolvedSet &) LLVM_DELETED_FUNCTION;
|
||||
void operator=(const ASTUnresolvedSet &) LLVM_DELETED_FUNCTION;
|
||||
|
||||
friend class LazyASTUnresolvedSet;
|
||||
|
||||
public:
|
||||
ASTUnresolvedSet() {}
|
||||
ASTUnresolvedSet(ASTContext &C, unsigned N) : Decls(C, N) {}
|
||||
|
@ -48,7 +57,7 @@ public:
|
|||
/// Replaces the given declaration with the new one, once.
|
||||
///
|
||||
/// \return true if the set changed
|
||||
bool replace(const NamedDecl* Old, NamedDecl *New, AccessSpecifier AS) {
|
||||
bool replace(const NamedDecl *Old, NamedDecl *New, AccessSpecifier AS) {
|
||||
for (DeclsTy::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
|
||||
if (I->getDecl() == Old) {
|
||||
I->set(New, AS);
|
||||
|
@ -76,7 +85,29 @@ public:
|
|||
DeclAccessPair &operator[](unsigned I) { return Decls[I]; }
|
||||
const DeclAccessPair &operator[](unsigned I) const { return Decls[I]; }
|
||||
};
|
||||
|
||||
|
||||
/// \brief An UnresolvedSet-like class that might not have been loaded from the
|
||||
/// external AST source yet.
|
||||
class LazyASTUnresolvedSet {
|
||||
mutable ASTUnresolvedSet Impl;
|
||||
|
||||
void getFromExternalSource(ASTContext &C) const;
|
||||
|
||||
public:
|
||||
ASTUnresolvedSet &get(ASTContext &C) const {
|
||||
if (Impl.Decls.isLazy())
|
||||
getFromExternalSource(C);
|
||||
return Impl;
|
||||
}
|
||||
|
||||
void reserve(ASTContext &C, unsigned N) { Impl.reserve(C, N); }
|
||||
void addLazyDecl(ASTContext &C, uintptr_t ID, AccessSpecifier AS) {
|
||||
assert(Impl.empty() || Impl.Decls.isLazy());
|
||||
Impl.Decls.setLazy(true);
|
||||
Impl.addDecl(C, reinterpret_cast<NamedDecl*>(ID << 2), AS);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
||||
|
|
|
@ -55,16 +55,24 @@ namespace clang {
|
|||
|
||||
template<typename T>
|
||||
class ASTVector {
|
||||
T *Begin, *End, *Capacity;
|
||||
private:
|
||||
T *Begin, *End;
|
||||
llvm::PointerIntPair<T*, 1, bool> Capacity;
|
||||
|
||||
void setEnd(T *P) { this->End = P; }
|
||||
|
||||
protected:
|
||||
// Make a tag bit available to users of this class.
|
||||
// FIXME: This is a horrible hack.
|
||||
bool getTag() const { return Capacity.getInt(); }
|
||||
void setTag(bool B) { Capacity.setInt(B); }
|
||||
|
||||
public:
|
||||
// Default ctor - Initialize to empty.
|
||||
ASTVector() : Begin(NULL), End(NULL), Capacity(NULL) { }
|
||||
ASTVector() : Begin(0), End(0), Capacity(0, false) {}
|
||||
|
||||
ASTVector(const ASTContext &C, unsigned N)
|
||||
: Begin(NULL), End(NULL), Capacity(NULL) {
|
||||
: Begin(0), End(0), Capacity(0, false) {
|
||||
reserve(C, N);
|
||||
}
|
||||
|
||||
|
@ -156,7 +164,7 @@ public:
|
|||
}
|
||||
|
||||
void push_back(const_reference Elt, const ASTContext &C) {
|
||||
if (End < Capacity) {
|
||||
if (End < this->capacity_ptr()) {
|
||||
Retry:
|
||||
new (End) T(Elt);
|
||||
++End;
|
||||
|
@ -167,13 +175,13 @@ public:
|
|||
}
|
||||
|
||||
void reserve(const ASTContext &C, unsigned N) {
|
||||
if (unsigned(Capacity-Begin) < N)
|
||||
if (unsigned(this->capacity_ptr()-Begin) < N)
|
||||
grow(C, N);
|
||||
}
|
||||
|
||||
/// capacity - Return the total number of elements in the currently allocated
|
||||
/// buffer.
|
||||
size_t capacity() const { return Capacity - Begin; }
|
||||
size_t capacity() const { return this->capacity_ptr() - Begin; }
|
||||
|
||||
/// append - Add the specified range to the end of the SmallVector.
|
||||
///
|
||||
|
@ -220,7 +228,7 @@ public:
|
|||
return this->end()-1;
|
||||
}
|
||||
|
||||
if (this->End < this->Capacity) {
|
||||
if (this->End < this->capacity_ptr()) {
|
||||
Retry:
|
||||
new (this->end()) T(this->back());
|
||||
this->setEnd(this->end()+1);
|
||||
|
@ -365,13 +373,16 @@ private:
|
|||
}
|
||||
|
||||
protected:
|
||||
iterator capacity_ptr() { return (iterator)this->Capacity; }
|
||||
const_iterator capacity_ptr() const {
|
||||
return (iterator) Capacity.getPointer();
|
||||
}
|
||||
iterator capacity_ptr() { return (iterator)Capacity.getPointer(); }
|
||||
};
|
||||
|
||||
// Define this out-of-line to dissuade the C++ compiler from inlining it.
|
||||
template <typename T>
|
||||
void ASTVector<T>::grow(const ASTContext &C, size_t MinSize) {
|
||||
size_t CurCapacity = Capacity-Begin;
|
||||
size_t CurCapacity = this->capacity();
|
||||
size_t CurSize = size();
|
||||
size_t NewCapacity = 2*CurCapacity;
|
||||
if (NewCapacity < MinSize)
|
||||
|
@ -394,7 +405,7 @@ void ASTVector<T>::grow(const ASTContext &C, size_t MinSize) {
|
|||
// ASTContext never frees any memory.
|
||||
Begin = NewElts;
|
||||
End = NewElts+CurSize;
|
||||
Capacity = Begin+NewCapacity;
|
||||
Capacity.setPointer(Begin+NewCapacity);
|
||||
}
|
||||
|
||||
} // end: clang namespace
|
||||
|
|
|
@ -473,14 +473,14 @@ class CXXRecordDecl : public RecordDecl {
|
|||
/// inherited conversion functions).
|
||||
///
|
||||
/// Each of the entries in this overload set is a CXXConversionDecl.
|
||||
ASTUnresolvedSet Conversions;
|
||||
LazyASTUnresolvedSet Conversions;
|
||||
|
||||
/// \brief The conversion functions of this C++ class and all those
|
||||
/// inherited conversion functions that are visible in this class.
|
||||
///
|
||||
/// Each of the entries in this overload set is a CXXConversionDecl or a
|
||||
/// FunctionTemplateDecl.
|
||||
ASTUnresolvedSet VisibleConversions;
|
||||
LazyASTUnresolvedSet VisibleConversions;
|
||||
|
||||
/// \brief The declaration which defines this record.
|
||||
CXXRecordDecl *Definition;
|
||||
|
@ -1014,10 +1014,10 @@ public:
|
|||
|
||||
typedef UnresolvedSetIterator conversion_iterator;
|
||||
conversion_iterator conversion_begin() const {
|
||||
return data().Conversions.begin();
|
||||
return data().Conversions.get(getASTContext()).begin();
|
||||
}
|
||||
conversion_iterator conversion_end() const {
|
||||
return data().Conversions.end();
|
||||
return data().Conversions.get(getASTContext()).end();
|
||||
}
|
||||
|
||||
/// Removes a conversion function from this class. The conversion
|
||||
|
|
|
@ -51,6 +51,7 @@ public:
|
|||
typedef std::iterator_traits<IteratorTy>::iterator_category iterator_category;
|
||||
|
||||
NamedDecl *getDecl() const { return ir->getDecl(); }
|
||||
void setDecl(NamedDecl *ND) const { return ir->setDecl(ND); }
|
||||
AccessSpecifier getAccess() const { return ir->getAccess(); }
|
||||
void setAccess(AccessSpecifier AS) { ir->setAccess(AS); }
|
||||
DeclAccessPair getPair() const { return *ir; }
|
||||
|
|
|
@ -88,7 +88,7 @@ class TypeLocReader;
|
|||
struct HeaderFileInfo;
|
||||
class VersionTuple;
|
||||
class TargetOptions;
|
||||
class ASTUnresolvedSet;
|
||||
class LazyASTUnresolvedSet;
|
||||
|
||||
/// \brief Abstract interface for callback invocations by the ASTReader.
|
||||
///
|
||||
|
@ -1745,7 +1745,7 @@ public:
|
|||
unsigned &Idx);
|
||||
|
||||
/// \brief Read a UnresolvedSet structure.
|
||||
void ReadUnresolvedSet(ModuleFile &F, ASTUnresolvedSet &Set,
|
||||
void ReadUnresolvedSet(ModuleFile &F, LazyASTUnresolvedSet &Set,
|
||||
const RecordData &Record, unsigned &Idx);
|
||||
|
||||
/// \brief Read a C++ base specifier.
|
||||
|
|
|
@ -35,6 +35,17 @@ AccessSpecDecl *AccessSpecDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
|
|||
return new (Mem) AccessSpecDecl(EmptyShell());
|
||||
}
|
||||
|
||||
void LazyASTUnresolvedSet::getFromExternalSource(ASTContext &C) const {
|
||||
ExternalASTSource *Source = C.getExternalSource();
|
||||
assert(Impl.Decls.isLazy() && "getFromExternalSource for non-lazy set");
|
||||
assert(Source && "getFromExternalSource with no external source");
|
||||
|
||||
for (ASTUnresolvedSet::iterator I = Impl.begin(); I != Impl.end(); ++I)
|
||||
I.setDecl(cast<NamedDecl>(Source->GetExternalDecl(
|
||||
reinterpret_cast<uintptr_t>(I.getDecl()) >> 2)));
|
||||
Impl.Decls.setLazy(false);
|
||||
}
|
||||
|
||||
CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
|
||||
: UserDeclaredConstructor(false), UserDeclaredSpecialMembers(0),
|
||||
Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false),
|
||||
|
@ -552,18 +563,16 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
|||
|
||||
if (Conversion->getPrimaryTemplate()) {
|
||||
// We don't record specializations.
|
||||
} else if (FunTmpl) {
|
||||
if (FunTmpl->getPreviousDecl())
|
||||
data().Conversions.replace(FunTmpl->getPreviousDecl(),
|
||||
FunTmpl, AS);
|
||||
else
|
||||
data().Conversions.addDecl(getASTContext(), FunTmpl, AS);
|
||||
} else {
|
||||
if (Conversion->getPreviousDecl())
|
||||
data().Conversions.replace(Conversion->getPreviousDecl(),
|
||||
Conversion, AS);
|
||||
ASTContext &Ctx = getASTContext();
|
||||
ASTUnresolvedSet &Conversions = data().Conversions.get(Ctx);
|
||||
NamedDecl *Primary =
|
||||
FunTmpl ? cast<NamedDecl>(FunTmpl) : cast<NamedDecl>(Conversion);
|
||||
if (Primary->getPreviousDecl())
|
||||
Conversions.replace(cast<NamedDecl>(Primary->getPreviousDecl()),
|
||||
Primary, AS);
|
||||
else
|
||||
data().Conversions.addDecl(getASTContext(), Conversion, AS);
|
||||
Conversions.addDecl(Ctx, Primary, AS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -880,10 +889,13 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
|||
}
|
||||
|
||||
// Handle using declarations of conversion functions.
|
||||
if (UsingShadowDecl *Shadow = dyn_cast<UsingShadowDecl>(D))
|
||||
if (UsingShadowDecl *Shadow = dyn_cast<UsingShadowDecl>(D)) {
|
||||
if (Shadow->getDeclName().getNameKind()
|
||||
== DeclarationName::CXXConversionFunctionName)
|
||||
data().Conversions.addDecl(getASTContext(), Shadow, Shadow->getAccess());
|
||||
== DeclarationName::CXXConversionFunctionName) {
|
||||
ASTContext &Ctx = getASTContext();
|
||||
data().Conversions.get(Ctx).addDecl(Ctx, Shadow, Shadow->getAccess());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) {
|
||||
|
@ -1083,16 +1095,21 @@ static void CollectVisibleConversions(ASTContext &Context,
|
|||
/// in current class; including conversion function templates.
|
||||
std::pair<CXXRecordDecl::conversion_iterator,CXXRecordDecl::conversion_iterator>
|
||||
CXXRecordDecl::getVisibleConversionFunctions() {
|
||||
// If root class, all conversions are visible.
|
||||
if (bases_begin() == bases_end())
|
||||
return std::make_pair(data().Conversions.begin(), data().Conversions.end());
|
||||
// If visible conversion list is already evaluated, return it.
|
||||
if (!data().ComputedVisibleConversions) {
|
||||
CollectVisibleConversions(getASTContext(), this, data().VisibleConversions);
|
||||
data().ComputedVisibleConversions = true;
|
||||
ASTContext &Ctx = getASTContext();
|
||||
|
||||
ASTUnresolvedSet *Set;
|
||||
if (bases_begin() == bases_end()) {
|
||||
// If root class, all conversions are visible.
|
||||
Set = &data().Conversions.get(Ctx);
|
||||
} else {
|
||||
Set = &data().VisibleConversions.get(Ctx);
|
||||
// If visible conversion list is not evaluated, evaluate it.
|
||||
if (!data().ComputedVisibleConversions) {
|
||||
CollectVisibleConversions(Ctx, this, *Set);
|
||||
data().ComputedVisibleConversions = true;
|
||||
}
|
||||
}
|
||||
return std::make_pair(data().VisibleConversions.begin(),
|
||||
data().VisibleConversions.end());
|
||||
return std::make_pair(Set->begin(), Set->end());
|
||||
}
|
||||
|
||||
void CXXRecordDecl::removeConversion(const NamedDecl *ConvDecl) {
|
||||
|
@ -1107,7 +1124,7 @@ void CXXRecordDecl::removeConversion(const NamedDecl *ConvDecl) {
|
|||
// with sufficiently large numbers of directly-declared conversions
|
||||
// that asymptotic behavior matters.
|
||||
|
||||
ASTUnresolvedSet &Convs = data().Conversions;
|
||||
ASTUnresolvedSet &Convs = data().Conversions.get(getASTContext());
|
||||
for (unsigned I = 0, E = Convs.size(); I != E; ++I) {
|
||||
if (Convs[I].getDecl() == ConvDecl) {
|
||||
Convs.erase(I);
|
||||
|
@ -1233,8 +1250,7 @@ void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) {
|
|||
}
|
||||
|
||||
// Set access bits correctly on the directly-declared conversions.
|
||||
for (UnresolvedSetIterator I = data().Conversions.begin(),
|
||||
E = data().Conversions.end();
|
||||
for (conversion_iterator I = conversion_begin(), E = conversion_end();
|
||||
I != E; ++I)
|
||||
I.setAccess((*I)->getAccess());
|
||||
}
|
||||
|
|
|
@ -6972,14 +6972,14 @@ ReadTemplateArgumentList(SmallVectorImpl<TemplateArgument> &TemplArgs,
|
|||
}
|
||||
|
||||
/// \brief Read a UnresolvedSet structure.
|
||||
void ASTReader::ReadUnresolvedSet(ModuleFile &F, ASTUnresolvedSet &Set,
|
||||
void ASTReader::ReadUnresolvedSet(ModuleFile &F, LazyASTUnresolvedSet &Set,
|
||||
const RecordData &Record, unsigned &Idx) {
|
||||
unsigned NumDecls = Record[Idx++];
|
||||
Set.reserve(Context, NumDecls);
|
||||
while (NumDecls--) {
|
||||
NamedDecl *D = ReadDeclAs<NamedDecl>(F, Record, Idx);
|
||||
DeclID ID = ReadDeclID(F, Record, Idx);
|
||||
AccessSpecifier AS = (AccessSpecifier)Record[Idx++];
|
||||
Set.addDecl(Context, D, AS);
|
||||
Set.addLazyDecl(Context, ID, AS);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5117,8 +5117,8 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
|
|||
AddCXXBaseSpecifiersRef(Data.getVBases(), Data.getVBases() + Data.NumVBases,
|
||||
Record);
|
||||
|
||||
AddUnresolvedSet(Data.Conversions, Record);
|
||||
AddUnresolvedSet(Data.VisibleConversions, Record);
|
||||
AddUnresolvedSet(Data.Conversions.get(*Context), Record);
|
||||
AddUnresolvedSet(Data.VisibleConversions.get(*Context), Record);
|
||||
// Data.Definition is the owning decl, no need to write it.
|
||||
AddDeclRef(D->getFirstFriend(), Record);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// RUN: %clang_cc1 -emit-pch -o %t.1 %s
|
||||
// RUN: %clang_cc1 -error-on-deserialized-decl S1_keyfunc -include-pch %t.1 -emit-pch -o %t.2 %s
|
||||
// RUN: %clang_cc1 -error-on-deserialized-decl S1_method -include-pch %t.2 -emit-llvm-only %s
|
||||
// RUN: %clang_cc1 -error-on-deserialized-decl S1_keyfunc -error-on-deserialized-decl S3 -include-pch %t.1 -emit-pch -o %t.2 %s
|
||||
// RUN: %clang_cc1 -error-on-deserialized-decl S1_method -error-on-deserialized-decl S3 -include-pch %t.2 -emit-llvm-only %s
|
||||
|
||||
#ifndef HEADER1
|
||||
#define HEADER1
|
||||
|
@ -11,17 +11,24 @@ struct S1 {
|
|||
virtual void S1_keyfunc();
|
||||
};
|
||||
|
||||
struct S3 {};
|
||||
|
||||
struct S2 {
|
||||
operator S3();
|
||||
};
|
||||
|
||||
#elif !defined(HEADER2)
|
||||
#define HEADER2
|
||||
|
||||
// Chained PCH.
|
||||
S1 *p;
|
||||
S1 *s1;
|
||||
S2 *s2;
|
||||
|
||||
#else
|
||||
|
||||
// Using the headers.
|
||||
|
||||
void test(S1*) {
|
||||
void test(S1*, S2*) {
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue