//===- ExternalASTMerger.cpp - Merging External AST Interface ---*- 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 // //===----------------------------------------------------------------------===// // // 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/DeclTemplate.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(); auto SourceNameOrErr = ReverseImporter.Import(Name); if (!SourceNameOrErr) { llvm::consumeError(SourceNameOrErr.takeError()); return nullptr; } Source SourceName = *SourceNameOrErr; DeclContext::lookup_result SearchResult = SourceParentDC.get()->lookup(SourceName.get()); // 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. if (SearchResult.isSingleResult()) { NamedDecl *SearchResultDecl = SearchResult.front(); if (isa(SearchResultDecl) && SearchResultDecl->getKind() == DC->getDeclKind()) return cast(SearchResultDecl)->getPrimaryContext(); return nullptr; // This type of lookup is unsupported } else { return nullptr; } } /// 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; /// @see ExternalASTMerger::ImporterSource::Temporary bool TemporarySource; /// Map of imported declarations back to the declarations they originated /// from. llvm::DenseMap ToOrigin; /// @see ExternalASTMerger::ImporterSource::Merger ExternalASTMerger *SourceMerger; llvm::raw_ostream &logs() { return Parent.logs(); } public: LazyASTImporter(ExternalASTMerger &_Parent, ASTContext &ToContext, FileManager &ToFileManager, const ExternalASTMerger::ImporterSource &S, std::shared_ptr SharedState) : ASTImporter(ToContext, ToFileManager, S.getASTContext(), S.getFileManager(), /*MinimalImport=*/true, SharedState), Parent(_Parent), Reverse(S.getASTContext(), S.getFileManager(), ToContext, ToFileManager, /*MinimalImport=*/true), FromOrigins(S.getOriginMap()), TemporarySource(S.isTemporary()), SourceMerger(S.getMerger()) {} llvm::Expected ImportImpl(Decl *FromD) override { if (!TemporarySource || !SourceMerger) return ASTImporter::ImportImpl(FromD); // If we get here, then this source is importing from a temporary ASTContext // that also has another ExternalASTMerger attached. It could be // possible that the current ExternalASTMerger and the temporary ASTContext // share a common ImporterSource, which means that the temporary // AST could contain declarations that were imported from a source // that this ExternalASTMerger can access directly. Instead of importing // such declarations from the temporary ASTContext, they should instead // be directly imported by this ExternalASTMerger from the original // source. This way the ExternalASTMerger can safely do a minimal import // without creating incomplete declarations originated from a temporary // ASTContext. If we would try to complete such declarations later on, we // would fail to do so as their temporary AST could be deleted (which means // that the missing parts of the minimally imported declaration in that // ASTContext were also deleted). // // The following code tracks back any declaration that needs to be // imported from the temporary ASTContext to a persistent ASTContext. // Then the ExternalASTMerger tries to import from the persistent // ASTContext directly by using the associated ASTImporter. If that // succeeds, this ASTImporter just maps the declarations imported by // the other (persistent) ASTImporter to this (temporary) ASTImporter. // The steps can be visualized like this: // // Target AST <--- 3. Indirect import --- Persistent AST // ^ of persistent decl ^ // | | // 1. Current import 2. Tracking back to persistent decl // 4. Map persistent decl | // & pretend we imported. | // | | // Temporary AST -------------------------------' // First, ask the ExternalASTMerger of the source where the temporary // declaration originated from. Decl *Persistent = SourceMerger->FindOriginalDecl(FromD); // FromD isn't from a persistent AST, so just do a normal import. if (!Persistent) return ASTImporter::ImportImpl(FromD); // Now ask the current ExternalASTMerger to try import the persistent // declaration into the target. ASTContext &PersistentCtx = Persistent->getASTContext(); ASTImporter &OtherImporter = Parent.ImporterForOrigin(PersistentCtx); // Check that we never end up in the current Importer again. assert((&PersistentCtx != &getFromContext()) && (&OtherImporter != this) && "Delegated to same Importer?"); auto DeclOrErr = OtherImporter.Import(Persistent); // Errors when importing the persistent decl are treated as if we // had errors with importing the temporary decl. if (!DeclOrErr) return DeclOrErr.takeError(); Decl *D = *DeclOrErr; // Tell the current ASTImporter that this has already been imported // to prevent any further queries for the temporary decl. MapImported(FromD, D); return D; } /// Implements the ASTImporter interface for tracking back a declaration /// to its original declaration it came from. Decl *GetOriginalDecl(Decl *To) override { return ToOrigin.lookup(To); } /// Whenever a DeclContext is imported, ensure that ExternalASTSource's origin /// map is kept up to date. Also set the appropriate flags. void Imported(Decl *From, Decl *To) override { ToOrigin[To] = From; 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->getPrimaryContext()->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->getPrimaryContext()->setMustBuildLookupTable(); assert(Parent.CanComplete(ToContainer)); } } 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.MapImported(SourceTag, Tag); if (llvm::Error Err = Forward.ImportDefinition(SourceTag)) llvm::consumeError(std::move(Err)); 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.MapImported(SourceInterface, Interface); if (llvm::Error Err = Forward.ImportDefinition(SourceInterface)) llvm::consumeError(std::move(Err)); 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::MapImported(cast(Origin.DC), const_cast(cast(ToDC))); } ExternalASTMerger::ExternalASTMerger(const ImporterTarget &Target, llvm::ArrayRef Sources) : LogStream(&llvm::nulls()), Target(Target) { SharedState = std::make_shared( *Target.AST.getTranslationUnitDecl()); AddSources(Sources); } Decl *ExternalASTMerger::FindOriginalDecl(Decl *D) { assert(&D->getASTContext() == &Target.AST); for (const auto &I : Importers) if (auto Result = I->GetOriginalDecl(D)) return Result; return nullptr; } void ExternalASTMerger::AddSources(llvm::ArrayRef Sources) { for (const ImporterSource &S : Sources) { assert(&S.getASTContext() != &Target.AST); // Check that the associated merger actually imports into the source AST. assert(!S.getMerger() || &S.getMerger()->Target.AST == &S.getASTContext()); Importers.push_back(std::make_unique( *this, Target.AST, Target.FM, S, SharedState)); } } void ExternalASTMerger::RemoveSources(llvm::ArrayRef Sources) { if (LoggingEnabled()) for (const ImporterSource &S : Sources) logs() << "(ExternalASTMerger*)" << (void *)this << " removing source (ASTContext*)" << (void *)&S.getASTContext() << "\n"; llvm::erase_if(Importers, [&Sources](std::unique_ptr &Importer) -> bool { for (const ImporterSource &S : Sources) { if (&Importer->getFromContext() == &S.getASTContext()) return true; } return false; }); 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.getASTContext() == Origin.second.AST) { Erase = true; break; } } if (Erase) OI = Origins.erase(OI); else ++OI; } } template static bool importSpecializations(DeclTy *D, ASTImporter *Importer) { for (auto *Spec : D->specializations()) { auto ImportedSpecOrError = Importer->Import(Spec); if (!ImportedSpecOrError) { llvm::consumeError(ImportedSpecOrError.takeError()); return true; } } return false; } /// Imports specializations from template declarations that can be specialized. static bool importSpecializationsIfNeeded(Decl *D, ASTImporter *Importer) { if (!isa(D)) return false; if (auto *FunctionTD = dyn_cast(D)) return importSpecializations(FunctionTD, Importer); else if (auto *ClassTD = dyn_cast(D)) return importSpecializations(ClassTD, Importer); else if (auto *VarTD = dyn_cast(D)) return importSpecializations(VarTD, Importer); return false; } 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 { auto FromNameOrErr = Reverse.Import(Name); if (!FromNameOrErr) { llvm::consumeError(FromNameOrErr.takeError()); return false; } DeclContextLookupResult Result = SourceDC.get()->lookup(*FromNameOrErr); 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) { Decl *LookupRes = C.first.get(); ASTImporter *Importer = C.second; auto NDOrErr = Importer->Import(LookupRes); NamedDecl *ND = cast(llvm::cantFail(std::move(NDOrErr))); assert(ND); // If we don't import specialization, they are not available via lookup // because the lookup result is imported TemplateDecl and it does not // reference its specializations until they are imported explicitly. bool IsSpecImportFailed = importSpecializationsIfNeeded(LookupRes, Importer); assert(!IsSpecImportFailed); (void)IsSpecImportFailed; Decls.push_back(ND); } 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())) { auto ImportedDeclOrErr = Forward.Import(SourceDecl); if (ImportedDeclOrErr) assert(!(*ImportedDeclOrErr) || IsSameDC((*ImportedDeclOrErr)->getDeclContext(), DC)); else llvm::consumeError(ImportedDeclOrErr.takeError()); } } return false; }); }