diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 7d707ed45cf5f..dd92d40b80423 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -262,9 +262,6 @@ class TemplateArgumentList final TemplateArgumentList(const TemplateArgumentList &) = delete; TemplateArgumentList &operator=(const TemplateArgumentList &) = delete; - /// Create hash for the given arguments. - static unsigned ComputeODRHash(ArrayRef Args); - /// Create a new template argument list that copies the given set of /// template arguments. static TemplateArgumentList *CreateCopy(ASTContext &Context, @@ -738,25 +735,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 struct SpecEntryTraits { @@ -800,16 +778,20 @@ class RedeclarableTemplateDecl : public TemplateDecl, void loadLazySpecializationsImpl(bool OnlyPartial = false) const; - void loadLazySpecializationsImpl(llvm::ArrayRef Args, + bool loadLazySpecializationsImpl(llvm::ArrayRef Args, TemplateParameterList *TPL = nullptr) const; - Decl *loadLazySpecializationImpl(LazySpecializationInfo &LazySpecInfo) const; - template typename SpecEntryTraits::DeclType* findSpecializationImpl(llvm::FoldingSetVector &Specs, void *&InsertPos, ProfileArguments &&...ProfileArgs); + template + typename SpecEntryTraits::DeclType * + findSpecializationLocally(llvm::FoldingSetVector &Specs, + void *&InsertPos, + ProfileArguments &&...ProfileArgs); + template void addSpecializationImpl(llvm::FoldingSetVector &Specs, EntryType *Entry, void *InsertPos); @@ -824,13 +806,6 @@ class RedeclarableTemplateDecl : public TemplateDecl, /// was explicitly specialized. llvm::PointerIntPair 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; }; /// Pointer to the common data shared by all declarations of this diff --git a/clang/include/clang/AST/ExternalASTSource.h b/clang/include/clang/AST/ExternalASTSource.h index 5f4f9a9a8d681..9f968ba05b446 100644 --- a/clang/include/clang/AST/ExternalASTSource.h +++ b/clang/include/clang/AST/ExternalASTSource.h @@ -155,11 +155,15 @@ class ExternalASTSource : public RefCountedBase { /// 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 TemplateArgs); diff --git a/clang/include/clang/Sema/MultiplexExternalSemaSource.h b/clang/include/clang/Sema/MultiplexExternalSemaSource.h index 78bbbaf2d7b5c..0c92c52854c9e 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 TemplateArgs) override; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index cb3ed6c1ecbb7..e2a165055a442 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -737,6 +737,10 @@ enum ASTRecordTypes { /// Record code for updated specialization UPDATE_SPECIALIZATION = 73, + + CXX_ADDED_TEMPLATE_SPECIALIZATION = 74, + + CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION = 75, }; /// Record types used within a source manager block. @@ -1503,11 +1507,14 @@ enum DeclCode { /// A HLSLBufferDecl record. DECL_HLSL_BUFFER, + /// An ImplicitConceptSpecializationDecl record. + DECL_IMPLICIT_CONCEPT_SPECIALIZATION, + // A decls specilization record. DECL_SPECIALIZATIONS, - /// An ImplicitConceptSpecializationDecl record. - DECL_IMPLICIT_CONCEPT_SPECIALIZATION, + // A decls specilization record. + DECL_PARTIAL_SPECIALIZATIONS, DECL_LAST = DECL_IMPLICIT_CONCEPT_SPECIALIZATION }; diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index 6306d4f08e81f..fb750175baf2c 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -635,10 +635,19 @@ class ASTReader llvm::DenseMap Lookups; + using SpecLookupTableTy = + llvm::DenseMap; /// Map from decls to specialized decls. - llvm::DenseMap - SpecializationsLookups; + SpecLookupTableTy SpecializationsLookups; + /// Split partial specialization from specialization to speed up lookups. + SpecLookupTableTy PartialSpecializationsLookups; + + bool LoadExternalSpecializationsImpl(SpecLookupTableTy &SpecLookups, + const Decl *D); + bool LoadExternalSpecializationsImpl(SpecLookupTableTy &SpecLookups, + const Decl *D, + ArrayRef TemplateArgs); // Updates for visible decls can occur for other contexts than just the // TU, and when we read those update records, the actual context may not @@ -655,8 +664,10 @@ class ASTReader llvm::DenseMap PendingVisibleUpdates; using SpecializationsUpdate = SmallVector; - llvm::DenseMap - PendingSpecializationsUpdates; + using SpecializationsUpdateMap = + llvm::DenseMap; + SpecializationsUpdateMap PendingSpecializationsUpdates; + SpecializationsUpdateMap PendingPartialSpecializationsUpdates; /// The set of C++ or Objective-C classes that have forward /// declarations that have not yet been linked to their definitions. @@ -691,9 +702,9 @@ class ASTReader uint64_t Offset, GlobalDeclID ID); bool ReadSpecializations(ModuleFile &M, llvm::BitstreamCursor &Cursor, - uint64_t Offset, Decl *D); + uint64_t Offset, Decl *D, bool IsPartial); void AddSpecializations(const Decl *D, const unsigned char *Data, - ModuleFile &M); + ModuleFile &M, bool IsPartial); /// A vector containing identifiers that have already been /// loaded. @@ -1439,7 +1450,10 @@ class ASTReader /// Get the loaded specializations lookup tables for \p D, /// if any. serialization::reader::LazySpecializationInfoLookupTable * - getLoadedSpecializationsLookupTables(const Decl *D); + getLoadedSpecializationsLookupTables(const Decl *D, bool IsPartial); + + /// If we have any unloaded specialization for \p D + bool haveUnloadedSpecializations(const Decl *D) const; private: struct ImportedModule { @@ -2098,9 +2112,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 TemplateArgs) override; diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index 9da22f96130a8..cdbcc96b9a470 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -428,6 +428,7 @@ class ASTWriter : public ASTDeserializationListener, using SpecializationUpdateMap = llvm::MapVector>; SpecializationUpdateMap SpecializationsUpdates; + SpecializationUpdateMap PartialSpecializationsUpdates; using FirstLatestDeclMap = llvm::DenseMap; @@ -580,9 +581,10 @@ class ASTWriter : public ASTDeserializationListener, void GenerateSpecializationInfoLookupTable( const NamedDecl *D, llvm::SmallVectorImpl &Specializations, - llvm::SmallVectorImpl &LookupTable); + llvm::SmallVectorImpl &LookupTable, bool IsPartial); uint64_t WriteSpecializationInfoLookupTable( - const NamedDecl *D, llvm::SmallVectorImpl &Specializations); + const NamedDecl *D, llvm::SmallVectorImpl &Specializations, + bool IsPartial); void GenerateNameLookupTable(ASTContext &Context, const DeclContext *DC, llvm::SmallVectorImpl &LookupTable); uint64_t WriteDeclContextLexicalBlock(ASTContext &Context, @@ -598,7 +600,7 @@ class ASTWriter : public ASTDeserializationListener, void WriteDeclAndTypes(ASTContext &Context); void PrepareWritingSpecialDecls(Sema &SemaRef); void WriteSpecialDeclRecords(Sema &SemaRef); - void WriteSpecializationsUpdates(); + void WriteSpecializationsUpdates(bool IsPartial); void WriteDeclUpdatesBlocks(ASTContext &Context, RecordDataImpl &OffsetsRecord); void WriteDeclContextVisibleUpdate(ASTContext &Context, @@ -629,6 +631,7 @@ class ASTWriter : public ASTDeserializationListener, unsigned DeclObjCIvarAbbrev = 0; unsigned DeclCXXMethodAbbrev = 0; unsigned DeclSpecializationsAbbrev = 0; + unsigned DeclPartialSpecializationsAbbrev = 0; unsigned DeclDependentNonTemplateCXXMethodAbbrev = 0; unsigned DeclTemplateCXXMethodAbbrev = 0; diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index a73af9c778532..7fc0f02ce0a93 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -362,63 +362,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 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 +template typename RedeclarableTemplateDecl::SpecEntryTraits::DeclType * -RedeclarableTemplateDecl::findSpecializationImpl( +RedeclarableTemplateDecl::findSpecializationLocally( llvm::FoldingSetVector &Specs, void *&InsertPos, - ProfileArguments&&... ProfileArgs) { - using SETraits = SpecEntryTraits; - - loadLazySpecializationsImpl(std::forward(ProfileArgs)...); + ProfileArguments &&...ProfileArgs) { + using SETraits = RedeclarableTemplateDecl::SpecEntryTraits; llvm::FoldingSetNodeID ID; EntryType::Profile(ID, std::forward(ProfileArgs)..., @@ -427,6 +394,24 @@ RedeclarableTemplateDecl::findSpecializationImpl( return Entry ? SETraits::getDecl(Entry)->getMostRecentDecl() : nullptr; } +template +typename RedeclarableTemplateDecl::SpecEntryTraits::DeclType * +RedeclarableTemplateDecl::findSpecializationImpl( + llvm::FoldingSetVector &Specs, void *&InsertPos, + ProfileArguments &&...ProfileArgs) { + + if (auto *Found = findSpecializationLocally( + Specs, InsertPos, std::forward(ProfileArgs)...)) + return Found; + + if (!loadLazySpecializationsImpl( + std::forward(ProfileArgs)...)) + return nullptr; + + return findSpecializationLocally( + Specs, InsertPos, std::forward(ProfileArgs)...); +} + template void RedeclarableTemplateDecl::addSpecializationImpl( llvm::FoldingSetVector &Specializations, EntryType *Entry, @@ -955,14 +940,6 @@ TemplateArgumentList::CreateCopy(ASTContext &Context, return new (Mem) TemplateArgumentList(Args); } -unsigned TemplateArgumentList::ComputeODRHash(ArrayRef 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 122014bfeb232..0eeb549d8cabf 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) {} +bool ExternalASTSource::LoadExternalSpecializations( + const Decl *D, ArrayRef) { + return false; +} void ExternalASTSource::completeVisibleDeclsMap(const DeclContext *DC) {} diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp index a8a3a5200d61e..c73570e405ab6 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(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 f39463712ce81..54944267b4868 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 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 2a765eafe0895..7c9ec884ea049 100644 --- a/clang/lib/Serialization/ASTCommon.h +++ b/clang/lib/Serialization/ASTCommon.h @@ -24,7 +24,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 75d81a25dd3ac..ce6c879127918 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" @@ -1310,13 +1311,11 @@ void LazySpecializationInfoLookupTrait::ReadDataInto(internal_key_type, using namespace llvm::support; for (unsigned NumDecls = - DataLen / serialization::reader::LazySpecializationInfo::Length; + DataLen / sizeof(serialization::reader::LazySpecializationInfo); NumDecls; --NumDecls) { LocalDeclID LocalID = LocalDeclID::get(Reader, F, endian::readNext(d)); - const bool IsPartial = - endian::readNext(d); - Val.insert({Reader.getGlobalDeclID(F, LocalID), IsPartial}); + Val.insert(Reader.getGlobalDeclID(F, LocalID)); } } @@ -1409,14 +1408,16 @@ bool ASTReader::ReadVisibleDeclContextStorage(ModuleFile &M, } void ASTReader::AddSpecializations(const Decl *D, const unsigned char *Data, - ModuleFile &M) { + ModuleFile &M, bool IsPartial) { D = D->getCanonicalDecl(); - SpecializationsLookups[D].Table.add( - &M, Data, reader::LazySpecializationInfoLookupTrait(*this, M)); + auto &SpecLookups = + IsPartial ? PartialSpecializationsLookups : SpecializationsLookups; + SpecLookups[D].Table.add(&M, Data, + reader::LazySpecializationInfoLookupTrait(*this, M)); } bool ASTReader::ReadSpecializations(ModuleFile &M, BitstreamCursor &Cursor, - uint64_t Offset, Decl *D) { + uint64_t Offset, Decl *D, bool IsPartial) { assert(Offset != 0); SavedStreamPosition SavedPosition(Cursor); @@ -1440,13 +1441,14 @@ bool ASTReader::ReadSpecializations(ModuleFile &M, BitstreamCursor &Cursor, return true; } unsigned RecCode = MaybeRecCode.get(); - if (RecCode != DECL_SPECIALIZATIONS) { + if (RecCode != DECL_SPECIALIZATIONS && + RecCode != DECL_PARTIAL_SPECIALIZATIONS) { Error("Expected decl specs block"); return true; } auto *Data = (const unsigned char *)Blob.data(); - AddSpecializations(D, Data, M); + AddSpecializations(D, Data, M, IsPartial); return false; } @@ -3542,7 +3544,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(); @@ -3555,6 +3557,19 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, break; } + case CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION: { + unsigned Idx = 0; + GlobalDeclID ID = ReadDeclID(F, Record, Idx); + auto *Data = (const unsigned char *)Blob.data(); + PendingPartialSpecializationsUpdates[ID].push_back(UpdateData{&F, Data}); + // If we've already loaded the decl, perform the updates when we finish + // loading this block. + if (Decl *D = GetExistingDecl(ID)) + PendingUpdateRecords.push_back( + PendingUpdateRecord(ID, D, /*JustLoaded=*/false)); + break; + } + case IDENTIFIER_TABLE: F.IdentifierTableData = reinterpret_cast(Blob.data()); @@ -7747,8 +7762,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(D)) + Template->loadLazySpecializationsImpl(); + else + Template->loadLazySpecializationsImpl(Args); + } } CXXCtorInitializer ** @@ -8129,12 +8150,13 @@ Stmt *ASTReader::GetExternalDeclStmt(uint64_t Offset) { return ReadStmtFromStream(*Loc.F); } -void ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) { +bool ASTReader::LoadExternalSpecializationsImpl(SpecLookupTableTy &SpecLookups, + const Decl *D) { assert(D); - auto It = SpecializationsLookups.find(D); - if (It == SpecializationsLookups.end()) - return; + auto It = SpecLookups.find(D); + if (It == SpecLookups.end()) + return false; // Get Decl may violate the iterator from SpecializationsLookups so we store // the DeclIDs in ahead. @@ -8143,32 +8165,69 @@ void ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) { // Since we've loaded all the specializations, we can erase it from // the lookup table. - if (!OnlyPartial) - SpecializationsLookups.erase(It); + SpecLookups.erase(It); + bool NewSpecsFound = false; Deserializing LookupResults(this); - for (auto &Info : Infos) - if (!OnlyPartial || Info.IsPartial) - GetDecl(Info.ID); + for (auto &Info : Infos) { + if (GetExistingDecl(Info)) + continue; + NewSpecsFound = true; + GetDecl(Info); + } + + return NewSpecsFound; } -void ASTReader::LoadExternalSpecializations( - const Decl *D, ArrayRef TemplateArgs) { +bool ASTReader::LoadExternalSpecializations(const Decl *D, bool OnlyPartial) { assert(D); - auto It = SpecializationsLookups.find(D); - if (It == SpecializationsLookups.end()) - return; + bool NewSpecsFound = + LoadExternalSpecializationsImpl(PartialSpecializationsLookups, D); + if (OnlyPartial) + return NewSpecsFound; + + NewSpecsFound |= LoadExternalSpecializationsImpl(SpecializationsLookups, D); + return NewSpecsFound; +} + +bool ASTReader::LoadExternalSpecializationsImpl( + SpecLookupTableTy &SpecLookups, const Decl *D, + ArrayRef TemplateArgs) { + assert(D); + + auto It = SpecLookups.find(D); + if (It == SpecLookups.end()) + return false; Deserializing LookupResults(this); - auto HashValue = TemplateArgumentList::ComputeODRHash(TemplateArgs); + auto HashValue = StableHashForTemplateArguments(TemplateArgs); - // Get Decl may violate the iterator from SpecializationsLookups + // Get Decl may violate the iterator from SpecLookups llvm::SmallVector Infos = It->second.Table.find(HashValue); - for (auto &Info : Infos) - GetDecl(Info.ID); + bool NewSpecsFound = false; + for (auto &Info : Infos) { + if (GetExistingDecl(Info)) + continue; + NewSpecsFound = true; + GetDecl(Info); + } + + return NewSpecsFound; +} + +bool ASTReader::LoadExternalSpecializations( + const Decl *D, ArrayRef TemplateArgs) { + assert(D); + + bool NewDeclsFound = LoadExternalSpecializationsImpl( + PartialSpecializationsLookups, D, TemplateArgs); + NewDeclsFound |= + LoadExternalSpecializationsImpl(SpecializationsLookups, D, TemplateArgs); + + return NewDeclsFound; } void ASTReader::FindExternalLexicalDecls( @@ -8351,10 +8410,19 @@ ASTReader::getLoadedLookupTables(DeclContext *Primary) const { } serialization::reader::LazySpecializationInfoLookupTable * -ASTReader::getLoadedSpecializationsLookupTables(const Decl *D) { +ASTReader::getLoadedSpecializationsLookupTables(const Decl *D, bool IsPartial) { + assert(D->isCanonicalDecl()); + auto &LookupTable = + IsPartial ? PartialSpecializationsLookups : SpecializationsLookups; + auto I = LookupTable.find(D); + return I == LookupTable.end() ? nullptr : &I->second; +} + +bool ASTReader::haveUnloadedSpecializations(const Decl *D) const { assert(D->isCanonicalDecl()); - auto I = SpecializationsLookups.find(D); - return I == SpecializationsLookups.end() ? nullptr : &I->second; + return (PartialSpecializationsLookups.find(D) != + PartialSpecializationsLookups.end()) || + (SpecializationsLookups.find(D) != SpecializationsLookups.end()); } /// Under non-PCH compilation the consumer receives the objc methods diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 87c491a977ffe..bd201e6cb14c1 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -187,21 +187,6 @@ class ASTDeclReader : public DeclVisitor { 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 &IDs) { - for (unsigned I = 0, Size = Record.readInt(); I != Size; ++I) - IDs.push_back(ReadLazySpecializationInfo()); - } - Decl *readDecl() { return Record.readDecl(); } template T *readDeclAs() { return Record.readDeclAs(); } @@ -294,30 +279,6 @@ class ASTDeclReader : public DeclVisitor { : Reader(Reader), MergeImpl(Reader), Record(Record), Loc(Loc), ThisDeclID(thisDeclID), ThisDeclLoc(ThisDeclLoc) {} - template - static void - AddLazySpecializations(T *D, SmallVectorImpl &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 static Decl *getMostRecentDeclImpl(Redeclarable *D); static Decl *getMostRecentDeclImpl(...); @@ -343,14 +304,12 @@ class ASTDeclReader : public DeclVisitor { static void markIncompleteDeclChainImpl(...); void ReadSpecializations(ModuleFile &M, Decl *D, - llvm::BitstreamCursor &DeclsCursor); + llvm::BitstreamCursor &DeclsCursor, bool IsPartial); void ReadFunctionDefinition(FunctionDecl *FD); void Visit(Decl *D); - void UpdateDecl( - Decl *D, - SmallVectorImpl &); + void UpdateDecl(Decl *D); static void setNextObjCCategory(ObjCCategoryDecl *Cat, ObjCCategoryDecl *Next) { @@ -2434,9 +2393,11 @@ void ASTDeclReader::VisitRequiresExprBodyDecl(RequiresExprBodyDecl *D) { } void ASTDeclReader::ReadSpecializations(ModuleFile &M, Decl *D, - llvm::BitstreamCursor &DeclsCursor) { + llvm::BitstreamCursor &DeclsCursor, + bool IsPartial) { uint64_t Offset = ReadLocalOffset(); - bool Failed = Reader.ReadSpecializations(M, DeclsCursor, Offset, D); + bool Failed = + Reader.ReadSpecializations(M, DeclsCursor, Offset, D, IsPartial); (void)Failed; assert(!Failed); } @@ -2479,10 +2440,8 @@ 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 SpecIDs; - readDeclIDList(SpecIDs); - ASTDeclReader::AddLazySpecializations(D, SpecIDs); - ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor); + ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor, /*IsPartial=*/false); + ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor, /*IsPartial=*/true); } if (D->getTemplatedDecl()->TemplateOrInstantiation) { @@ -2508,10 +2467,8 @@ 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 SpecIDs; - readDeclIDList(SpecIDs); - ASTDeclReader::AddLazySpecializations(D, SpecIDs); - ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor); + ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor, /*IsPartial=*/false); + ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor, /*IsPartial=*/true); } } @@ -2610,10 +2567,7 @@ void ASTDeclReader::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { if (ThisDeclID == Redecl.getFirstID()) { // This FunctionTemplateDecl owns a CommonPtr; read it. - SmallVector SpecIDs; - readDeclIDList(SpecIDs); - ASTDeclReader::AddLazySpecializations(D, SpecIDs); - ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor); + ReadSpecializations(*Loc.F, D, Loc.F->DeclsCursor, /*IsPartial=*/false); } } @@ -3904,6 +3858,7 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) { case DECL_CONTEXT_LEXICAL: case DECL_CONTEXT_VISIBLE: case DECL_SPECIALIZATIONS: + case DECL_PARTIAL_SPECIALIZATIONS: llvm_unreachable("Record cannot be de-serialized with readDeclRecord"); case DECL_TYPEDEF: D = TypedefDecl::CreateDeserialized(Context, ID); @@ -4313,10 +4268,6 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { ProcessingUpdatesRAIIObj ProcessingUpdates(*this); DeclUpdateOffsetsMap::iterator UpdI = DeclUpdateOffsets.find(ID); - using LazySpecializationInfo = - RedeclarableTemplateDecl::LazySpecializationInfo; - llvm::SmallVector PendingLazySpecializationIDs; - if (UpdI != DeclUpdateOffsets.end()) { auto UpdateOffsets = std::move(UpdI->second); DeclUpdateOffsets.erase(UpdI); @@ -4353,7 +4304,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. @@ -4363,19 +4314,6 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { } } } - // Add the lazy specializations to the template. - assert((PendingLazySpecializationIDs.empty() || isa(D) || - isa(D)) && - "Must not have pending specializations"); - /* - if (auto *CTD = dyn_cast(D)) - ASTDeclReader::AddLazySpecializations(CTD, PendingLazySpecializationIDs); - else if (auto *FTD = dyn_cast(D)) - ASTDeclReader::AddLazySpecializations(FTD, PendingLazySpecializationIDs); - else if (auto *VTD = dyn_cast(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); @@ -4408,7 +4346,17 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { PendingSpecializationsUpdates.erase(I); for (const auto &Update : SpecializationUpdates) - AddSpecializations(D, Update.Data, *Update.Mod); + AddSpecializations(D, Update.Data, *Update.Mod, /*IsPartial=*/false); + } + + // Load the pending specializations update for this decl, if it has any. + if (auto I = PendingPartialSpecializationsUpdates.find(ID); + I != PendingPartialSpecializationsUpdates.end()) { + auto SpecializationUpdates = std::move(I->second); + PendingPartialSpecializationsUpdates.erase(I); + + for (const auto &Update : SpecializationUpdates) + AddSpecializations(D, Update.Data, *Update.Mod, /*IsPartial=*/true); } } @@ -4602,9 +4550,7 @@ static void forAllLaterRedecls(DeclT *D, Fn F) { } } -void ASTDeclReader::UpdateDecl( - Decl *D, - SmallVectorImpl &PendingLazySpecializationIDs) { +void ASTDeclReader::UpdateDecl(Decl *D) { while (Record.getIdx() < Record.size()) { switch ((DeclUpdateKind)Record.readInt()) { case UPD_CXX_ADDED_IMPLICIT_MEMBER: { @@ -4615,11 +4561,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(); diff --git a/clang/lib/Serialization/ASTReaderInternals.h b/clang/lib/Serialization/ASTReaderInternals.h index b921d8d174c3a..be0d22d1f4094 100644 --- a/clang/lib/Serialization/ASTReaderInternals.h +++ b/clang/lib/Serialization/ASTReaderInternals.h @@ -119,21 +119,7 @@ struct DeclContextLookupTable { MultiOnDiskHashTable Table; }; -struct LazySpecializationInfo { - // The Decl ID for the specialization. - GlobalDeclID ID; - // Whether or not this specialization is partial. - bool IsPartial; - - bool operator==(const LazySpecializationInfo &Other) { - 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); -}; +using LazySpecializationInfo = GlobalDeclID; /// Class that performs lookup to specialized decls. class LazySpecializationInfoLookupTrait { @@ -384,32 +370,4 @@ using HeaderFileInfoLookupTable = } // namespace clang -namespace llvm { -// ID is unique in LazySpecializationInfo, it is redundant to calculate -// IsPartial. -template <> -struct DenseMapInfo { - using LazySpecializationInfo = - clang::serialization::reader::LazySpecializationInfo; - using Wrapped = DenseMapInfo; - - static inline LazySpecializationInfo getEmptyKey() { - return {(clang::GlobalDeclID)Wrapped::getEmptyKey(), false}; - } - - static inline LazySpecializationInfo getTombstoneKey() { - return {(clang::GlobalDeclID)Wrapped::getTombstoneKey(), false}; - } - - static unsigned getHashValue(const LazySpecializationInfo &Key) { - return Wrapped::getHashValue(Key.ID.getRawValue()); - } - - static bool isEqual(const LazySpecializationInfo &LHS, - const LazySpecializationInfo &RHS) { - return LHS.ID == RHS.ID; - } -}; -} // end namespace llvm - #endif // LLVM_CLANG_LIB_SERIALIZATION_ASTREADERINTERNALS_H diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index adfe9fc8b369d..648096387578b 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" @@ -4125,16 +4126,15 @@ class LazySpecializationInfoLookupTrait { explicit LazySpecializationInfoLookupTrait(ASTWriter &Writer) : Writer(Writer) {} - template data_type getData(Col &&C) { + template data_type getData(Col &&C, Col2 &ExistingInfo) { unsigned Start = Specs.size(); for (auto *D : C) { - bool IsPartial = isa(D); NamedDecl *ND = getDeclForLocalLookup( Writer.getLangOpts(), const_cast(D)); - Specs.push_back({GlobalDeclID(Writer.GetDeclRef(ND).getRawValue()), - IsPartial}); + Specs.push_back(GlobalDeclID(Writer.GetDeclRef(ND).getRawValue())); } + for (const serialization::reader::LazySpecializationInfo &Info : ExistingInfo) + Specs.push_back(Info); return std::make_pair(Start, Specs.size()); } @@ -4164,7 +4164,7 @@ class LazySpecializationInfoLookupTrait { data_type_ref Lookup) { // 4 bytes for each slot. unsigned KeyLen = 4; - unsigned DataLen = serialization::reader::LazySpecializationInfo::Length * + unsigned DataLen = sizeof(serialization::reader::LazySpecializationInfo) * (Lookup.second - Lookup.first); return emitULEBKeyDataLength(KeyLen, DataLen, Out); @@ -4185,8 +4185,7 @@ class LazySpecializationInfoLookupTrait { uint64_t Start = Out.tell(); (void)Start; for (unsigned I = Lookup.first, N = Lookup.second; I != N; ++I) { - LE.write(Specs[I].ID.getRawValue()); - LE.write(Specs[I].IsPartial); + LE.write(Specs[I].getRawValue()); } assert(Out.tell() - Start == DataLen && "Data length is wrong"); } @@ -4203,13 +4202,13 @@ unsigned CalculateODRHashForSpecs(const Decl *Spec) { else llvm_unreachable("New Specialization Kind?"); - return TemplateArgumentList::ComputeODRHash(Args); + return StableHashForTemplateArguments(Args); } } // namespace void ASTWriter::GenerateSpecializationInfoLookupTable( const NamedDecl *D, llvm::SmallVectorImpl &Specializations, - llvm::SmallVectorImpl &LookupTable) { + llvm::SmallVectorImpl &LookupTable, bool IsPartial) { assert(D->isFirstDecl()); // Create the on-disk hash table representation. @@ -4234,23 +4233,41 @@ void ASTWriter::GenerateSpecializationInfoLookupTable( Iter->second.push_back(cast(Specialization)); } - for (auto Iter : SpecializationMaps) - Generator.insert(Iter.first, Trait.getData(Iter.second), Trait); - auto *Lookups = - Chain ? Chain->getLoadedSpecializationsLookupTables(D) : nullptr; + Chain ? Chain->getLoadedSpecializationsLookupTables(D, IsPartial) + : nullptr; + + for (auto &[HashValue, Specs] : SpecializationMaps) { + SmallVector 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); } uint64_t ASTWriter::WriteSpecializationInfoLookupTable( - const NamedDecl *D, llvm::SmallVectorImpl &Specializations) { + const NamedDecl *D, llvm::SmallVectorImpl &Specializations, + bool IsPartial) { llvm::SmallString<4096> LookupTable; - GenerateSpecializationInfoLookupTable(D, Specializations, LookupTable); + GenerateSpecializationInfoLookupTable(D, Specializations, LookupTable, + IsPartial); uint64_t Offset = Stream.GetCurrentBitNo(); - RecordData::value_type Record[] = {DECL_SPECIALIZATIONS}; - Stream.EmitRecordWithBlob(DeclSpecializationsAbbrev, Record, LookupTable); + RecordData::value_type Record[] = {IsPartial ? DECL_PARTIAL_SPECIALIZATIONS + : DECL_SPECIALIZATIONS}; + Stream.EmitRecordWithBlob(IsPartial ? DeclPartialSpecializationsAbbrev + : DeclSpecializationsAbbrev, + Record, LookupTable); return Offset; } @@ -5873,8 +5890,15 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) { FunctionToLambdaMapAbbrev); } - if (!SpecializationsUpdates.empty()) - WriteSpecializationsUpdates(); + if (!SpecializationsUpdates.empty()) { + WriteSpecializationsUpdates(/*IsPartial=*/false); + SpecializationsUpdates.clear(); + } + + if (!PartialSpecializationsUpdates.empty()) { + WriteSpecializationsUpdates(/*IsPartial=*/true); + PartialSpecializationsUpdates.clear(); + } const TranslationUnitDecl *TU = Context.getTranslationUnitDecl(); // Create a lexical update block containing all of the declarations in the @@ -5919,22 +5943,27 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) { WriteDeclContextVisibleUpdate(Context, DC); } -void ASTWriter::WriteSpecializationsUpdates() { +void ASTWriter::WriteSpecializationsUpdates(bool IsPartial) { + auto RecordType = IsPartial ? CXX_ADDED_TEMPLATE_PARTIAL_SPECIALIZATION + : CXX_ADDED_TEMPLATE_SPECIALIZATION; + auto Abv = std::make_shared(); - Abv->Add(llvm::BitCodeAbbrevOp(UPDATE_SPECIALIZATION)); + Abv->Add(llvm::BitCodeAbbrevOp(RecordType)); Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6)); Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)); auto UpdateSpecializationAbbrev = Stream.EmitAbbrev(std::move(Abv)); - for (auto &SpecializationUpdate : SpecializationsUpdates) { + auto &SpecUpdates = + IsPartial ? PartialSpecializationsUpdates : SpecializationsUpdates; + for (auto &SpecializationUpdate : SpecUpdates) { const NamedDecl *D = SpecializationUpdate.first; llvm::SmallString<4096> LookupTable; GenerateSpecializationInfoLookupTable(D, SpecializationUpdate.second, - LookupTable); + LookupTable, IsPartial); // Write the lookup table - RecordData::value_type Record[] = {UPDATE_SPECIALIZATION, getDeclID(D).getRawValue()}; + RecordData::value_type Record[] = {RecordType, getDeclID(D).getRawValue()}; Stream.EmitRecordWithBlob(UpdateSpecializationAbbrev, Record, LookupTable); } } @@ -5972,25 +6001,6 @@ void ASTWriter::WriteDeclUpdatesBlocks(ASTContext &Context, 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 Args; - if (auto *CTSD = dyn_cast(Spec)) - Args = CTSD->getTemplateArgs().asArray(); - else if (auto *VTSD = dyn_cast(Spec)) - Args = VTSD->getTemplateArgs().asArray(); - else if (auto *FD = dyn_cast(Spec)) - Args = FD->getTemplateSpecializationArgs()->asArray(); - assert(Args.size()); - Record.push_back(TemplateArgumentList::ComputeODRHash(Args)); - bool IsPartialSpecialization = - isa(Spec) || - isa(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 14c2f23700e8c..b3119607a1404 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -208,7 +208,8 @@ namespace clang { /// ODRHash of the template arguments of D which should provide enough /// information to load D only if the template instantiator needs it. void AddFirstSpecializationDeclFromEachModule( - const Decl *D, llvm::SmallVectorImpl &SpecsInMap) { + const Decl *D, llvm::SmallVectorImpl &SpecsInMap, + llvm::SmallVectorImpl &PartialSpecsInMap) { assert((isa(D) || isa(D) || isa(D)) && "Must not be called with other decls"); @@ -216,22 +217,11 @@ namespace clang { CollectFirstDeclFromEachModule(D, /*IncludeLocal*/ true, Firsts); for (const auto &F : Firsts) { - SpecsInMap.push_back(F.second); - - Record.AddDeclRef(F.second); - ArrayRef Args; - if (auto *CTSD = dyn_cast(D)) - Args = CTSD->getTemplateArgs().asArray(); - else if (auto *VTSD = dyn_cast(D)) - Args = VTSD->getTemplateArgs().asArray(); - else if (auto *FD = dyn_cast(D)) - Args = FD->getTemplateSpecializationArgs()->asArray(); - assert(Args.size()); - Record.push_back(TemplateArgumentList::ComputeODRHash(Args)); - bool IsPartialSpecialization = - isa(D) || - isa(D); - Record.push_back(IsPartialSpecialization); + if (isa(F.second)) + PartialSpecsInMap.push_back(F.second); + else + SpecsInMap.push_back(F.second); } } @@ -260,54 +250,37 @@ namespace clang { // our chained AST reader, we can just write out the DeclIDs. Otherwise, // we need to resolve them to actual declarations. if (Writer.Chain != Record.getASTContext().getExternalSource() && - Common->LazySpecializations) { + Writer.Chain && Writer.Chain->haveUnloadedSpecializations(D)) { D->LoadLazySpecializations(); - assert(!Common->LazySpecializations); + assert(!Writer.Chain->haveUnloadedSpecializations(D)); } - using LazySpecializationInfo = - RedeclarableTemplateDecl::LazySpecializationInfo; - ArrayRef 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 Specs; + // AddFirstSpecializationDeclFromEachModule might trigger deserialization, + // invalidating *Specializations iterators. + llvm::SmallVector AllSpecs; for (auto &Entry : Common->Specializations) - Specs.push_back(getSpecializationDecl(Entry)); + AllSpecs.push_back(getSpecializationDecl(Entry)); for (auto &Entry : getPartialSpecializations(Common)) - Specs.push_back(getSpecializationDecl(Entry)); + AllSpecs.push_back(getSpecializationDecl(Entry)); - llvm::SmallVector SpecsInOnDiskMap = Specs; - - for (auto *D : Specs) { + llvm::SmallVector Specs; + llvm::SmallVector PartialSpecs; + for (auto *D : AllSpecs) { assert(D->isCanonicalDecl() && "non-canonical decl in set"); - AddFirstSpecializationDeclFromEachModule(D, SpecsInOnDiskMap); + AddFirstSpecializationDeclFromEachModule(D, Specs, PartialSpecs); } - // 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); - } + Record.AddOffset(Writer.WriteSpecializationInfoLookupTable( + D, Specs, /*IsPartial=*/false)); - // 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; + // Function Template Decl doesn't have partial decls. + if (isa(D)) { + assert(PartialSpecs.empty()); + return; + } - Record.AddOffset( - Writer.WriteSpecializationInfoLookupTable(D, SpecsInOnDiskMap)); + Record.AddOffset(Writer.WriteSpecializationInfoLookupTable( + D, PartialSpecs, /*IsPartial=*/true)); } /// Ensure that this template specialization is associated with the specified @@ -328,11 +301,13 @@ namespace clang { if (Writer.getFirstLocalDecl(Specialization) != Specialization) return; - Writer.DeclUpdates[Template].push_back(ASTWriter::DeclUpdate( - UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION, Specialization)); - - Writer.SpecializationsUpdates[cast(Template)].push_back( - cast(Specialization)); + if (isa(Specialization)) + Writer.PartialSpecializationsUpdates[cast(Template)] + .push_back(cast(Specialization)); + else + Writer.SpecializationsUpdates[cast(Template)].push_back( + cast(Specialization)); } }; } @@ -2842,6 +2817,11 @@ void ASTWriter::WriteDeclAbbrevs() { Abv->Add(BitCodeAbbrevOp(serialization::DECL_SPECIALIZATIONS)); Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); DeclSpecializationsAbbrev = Stream.EmitAbbrev(std::move(Abv)); + + Abv = std::make_shared(); + Abv->Add(BitCodeAbbrevOp(serialization::DECL_PARTIAL_SPECIALIZATIONS)); + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + DeclPartialSpecializationsAbbrev = Stream.EmitAbbrev(std::move(Abv)); } /// isRequiredDecl - Check if this is a "required" Decl, which must be seen by diff --git a/clang/lib/Serialization/CMakeLists.txt b/clang/lib/Serialization/CMakeLists.txt index 99c47c15a2f47..b1fc0345047f2 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 0000000000000..598f098f526d0 --- /dev/null +++ b/clang/lib/Serialization/TemplateArgumentHasher.cpp @@ -0,0 +1,409 @@ +//===- 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; + } + case TemplateName::DeducedTemplate: + AddTemplateName(Name.getAsDeducedTemplateName()->getUnderlying()); + 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(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 { + typedef TypeVisitor 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 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 0000000000000..f23f1318afbbf --- /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 Args); + +} // namespace serialization +} // namespace clang diff --git a/clang/test/Modules/cxx-templates.cpp b/clang/test/Modules/cxx-templates.cpp index e10ba7c2ac3ef..b7d5741e69af6 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 0000000000000..d5854b0e647e3 --- /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 +constexpr bool is_pod_v = __is_pod(T); + +//--- test.cpp +// expected-no-diagnostics +import type_traits; +// Base is either void or wrapper. +template struct wrapper : Base {}; +template <> struct wrapper {}; + +// wrap<0>::type is wrapper, wrap<1>::type is wrapper>, +// and so on. +template +struct wrap { + template + using type = wrapper::template type>; +}; + +template <> +struct wrap<0> { + template + using type = wrapper; +}; + +inline constexpr int kMaxRank = 40; +template +using rank = typename wrap::template type; +using rank_selector_t = rank; + +static_assert(is_pod_v, "Must be POD"); diff --git a/clang/test/OpenMP/target_parallel_ast_print.cpp b/clang/test/OpenMP/target_parallel_ast_print.cpp index 7e27ac7b92ca4..3ee98bc525c1b 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::TS) // CHECK-NEXT: } -// CHECK: template<> struct S { -// CHECK: static char TS; -// CHECK-NEXT: #pragma omp threadprivate(S::TS) -// CHECK-NEXT: } template 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 8338f2a68f922..cc47ae92efac0 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::TS) // CHECK-NEXT: } -// CHECK: template<> struct S { -// CHECK: static long TS; -// CHECK-NEXT: #pragma omp threadprivate(S::TS) -// CHECK-NEXT: } template 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 2a6b8908a1e2d..30fb7ab75cc87 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::TS) // CHECK-NEXT: } -// CHECK: template<> struct S { -// CHECK: static long TS; -// CHECK-NEXT: #pragma omp threadprivate(S::TS) -// CHECK-NEXT: } template 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 0087f71ac9f74..597a9b2bdbdc5 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::TS) // CHECK-NEXT: } -// CHECK: template<> struct S { -// CHECK: static long TS; -// CHECK-NEXT: #pragma omp threadprivate(S::TS) -// CHECK-NEXT: } template T tmain(T argc, T *argv) {