https://github.com/ChuanqiXu9 updated https://github.com/llvm/llvm-project/pull/83237
>From f2e53e44eebab4720a1dbade24fcb14d698fb03f Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <yedeng...@linux.alibaba.com> Date: Wed, 28 Feb 2024 11:41:53 +0800 Subject: [PATCH 1/3] [Serialization] Code cleanups and polish 83233 --- clang/include/clang/AST/DeclTemplate.h | 39 +- clang/include/clang/AST/ExternalASTSource.h | 8 +- .../clang/Sema/MultiplexExternalSemaSource.h | 4 +- .../include/clang/Serialization/ASTBitCodes.h | 2 +- clang/include/clang/Serialization/ASTReader.h | 4 +- clang/lib/AST/DeclTemplate.cpp | 85 ++-- clang/lib/AST/ExternalASTSource.cpp | 10 +- clang/lib/AST/ODRHash.cpp | 10 - .../lib/Sema/MultiplexExternalSemaSource.cpp | 13 +- clang/lib/Serialization/ASTCommon.h | 1 - clang/lib/Serialization/ASTReader.cpp | 42 +- clang/lib/Serialization/ASTReaderDecl.cpp | 76 +--- clang/lib/Serialization/ASTReaderInternals.h | 1 - clang/lib/Serialization/ASTWriter.cpp | 27 +- clang/lib/Serialization/ASTWriterDecl.cpp | 52 +-- clang/lib/Serialization/CMakeLists.txt | 1 + .../Serialization/TemplateArgumentHasher.cpp | 423 ++++++++++++++++++ .../Serialization/TemplateArgumentHasher.h | 34 ++ clang/test/Modules/cxx-templates.cpp | 8 +- .../Modules/recursive-instantiations.cppm | 40 ++ .../test/OpenMP/target_parallel_ast_print.cpp | 4 - clang/test/OpenMP/target_teams_ast_print.cpp | 4 - clang/test/OpenMP/task_ast_print.cpp | 4 - clang/test/OpenMP/teams_ast_print.cpp | 4 - 24 files changed, 610 insertions(+), 286 deletions(-) create mode 100644 clang/lib/Serialization/TemplateArgumentHasher.cpp create mode 100644 clang/lib/Serialization/TemplateArgumentHasher.h create mode 100644 clang/test/Modules/recursive-instantiations.cppm diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 44f840d297465d..7406252363d223 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -256,9 +256,6 @@ 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, @@ -732,25 +729,6 @@ 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 { @@ -794,16 +772,20 @@ class RedeclarableTemplateDecl : public TemplateDecl, void loadLazySpecializationsImpl(bool OnlyPartial = false) const; - void loadLazySpecializationsImpl(llvm::ArrayRef<TemplateArgument> Args, + bool loadLazySpecializationsImpl(llvm::ArrayRef<TemplateArgument> Args, TemplateParameterList *TPL = nullptr) const; - Decl *loadLazySpecializationImpl(LazySpecializationInfo &LazySpecInfo) const; - template <class EntryType, typename ...ProfileArguments> typename SpecEntryTraits<EntryType>::DeclType* findSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs, void *&InsertPos, ProfileArguments &&...ProfileArgs); + template <class EntryType, typename... ProfileArguments> + typename SpecEntryTraits<EntryType>::DeclType * + findSpecializationLocally(llvm::FoldingSetVector<EntryType> &Specs, + void *&InsertPos, + ProfileArguments &&...ProfileArgs); + template <class Derived, class EntryType> void addSpecializationImpl(llvm::FoldingSetVector<EntryType> &Specs, EntryType *Entry, void *InsertPos); @@ -819,13 +801,6 @@ class RedeclarableTemplateDecl : public TemplateDecl, llvm::PointerIntPair<RedeclarableTemplateDecl*, 1, bool> InstantiatedFromMember; - /// If non-null, points to an array of specializations (including - /// partial specializations) known only by their external declaration IDs. - /// - /// The first value in the array is the number of specializations/partial - /// specializations that follow. - LazySpecializationInfo *LazySpecializations = nullptr; - /// The set of "injected" template arguments used within this /// template. /// diff --git a/clang/include/clang/AST/ExternalASTSource.h b/clang/include/clang/AST/ExternalASTSource.h index 24c4490d160f3f..2b73b543fabb3b 100644 --- a/clang/include/clang/AST/ExternalASTSource.h +++ b/clang/include/clang/AST/ExternalASTSource.h @@ -153,11 +153,15 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> { /// Load all the external specializations for the Decl \param D if \param /// OnlyPartial is false. Otherwise, load all the external **partial** /// specializations for the \param D. - virtual void LoadExternalSpecializations(const Decl *D, bool OnlyPartial); + /// + /// Return true if any new specializations get loaded. Return false otherwise. + virtual bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial); /// Load all the specializations for the Decl \param D with the same template /// args specified by \param TemplateArgs. - virtual void + /// + /// Return true if any new specializations get loaded. Return false otherwise. + virtual bool LoadExternalSpecializations(const Decl *D, ArrayRef<TemplateArgument> TemplateArgs); diff --git a/clang/include/clang/Sema/MultiplexExternalSemaSource.h b/clang/include/clang/Sema/MultiplexExternalSemaSource.h index f81b70daa4b3d0..da13bef15de0ea 100644 --- a/clang/include/clang/Sema/MultiplexExternalSemaSource.h +++ b/clang/include/clang/Sema/MultiplexExternalSemaSource.h @@ -97,9 +97,9 @@ class MultiplexExternalSemaSource : public ExternalSemaSource { bool FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) override; - void LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override; + bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override; - void + bool LoadExternalSpecializations(const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) override; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index eefc0e5218d7d3..c7c1fdaf03ef4b 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -726,7 +726,7 @@ enum ASTRecordTypes { VTABLES_TO_EMIT = 70, /// Record code for updated specialization - UPDATE_SPECIALIZATION = 71, + CXX_ADDED_TEMPLATE_SPECIALIZATION = 71, }; /// Record types used within a source manager block. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 0356fd14d9fd61..bb87998aba3844 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -2008,9 +2008,9 @@ class ASTReader unsigned BlockID, uint64_t *StartOfBlockOffset = nullptr); - void LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override; + bool LoadExternalSpecializations(const Decl *D, bool OnlyPartial) override; - void + bool LoadExternalSpecializations(const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) override; diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 1c81218626fdfb..419b2e7c18e48c 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -342,63 +342,30 @@ void RedeclarableTemplateDecl::loadLazySpecializationsImpl( ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(), OnlyPartial); return; - - // Grab the most recent declaration to ensure we've loaded any lazy - // redeclarations of this template. - CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr(); - 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 { - llvm_unreachable("We don't use LazySpecializationInfo any more"); - - 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( +bool RedeclarableTemplateDecl::loadLazySpecializationsImpl( ArrayRef<TemplateArgument> Args, TemplateParameterList *TPL) const { auto *ExternalSource = getASTContext().getExternalSource(); if (!ExternalSource) - return; + return false; - ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(), Args); - return; + // If TPL is not null, it implies that we're loading specializations for + // partial templates. We need to load all specializations in such cases. + if (TPL) + return ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(), + /*OnlyPartial=*/false); - 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]); - } + return ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(), + Args); } -template<class EntryType, typename... ProfileArguments> +template <class EntryType, typename... ProfileArguments> typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType * -RedeclarableTemplateDecl::findSpecializationImpl( +RedeclarableTemplateDecl::findSpecializationLocally( llvm::FoldingSetVector<EntryType> &Specs, void *&InsertPos, - ProfileArguments&&... ProfileArgs) { - using SETraits = SpecEntryTraits<EntryType>; - - loadLazySpecializationsImpl(std::forward<ProfileArguments>(ProfileArgs)...); + ProfileArguments &&...ProfileArgs) { + using SETraits = RedeclarableTemplateDecl::SpecEntryTraits<EntryType>; llvm::FoldingSetNodeID ID; EntryType::Profile(ID, std::forward<ProfileArguments>(ProfileArgs)..., @@ -407,6 +374,24 @@ RedeclarableTemplateDecl::findSpecializationImpl( return Entry ? SETraits::getDecl(Entry)->getMostRecentDecl() : nullptr; } +template <class EntryType, typename... ProfileArguments> +typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType * +RedeclarableTemplateDecl::findSpecializationImpl( + llvm::FoldingSetVector<EntryType> &Specs, void *&InsertPos, + ProfileArguments &&...ProfileArgs) { + + if (auto *Found = findSpecializationLocally( + Specs, InsertPos, std::forward<ProfileArguments>(ProfileArgs)...)) + return Found; + + if (!loadLazySpecializationsImpl( + std::forward<ProfileArguments>(ProfileArgs)...)) + return nullptr; + + return findSpecializationLocally( + Specs, InsertPos, std::forward<ProfileArguments>(ProfileArgs)...); +} + template<class Derived, class EntryType> void RedeclarableTemplateDecl::addSpecializationImpl( llvm::FoldingSetVector<EntryType> &Specializations, EntryType *Entry, @@ -955,14 +940,6 @@ 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, diff --git a/clang/lib/AST/ExternalASTSource.cpp b/clang/lib/AST/ExternalASTSource.cpp index 122014bfeb2321..0eeb549d8cabf3 100644 --- a/clang/lib/AST/ExternalASTSource.cpp +++ b/clang/lib/AST/ExternalASTSource.cpp @@ -98,10 +98,14 @@ ExternalASTSource::FindExternalVisibleDeclsByName(const DeclContext *DC, return false; } -void ExternalASTSource::LoadExternalSpecializations(const Decl *D, bool) {} +bool ExternalASTSource::LoadExternalSpecializations(const Decl *D, bool) { + return false; +} -void ExternalASTSource::LoadExternalSpecializations( - const Decl *D, ArrayRef<TemplateArgument>) {} +bool ExternalASTSource::LoadExternalSpecializations( + const Decl *D, ArrayRef<TemplateArgument>) { + return false; +} void ExternalASTSource::completeVisibleDeclsMap(const DeclContext *DC) {} diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index 7b361c958c54fe..8e91add1d5875d 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -816,16 +816,6 @@ void ODRHash::AddDecl(const Decl *D) { AddDeclarationName(ND->getDeclName()); - const auto *Specialization = - dyn_cast<ClassTemplateSpecializationDecl>(D); - AddBoolean(Specialization); - if (Specialization) { - const TemplateArgumentList &List = Specialization->getTemplateArgs(); - ID.AddInteger(List.size()); - 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). diff --git a/clang/lib/Sema/MultiplexExternalSemaSource.cpp b/clang/lib/Sema/MultiplexExternalSemaSource.cpp index 8b1642cf88a12d..2e98f7ef9519cf 100644 --- a/clang/lib/Sema/MultiplexExternalSemaSource.cpp +++ b/clang/lib/Sema/MultiplexExternalSemaSource.cpp @@ -115,16 +115,21 @@ FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) { return AnyDeclsFound; } -void MultiplexExternalSemaSource::LoadExternalSpecializations( +bool MultiplexExternalSemaSource::LoadExternalSpecializations( const Decl *D, bool OnlyPartial) { + bool Loaded = false; for (size_t i = 0; i < Sources.size(); ++i) - Sources[i]->LoadExternalSpecializations(D, OnlyPartial); + Loaded |= Sources[i]->LoadExternalSpecializations(D, OnlyPartial); + return Loaded; } -void MultiplexExternalSemaSource::LoadExternalSpecializations( +bool MultiplexExternalSemaSource::LoadExternalSpecializations( const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) { + bool AnyNewSpecsLoaded = false; for (size_t i = 0; i < Sources.size(); ++i) - Sources[i]->LoadExternalSpecializations(D, TemplateArgs); + AnyNewSpecsLoaded |= + Sources[i]->LoadExternalSpecializations(D, TemplateArgs); + return AnyNewSpecsLoaded; } void MultiplexExternalSemaSource::completeVisibleDeclsMap(const DeclContext *DC){ diff --git a/clang/lib/Serialization/ASTCommon.h b/clang/lib/Serialization/ASTCommon.h index 0230908d3e0528..0cef55c53ffcba 100644 --- a/clang/lib/Serialization/ASTCommon.h +++ b/clang/lib/Serialization/ASTCommon.h @@ -23,7 +23,6 @@ namespace serialization { enum DeclUpdateKind { UPD_CXX_ADDED_IMPLICIT_MEMBER, - UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION, UPD_CXX_ADDED_ANONYMOUS_NAMESPACE, UPD_CXX_ADDED_FUNCTION_DEFINITION, UPD_CXX_ADDED_VAR_DEFINITION, diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 2a5216ab036344..f75d8fc4676b32 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -12,6 +12,7 @@ #include "ASTCommon.h" #include "ASTReaderInternals.h" +#include "TemplateArgumentHasher.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTMutationListener.h" @@ -3521,7 +3522,7 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, break; } - case UPDATE_SPECIALIZATION: { + case CXX_ADDED_TEMPLATE_SPECIALIZATION: { unsigned Idx = 0; GlobalDeclID ID = ReadDeclID(F, Record, Idx); auto *Data = (const unsigned char *)Blob.data(); @@ -7694,8 +7695,13 @@ void ASTReader::CompleteRedeclChain(const Decl *D) { } } - if (Template) - Template->loadLazySpecializationsImpl(Args); + if (Template) { + // For partitial specialization, load all the specializations for safety. + if (isa<ClassTemplatePartialSpecializationDecl, VarTemplatePartialSpecializationDecl>(D)) + Template->loadLazySpecializationsImpl(); + else + Template->loadLazySpecializationsImpl(Args); + } } CXXCtorInitializer ** @@ -8011,12 +8017,12 @@ Stmt *ASTReader::GetExternalDeclStmt(uint64_t Offset) { return ReadStmtFromStream(*Loc.F); } -void ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) { +bool ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) { assert(D); auto It = SpecializationsLookups.find(D); if (It == SpecializationsLookups.end()) - return; + return false; // Get Decl may violate the iterator from SpecializationsLookups so we store // the DeclIDs in ahead. @@ -8027,30 +8033,44 @@ void ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) { // the lookup table. if (!OnlyPartial) SpecializationsLookups.erase(It); - + + bool NewSpecsFound = false; Deserializing LookupResults(this); for (auto &Info : Infos) - if (!OnlyPartial || Info.IsPartial) + if (!OnlyPartial || Info.IsPartial) { + if (GetExistingDecl(Info.ID)) + continue; + NewSpecsFound = true; GetDecl(Info.ID); + } + + return NewSpecsFound; } -void ASTReader::LoadExternalSpecializations( +bool ASTReader::LoadExternalSpecializations( const Decl *D, ArrayRef<TemplateArgument> TemplateArgs) { assert(D); auto It = SpecializationsLookups.find(D); if (It == SpecializationsLookups.end()) - return; + return false; Deserializing LookupResults(this); - auto HashValue = TemplateArgumentList::ComputeODRHash(TemplateArgs); + auto HashValue = StableHashForTemplateArguments(TemplateArgs); // Get Decl may violate the iterator from SpecializationsLookups llvm::SmallVector<serialization::reader::LazySpecializationInfo, 8> Infos = It->second.Table.find(HashValue); - for (auto &Info : Infos) + bool NewSpecsFound = false; + for (auto &Info : Infos) { + if (GetExistingDecl(Info.ID)) + continue; + NewSpecsFound = true; GetDecl(Info.ID); + } + + return NewSpecsFound; } void ASTReader::FindExternalLexicalDecls( diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 5c1ee02c561de3..9fa07137daabe6 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -89,8 +89,6 @@ namespace clang { const SourceLocation ThisDeclLoc; using RecordData = ASTReader::RecordData; - using LazySpecializationInfo = - RedeclarableTemplateDecl::LazySpecializationInfo; TypeID DeferredTypeID = 0; unsigned AnonymousDeclNumber = 0; @@ -133,18 +131,6 @@ namespace clang { return Record.readString(); } - LazySpecializationInfo ReadLazySpecializationInfo() { - GlobalDeclID ID = readDeclID(); - unsigned Hash = Record.readInt(); - bool IsPartial = Record.readInt(); - return LazySpecializationInfo(ID, Hash, IsPartial); - } - - void readDeclIDList(SmallVectorImpl<LazySpecializationInfo> &IDs) { - for (unsigned I = 0, Size = Record.readInt(); I != Size; ++I) - IDs.push_back(ReadLazySpecializationInfo()); - } - Decl *readDecl() { return Record.readDecl(); } @@ -271,29 +257,6 @@ namespace clang { : Reader(Reader), Record(Record), Loc(Loc), ThisDeclID(thisDeclID), ThisDeclLoc(ThisDeclLoc) {} - template <typename T> - static void - AddLazySpecializations(T *D, SmallVectorImpl<LazySpecializationInfo> &IDs) { - if (IDs.empty()) - return; - - // FIXME: We should avoid this pattern of getting the ASTContext. - ASTContext &C = D->getASTContext(); - - auto *&LazySpecializations = D->getCommonPtr()->LazySpecializations; - - if (auto &Old = LazySpecializations) { - IDs.insert(IDs.end(), Old + 1, Old + 1 + Old[0].DeclID.getRawValue()); - llvm::sort(IDs); - IDs.erase(std::unique(IDs.begin(), IDs.end()), IDs.end()); - } - auto *Result = new (C) LazySpecializationInfo[1 + IDs.size()]; - Result->DeclID = GlobalDeclID(IDs.size()); - std::copy(IDs.begin(), IDs.end(), Result + 1); - - LazySpecializations = Result; - } - template <typename DeclT> static Decl *getMostRecentDeclImpl(Redeclarable<DeclT> *D); static Decl *getMostRecentDeclImpl(...); @@ -322,7 +285,7 @@ namespace clang { void ReadFunctionDefinition(FunctionDecl *FD); void Visit(Decl *D); - void UpdateDecl(Decl *D, llvm::SmallVectorImpl<LazySpecializationInfo> &); + void UpdateDecl(Decl *D); static void setNextObjCCategory(ObjCCategoryDecl *Cat, ObjCCategoryDecl *Next) { @@ -2473,9 +2436,6 @@ void ASTDeclReader::VisitClassTemplateDecl(ClassTemplateDecl *D) { if (ThisDeclID == Redecl.getFirstID()) { // This ClassTemplateDecl owns a CommonPtr; read it to keep track of all of // the specializations. - SmallVector<LazySpecializationInfo, 32> SpecIDs; - readDeclIDList(SpecIDs); - ASTDeclReader::AddLazySpecializations(D, SpecIDs); ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor); } @@ -2502,9 +2462,6 @@ void ASTDeclReader::VisitVarTemplateDecl(VarTemplateDecl *D) { if (ThisDeclID == Redecl.getFirstID()) { // This VarTemplateDecl owns a CommonPtr; read it to keep track of all of // the specializations. - SmallVector<LazySpecializationInfo, 32> SpecIDs; - readDeclIDList(SpecIDs); - ASTDeclReader::AddLazySpecializations(D, SpecIDs); ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor); } } @@ -2605,9 +2562,6 @@ void ASTDeclReader::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { if (ThisDeclID == Redecl.getFirstID()) { // This FunctionTemplateDecl owns a CommonPtr; read it. - SmallVector<LazySpecializationInfo, 32> SpecIDs; - readDeclIDList(SpecIDs); - ASTDeclReader::AddLazySpecializations(D, SpecIDs); ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor); } } @@ -4268,10 +4222,6 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { ProcessingUpdatesRAIIObj ProcessingUpdates(*this); DeclUpdateOffsetsMap::iterator UpdI = DeclUpdateOffsets.find(ID); - using LazySpecializationInfo = - RedeclarableTemplateDecl::LazySpecializationInfo; - llvm::SmallVector<LazySpecializationInfo, 8> PendingLazySpecializationIDs; - if (UpdI != DeclUpdateOffsets.end()) { auto UpdateOffsets = std::move(UpdI->second); DeclUpdateOffsets.erase(UpdI); @@ -4308,7 +4258,7 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { ASTDeclReader Reader(*this, Record, RecordLocation(F, Offset), ID, SourceLocation()); - Reader.UpdateDecl(D, PendingLazySpecializationIDs); + Reader.UpdateDecl(D); // We might have made this declaration interesting. If so, remember that // we need to hand it off to the consumer. @@ -4318,19 +4268,6 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { } } } - // Add the lazy specializations to the template. - assert((PendingLazySpecializationIDs.empty() || isa<ClassTemplateDecl>(D) || - isa<FunctionTemplateDecl, VarTemplateDecl>(D)) && - "Must not have pending specializations"); - /* - if (auto *CTD = dyn_cast<ClassTemplateDecl>(D)) - ASTDeclReader::AddLazySpecializations(CTD, PendingLazySpecializationIDs); - else if (auto *FTD = dyn_cast<FunctionTemplateDecl>(D)) - ASTDeclReader::AddLazySpecializations(FTD, PendingLazySpecializationIDs); - else if (auto *VTD = dyn_cast<VarTemplateDecl>(D)) - ASTDeclReader::AddLazySpecializations(VTD, PendingLazySpecializationIDs); - */ - PendingLazySpecializationIDs.clear(); // Load the pending visible updates for this decl context, if it has any. auto I = PendingVisibleUpdates.find(ID); @@ -4547,9 +4484,7 @@ static void forAllLaterRedecls(DeclT *D, Fn F) { } } -void ASTDeclReader::UpdateDecl( - Decl *D, - SmallVectorImpl<LazySpecializationInfo> &PendingLazySpecializationIDs) { +void ASTDeclReader::UpdateDecl(Decl *D) { while (Record.getIdx() < Record.size()) { switch ((DeclUpdateKind)Record.readInt()) { case UPD_CXX_ADDED_IMPLICIT_MEMBER: { @@ -4560,11 +4495,6 @@ void ASTDeclReader::UpdateDecl( break; } - case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION: - // It will be added to the template's lazy specialization set. - PendingLazySpecializationIDs.push_back(ReadLazySpecializationInfo()); - break; - case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE: { auto *Anon = readDeclAs<NamespaceDecl>(); diff --git a/clang/lib/Serialization/ASTReaderInternals.h b/clang/lib/Serialization/ASTReaderInternals.h index 43b8aa41f91a20..30bc8ead8b04f0 100644 --- a/clang/lib/Serialization/ASTReaderInternals.h +++ b/clang/lib/Serialization/ASTReaderInternals.h @@ -129,7 +129,6 @@ struct LazySpecializationInfo { assert(ID != Other.ID || IsPartial == Other.IsPartial); return ID == Other.ID; } - // Records the size record in OnDiskHashTable. // sizeof() may return 8 due to align requirements. static constexpr unsigned Length = sizeof(DeclID) + sizeof(IsPartial); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index aed09cd741efba..0b0c555ff1b65f 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -13,6 +13,7 @@ #include "ASTCommon.h" #include "ASTReaderInternals.h" #include "MultiOnDiskHashTable.h" +#include "TemplateArgumentHasher.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTUnresolvedSet.h" #include "clang/AST/AbstractTypeWriter.h" @@ -4208,7 +4209,7 @@ unsigned CalculateODRHashForSpecs(const Decl *Spec) { else llvm_unreachable("New Specialization Kind?"); - return TemplateArgumentList::ComputeODRHash(Args); + return StableHashForTemplateArguments(Args); } } // namespace @@ -5870,7 +5871,7 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) { void ASTWriter::WriteSpecializationsUpdates() { auto Abv = std::make_shared<llvm::BitCodeAbbrev>(); - Abv->Add(llvm::BitCodeAbbrevOp(UPDATE_SPECIALIZATION)); + Abv->Add(llvm::BitCodeAbbrevOp(CXX_ADDED_TEMPLATE_SPECIALIZATION)); Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6)); Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)); auto UpdateSpecializationAbbrev = Stream.EmitAbbrev(std::move(Abv)); @@ -5883,7 +5884,8 @@ void ASTWriter::WriteSpecializationsUpdates() { LookupTable); // Write the lookup table - RecordData::value_type Record[] = {UPDATE_SPECIALIZATION, getDeclID(D).getRawValue()}; + RecordData::value_type Record[] = {CXX_ADDED_TEMPLATE_SPECIALIZATION, + getDeclID(D).getRawValue()}; Stream.EmitRecordWithBlob(UpdateSpecializationAbbrev, Record, LookupTable); } } @@ -5920,25 +5922,6 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) { assert(Update.getDecl() && "no decl to add?"); Record.AddDeclRef(Update.getDecl()); break; - case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION: { - const Decl *Spec = Update.getDecl(); - assert(Spec && "no decl to add?"); - Record.AddDeclRef(Spec); - ArrayRef<TemplateArgument> Args; - if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(Spec)) - Args = CTSD->getTemplateArgs().asArray(); - else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(Spec)) - Args = VTSD->getTemplateArgs().asArray(); - else if (auto *FD = dyn_cast<FunctionDecl>(Spec)) - Args = FD->getTemplateSpecializationArgs()->asArray(); - assert(Args.size()); - Record.push_back(TemplateArgumentList::ComputeODRHash(Args)); - bool IsPartialSpecialization = - isa<ClassTemplatePartialSpecializationDecl>(Spec) || - isa<VarTemplatePartialSpecializationDecl>(Spec); - Record.push_back(IsPartialSpecialization); - break; - } case UPD_CXX_ADDED_FUNCTION_DEFINITION: case UPD_CXX_ADDED_VAR_DEFINITION: break; diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index d14889271fd326..a334c459cc892c 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -216,24 +216,8 @@ namespace clang { llvm::MapVector<ModuleFile *, const Decl *> Firsts; CollectFirstDeclFromEachModule(D, /*IncludeLocal*/ true, Firsts); - for (const auto &F : Firsts) { + for (const auto &F : Firsts) SpecsInMap.push_back(F.second); - - Record.AddDeclRef(F.second); - 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)) - Args = FD->getTemplateSpecializationArgs()->asArray(); - assert(Args.size()); - Record.push_back(TemplateArgumentList::ComputeODRHash(Args)); - bool IsPartialSpecialization = - isa<ClassTemplatePartialSpecializationDecl>(D) || - isa<VarTemplatePartialSpecializationDecl>(D); - Record.push_back(IsPartialSpecialization); - } } /// Get the specialization decl from an entry in the specialization list. @@ -260,22 +244,12 @@ namespace clang { // If we have any lazy specializations, and the external AST source is // our chained AST reader, we can just write out the DeclIDs. Otherwise, // we need to resolve them to actual declarations. - if (Writer.Chain != Writer.Context->getExternalSource() && - Common->LazySpecializations) { + if (Writer.Chain != Writer.Context->getExternalSource() && Writer.Chain && + Writer.Chain->getLoadedSpecializationsLookupTables(D)) { D->LoadLazySpecializations(); - assert(!Common->LazySpecializations); + assert(!Writer.Chain->getLoadedSpecializationsLookupTables(D)); } - using LazySpecializationInfo = - RedeclarableTemplateDecl::LazySpecializationInfo; - ArrayRef<LazySpecializationInfo> LazySpecializations; - if (auto *LS = Common->LazySpecializations) - LazySpecializations = llvm::ArrayRef(LS + 1, LS[0].DeclID.getRawValue()); - - // Add a slot to the record for the number of specializations. - unsigned I = Record.size(); - Record.push_back(0); - // AddFirstDeclFromEachModule might trigger deserialization, invalidating // *Specializations iterators. llvm::SmallVector<const Decl*, 16> Specs; @@ -291,21 +265,6 @@ namespace clang { AddFirstSpecializationDeclFromEachModule(D, SpecsInOnDiskMap); } - // We don't need to insert LazySpecializations to SpecsInOnDiskMap, - // since we'll handle that in GenerateSpecializationInfoLookupTable. - for (auto &SpecInfo : LazySpecializations) { - Record.push_back(SpecInfo.DeclID.getRawValue()); - Record.push_back(SpecInfo.ODRHash); - Record.push_back(SpecInfo.IsPartial); - } - - // Update the size entry we added earlier. We linerized the - // LazySpecializationInfo members and we need to adjust the size as we - // will read them always together. - assert((Record.size() - I - 1) % 3 == 0 && - "Must be divisible by LazySpecializationInfo count!"); - Record[I] = (Record.size() - I - 1) / 3; - Record.AddOffset( Writer.WriteSpecializationInfoLookupTable(D, SpecsInOnDiskMap)); } @@ -328,9 +287,6 @@ namespace clang { if (Writer.getFirstLocalDecl(Specialization) != Specialization) return; - Writer.DeclUpdates[Template].push_back(ASTWriter::DeclUpdate( - UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION, Specialization)); - Writer.SpecializationsUpdates[cast<NamedDecl>(Template)].push_back( cast<NamedDecl>(Specialization)); } diff --git a/clang/lib/Serialization/CMakeLists.txt b/clang/lib/Serialization/CMakeLists.txt index 5a4b3a58e9c45e..59a95546ec57dc 100644 --- a/clang/lib/Serialization/CMakeLists.txt +++ b/clang/lib/Serialization/CMakeLists.txt @@ -21,6 +21,7 @@ add_clang_library(clangSerialization ModuleFileExtension.cpp ModuleManager.cpp PCHContainerOperations.cpp + TemplateArgumentHasher.cpp ADDITIONAL_HEADERS ASTCommon.h diff --git a/clang/lib/Serialization/TemplateArgumentHasher.cpp b/clang/lib/Serialization/TemplateArgumentHasher.cpp new file mode 100644 index 00000000000000..de0d8633a5c54e --- /dev/null +++ b/clang/lib/Serialization/TemplateArgumentHasher.cpp @@ -0,0 +1,423 @@ +//===- TemplateArgumentHasher.cpp - Hash Template Arguments -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TemplateArgumentHasher.h" +#include "clang/AST/APValue.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/TypeVisitor.h" +#include "clang/Basic/IdentifierTable.h" +#include "llvm/ADT/FoldingSet.h" + +using namespace clang; + +namespace { + +class TemplateArgumentHasher { + // If we bail out during the process of calculating hash values for + // template arguments for any reason. We're allowed to do it since + // TemplateArgumentHasher are only required to give the same hash value + // for the same template arguments, but not required to give different + // hash value for different template arguments. + // + // So in the worst case, it is still a valid implementation to give all + // inputs the same BailedOutValue as output. + bool BailedOut = false; + static constexpr unsigned BailedOutValue = 0x12345678; + + llvm::FoldingSetNodeID ID; +public: + TemplateArgumentHasher() = default; + + void AddTemplateArgument(TemplateArgument TA); + + void AddInteger(unsigned V) { + ID.AddInteger(V); + } + + unsigned getValue() { + if (BailedOut) + return BailedOutValue; + + return ID.computeStableHash(); + } + + void setBailedOut() { BailedOut = true; } + + void AddType(const Type *T); + void AddQualType(QualType T); + void AddDecl(const Decl *D); + void AddStructuralValue(const APValue &); + void AddTemplateName(TemplateName Name); + void AddDeclarationName(DeclarationName Name); + void AddIdentifierInfo(const IdentifierInfo *II); +}; + +void TemplateArgumentHasher::AddTemplateArgument(TemplateArgument TA) { + const auto Kind = TA.getKind(); + AddInteger(Kind); + + switch (Kind) { + case TemplateArgument::Null: + llvm_unreachable("Expected valid TemplateArgument"); + case TemplateArgument::Type: + AddQualType(TA.getAsType()); + break; + case TemplateArgument::Declaration: + AddDecl(TA.getAsDecl()); + break; + case TemplateArgument::NullPtr: + ID.AddPointer(nullptr); + break; + case TemplateArgument::Integral: { + // There are integrals (e.g.: _BitInt(128)) that cannot be represented as + // any builtin integral type, so we use the hash of APSInt instead. + TA.getAsIntegral().Profile(ID); + break; + } + case TemplateArgument::StructuralValue: + AddQualType(TA.getStructuralValueType()); + AddStructuralValue(TA.getAsStructuralValue()); + break; + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + AddTemplateName(TA.getAsTemplateOrTemplatePattern()); + break; + case TemplateArgument::Expression: + // If we meet expression in template argument, it implies + // that the template is still dependent. It is meaningless + // to get a stable hash for the template. Bail out simply. + BailedOut = true; + break; + case TemplateArgument::Pack: + AddInteger(TA.pack_size()); + for (auto SubTA : TA.pack_elements()) { + AddTemplateArgument(SubTA); + } + break; + } +} + +void TemplateArgumentHasher::AddStructuralValue(const APValue &Value) { + auto Kind = Value.getKind(); + AddInteger(Kind); + + // 'APValue::Profile' uses pointer values to make hash for LValue and + // MemberPointer, but they differ from one compiler invocation to another. + // It may be difficult to handle such cases. Bail out simply. + + if (Kind == APValue::LValue || Kind == APValue::MemberPointer) { + BailedOut = true; + return; + } + + Value.Profile(ID); +} + +void TemplateArgumentHasher::AddTemplateName(TemplateName Name) { + switch (Name.getKind()) { + case TemplateName::Template: + AddDecl(Name.getAsTemplateDecl()); + break; + case TemplateName::QualifiedTemplate: { + QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName(); + AddTemplateName(QTN->getUnderlyingTemplate()); + break; + } + case TemplateName::OverloadedTemplate: + case TemplateName::AssumedTemplate: + case TemplateName::DependentTemplate: + case TemplateName::SubstTemplateTemplateParm: + case TemplateName::SubstTemplateTemplateParmPack: + BailedOut = true; + break; + case TemplateName::UsingTemplate: + UsingShadowDecl *USD = Name.getAsUsingShadowDecl(); + if (USD) + AddDecl(USD->getTargetDecl()); + else + BailedOut = true; + break; + } +} + +void TemplateArgumentHasher::AddIdentifierInfo(const IdentifierInfo *II) { + assert(II && "Expecting non-null pointer."); + ID.AddString(II->getName()); +} + +void TemplateArgumentHasher::AddDeclarationName(DeclarationName Name) { + if (Name.isEmpty()) + return; + + switch(Name.getNameKind()) { + case DeclarationName::Identifier: + AddIdentifierInfo(Name.getAsIdentifierInfo()); + break; + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + BailedOut = true; + break; + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + AddQualType(Name.getCXXNameType()); + break; + case DeclarationName::CXXOperatorName: + AddInteger(Name.getCXXOverloadedOperator()); + break; + case DeclarationName::CXXLiteralOperatorName: + AddIdentifierInfo(Name.getCXXLiteralIdentifier()); + break; + case DeclarationName::CXXConversionFunctionName: + AddQualType(Name.getCXXNameType()); + break; + case DeclarationName::CXXUsingDirective: + break; + case DeclarationName::CXXDeductionGuideName: { + if (auto *Template = Name.getCXXDeductionGuideTemplate()) + AddDecl(Template); + } + } +} + +void TemplateArgumentHasher::AddDecl(const Decl *D) { + const NamedDecl *ND = dyn_cast<NamedDecl>(D); + if (!ND) { + BailedOut = true; + return; + } + + AddDeclarationName(ND->getDeclName()); +} + +void TemplateArgumentHasher::AddQualType(QualType T) { + if (T.isNull()) { + BailedOut = true; + return; + } + SplitQualType split = T.split(); + AddInteger(split.Quals.getAsOpaqueValue()); + AddType(split.Ty); +} + +// Process a Type pointer. Add* methods call back into TemplateArgumentHasher +// while Visit* methods process the relevant parts of the Type. +// Any unhandled type will make the hash computation bail out. +class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> { + typedef TypeVisitor<TypeVisitorHelper> Inherited; + llvm::FoldingSetNodeID &ID; + TemplateArgumentHasher &Hash; + +public: + TypeVisitorHelper(llvm::FoldingSetNodeID &ID, TemplateArgumentHasher &Hash) + : ID(ID), Hash(Hash) {} + + void AddDecl(const Decl *D) { + if (D) + Hash.AddDecl(D); + else + Hash.AddInteger(0); + } + + void AddQualType(QualType T) { + Hash.AddQualType(T); + } + + void AddType(const Type *T) { + if (T) + Hash.AddType(T); + else + Hash.AddInteger(0); + } + + void VisitQualifiers(Qualifiers Quals) { + Hash.AddInteger(Quals.getAsOpaqueValue()); + } + + void Visit(const Type *T) { + Inherited::Visit(T); + } + + // Unhandled types. Bail out simply. + void VisitType(const Type *T) { + Hash.setBailedOut(); + } + + void VisitAdjustedType(const AdjustedType *T) { + AddQualType(T->getOriginalType()); + } + + void VisitDecayedType(const DecayedType *T) { + // getDecayedType and getPointeeType are derived from getAdjustedType + // and don't need to be separately processed. + VisitAdjustedType(T); + } + + void VisitArrayType(const ArrayType *T) { + AddQualType(T->getElementType()); + Hash.AddInteger(llvm::to_underlying(T->getSizeModifier())); + VisitQualifiers(T->getIndexTypeQualifiers()); + } + void VisitConstantArrayType(const ConstantArrayType *T) { + T->getSize().Profile(ID); + VisitArrayType(T); + } + + void VisitAttributedType(const AttributedType *T) { + Hash.AddInteger(T->getAttrKind()); + AddQualType(T->getModifiedType()); + } + + void VisitBuiltinType(const BuiltinType *T) { + Hash.AddInteger(T->getKind()); + } + + void VisitComplexType(const ComplexType *T) { + AddQualType(T->getElementType()); + } + + void VisitDecltypeType(const DecltypeType *T) { + AddQualType(T->getUnderlyingType()); + } + + void VisitDeducedType(const DeducedType *T) { + AddQualType(T->getDeducedType()); + } + + void VisitAutoType(const AutoType *T) { + VisitDeducedType(T); + } + + void VisitDeducedTemplateSpecializationType( + const DeducedTemplateSpecializationType *T) { + Hash.AddTemplateName(T->getTemplateName()); + VisitDeducedType(T); + } + + void VisitFunctionType(const FunctionType *T) { + AddQualType(T->getReturnType()); + T->getExtInfo().Profile(ID); + Hash.AddInteger(T->isConst()); + Hash.AddInteger(T->isVolatile()); + Hash.AddInteger(T->isRestrict()); + } + + void VisitFunctionNoProtoType(const FunctionNoProtoType *T) { + VisitFunctionType(T); + } + + void VisitFunctionProtoType(const FunctionProtoType *T) { + Hash.AddInteger(T->getNumParams()); + for (auto ParamType : T->getParamTypes()) + AddQualType(ParamType); + + VisitFunctionType(T); + } + + void VisitMemberPointerType(const MemberPointerType *T) { + AddQualType(T->getPointeeType()); + AddType(T->getClass()); + } + + void VisitPackExpansionType(const PackExpansionType *T) { + AddQualType(T->getPattern()); + } + + void VisitParenType(const ParenType *T) { + AddQualType(T->getInnerType()); + } + + void VisitPointerType(const PointerType *T) { + AddQualType(T->getPointeeType()); + } + + void VisitReferenceType(const ReferenceType *T) { + AddQualType(T->getPointeeTypeAsWritten()); + } + + void VisitLValueReferenceType(const LValueReferenceType *T) { + VisitReferenceType(T); + } + + void VisitRValueReferenceType(const RValueReferenceType *T) { + VisitReferenceType(T); + } + + void + VisitSubstTemplateTypeParmPackType(const SubstTemplateTypeParmPackType *T) { + AddDecl(T->getAssociatedDecl()); + Hash.AddTemplateArgument(T->getArgumentPack()); + } + + void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType *T) { + AddDecl(T->getAssociatedDecl()); + AddQualType(T->getReplacementType()); + } + + void VisitTagType(const TagType *T) { + AddDecl(T->getDecl()); + } + + void VisitRecordType(const RecordType *T) { VisitTagType(T); } + void VisitEnumType(const EnumType *T) { VisitTagType(T); } + + void VisitTemplateSpecializationType(const TemplateSpecializationType *T) { + Hash.AddInteger(T->template_arguments().size()); + for (const auto &TA : T->template_arguments()) { + Hash.AddTemplateArgument(TA); + } + Hash.AddTemplateName(T->getTemplateName()); + } + + void VisitTemplateTypeParmType(const TemplateTypeParmType *T) { + Hash.AddInteger(T->getDepth()); + Hash.AddInteger(T->getIndex()); + Hash.AddInteger(T->isParameterPack()); + } + + void VisitTypedefType(const TypedefType *T) { + AddDecl(T->getDecl()); + } + + void VisitElaboratedType(const ElaboratedType *T) { + AddQualType(T->getNamedType()); + } + + void VisitUnaryTransformType(const UnaryTransformType *T) { + AddQualType(T->getUnderlyingType()); + AddQualType(T->getBaseType()); + } + + void VisitVectorType(const VectorType *T) { + AddQualType(T->getElementType()); + Hash.AddInteger(T->getNumElements()); + Hash.AddInteger(llvm::to_underlying(T->getVectorKind())); + } + + void VisitExtVectorType(const ExtVectorType * T) { + VisitVectorType(T); + } +}; + +void TemplateArgumentHasher::AddType(const Type *T) { + assert(T && "Expecting non-null pointer."); + TypeVisitorHelper(ID, *this).Visit(T); +} + +} + +unsigned clang::serialization::StableHashForTemplateArguments(llvm::ArrayRef<TemplateArgument> Args) { + TemplateArgumentHasher Hasher; + Hasher.AddInteger(Args.size()); + for (TemplateArgument Arg : Args) + Hasher.AddTemplateArgument(Arg); + return Hasher.getValue(); +} diff --git a/clang/lib/Serialization/TemplateArgumentHasher.h b/clang/lib/Serialization/TemplateArgumentHasher.h new file mode 100644 index 00000000000000..8ead99556be5f2 --- /dev/null +++ b/clang/lib/Serialization/TemplateArgumentHasher.h @@ -0,0 +1,34 @@ +//===- TemplateArgumentHasher.h - Hash Template Arguments -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/TemplateBase.h" + +namespace clang { +namespace serialization { + +/// Calculate a stable hash value for template arguments. We guarantee that +/// the same template arguments must have the same hashed values. But we don't +/// guarantee that the template arguments with the same hashed value are the +/// same template arguments. +/// +/// ODR hashing may not be the best mechanism to hash the template +/// arguments. ODR hashing is (or perhaps, should be) about determining whether +/// two things are spelled the same way and have the same meaning (as required +/// by the C++ ODR), whereas what we want here is whether they have the same +/// meaning regardless of spelling. Maybe we can get away with reusing ODR +/// hashing anyway, on the basis that any canonical, non-dependent template +/// argument should have the same (invented) spelling in every translation +/// unit, but it is not sure that's true in all cases. There may still be cases +/// where the canonical type includes some aspect of "whatever we saw first", +/// in which case the ODR hash can differ across translation units for +/// non-dependent, canonical template arguments that are spelled differently +/// but have the same meaning. But it is not easy to raise examples. +unsigned StableHashForTemplateArguments(llvm::ArrayRef<TemplateArgument> Args); + +} +} diff --git a/clang/test/Modules/cxx-templates.cpp b/clang/test/Modules/cxx-templates.cpp index e10ba7c2ac3ef2..b7d5741e69af61 100644 --- a/clang/test/Modules/cxx-templates.cpp +++ b/clang/test/Modules/cxx-templates.cpp @@ -251,7 +251,7 @@ namespace Std { // CHECK-DUMP: ClassTemplateDecl {{.*}} <{{.*[/\\]}}cxx-templates-common.h:1:1, {{.*}}> col:{{.*}} in cxx_templates_common SomeTemplate // CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate -// CHECK-DUMP-NEXT: TemplateArgument type 'char[1]' +// CHECK-DUMP-NEXT: TemplateArgument type 'char[2]' // CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition // CHECK-DUMP-NEXT: DefinitionData // CHECK-DUMP-NEXT: DefaultConstructor @@ -260,9 +260,9 @@ namespace Std { // CHECK-DUMP-NEXT: CopyAssignment // CHECK-DUMP-NEXT: MoveAssignment // CHECK-DUMP-NEXT: Destructor -// CHECK-DUMP-NEXT: TemplateArgument type 'char[1]' -// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate // CHECK-DUMP-NEXT: TemplateArgument type 'char[2]' +// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate +// CHECK-DUMP-NEXT: TemplateArgument type 'char[1]' // CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition // CHECK-DUMP-NEXT: DefinitionData // CHECK-DUMP-NEXT: DefaultConstructor @@ -271,4 +271,4 @@ namespace Std { // CHECK-DUMP-NEXT: CopyAssignment // CHECK-DUMP-NEXT: MoveAssignment // CHECK-DUMP-NEXT: Destructor -// CHECK-DUMP-NEXT: TemplateArgument type 'char[2]' +// CHECK-DUMP-NEXT: TemplateArgument type 'char[1]' diff --git a/clang/test/Modules/recursive-instantiations.cppm b/clang/test/Modules/recursive-instantiations.cppm new file mode 100644 index 00000000000000..d5854b0e647e37 --- /dev/null +++ b/clang/test/Modules/recursive-instantiations.cppm @@ -0,0 +1,40 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/type_traits.cppm -emit-module-interface -o %t/type_traits.pcm +// RUN: %clang_cc1 -std=c++20 %t/test.cpp -fprebuilt-module-path=%t -verify + +//--- type_traits.cppm +export module type_traits; + +export template <typename T> +constexpr bool is_pod_v = __is_pod(T); + +//--- test.cpp +// expected-no-diagnostics +import type_traits; +// Base is either void or wrapper<T>. +template <class Base> struct wrapper : Base {}; +template <> struct wrapper<void> {}; + +// wrap<0>::type<T> is wrapper<T>, wrap<1>::type<T> is wrapper<wrapper<T>>, +// and so on. +template <int N> +struct wrap { + template <class Base> + using type = wrapper<typename wrap<N-1>::template type<Base>>; +}; + +template <> +struct wrap<0> { + template <class Base> + using type = wrapper<Base>; +}; + +inline constexpr int kMaxRank = 40; +template <int N, class Base = void> +using rank = typename wrap<N>::template type<Base>; +using rank_selector_t = rank<kMaxRank>; + +static_assert(is_pod_v<rank_selector_t>, "Must be POD"); diff --git a/clang/test/OpenMP/target_parallel_ast_print.cpp b/clang/test/OpenMP/target_parallel_ast_print.cpp index 7e27ac7b92ca4a..3ee98bc525c1bd 100644 --- a/clang/test/OpenMP/target_parallel_ast_print.cpp +++ b/clang/test/OpenMP/target_parallel_ast_print.cpp @@ -38,10 +38,6 @@ struct S { // CHECK: static int TS; // CHECK-NEXT: #pragma omp threadprivate(S<int>::TS) // CHECK-NEXT: } -// CHECK: template<> struct S<char> { -// CHECK: static char TS; -// CHECK-NEXT: #pragma omp threadprivate(S<char>::TS) -// CHECK-NEXT: } template <typename T, int C> T tmain(T argc, T *argv) { diff --git a/clang/test/OpenMP/target_teams_ast_print.cpp b/clang/test/OpenMP/target_teams_ast_print.cpp index 2ff34e4498bfe1..358200bd57c182 100644 --- a/clang/test/OpenMP/target_teams_ast_print.cpp +++ b/clang/test/OpenMP/target_teams_ast_print.cpp @@ -40,10 +40,6 @@ struct S { // CHECK: static int TS; // CHECK-NEXT: #pragma omp threadprivate(S<int>::TS) // CHECK-NEXT: } -// CHECK: template<> struct S<long> { -// CHECK: static long TS; -// CHECK-NEXT: #pragma omp threadprivate(S<long>::TS) -// CHECK-NEXT: } template <typename T, int C> T tmain(T argc, T *argv) { diff --git a/clang/test/OpenMP/task_ast_print.cpp b/clang/test/OpenMP/task_ast_print.cpp index 2a6b8908a1e2dc..30fb7ab75cc87a 100644 --- a/clang/test/OpenMP/task_ast_print.cpp +++ b/clang/test/OpenMP/task_ast_print.cpp @@ -87,10 +87,6 @@ struct S { // CHECK: static int TS; // CHECK-NEXT: #pragma omp threadprivate(S<int>::TS) // CHECK-NEXT: } -// CHECK: template<> struct S<long> { -// CHECK: static long TS; -// CHECK-NEXT: #pragma omp threadprivate(S<long>::TS) -// CHECK-NEXT: } template <typename T, int C> T tmain(T argc, T *argv) { diff --git a/clang/test/OpenMP/teams_ast_print.cpp b/clang/test/OpenMP/teams_ast_print.cpp index 0087f71ac9f742..597a9b2bdbdc5d 100644 --- a/clang/test/OpenMP/teams_ast_print.cpp +++ b/clang/test/OpenMP/teams_ast_print.cpp @@ -27,10 +27,6 @@ struct S { // CHECK: static int TS; // CHECK-NEXT: #pragma omp threadprivate(S<int>::TS) // CHECK-NEXT: } -// CHECK: template<> struct S<long> { -// CHECK: static long TS; -// CHECK-NEXT: #pragma omp threadprivate(S<long>::TS) -// CHECK-NEXT: } template <typename T, int C> T tmain(T argc, T *argv) { >From e883be85e91243137ca4c44a8b78f4617f9dba54 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <yedeng...@linux.alibaba.com> Date: Wed, 10 Jul 2024 17:52:56 +0800 Subject: [PATCH 2/3] fmt --- clang/lib/Serialization/ASTReader.cpp | 5 +- .../Serialization/TemplateArgumentHasher.cpp | 122 ++++++++---------- .../Serialization/TemplateArgumentHasher.h | 4 +- 3 files changed, 57 insertions(+), 74 deletions(-) diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index f75d8fc4676b32..bd0ebda69b0d8a 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7697,7 +7697,8 @@ void ASTReader::CompleteRedeclChain(const Decl *D) { if (Template) { // For partitial specialization, load all the specializations for safety. - if (isa<ClassTemplatePartialSpecializationDecl, VarTemplatePartialSpecializationDecl>(D)) + if (isa<ClassTemplatePartialSpecializationDecl, + VarTemplatePartialSpecializationDecl>(D)) Template->loadLazySpecializationsImpl(); else Template->loadLazySpecializationsImpl(Args); @@ -8033,7 +8034,7 @@ bool ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) { // the lookup table. if (!OnlyPartial) SpecializationsLookups.erase(It); - + bool NewSpecsFound = false; Deserializing LookupResults(this); for (auto &Info : Infos) diff --git a/clang/lib/Serialization/TemplateArgumentHasher.cpp b/clang/lib/Serialization/TemplateArgumentHasher.cpp index de0d8633a5c54e..ab7ea262cddf61 100644 --- a/clang/lib/Serialization/TemplateArgumentHasher.cpp +++ b/clang/lib/Serialization/TemplateArgumentHasher.cpp @@ -33,19 +33,18 @@ class TemplateArgumentHasher { static constexpr unsigned BailedOutValue = 0x12345678; llvm::FoldingSetNodeID ID; + public: TemplateArgumentHasher() = default; void AddTemplateArgument(TemplateArgument TA); - void AddInteger(unsigned V) { - ID.AddInteger(V); - } + void AddInteger(unsigned V) { ID.AddInteger(V); } unsigned getValue() { if (BailedOut) return BailedOutValue; - + return ID.computeStableHash(); } @@ -65,43 +64,43 @@ void TemplateArgumentHasher::AddTemplateArgument(TemplateArgument TA) { AddInteger(Kind); switch (Kind) { - case TemplateArgument::Null: - llvm_unreachable("Expected valid TemplateArgument"); - case TemplateArgument::Type: - AddQualType(TA.getAsType()); - break; - case TemplateArgument::Declaration: - AddDecl(TA.getAsDecl()); - break; - case TemplateArgument::NullPtr: - ID.AddPointer(nullptr); - break; - case TemplateArgument::Integral: { - // There are integrals (e.g.: _BitInt(128)) that cannot be represented as - // any builtin integral type, so we use the hash of APSInt instead. - TA.getAsIntegral().Profile(ID); - break; + case TemplateArgument::Null: + llvm_unreachable("Expected valid TemplateArgument"); + case TemplateArgument::Type: + AddQualType(TA.getAsType()); + break; + case TemplateArgument::Declaration: + AddDecl(TA.getAsDecl()); + break; + case TemplateArgument::NullPtr: + ID.AddPointer(nullptr); + break; + case TemplateArgument::Integral: { + // There are integrals (e.g.: _BitInt(128)) that cannot be represented as + // any builtin integral type, so we use the hash of APSInt instead. + TA.getAsIntegral().Profile(ID); + break; + } + case TemplateArgument::StructuralValue: + AddQualType(TA.getStructuralValueType()); + AddStructuralValue(TA.getAsStructuralValue()); + break; + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + AddTemplateName(TA.getAsTemplateOrTemplatePattern()); + break; + case TemplateArgument::Expression: + // If we meet expression in template argument, it implies + // that the template is still dependent. It is meaningless + // to get a stable hash for the template. Bail out simply. + BailedOut = true; + break; + case TemplateArgument::Pack: + AddInteger(TA.pack_size()); + for (auto SubTA : TA.pack_elements()) { + AddTemplateArgument(SubTA); } - case TemplateArgument::StructuralValue: - AddQualType(TA.getStructuralValueType()); - AddStructuralValue(TA.getAsStructuralValue()); - break; - case TemplateArgument::Template: - case TemplateArgument::TemplateExpansion: - AddTemplateName(TA.getAsTemplateOrTemplatePattern()); - break; - case TemplateArgument::Expression: - // If we meet expression in template argument, it implies - // that the template is still dependent. It is meaningless - // to get a stable hash for the template. Bail out simply. - BailedOut = true; - break; - case TemplateArgument::Pack: - AddInteger(TA.pack_size()); - for (auto SubTA : TA.pack_elements()) { - AddTemplateArgument(SubTA); - } - break; + break; } } @@ -157,7 +156,7 @@ void TemplateArgumentHasher::AddDeclarationName(DeclarationName Name) { if (Name.isEmpty()) return; - switch(Name.getNameKind()) { + switch (Name.getNameKind()) { case DeclarationName::Identifier: AddIdentifierInfo(Name.getAsIdentifierInfo()); break; @@ -227,9 +226,7 @@ class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> { Hash.AddInteger(0); } - void AddQualType(QualType T) { - Hash.AddQualType(T); - } + void AddQualType(QualType T) { Hash.AddQualType(T); } void AddType(const Type *T) { if (T) @@ -242,14 +239,10 @@ class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> { Hash.AddInteger(Quals.getAsOpaqueValue()); } - void Visit(const Type *T) { - Inherited::Visit(T); - } + void Visit(const Type *T) { Inherited::Visit(T); } // Unhandled types. Bail out simply. - void VisitType(const Type *T) { - Hash.setBailedOut(); - } + void VisitType(const Type *T) { Hash.setBailedOut(); } void VisitAdjustedType(const AdjustedType *T) { AddQualType(T->getOriginalType()); @@ -276,9 +269,7 @@ class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> { AddQualType(T->getModifiedType()); } - void VisitBuiltinType(const BuiltinType *T) { - Hash.AddInteger(T->getKind()); - } + void VisitBuiltinType(const BuiltinType *T) { Hash.AddInteger(T->getKind()); } void VisitComplexType(const ComplexType *T) { AddQualType(T->getElementType()); @@ -292,9 +283,7 @@ class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> { AddQualType(T->getDeducedType()); } - void VisitAutoType(const AutoType *T) { - VisitDeducedType(T); - } + void VisitAutoType(const AutoType *T) { VisitDeducedType(T); } void VisitDeducedTemplateSpecializationType( const DeducedTemplateSpecializationType *T) { @@ -331,9 +320,7 @@ class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> { AddQualType(T->getPattern()); } - void VisitParenType(const ParenType *T) { - AddQualType(T->getInnerType()); - } + void VisitParenType(const ParenType *T) { AddQualType(T->getInnerType()); } void VisitPointerType(const PointerType *T) { AddQualType(T->getPointeeType()); @@ -362,9 +349,7 @@ class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> { AddQualType(T->getReplacementType()); } - void VisitTagType(const TagType *T) { - AddDecl(T->getDecl()); - } + void VisitTagType(const TagType *T) { AddDecl(T->getDecl()); } void VisitRecordType(const RecordType *T) { VisitTagType(T); } void VisitEnumType(const EnumType *T) { VisitTagType(T); } @@ -383,9 +368,7 @@ class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> { Hash.AddInteger(T->isParameterPack()); } - void VisitTypedefType(const TypedefType *T) { - AddDecl(T->getDecl()); - } + void VisitTypedefType(const TypedefType *T) { AddDecl(T->getDecl()); } void VisitElaboratedType(const ElaboratedType *T) { AddQualType(T->getNamedType()); @@ -402,9 +385,7 @@ class TypeVisitorHelper : public TypeVisitor<TypeVisitorHelper> { Hash.AddInteger(llvm::to_underlying(T->getVectorKind())); } - void VisitExtVectorType(const ExtVectorType * T) { - VisitVectorType(T); - } + void VisitExtVectorType(const ExtVectorType *T) { VisitVectorType(T); } }; void TemplateArgumentHasher::AddType(const Type *T) { @@ -412,9 +393,10 @@ void TemplateArgumentHasher::AddType(const Type *T) { TypeVisitorHelper(ID, *this).Visit(T); } -} +} // namespace -unsigned clang::serialization::StableHashForTemplateArguments(llvm::ArrayRef<TemplateArgument> Args) { +unsigned clang::serialization::StableHashForTemplateArguments( + llvm::ArrayRef<TemplateArgument> Args) { TemplateArgumentHasher Hasher; Hasher.AddInteger(Args.size()); for (TemplateArgument Arg : Args) diff --git a/clang/lib/Serialization/TemplateArgumentHasher.h b/clang/lib/Serialization/TemplateArgumentHasher.h index 8ead99556be5f2..f23f1318afbbf4 100644 --- a/clang/lib/Serialization/TemplateArgumentHasher.h +++ b/clang/lib/Serialization/TemplateArgumentHasher.h @@ -30,5 +30,5 @@ namespace serialization { /// but have the same meaning. But it is not easy to raise examples. unsigned StableHashForTemplateArguments(llvm::ArrayRef<TemplateArgument> Args); -} -} +} // namespace serialization +} // namespace clang >From 61c451d2cce4d9b613de93aedd3f3fd4dcc296ee Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <yedeng...@linux.alibaba.com> Date: Tue, 27 Aug 2024 13:37:34 +0800 Subject: [PATCH 3/3] load specializations before writing specialization decls --- clang/lib/Serialization/ASTWriterDecl.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index a334c459cc892c..bdcf26ccd67e5f 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1759,6 +1759,12 @@ void ASTDeclWriter::VisitClassTemplateDecl(ClassTemplateDecl *D) { void ASTDeclWriter::VisitClassTemplateSpecializationDecl( ClassTemplateSpecializationDecl *D) { + // FIXME: We need to load the "logical" first declaration before writing + // the Redeclarable part. But it may be too expensive to load all the + // specializations. Maybe we can find a way to load the "logical" first + // declaration only. Or we should try to solve this on the reader side. + D->getSpecializedTemplate()->specializations(); + RegisterTemplateSpecialization(D->getSpecializedTemplate(), D); VisitCXXRecordDecl(D); @@ -1827,6 +1833,12 @@ void ASTDeclWriter::VisitVarTemplateDecl(VarTemplateDecl *D) { void ASTDeclWriter::VisitVarTemplateSpecializationDecl( VarTemplateSpecializationDecl *D) { + // FIXME: We need to load the "logical" first declaration before writing + // the Redeclarable part. But it may be too expensive to load all the + // specializations. Maybe we can find a way to load the "logical" first + // declaration only. Or we should try to solve this on the reader side. + D->getSpecializedTemplate()->specializations(); + RegisterTemplateSpecialization(D->getSpecializedTemplate(), D); llvm::PointerUnion<VarTemplateDecl *, VarTemplatePartialSpecializationDecl *> _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits