Skip to content

D41416: [modules] [pch] Do not deserialize all lazy template specializations when looking for one. #83108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,8 @@ Improvements to Clang's diagnostics

- Clang now diagnoses ``[[deprecated]]`` attribute usage on local variables (#GH90073).

- Clang now downgrades the inconsistent language options between modules to warnings instead of errors.

Improvements to Clang's time-trace
----------------------------------

Expand Down
36 changes: 32 additions & 4 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ class TemplateArgumentList final
TemplateArgumentList(const TemplateArgumentList &) = delete;
TemplateArgumentList &operator=(const TemplateArgumentList &) = delete;

/// Create hash for the given arguments.
static unsigned ComputeODRHash(ArrayRef<TemplateArgument> Args);

/// Create a new template argument list that copies the given set of
/// template arguments.
static TemplateArgumentList *CreateCopy(ASTContext &Context,
Expand Down Expand Up @@ -735,6 +738,26 @@ class RedeclarableTemplateDecl : public TemplateDecl,
}

void anchor() override;
struct LazySpecializationInfo {
GlobalDeclID DeclID = GlobalDeclID();
unsigned ODRHash = ~0U;
bool IsPartial = false;
LazySpecializationInfo(GlobalDeclID ID, unsigned Hash = ~0U,
bool Partial = false)
: DeclID(ID), ODRHash(Hash), IsPartial(Partial) {}
LazySpecializationInfo() {}
bool operator<(const LazySpecializationInfo &Other) const {
return DeclID < Other.DeclID;
}
bool operator==(const LazySpecializationInfo &Other) const {
assert((DeclID != Other.DeclID || ODRHash == Other.ODRHash) &&
"Hashes differ!");
assert((DeclID != Other.DeclID || IsPartial == Other.IsPartial) &&
"Both must be the same kinds!");
return DeclID == Other.DeclID;
}
};

protected:
template <typename EntryType> struct SpecEntryTraits {
using DeclType = EntryType;
Expand Down Expand Up @@ -775,7 +798,12 @@ class RedeclarableTemplateDecl : public TemplateDecl,
return SpecIterator<EntryType>(isEnd ? Specs.end() : Specs.begin());
}

void loadLazySpecializationsImpl() const;
void loadLazySpecializationsImpl(bool OnlyPartial = false) const;

void loadLazySpecializationsImpl(llvm::ArrayRef<TemplateArgument> Args,
TemplateParameterList *TPL = nullptr) const;

Decl *loadLazySpecializationImpl(LazySpecializationInfo &LazySpecInfo) const;

template <class EntryType, typename ...ProfileArguments>
typename SpecEntryTraits<EntryType>::DeclType*
Expand All @@ -802,7 +830,7 @@ class RedeclarableTemplateDecl : public TemplateDecl,
///
/// The first value in the array is the number of specializations/partial
/// specializations that follow.
GlobalDeclID *LazySpecializations = nullptr;
LazySpecializationInfo *LazySpecializations = nullptr;
};

/// Pointer to the common data shared by all declarations of this
Expand Down Expand Up @@ -2283,7 +2311,7 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
friend class TemplateDeclInstantiator;

/// Load any lazily-loaded specializations from the external source.
void LoadLazySpecializations() const;
void LoadLazySpecializations(bool OnlyPartial = false) const;

/// Get the underlying class declarations of the template.
CXXRecordDecl *getTemplatedDecl() const {
Expand Down Expand Up @@ -3033,7 +3061,7 @@ class VarTemplateDecl : public RedeclarableTemplateDecl {
friend class ASTDeclWriter;

/// Load any lazily-loaded specializations from the external source.
void LoadLazySpecializations() const;
void LoadLazySpecializations(bool OnlyPartial = false) const;

/// Get the underlying variable declarations of the template.
VarDecl *getTemplatedDecl() const {
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ def ModuleLock : DiagGroup<"module-lock">;
def ModuleBuild : DiagGroup<"module-build">;
def ModuleImport : DiagGroup<"module-import">;
def ModuleConflict : DiagGroup<"module-conflict">;
def ModuleMismatchedOption : DiagGroup<"module-mismatched-option">;
def ModuleFileExtension : DiagGroup<"module-file-extension">;
def ModuleIncludeDirectiveTranslation : DiagGroup<"module-include-translation">;
def RoundTripCC1Args : DiagGroup<"round-trip-cc1-args">;
Expand Down
10 changes: 6 additions & 4 deletions clang/include/clang/Basic/DiagnosticSerializationKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ def err_ast_file_targetopt_feature_mismatch : Error<
"%select{AST file '%1' was|current translation unit is}0 compiled with the target "
"feature '%2' but the %select{current translation unit is|AST file '%1' was}0 "
"not">;
def err_ast_file_langopt_mismatch : Error<"%0 was %select{disabled|enabled}1 in "
"AST file '%3' but is currently %select{disabled|enabled}2">;
def err_ast_file_langopt_value_mismatch : Error<
"%0 differs in AST file '%1' vs. current file">;
def warn_ast_file_langopt_mismatch : Warning<"%0 was %select{disabled|enabled}1 in "
"AST file '%3' but is currently %select{disabled|enabled}2">,
InGroup<ModuleMismatchedOption>;
def warn_ast_file_langopt_value_mismatch : Warning<
"%0 differs in AST file '%1' vs. current file">,
InGroup<ModuleMismatchedOption>;
def err_ast_file_diagopt_mismatch : Error<"%0 is currently enabled, but was not in "
"the AST file '%1'">;
def err_ast_file_modulecache_mismatch : Error<"AST file '%2' was compiled with module cache "
Expand Down
98 changes: 75 additions & 23 deletions clang/lib/AST/DeclTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExternalASTSource.h"
#include "clang/AST/ODRHash.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
Expand Down Expand Up @@ -351,17 +353,44 @@ RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() c
return Common;
}

void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const {
void RedeclarableTemplateDecl::loadLazySpecializationsImpl(
bool OnlyPartial /*=false*/) const {
// Grab the most recent declaration to ensure we've loaded any lazy
// redeclarations of this template.
CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr();
if (CommonBasePtr->LazySpecializations) {
ASTContext &Context = getASTContext();
GlobalDeclID *Specs = CommonBasePtr->LazySpecializations;
CommonBasePtr->LazySpecializations = nullptr;
unsigned SpecSize = (*Specs++).getRawValue();
for (unsigned I = 0; I != SpecSize; ++I)
(void)Context.getExternalSource()->GetExternalDecl(Specs[I]);
if (auto *Specs = CommonBasePtr->LazySpecializations) {
if (!OnlyPartial)
CommonBasePtr->LazySpecializations = nullptr;
unsigned N = Specs[0].DeclID.getRawValue();
for (unsigned I = 0; I != N; ++I) {
// Skip over already loaded specializations.
if (!Specs[I + 1].ODRHash)
continue;
if (!OnlyPartial || Specs[I + 1].IsPartial)
(void)loadLazySpecializationImpl(Specs[I + 1]);
}
}
}

Decl *RedeclarableTemplateDecl::loadLazySpecializationImpl(
LazySpecializationInfo &LazySpecInfo) const {
GlobalDeclID ID = LazySpecInfo.DeclID;
assert(ID.isValid() && "Loading already loaded specialization!");
// Note that we loaded the specialization.
LazySpecInfo.DeclID = GlobalDeclID();
LazySpecInfo.ODRHash = LazySpecInfo.IsPartial = 0;
return getASTContext().getExternalSource()->GetExternalDecl(ID);
}

void RedeclarableTemplateDecl::loadLazySpecializationsImpl(
ArrayRef<TemplateArgument> Args, TemplateParameterList *TPL) const {
CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr();
if (auto *Specs = CommonBasePtr->LazySpecializations) {
unsigned Hash = TemplateArgumentList::ComputeODRHash(Args);
unsigned N = Specs[0].DeclID.getRawValue();
for (unsigned I = 0; I != N; ++I)
if (Specs[I + 1].ODRHash && Specs[I + 1].ODRHash == Hash)
(void)loadLazySpecializationImpl(Specs[I + 1]);
}
}

Expand All @@ -372,6 +401,8 @@ RedeclarableTemplateDecl::findSpecializationImpl(
ProfileArguments&&... ProfileArgs) {
using SETraits = SpecEntryTraits<EntryType>;

loadLazySpecializationsImpl(std::forward<ProfileArguments>(ProfileArgs)...);

llvm::FoldingSetNodeID ID;
EntryType::Profile(ID, std::forward<ProfileArguments>(ProfileArgs)...,
getASTContext());
Expand All @@ -387,10 +418,14 @@ void RedeclarableTemplateDecl::addSpecializationImpl(

if (InsertPos) {
#ifndef NDEBUG
auto Args = SETraits::getTemplateArgs(Entry);
// Due to hash collisions, it can happen that we load another template
// specialization with the same hash. This is fine, as long as the next
// call to findSpecializationImpl does not find a matching Decl for the
// template arguments.
loadLazySpecializationsImpl(Args);
void *CorrectInsertPos;
assert(!findSpecializationImpl(Specializations,
CorrectInsertPos,
SETraits::getTemplateArgs(Entry)) &&
assert(!findSpecializationImpl(Specializations, CorrectInsertPos, Args) &&
InsertPos == CorrectInsertPos &&
"given incorrect InsertPos for specialization");
#endif
Expand Down Expand Up @@ -448,12 +483,14 @@ FunctionTemplateDecl::getSpecializations() const {
FunctionDecl *
FunctionTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args,
void *&InsertPos) {
return findSpecializationImpl(getSpecializations(), InsertPos, Args);
auto *Common = getCommonPtr();
return findSpecializationImpl(Common->Specializations, InsertPos, Args);
}

void FunctionTemplateDecl::addSpecialization(
FunctionTemplateSpecializationInfo *Info, void *InsertPos) {
addSpecializationImpl<FunctionTemplateDecl>(getSpecializations(), Info,
auto *Common = getCommonPtr();
addSpecializationImpl<FunctionTemplateDecl>(Common->Specializations, Info,
InsertPos);
}

Expand Down Expand Up @@ -513,8 +550,9 @@ ClassTemplateDecl *ClassTemplateDecl::CreateDeserialized(ASTContext &C,
DeclarationName(), nullptr, nullptr);
}

void ClassTemplateDecl::LoadLazySpecializations() const {
loadLazySpecializationsImpl();
void ClassTemplateDecl::LoadLazySpecializations(
bool OnlyPartial /*=false*/) const {
loadLazySpecializationsImpl(OnlyPartial);
}

llvm::FoldingSetVector<ClassTemplateSpecializationDecl> &
Expand All @@ -525,7 +563,7 @@ ClassTemplateDecl::getSpecializations() const {

llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> &
ClassTemplateDecl::getPartialSpecializations() const {
LoadLazySpecializations();
LoadLazySpecializations(/*PartialOnly = */ true);
return getCommonPtr()->PartialSpecializations;
}

Expand All @@ -539,12 +577,15 @@ ClassTemplateDecl::newCommon(ASTContext &C) const {
ClassTemplateSpecializationDecl *
ClassTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args,
void *&InsertPos) {
return findSpecializationImpl(getSpecializations(), InsertPos, Args);
auto *Common = getCommonPtr();
return findSpecializationImpl(Common->Specializations, InsertPos, Args);
}

void ClassTemplateDecl::AddSpecialization(ClassTemplateSpecializationDecl *D,
void *InsertPos) {
addSpecializationImpl<ClassTemplateDecl>(getSpecializations(), D, InsertPos);
auto *Common = getCommonPtr();
addSpecializationImpl<ClassTemplateDecl>(Common->Specializations, D,
InsertPos);
}

ClassTemplatePartialSpecializationDecl *
Expand Down Expand Up @@ -897,6 +938,14 @@ TemplateArgumentList::CreateCopy(ASTContext &Context,
return new (Mem) TemplateArgumentList(Args);
}

unsigned TemplateArgumentList::ComputeODRHash(ArrayRef<TemplateArgument> Args) {
ODRHash Hasher;
for (TemplateArgument TA : Args)
Hasher.AddTemplateArgument(TA);

return Hasher.CalculateHash();
}

FunctionTemplateSpecializationInfo *FunctionTemplateSpecializationInfo::Create(
ASTContext &C, FunctionDecl *FD, FunctionTemplateDecl *Template,
TemplateSpecializationKind TSK, TemplateArgumentList *TemplateArgs,
Expand Down Expand Up @@ -1262,8 +1311,9 @@ VarTemplateDecl *VarTemplateDecl::CreateDeserialized(ASTContext &C,
DeclarationName(), nullptr, nullptr);
}

void VarTemplateDecl::LoadLazySpecializations() const {
loadLazySpecializationsImpl();
void VarTemplateDecl::LoadLazySpecializations(
bool OnlyPartial /*=false*/) const {
loadLazySpecializationsImpl(OnlyPartial);
}

llvm::FoldingSetVector<VarTemplateSpecializationDecl> &
Expand All @@ -1274,7 +1324,7 @@ VarTemplateDecl::getSpecializations() const {

llvm::FoldingSetVector<VarTemplatePartialSpecializationDecl> &
VarTemplateDecl::getPartialSpecializations() const {
LoadLazySpecializations();
LoadLazySpecializations(/*PartialOnly = */ true);
return getCommonPtr()->PartialSpecializations;
}

Expand All @@ -1288,12 +1338,14 @@ VarTemplateDecl::newCommon(ASTContext &C) const {
VarTemplateSpecializationDecl *
VarTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args,
void *&InsertPos) {
return findSpecializationImpl(getSpecializations(), InsertPos, Args);
auto *Common = getCommonPtr();
return findSpecializationImpl(Common->Specializations, InsertPos, Args);
}

void VarTemplateDecl::AddSpecialization(VarTemplateSpecializationDecl *D,
void *InsertPos) {
addSpecializationImpl<VarTemplateDecl>(getSpecializations(), D, InsertPos);
auto *Common = getCommonPtr();
addSpecializationImpl<VarTemplateDecl>(Common->Specializations, D, InsertPos);
}

VarTemplatePartialSpecializationDecl *
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/AST/ODRHash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,21 @@ void ODRHash::AddDecl(const Decl *D) {
for (const TemplateArgument &TA : List.asArray())
AddTemplateArgument(TA);
}

// If this was a specialization we should take into account its template
// arguments. This helps to reduce collisions coming when visiting template
// specialization types (eg. when processing type template arguments).
ArrayRef<TemplateArgument> Args;
if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D))
Args = CTSD->getTemplateArgs().asArray();
else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D))
Args = VTSD->getTemplateArgs().asArray();
else if (auto *FD = dyn_cast<FunctionDecl>(D))
if (FD->getTemplateSpecializationArgs())
Args = FD->getTemplateSpecializationArgs()->asArray();

for (auto &TA : Args)
AddTemplateArgument(TA);
}

namespace {
Expand Down
Loading
Loading