https://github.com/ChuanqiXu9 created https://github.com/llvm/llvm-project/pull/83237
This is the following of https://github.com/llvm/llvm-project/pull/83233. This simply removes `LazySpecializationInfo` from `RedeclarableTemplateDecl`. I feel it can make the review process much more easier after splitting this. >From ecdd0c35ea131b2cdc0943dfd2dfdcd59f973409 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu <yedeng...@linux.alibaba.com> Date: Wed, 28 Feb 2024 16:51:15 +0800 Subject: [PATCH] [Serialization] Code cleanups and polish 83233 --- clang/include/clang/AST/DeclTemplate.h | 32 +------- .../include/clang/Serialization/ASTBitCodes.h | 2 +- clang/lib/AST/DeclTemplate.cpp | 49 ++++-------- clang/lib/Serialization/ASTCommon.h | 1 - clang/lib/Serialization/ASTReader.cpp | 4 +- clang/lib/Serialization/ASTReaderDecl.cpp | 76 +------------------ clang/lib/Serialization/ASTWriter.cpp | 26 +------ clang/lib/Serialization/ASTWriterDecl.cpp | 52 +------------ 8 files changed, 30 insertions(+), 212 deletions(-) diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 51caef54baac26..d25e10adb5453d 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -256,8 +256,8 @@ 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 stable hash for the given arguments across compiler invocations. + static unsigned ComputeStableHash(ArrayRef<TemplateArgument> Args); /// Create a new template argument list that copies the given set of /// template arguments. @@ -733,25 +733,6 @@ class RedeclarableTemplateDecl : public TemplateDecl, } void anchor() override; - struct LazySpecializationInfo { - uint32_t DeclID = ~0U; - unsigned ODRHash = ~0U; - bool IsPartial = false; - LazySpecializationInfo(uint32_t 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 { @@ -798,8 +779,6 @@ class RedeclarableTemplateDecl : public TemplateDecl, void 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, @@ -820,13 +799,6 @@ class RedeclarableTemplateDecl : public TemplateDecl, llvm::PointerIntPair<RedeclarableTemplateDecl*, 1, bool> InstantiatedFromMember; - /// If non-null, points to an array of specializations (including - /// partial specializations) known only by their external declaration IDs. - /// - /// The first value in the array is the number of specializations/partial - /// specializations that follow. - LazySpecializationInfo *LazySpecializations = nullptr; - /// The set of "injected" template arguments used within this /// template. /// diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 15e7aef826a52a..827799ec7f0a3b 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -699,7 +699,7 @@ enum ASTRecordTypes { /// recorded in a preamble. PP_ASSUME_NONNULL_LOC = 67, - UPDATE_SPECIALIZATION = 68, + CXX_ADDED_TEMPLATE_SPECIALIZATION = 68, }; /// Record types used within a source manager block. diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 7b98b046d00725..4115f8ae4f0382 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -342,32 +342,6 @@ 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; - for (uint32_t I = 0, N = Specs[0].DeclID; 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"); - - uint32_t ID = LazySpecInfo.DeclID; - assert(ID && "Loading already loaded specialization!"); - // Note that we loaded the specialization. - LazySpecInfo.DeclID = LazySpecInfo.ODRHash = LazySpecInfo.IsPartial = 0; - return getASTContext().getExternalSource()->GetExternalDecl(ID); } void RedeclarableTemplateDecl::loadLazySpecializationsImpl( @@ -378,14 +352,6 @@ void RedeclarableTemplateDecl::loadLazySpecializationsImpl( ExternalSource->LoadExternalSpecializations(this->getCanonicalDecl(), Args); return; - - CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr(); - if (auto *Specs = CommonBasePtr->LazySpecializations) { - unsigned Hash = TemplateArgumentList::ComputeODRHash(Args); - for (uint32_t I = 0, N = Specs[0].DeclID; I != N; ++I) - if (Specs[I + 1].ODRHash && Specs[I + 1].ODRHash == Hash) - (void)loadLazySpecializationImpl(Specs[I + 1]); - } } template<class EntryType, typename... ProfileArguments> @@ -939,7 +905,20 @@ TemplateArgumentList::CreateCopy(ASTContext &Context, return new (Mem) TemplateArgumentList(Args); } -unsigned TemplateArgumentList::ComputeODRHash(ArrayRef<TemplateArgument> Args) { +unsigned +TemplateArgumentList::ComputeStableHash(ArrayRef<TemplateArgument> Args) { + // FIXME: 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. ODRHash Hasher; for (TemplateArgument TA : Args) Hasher.AddTemplateArgument(TA); diff --git a/clang/lib/Serialization/ASTCommon.h b/clang/lib/Serialization/ASTCommon.h index 296642e3674a49..485809f234113f 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 0c6c19dfe5f908..94d7b959bcfdcc 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3497,7 +3497,7 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, break; } - case UPDATE_SPECIALIZATION: { + case CXX_ADDED_TEMPLATE_SPECIALIZATION: { unsigned Idx = 0; serialization::DeclID ID = ReadDeclID(F, Record, Idx); auto *Data = (const unsigned char *)Blob.data(); @@ -7972,7 +7972,7 @@ void ASTReader::LoadExternalSpecializations( return; Deserializing LookupResults(this); - auto HashValue = TemplateArgumentList::ComputeODRHash(TemplateArgs); + auto HashValue = TemplateArgumentList::ComputeStableHash(TemplateArgs); // Get Decl may violate the iterator from SpecializationsLookups llvm::SmallVector<serialization::reader::LazySpecializationInfo, 8> Infos; diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 0b42b4c2713162..6236795dcd4156 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -88,8 +88,6 @@ namespace clang { const SourceLocation ThisDeclLoc; using RecordData = ASTReader::RecordData; - using LazySpecializationInfo = - RedeclarableTemplateDecl::LazySpecializationInfo; TypeID DeferredTypeID = 0; unsigned AnonymousDeclNumber = 0; @@ -136,18 +134,6 @@ namespace clang { return Record.readString(); } - LazySpecializationInfo ReadLazySpecializationInfo() { - DeclID 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(); } @@ -274,29 +260,6 @@ namespace clang { : Reader(Reader), Record(Record), Loc(Loc), ThisDeclID(thisDeclID), ThisDeclLoc(ThisDeclLoc) {} - template <typename T> - static void - AddLazySpecializations(T *D, SmallVectorImpl<LazySpecializationInfo> &IDs) { - if (IDs.empty()) - return; - - // FIXME: We should avoid this pattern of getting the ASTContext. - ASTContext &C = D->getASTContext(); - - auto *&LazySpecializations = D->getCommonPtr()->LazySpecializations; - - if (auto &Old = LazySpecializations) { - IDs.insert(IDs.end(), Old + 1, Old + 1 + Old[0].DeclID); - llvm::sort(IDs); - IDs.erase(std::unique(IDs.begin(), IDs.end()), IDs.end()); - } - auto *Result = new (C) LazySpecializationInfo[1 + IDs.size()]; - *Result = IDs.size(); - std::copy(IDs.begin(), IDs.end(), Result + 1); - - LazySpecializations = Result; - } - template <typename DeclT> static Decl *getMostRecentDeclImpl(Redeclarable<DeclT> *D); static Decl *getMostRecentDeclImpl(...); @@ -328,7 +291,7 @@ namespace clang { void ReadFunctionDefinition(FunctionDecl *FD); void Visit(Decl *D); - void UpdateDecl(Decl *D, llvm::SmallVectorImpl<LazySpecializationInfo> &); + void UpdateDecl(Decl *D); static void setNextObjCCategory(ObjCCategoryDecl *Cat, ObjCCategoryDecl *Next) { @@ -2469,9 +2432,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); } @@ -2498,9 +2458,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 +2558,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); } } @@ -4214,10 +4168,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); @@ -4255,7 +4205,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. @@ -4267,19 +4217,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); @@ -4497,9 +4434,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: { @@ -4510,11 +4445,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/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 20d5a357647a75..0f217919ce3993 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -4044,7 +4044,7 @@ unsigned CalculateODRHashForSpecs(const Decl *Spec) { else llvm_unreachable("New Specialization Kind?"); - return TemplateArgumentList::ComputeODRHash(Args); + return TemplateArgumentList::ComputeStableHash(Args); } } // namespace @@ -5453,7 +5453,7 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, 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)); @@ -5466,7 +5466,8 @@ void ASTWriter::WriteSpecializationsUpdates() { LookupTable); // Write the lookup table - RecordData::value_type Record[] = {UPDATE_SPECIALIZATION, getDeclID(D)}; + RecordData::value_type Record[] = {CXX_ADDED_TEMPLATE_SPECIALIZATION, + getDeclID(D)}; Stream.EmitRecordWithBlob(UpdateSpecializationAbbrev, Record, LookupTable); } } @@ -5503,25 +5504,6 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) { assert(Update.getDecl() && "no decl to add?"); Record.push_back(GetDeclRef(Update.getDecl())); break; - case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION: { - const Decl *Spec = Update.getDecl(); - assert(Spec && "no decl to add?"); - Record.push_back(GetDeclRef(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 f021d82a6d2409..30c1ef403ecf01 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -213,24 +213,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. @@ -257,22 +241,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); - - // 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; @@ -288,21 +262,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); - 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)); } @@ -325,9 +284,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)); } _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits