diff options
Diffstat (limited to 'clang/lib/AST/ExternalASTMerger.cpp')
| -rw-r--r-- | clang/lib/AST/ExternalASTMerger.cpp | 548 | 
1 files changed, 548 insertions, 0 deletions
| diff --git a/clang/lib/AST/ExternalASTMerger.cpp b/clang/lib/AST/ExternalASTMerger.cpp new file mode 100644 index 000000000000..f678c2dd3b59 --- /dev/null +++ b/clang/lib/AST/ExternalASTMerger.cpp @@ -0,0 +1,548 @@ +//===- 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 <typename T> struct Source { +  T t; +  Source(T t) : t(t) {} +  operator T() { return t; } +  template <typename U = T> U &get() { return t; } +  template <typename U = T> const U &get() const { return t; } +  template <typename U> operator Source<U>() { return Source<U>(t); } +}; + +typedef std::pair<Source<NamedDecl *>, 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<LinkageSpecDecl>(DC)) +    return DC->getRedeclContext(); +  return DC; +} + +Source<const DeclContext *> +LookupSameContext(Source<TranslationUnitDecl *> SourceTU, const DeclContext *DC, +                  ASTImporter &ReverseImporter) { +  DC = CanonicalizeDC(DC); +  if (DC->isTranslationUnit()) { +    return SourceTU; +  } +  Source<const DeclContext *> 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<NamedDecl>(DC); +  DeclarationName Name = ND->getDeclName(); +  auto SourceNameOrErr = ReverseImporter.Import(Name); +  if (!SourceNameOrErr) { +    llvm::consumeError(SourceNameOrErr.takeError()); +    return nullptr; +  } +  Source<DeclarationName> SourceName = *SourceNameOrErr; +  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<DeclContext>(SearchResultDecl) && +        SearchResultDecl->getKind() == DC->getDeclKind()) +      return cast<DeclContext>(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; +  /// @see ExternalASTMerger::ImporterSource::Temporary +  bool TemporarySource; +  /// Map of imported declarations back to the declarations they originated +  /// from. +  llvm::DenseMap<Decl *, Decl *> 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<ASTImporterSharedState> 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<Decl *> 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 { +    auto It = ToOrigin.find(To); +    if (It != ToOrigin.end()) +      return It->second; +    return nullptr; +  } + +  /// 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<DeclContext>(To)) { +      const bool LoggingEnabled = Parent.LoggingEnabled(); +      if (LoggingEnabled) +        logs() << "(ExternalASTMerger*)" << (void*)&Parent +               << " imported (DeclContext*)" << (void*)ToDC +               << ", (ASTContext*)" << (void*)&getToContext() +               << " from (DeclContext*)" << (void*)llvm::cast<DeclContext>(From) +               << ", (ASTContext*)" << (void*)&getFromContext() +               << "\n"; +      Source<DeclContext *> FromDC( +          cast<DeclContext>(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<TagDecl>(To)) { +      ToTag->setHasExternalLexicalStorage(); +      ToTag->getPrimaryContext()->setMustBuildLookupTable(); +      assert(Parent.CanComplete(ToTag)); +    } else if (auto *ToNamespace = dyn_cast<NamespaceDecl>(To)) { +      ToNamespace->setHasExternalVisibleStorage(); +      assert(Parent.CanComplete(ToNamespace)); +    } else if (auto *ToContainer = dyn_cast<ObjCContainerDecl>(To)) { +      ToContainer->setHasExternalLexicalStorage(); +      ToContainer->getPrimaryContext()->setMustBuildLookupTable(); +      assert(Parent.CanComplete(ToContainer)); +    } +  } +  ASTImporter &GetReverse() { return Reverse; } +}; + +bool HasDeclOfSameType(llvm::ArrayRef<Candidate> Decls, const Candidate &C) { +  if (isa<FunctionDecl>(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<ASTImporter> &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<LazyASTImporter &>( +      Merger.ImporterForOrigin(OriginContext)); +} +} + +bool ExternalASTMerger::HasImporterForOrigin(ASTContext &OriginContext) { +  for (const std::unique_ptr<ASTImporter> &I : Importers) +    if (&I->getFromContext() == &OriginContext) +      return true; +  return false; +} + +template <typename CallbackType> +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<ASTImporter> &Importer : Importers) { +      Source<TranslationUnitDecl *> SourceTU = +          Importer->getFromContext().getTranslationUnitDecl(); +      ASTImporter &Reverse = +          static_cast<LazyASTImporter *>(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<const DeclContext *> SourceDC) -> bool { +    auto *SourceTag = const_cast<TagDecl *>(cast<TagDecl>(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<const DeclContext *> SourceDC) -> bool { +        auto *SourceInterface = const_cast<ObjCInterfaceDecl *>( +            cast<ObjCInterfaceDecl>(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<const DeclContext *> SourceDC) -> bool { +                      FoundMatchingDC = true; +                      return true; +                    }); +  return FoundMatchingDC; +} + +namespace { +bool IsSameDC(const DeclContext *D1, const DeclContext *D2) { +  if (isa<ObjCContainerDecl>(D1) && isa<ObjCContainerDecl>(D2)) +    return true; // There are many cases where Objective-C is ambiguous. +  if (auto *T1 = dyn_cast<TagDecl>(D1)) +    if (auto *T2 = dyn_cast<TagDecl>(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<const DeclContext *> 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<Decl>(Origin.DC), const_cast<Decl*>(cast<Decl>(ToDC))); +} + +ExternalASTMerger::ExternalASTMerger(const ImporterTarget &Target, +                                     llvm::ArrayRef<ImporterSource> Sources) : LogStream(&llvm::nulls()), Target(Target) { +  SharedState = std::make_shared<ASTImporterSharedState>( +      *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<ImporterSource> 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<LazyASTImporter>( +        *this, Target.AST, Target.FM, S, SharedState)); +  } +} + +void ExternalASTMerger::RemoveSources(llvm::ArrayRef<ImporterSource> Sources) { +  if (LoggingEnabled()) +    for (const ImporterSource &S : Sources) +      logs() << "(ExternalASTMerger*)" << (void *)this +             << " removing source (ASTContext*)" << (void *)&S.getASTContext() +             << "\n"; +  Importers.erase( +      std::remove_if(Importers.begin(), Importers.end(), +                     [&Sources](std::unique_ptr<ASTImporter> &Importer) -> bool { +                       for (const ImporterSource &S : Sources) { +                         if (&Importer->getFromContext() == &S.getASTContext()) +                           return true; +                       } +                       return false; +                     }), +      Importers.end()); +  for (OriginMap::iterator OI = Origins.begin(), OE = Origins.end(); OI != OE; ) { +    std::pair<const DeclContext *, DCOrigin> 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 <typename DeclTy> +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<TemplateDecl>(D)) +    return false; +  if (auto *FunctionTD = dyn_cast<FunctionTemplateDecl>(D)) +    return importSpecializations(FunctionTD, Importer); +  else if (auto *ClassTD = dyn_cast<ClassTemplateDecl>(D)) +    return importSpecializations(ClassTD, Importer); +  else if (auto *VarTD = dyn_cast<VarTemplateDecl>(D)) +    return importSpecializations(VarTD, Importer); +  return false; +} + +bool ExternalASTMerger::FindExternalVisibleDeclsByName(const DeclContext *DC, +                                                       DeclarationName Name) { +  llvm::SmallVector<NamedDecl *, 1> Decls; +  llvm::SmallVector<Candidate, 4> Candidates; + +  auto FilterFoundDecl = [&Candidates](const Candidate &C) { +   if (!HasDeclOfSameType(Candidates, C)) +     Candidates.push_back(C); +  }; + +  ForEachMatchingDC(DC, +                    [&](ASTImporter &Forward, ASTImporter &Reverse, +                        Source<const DeclContext *> 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); +    assert(NDOrErr); +    (void)static_cast<bool>(NDOrErr); +    NamedDecl *ND = cast_or_null<NamedDecl>(*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<bool(Decl::Kind)> IsKindWeWant, +    SmallVectorImpl<Decl *> &Result) { +  ForEachMatchingDC(DC, [&](ASTImporter &Forward, ASTImporter &Reverse, +                            Source<const DeclContext *> 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; +  }); +} + | 
