https://github.com/ChuanqiXu9 updated https://github.com/llvm/llvm-project/pull/83237
>From 5df9f526236cff3b2212088bf6bf52c6802044e2 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] [Serialization] Code cleanups and polish 83233 fmt load specializations before writing specialization decls address comments Revert "load specializations before writing specialization decls" This reverts commit 61c451d2cce4d9b613de93aedd3f3fd4dcc296ee. Do not omit data from imported modules with same key Handle merging spec info manually --- 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 | 41 +- clang/lib/Serialization/ASTReaderDecl.cpp | 80 +--- clang/lib/Serialization/ASTReaderInternals.h | 3 +- clang/lib/Serialization/ASTWriter.cpp | 49 +-- clang/lib/Serialization/ASTWriterDecl.cpp | 53 +-- clang/lib/Serialization/CMakeLists.txt | 1 + .../Serialization/TemplateArgumentHasher.cpp | 405 ++++++++++++++++++ .../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, 612 insertions(+), 294 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 5ee373a0db5027..7526e83a60f903 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); @@ -815,13 +797,6 @@ class RedeclarableTemplateDecl : public TemplateDecl, /// directly instantiated (or null). RedeclarableTemplateDecl *InstantiatedFromMember = nullptr; - /// 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 78bbbaf2d7b5c6..0c92c52854c9e7 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 712ab2ecc86c18..3c9a652052ef9f 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -737,6 +737,8 @@ enum ASTRecordTypes { /// Record code for updated specialization UPDATE_SPECIALIZATION = 73, + + CXX_ADDED_TEMPLATE_SPECIALIZATION = 74, }; /// 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 879584a7a11003..b4906ebe4cf3bc 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -2057,9 +2057,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 223c0f29128c44..5bbbf22e6243d9 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -351,63 +351,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)..., @@ -416,6 +383,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, @@ -962,14 +947,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 a8a3a5200d61ed..c73570e405ab68 100644 --- a/clang/lib/AST/ODRHash.cpp +++ b/clang/lib/AST/ODRHash.cpp @@ -819,16 +819,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 f39463712ce81f..54944267b4868a 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 080ca274a52ee7..ecf9078e291e54 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" @@ -3550,7 +3551,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(); @@ -7764,8 +7765,14 @@ 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 ** @@ -8146,12 +8153,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. @@ -8163,29 +8170,43 @@ void ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) { 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 0e2dddee607541..21c610bb2a795c 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -187,21 +187,6 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> { std::string readString() { return Record.readString(); } - using LazySpecializationInfo = - RedeclarableTemplateDecl::LazySpecializationInfo; - - 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(); } template <typename T> T *readDeclAs() { return Record.readDeclAs<T>(); } @@ -294,30 +279,6 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> { : Reader(Reader), MergeImpl(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) - RedeclarableTemplateDecl::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(...); @@ -348,9 +309,7 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> { void ReadFunctionDefinition(FunctionDecl *FD); void Visit(Decl *D); - void UpdateDecl( - Decl *D, - SmallVectorImpl<RedeclarableTemplateDecl::LazySpecializationInfo> &); + void UpdateDecl(Decl *D); static void setNextObjCCategory(ObjCCategoryDecl *Cat, ObjCCategoryDecl *Next) { @@ -2470,9 +2429,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); } @@ -2499,9 +2455,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); } } @@ -2601,9 +2554,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); } } @@ -4305,10 +4255,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); @@ -4345,7 +4291,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. @@ -4355,19 +4301,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); @@ -4594,9 +4527,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: { @@ -4607,11 +4538,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..0611255b7858d8 100644 --- a/clang/lib/Serialization/ASTReaderInternals.h +++ b/clang/lib/Serialization/ASTReaderInternals.h @@ -125,11 +125,10 @@ struct LazySpecializationInfo { // Whether or not this specialization is partial. bool IsPartial; - bool operator==(const LazySpecializationInfo &Other) { + bool operator==(const LazySpecializationInfo &Other) const { 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 8c48f8bbfe4e44..76190177ad3214 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" @@ -4145,7 +4146,7 @@ class LazySpecializationInfoLookupTrait { explicit LazySpecializationInfoLookupTrait(ASTWriter &Writer) : Writer(Writer) {} - template <typename Col> data_type getData(Col &&C) { + template <typename Col, typename Col2> data_type getData(Col &&C, Col2 &ExistingInfo) { unsigned Start = Specs.size(); for (auto *D : C) { bool IsPartial = isa<ClassTemplatePartialSpecializationDecl, @@ -4155,6 +4156,8 @@ class LazySpecializationInfoLookupTrait { Specs.push_back({GlobalDeclID(Writer.GetDeclRef(ND).getRawValue()), IsPartial}); } + for (const serialization::reader::LazySpecializationInfo &Info : ExistingInfo) + Specs.push_back(Info); return std::make_pair(Start, Specs.size()); } @@ -4223,7 +4226,7 @@ unsigned CalculateODRHashForSpecs(const Decl *Spec) { else llvm_unreachable("New Specialization Kind?"); - return TemplateArgumentList::ComputeODRHash(Args); + return StableHashForTemplateArguments(Args); } } // namespace @@ -4254,11 +4257,23 @@ void ASTWriter::GenerateSpecializationInfoLookupTable( Iter->second.push_back(cast<NamedDecl>(Specialization)); } - for (auto Iter : SpecializationMaps) - Generator.insert(Iter.first, Trait.getData(Iter.second), Trait); - auto *Lookups = Chain ? Chain->getLoadedSpecializationsLookupTables(D) : nullptr; + + for (auto &[HashValue, Specs] : SpecializationMaps) { + SmallVector<serialization::reader::LazySpecializationInfo, 16> ExisitingSpecs; + // We have to merge the lookup table manually here. We can't depend on the merge mechanism + // offered by clang::serialization::MultiOnDiskHashTableGenerator since that generator + // assumes the we'll get the same value with the same key. + // And also underlying llvm::OnDiskChainedHashTableGenerator assumes that we won't insert + // the values with the same key twice. + // So we have to merge the lookup table here manually. + if (Lookups) + ExisitingSpecs = Lookups->Table.find(HashValue); + + Generator.insert(HashValue, Trait.getData(Specs, ExisitingSpecs), Trait); + } + Generator.emit(LookupTable, Trait, Lookups ? &Lookups->Table : nullptr); } @@ -5937,7 +5952,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)); @@ -5950,7 +5965,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); } } @@ -5987,25 +6003,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 d2766ceae9b8c9..862e476d962bf6 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,23 +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; @@ -292,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)); } @@ -329,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 99c47c15a2f479..b1fc0345047f24 100644 --- a/clang/lib/Serialization/CMakeLists.txt +++ b/clang/lib/Serialization/CMakeLists.txt @@ -23,6 +23,7 @@ add_clang_library(clangSerialization ModuleManager.cpp PCHContainerOperations.cpp ObjectFilePCHContainerReader.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..ab7ea262cddf61 --- /dev/null +++ b/clang/lib/Serialization/TemplateArgumentHasher.cpp @@ -0,0 +1,405 @@ +//===- 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); +} + +} // namespace + +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..f23f1318afbbf4 --- /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); + +} // namespace serialization +} // namespace clang diff --git a/clang/test/Modules/cxx-templates.cpp b/clang/test/Modules/cxx-templates.cpp index f0d38f1702549e..44b02ad3ec748c 100644 --- a/clang/test/Modules/cxx-templates.cpp +++ b/clang/test/Modules/cxx-templates.cpp @@ -249,7 +249,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 @@ -258,9 +258,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 @@ -269,4 +269,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 8338f2a68f9228..cc47ae92efac0f 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) { _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits