//===- ExternalASTMerger.cpp - Merging External AST Interface ---*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the ExternalASTMerger, which vends a combination of // ASTs from several different ASTContext/FileManager pairs // //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ExternalASTMerger.h" using namespace clang; namespace { template struct Source { T t; Source(T t) : t(t) {} operator T() { return t; } template U &get() { return t; } template const U &get() const { return t; } template operator Source() { return Source(t); } }; typedef std::pair, ASTImporter *> Candidate; /// For the given DC, return the DC that is safe to perform lookups on. This is /// the DC we actually want to work with most of the time. const DeclContext *CanonicalizeDC(const DeclContext *DC) { if (isa(DC)) return DC->getRedeclContext(); return DC; } Source LookupSameContext(Source SourceTU, const DeclContext *DC, ASTImporter &ReverseImporter) { DC = CanonicalizeDC(DC); if (DC->isTranslationUnit()) { return SourceTU; } Source SourceParentDC = LookupSameContext(SourceTU, DC->getParent(), ReverseImporter); if (!SourceParentDC) { // If we couldn't find the parent DC in this TranslationUnit, give up. return nullptr; } auto *ND = cast(DC); DeclarationName Name = ND->getDeclName(); Source SourceName = ReverseImporter.Import(Name); DeclContext::lookup_result SearchResult = SourceParentDC.get()->lookup(SourceName.get()); size_t SearchResultSize = SearchResult.size(); if (SearchResultSize == 0 || SearchResultSize > 1) { // There are two cases here. First, we might not find the name. // We might also find multiple copies, in which case we have no // guarantee that the one we wanted is the one we pick. (E.g., // if we have two specializations of the same template it is // very hard to determine which is the one you want.) // // The Origins map fixes this problem by allowing the origin to be // explicitly recorded, so we trigger that recording by returning // nothing (rather than a possibly-inaccurate guess) here. return nullptr; } else { NamedDecl *SearchResultDecl = SearchResult[0]; if (isa(SearchResultDecl) && SearchResultDecl->getKind() == DC->getDeclKind()) return cast(SearchResultDecl)->getPrimaryContext(); return nullptr; // This type of lookup is unsupported } } /// A custom implementation of ASTImporter, for ExternalASTMerger's purposes. /// /// There are several modifications: /// /// - It enables lazy lookup (via the HasExternalLexicalStorage flag and a few /// others), which instructs Clang to refer to ExternalASTMerger. Also, it /// forces MinimalImport to true, which is necessary to make this work. /// - It maintains a reverse importer for use with names. This allows lookup of /// arbitrary names in the source context. /// - It updates the ExternalASTMerger's origin map as needed whenever a /// it sees a DeclContext. class LazyASTImporter : public ASTImporter { private: ExternalASTMerger &Parent; ASTImporter Reverse; const ExternalASTMerger::OriginMap &FromOrigins; llvm::raw_ostream &logs() { return Parent.logs(); } public: LazyASTImporter(ExternalASTMerger &_Parent, ASTContext &ToContext, FileManager &ToFileManager, ASTContext &FromContext, FileManager &FromFileManager, const ExternalASTMerger::OriginMap &_FromOrigins) : ASTImporter(ToContext, ToFileManager, FromContext, FromFileManager, /*MinimalImport=*/true), Parent(_Parent), Reverse(FromContext, FromFileManager, ToContext, ToFileManager, /*MinimalImport=*/true), FromOrigins(_FromOrigins) {} /// Whenever a DeclContext is imported, ensure that ExternalASTSource's origin /// map is kept up to date. Also set the appropriate flags. Decl *Imported(Decl *From, Decl *To) override { if (auto *ToDC = dyn_cast(To)) { const bool LoggingEnabled = Parent.LoggingEnabled(); if (LoggingEnabled) logs() << "(ExternalASTMerger*)" << (void*)&Parent << " imported (DeclContext*)" << (void*)ToDC << ", (ASTContext*)" << (void*)&getToContext() << " from (DeclContext*)" << (void*)llvm::cast(From) << ", (ASTContext*)" << (void*)&getFromContext() << "\n"; Source FromDC( cast(From)->getPrimaryContext()); if (FromOrigins.count(FromDC) && Parent.HasImporterForOrigin(*FromOrigins.at(FromDC).AST)) { if (LoggingEnabled) logs() << "(ExternalASTMerger*)" << (void*)&Parent << " forced origin (DeclContext*)" << (void*)FromOrigins.at(FromDC).DC << ", (ASTContext*)" << (void*)FromOrigins.at(FromDC).AST << "\n"; Parent.ForceRecordOrigin(ToDC, FromOrigins.at(FromDC)); } else { if (LoggingEnabled) logs() << "(ExternalASTMerger*)" << (void*)&Parent << " maybe recording origin (DeclContext*)" << (void*)FromDC << ", (ASTContext*)" << (void*)&getFromContext() << "\n"; Parent.MaybeRecordOrigin(ToDC, {FromDC, &getFromContext()}); } } if (auto *ToTag = dyn_cast(To)) { ToTag->setHasExternalLexicalStorage(); ToTag->setMustBuildLookupTable(); assert(Parent.CanComplete(ToTag)); } else if (auto *ToNamespace = dyn_cast(To)) { ToNamespace->setHasExternalVisibleStorage(); assert(Parent.CanComplete(ToNamespace)); } else if (auto *ToContainer = dyn_cast(To)) { ToContainer->setHasExternalLexicalStorage(); ToContainer->setMustBuildLookupTable(); assert(Parent.CanComplete(ToContainer)); } return ASTImporter::Imported(From, To); } ASTImporter &GetReverse() { return Reverse; } }; bool HasDeclOfSameType(llvm::ArrayRef Decls, const Candidate &C) { if (isa(C.first.get())) return false; return llvm::any_of(Decls, [&](const Candidate &D) { return C.first.get()->getKind() == D.first.get()->getKind(); }); } } // end namespace ASTImporter &ExternalASTMerger::ImporterForOrigin(ASTContext &OriginContext) { for (const std::unique_ptr &I : Importers) if (&I->getFromContext() == &OriginContext) return *I; llvm_unreachable("We should have an importer for this origin!"); } namespace { LazyASTImporter &LazyImporterForOrigin(ExternalASTMerger &Merger, ASTContext &OriginContext) { return static_cast( Merger.ImporterForOrigin(OriginContext)); } } bool ExternalASTMerger::HasImporterForOrigin(ASTContext &OriginContext) { for (const std::unique_ptr &I : Importers) if (&I->getFromContext() == &OriginContext) return true; return false; } template void ExternalASTMerger::ForEachMatchingDC(const DeclContext *DC, CallbackType Callback) { if (Origins.count(DC)) { ExternalASTMerger::DCOrigin Origin = Origins[DC]; LazyASTImporter &Importer = LazyImporterForOrigin(*this, *Origin.AST); Callback(Importer, Importer.GetReverse(), Origin.DC); } else { bool DidCallback = false; for (const std::unique_ptr &Importer : Importers) { Source SourceTU = Importer->getFromContext().getTranslationUnitDecl(); ASTImporter &Reverse = static_cast(Importer.get())->GetReverse(); if (auto SourceDC = LookupSameContext(SourceTU, DC, Reverse)) { DidCallback = true; if (Callback(*Importer, Reverse, SourceDC)) break; } } if (!DidCallback && LoggingEnabled()) logs() << "(ExternalASTMerger*)" << (void*)this << " asserting for (DeclContext*)" << (const void*)DC << ", (ASTContext*)" << (void*)&Target.AST << "\n"; assert(DidCallback && "Couldn't find a source context matching our DC"); } } void ExternalASTMerger::CompleteType(TagDecl *Tag) { assert(Tag->hasExternalLexicalStorage()); ForEachMatchingDC(Tag, [&](ASTImporter &Forward, ASTImporter &Reverse, Source SourceDC) -> bool { auto *SourceTag = const_cast(cast(SourceDC.get())); if (SourceTag->hasExternalLexicalStorage()) SourceTag->getASTContext().getExternalSource()->CompleteType(SourceTag); if (!SourceTag->getDefinition()) return false; Forward.Imported(SourceTag, Tag); Forward.ImportDefinition(SourceTag); Tag->setCompleteDefinition(SourceTag->isCompleteDefinition()); return true; }); } void ExternalASTMerger::CompleteType(ObjCInterfaceDecl *Interface) { assert(Interface->hasExternalLexicalStorage()); ForEachMatchingDC( Interface, [&](ASTImporter &Forward, ASTImporter &Reverse, Source SourceDC) -> bool { auto *SourceInterface = const_cast( cast(SourceDC.get())); if (SourceInterface->hasExternalLexicalStorage()) SourceInterface->getASTContext().getExternalSource()->CompleteType( SourceInterface); if (!SourceInterface->getDefinition()) return false; Forward.Imported(SourceInterface, Interface); Forward.ImportDefinition(SourceInterface); return true; }); } bool ExternalASTMerger::CanComplete(DeclContext *Interface) { assert(Interface->hasExternalLexicalStorage() || Interface->hasExternalVisibleStorage()); bool FoundMatchingDC = false; ForEachMatchingDC(Interface, [&](ASTImporter &Forward, ASTImporter &Reverse, Source SourceDC) -> bool { FoundMatchingDC = true; return true; }); return FoundMatchingDC; } namespace { bool IsSameDC(const DeclContext *D1, const DeclContext *D2) { if (isa(D1) && isa(D2)) return true; // There are many cases where Objective-C is ambiguous. if (auto *T1 = dyn_cast(D1)) if (auto *T2 = dyn_cast(D2)) if (T1->getFirstDecl() == T2->getFirstDecl()) return true; return D1 == D2 || D1 == CanonicalizeDC(D2); } } void ExternalASTMerger::MaybeRecordOrigin(const DeclContext *ToDC, DCOrigin Origin) { LazyASTImporter &Importer = LazyImporterForOrigin(*this, *Origin.AST); ASTImporter &Reverse = Importer.GetReverse(); Source FoundFromDC = LookupSameContext(Origin.AST->getTranslationUnitDecl(), ToDC, Reverse); const bool DoRecord = !FoundFromDC || !IsSameDC(FoundFromDC.get(), Origin.DC); if (DoRecord) RecordOriginImpl(ToDC, Origin, Importer); if (LoggingEnabled()) logs() << "(ExternalASTMerger*)" << (void*)this << (DoRecord ? " decided " : " decided NOT") << " to record origin (DeclContext*)" << (void*)Origin.DC << ", (ASTContext*)" << (void*)&Origin.AST << "\n"; } void ExternalASTMerger::ForceRecordOrigin(const DeclContext *ToDC, DCOrigin Origin) { RecordOriginImpl(ToDC, Origin, ImporterForOrigin(*Origin.AST)); } void ExternalASTMerger::RecordOriginImpl(const DeclContext *ToDC, DCOrigin Origin, ASTImporter &Importer) { Origins[ToDC] = Origin; Importer.ASTImporter::Imported(cast(Origin.DC), const_cast(cast(ToDC))); } ExternalASTMerger::ExternalASTMerger(const ImporterTarget &Target, llvm::ArrayRef Sources) : LogStream(&llvm::nulls()), Target(Target) { AddSources(Sources); } void ExternalASTMerger::AddSources(llvm::ArrayRef Sources) { for (const ImporterSource &S : Sources) { assert(&S.AST != &Target.AST); Importers.push_back(llvm::make_unique( *this, Target.AST, Target.FM, S.AST, S.FM, S.OM)); } } void ExternalASTMerger::RemoveSources(llvm::ArrayRef Sources) { if (LoggingEnabled()) for (const ImporterSource &S : Sources) logs() << "(ExternalASTMerger*)" << (void*)this << " removing source (ASTContext*)" << (void*)&S.AST << "\n"; Importers.erase( std::remove_if(Importers.begin(), Importers.end(), [&Sources](std::unique_ptr &Importer) -> bool { for (const ImporterSource &S : Sources) { if (&Importer->getFromContext() == &S.AST) return true; } return false; }), Importers.end()); for (OriginMap::iterator OI = Origins.begin(), OE = Origins.end(); OI != OE; ) { std::pair Origin = *OI; bool Erase = false; for (const ImporterSource &S : Sources) { if (&S.AST == Origin.second.AST) { Erase = true; break; } } if (Erase) OI = Origins.erase(OI); else ++OI; } } bool ExternalASTMerger::FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) { llvm::SmallVector Decls; llvm::SmallVector Candidates; auto FilterFoundDecl = [&Candidates](const Candidate &C) { if (!HasDeclOfSameType(Candidates, C)) Candidates.push_back(C); }; ForEachMatchingDC(DC, [&](ASTImporter &Forward, ASTImporter &Reverse, Source SourceDC) -> bool { DeclarationName FromName = Reverse.Import(Name); DeclContextLookupResult Result = SourceDC.get()->lookup(FromName); for (NamedDecl *FromD : Result) { FilterFoundDecl(std::make_pair(FromD, &Forward)); } return false; }); if (Candidates.empty()) return false; Decls.reserve(Candidates.size()); for (const Candidate &C : Candidates) { NamedDecl *d = cast(C.second->Import(C.first.get())); assert(d); Decls.push_back(d); } SetExternalVisibleDeclsForName(DC, Name, Decls); return true; } void ExternalASTMerger::FindExternalLexicalDecls( const DeclContext *DC, llvm::function_ref IsKindWeWant, SmallVectorImpl &Result) { ForEachMatchingDC(DC, [&](ASTImporter &Forward, ASTImporter &Reverse, Source SourceDC) -> bool { for (const Decl *SourceDecl : SourceDC.get()->decls()) { if (IsKindWeWant(SourceDecl->getKind())) { Decl *ImportedDecl = Forward.Import(const_cast(SourceDecl)); assert(!ImportedDecl || IsSameDC(ImportedDecl->getDeclContext(), DC)); (void)ImportedDecl; } } return false; }); }