diff --git a/include/clang/AST/ExternalASTMerger.h b/include/clang/AST/ExternalASTMerger.h new file mode 100644 index 0000000000000000000000000000000000000000..51d0c30ad23bf5b7b897e126bd87229ba030e996 --- /dev/null +++ b/include/clang/AST/ExternalASTMerger.h @@ -0,0 +1,51 @@ +//===--- ExternalASTMerger.h - 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 declares the ExternalASTMerger, which vends a combination of ASTs +// from several different ASTContext/FileManager pairs +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_AST_EXTERNALASTMERGER_H +#define LLVM_CLANG_AST_EXTERNALASTMERGER_H + +#include "clang/AST/ASTImporter.h" +#include "clang/AST/ExternalASTSource.h" + +namespace clang { + +class ExternalASTMerger : public ExternalASTSource { +public: + struct ImporterPair { + std::unique_ptr<ASTImporter> Forward; + std::unique_ptr<ASTImporter> Reverse; + }; + +private: + std::vector<ImporterPair> Importers; + +public: + struct ImporterEndpoint { + ASTContext &AST; + FileManager &FM; + }; + ExternalASTMerger(const ImporterEndpoint &Target, + llvm::ArrayRef<ImporterEndpoint> Sources); + + bool FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) override; + + void + FindExternalLexicalDecls(const DeclContext *DC, + llvm::function_ref<bool(Decl::Kind)> IsKindWeWant, + SmallVectorImpl<Decl *> &Result) override; +}; + +} // end namespace clang + +#endif diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 2e98f524da837d92c01a3da652daaa64b9f9a384..13bf352c2f21c7c76166997576ea9e13bce74258 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -31,6 +31,7 @@ add_clang_library(clangAST ExprConstant.cpp ExprCXX.cpp ExprObjC.cpp + ExternalASTMerger.cpp ExternalASTSource.cpp InheritViz.cpp ItaniumCXXABI.cpp diff --git a/lib/AST/ExternalASTMerger.cpp b/lib/AST/ExternalASTMerger.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1d144047bd1e0717f87185276a24523b633f64c7 --- /dev/null +++ b/lib/AST/ExternalASTMerger.cpp @@ -0,0 +1,183 @@ +//===- 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/DeclObjC.h" +#include "clang/AST/ExternalASTMerger.h" + +using namespace clang; + +namespace { + +template <typename T> struct Source { + T t; + Source(T &&t) : t(std::move(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; + +class LazyASTImporter : public ASTImporter { +public: + LazyASTImporter(ASTContext &ToContext, FileManager &ToFileManager, + ASTContext &FromContext, FileManager &FromFileManager) + : ASTImporter(ToContext, ToFileManager, FromContext, FromFileManager, + /*MinimalImport=*/true) {} + Decl *Imported(Decl *From, Decl *To) override { + if (auto ToTag = dyn_cast<TagDecl>(To)) { + ToTag->setHasExternalLexicalStorage(); + } else if (auto ToNamespace = dyn_cast<NamespaceDecl>(To)) { + ToNamespace->setHasExternalVisibleStorage(); + } + return ASTImporter::Imported(From, To); + } +}; + +Source<const DeclContext *> +LookupSameContext(Source<TranslationUnitDecl *> SourceTU, const DeclContext *DC, + ASTImporter &ReverseImporter) { + 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(); + Source<DeclarationName> SourceName = ReverseImporter.Import(Name); + DeclContext::lookup_result SearchResult = + SourceParentDC.get()->lookup(SourceName.get()); + size_t SearchResultSize = SearchResult.size(); + // Handle multiple candidates once we have a test for it. + // This may turn up when we import template specializations correctly. + assert(SearchResultSize < 2); + if (SearchResultSize == 0) { + // couldn't find the name, so we have to give up + return nullptr; + } else { + NamedDecl *SearchResultDecl = SearchResult[0]; + return dyn_cast<DeclContext>(SearchResultDecl); + } +} + +bool IsForwardDeclaration(Decl *D) { + assert(!isa<ObjCInterfaceDecl>(D)); // TODO handle this case + if (auto TD = dyn_cast<TagDecl>(D)) { + return !TD->isThisDeclarationADefinition(); + } else if (auto FD = dyn_cast<FunctionDecl>(D)) { + return !FD->isThisDeclarationADefinition(); + } else { + return false; + } +} + +void ForEachMatchingDC( + const DeclContext *DC, + llvm::ArrayRef<ExternalASTMerger::ImporterPair> Importers, + std::function<void(const ExternalASTMerger::ImporterPair &IP, + Source<const DeclContext *> SourceDC)> + Callback) { + for (const ExternalASTMerger::ImporterPair &IP : Importers) { + Source<TranslationUnitDecl *> SourceTU( + IP.Forward->getFromContext().getTranslationUnitDecl()); + Source<const DeclContext *> SourceDC = + LookupSameContext(SourceTU, DC, *IP.Reverse); + if (SourceDC.get()) { + Callback(IP, SourceDC); + } + } +} + +bool HasDeclOfSameType(llvm::ArrayRef<Candidate> Decls, const Candidate &C) { + return std::any_of(Decls.begin(), Decls.end(), [&C](const Candidate &D) { + return C.first.get()->getKind() == D.first.get()->getKind(); + }); +} +} // end namespace + +ExternalASTMerger::ExternalASTMerger(const ImporterEndpoint &Target, + llvm::ArrayRef<ImporterEndpoint> Sources) { + for (const ImporterEndpoint &S : Sources) { + Importers.push_back( + {llvm::make_unique<LazyASTImporter>(Target.AST, Target.FM, S.AST, S.FM), + llvm::make_unique<ASTImporter>(S.AST, S.FM, Target.AST, Target.FM, + /*MinimalImport=*/true)}); + } +} + +bool ExternalASTMerger::FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) { + llvm::SmallVector<NamedDecl *, 1> Decls; + llvm::SmallVector<Candidate, 4> CompleteDecls; + llvm::SmallVector<Candidate, 4> ForwardDecls; + + auto FilterFoundDecl = [&CompleteDecls, &ForwardDecls](const Candidate &C) { + if (IsForwardDeclaration(C.first.get())) { + if (!HasDeclOfSameType(ForwardDecls, C)) { + ForwardDecls.push_back(C); + } + } else { + CompleteDecls.push_back(C); + } + }; + + ForEachMatchingDC(DC, Importers, [Name, &FilterFoundDecl]( + const ImporterPair &IP, + Source<const DeclContext *> SourceDC) { + DeclarationName FromName = IP.Reverse->Import(Name); + DeclContextLookupResult Result = SourceDC.get()->lookup(FromName); + for (NamedDecl *FromD : Result) { + FilterFoundDecl(std::make_pair(FromD, IP.Forward.get())); + } + }); + + llvm::ArrayRef<Candidate> DeclsToReport = + CompleteDecls.empty() ? ForwardDecls : CompleteDecls; + + if (DeclsToReport.empty()) { + return false; + } + + Decls.reserve(DeclsToReport.size()); + for (const Candidate &C : DeclsToReport) { + NamedDecl *d = cast<NamedDecl>(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<bool(Decl::Kind)> IsKindWeWant, + SmallVectorImpl<Decl *> &Result) { + ForEachMatchingDC( + DC, Importers, [DC, IsKindWeWant](const ImporterPair &IP, + Source<const DeclContext *> SourceDC) { + for (Source<const Decl *> SourceDecl : SourceDC.get()->decls()) { + if (IsKindWeWant(SourceDecl.get()->getKind())) { + Decl *ImportedDecl = + IP.Forward->Import(const_cast<Decl *>(SourceDecl.get())); + assert(ImportedDecl->getDeclContext() == DC); + } + } + }); +} diff --git a/test/Import/forward-declared-struct/Inputs/S1.c b/test/Import/forward-declared-struct/Inputs/S1.c new file mode 100644 index 0000000000000000000000000000000000000000..28377c2760ba8dde214ff71b836bb949a554e8be --- /dev/null +++ b/test/Import/forward-declared-struct/Inputs/S1.c @@ -0,0 +1 @@ +struct S; diff --git a/test/Import/forward-declared-struct/Inputs/S2.c b/test/Import/forward-declared-struct/Inputs/S2.c new file mode 100644 index 0000000000000000000000000000000000000000..b0876d27df441c788e0ca91cb55359c78bf8f766 --- /dev/null +++ b/test/Import/forward-declared-struct/Inputs/S2.c @@ -0,0 +1,3 @@ +struct S { + int a; +}; diff --git a/test/Import/forward-declared-struct/test.c b/test/Import/forward-declared-struct/test.c new file mode 100644 index 0000000000000000000000000000000000000000..7ccdcf9e97d08dc97eb18f515e8eb2a998c86d9a --- /dev/null +++ b/test/Import/forward-declared-struct/test.c @@ -0,0 +1,5 @@ +// RUN: clang-import-test -import %S/Inputs/S1.c --import %S/Inputs/S2.c -expression %s +void expr() { + struct S MyS; + MyS.a = 3; +} diff --git a/test/Import/member-in-struct/Inputs/S.c b/test/Import/member-in-struct/Inputs/S.c new file mode 100644 index 0000000000000000000000000000000000000000..b0876d27df441c788e0ca91cb55359c78bf8f766 --- /dev/null +++ b/test/Import/member-in-struct/Inputs/S.c @@ -0,0 +1,3 @@ +struct S { + int a; +}; diff --git a/test/Import/member-in-struct/test.c b/test/Import/member-in-struct/test.c new file mode 100644 index 0000000000000000000000000000000000000000..bde2b60d275fa71205452c1de8b8f914c8ade811 --- /dev/null +++ b/test/Import/member-in-struct/test.c @@ -0,0 +1,5 @@ +// RUN: clang-import-test -import %S/Inputs/S.c -expression %s +void expr() { + struct S MyS; + MyS.a = 3; +} diff --git a/test/Import/multiple-forward-declarations/Inputs/S1.c b/test/Import/multiple-forward-declarations/Inputs/S1.c new file mode 100644 index 0000000000000000000000000000000000000000..28377c2760ba8dde214ff71b836bb949a554e8be --- /dev/null +++ b/test/Import/multiple-forward-declarations/Inputs/S1.c @@ -0,0 +1 @@ +struct S; diff --git a/test/Import/multiple-forward-declarations/Inputs/S2.c b/test/Import/multiple-forward-declarations/Inputs/S2.c new file mode 100644 index 0000000000000000000000000000000000000000..28377c2760ba8dde214ff71b836bb949a554e8be --- /dev/null +++ b/test/Import/multiple-forward-declarations/Inputs/S2.c @@ -0,0 +1 @@ +struct S; diff --git a/test/Import/multiple-forward-declarations/test.c b/test/Import/multiple-forward-declarations/test.c new file mode 100644 index 0000000000000000000000000000000000000000..fdaaaf6114340a88df23b54f130c83c6eb6fe6f6 --- /dev/null +++ b/test/Import/multiple-forward-declarations/test.c @@ -0,0 +1,4 @@ +// RUN: clang-import-test -import %S/Inputs/S1.c --import %S/Inputs/S2.c -expression %s +void expr() { + struct S *MySPtr; +} diff --git a/test/Import/overloaded-function/Inputs/F1.c b/test/Import/overloaded-function/Inputs/F1.c new file mode 100644 index 0000000000000000000000000000000000000000..fb548b464f2ead7b4840a3f23bebc751e185f53b --- /dev/null +++ b/test/Import/overloaded-function/Inputs/F1.c @@ -0,0 +1 @@ +void f(int arg) { } diff --git a/test/Import/overloaded-function/Inputs/F2.c b/test/Import/overloaded-function/Inputs/F2.c new file mode 100644 index 0000000000000000000000000000000000000000..937efe54d84e0d2f3602f63be07c03f67f208e05 --- /dev/null +++ b/test/Import/overloaded-function/Inputs/F2.c @@ -0,0 +1,4 @@ +struct S { int a; }; + +void f(const char *arg) { } +void f(S arg) { } diff --git a/test/Import/overloaded-function/test.c b/test/Import/overloaded-function/test.c new file mode 100644 index 0000000000000000000000000000000000000000..4f7781bc8e6d455f6df8a919a2bcb508d7ca621e --- /dev/null +++ b/test/Import/overloaded-function/test.c @@ -0,0 +1,7 @@ +// RUN: clang-import-test -import %S/Inputs/F1.c -import %S/Inputs/F2.c -expression %s +void expr() { + f(2); + f("world"); + S s; + f(s); +} diff --git a/test/Import/struct-in-namespace/Inputs/N1.cpp b/test/Import/struct-in-namespace/Inputs/N1.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ddb67a5162130d4eb29162bafccac406ab818618 --- /dev/null +++ b/test/Import/struct-in-namespace/Inputs/N1.cpp @@ -0,0 +1,11 @@ +namespace N { + struct S { + int a; + }; +} + +namespace N { + struct T { + int b; + }; +} diff --git a/test/Import/struct-in-namespace/Inputs/N2.cpp b/test/Import/struct-in-namespace/Inputs/N2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ad97d5dd52ea582899a0088f3d93415e803091b1 --- /dev/null +++ b/test/Import/struct-in-namespace/Inputs/N2.cpp @@ -0,0 +1,5 @@ +namespace N { + struct U { + int c; + }; +} diff --git a/test/Import/struct-in-namespace/Inputs/N3.cpp b/test/Import/struct-in-namespace/Inputs/N3.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e0ec4146747732d2b006f0a0610b3a8f1461b663 --- /dev/null +++ b/test/Import/struct-in-namespace/Inputs/N3.cpp @@ -0,0 +1,5 @@ +namespace M { + struct V { + int d; + }; +} diff --git a/test/Import/struct-in-namespace/test.cpp b/test/Import/struct-in-namespace/test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd14d82d178c98bde1b60e9e2a106a5fa59cd4f4 --- /dev/null +++ b/test/Import/struct-in-namespace/test.cpp @@ -0,0 +1,7 @@ +// RUN: clang-import-test -import %S/Inputs/N1.cpp -import %S/Inputs/N2.cpp -import %S/Inputs/N3.cpp -expression %s +void expr() { + N::S s; + N::T t; + N::U u; + int d = s.a + t.b + u.c; +} diff --git a/test/Import/template-specialization/Inputs/T.cpp b/test/Import/template-specialization/Inputs/T.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b31e2439efebd60171860ba88aa27635ec379b6f --- /dev/null +++ b/test/Import/template-specialization/Inputs/T.cpp @@ -0,0 +1,14 @@ +template <typename T> struct A { +}; + +template <> struct A<int> { + struct B { + int f; + }; +}; + +template <> struct A<bool> { + struct B { + int g; + }; +}; diff --git a/test/Import/template-specialization/test.cpp b/test/Import/template-specialization/test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..43996c53a77eda225f77db8f247465d642a0db4d --- /dev/null +++ b/test/Import/template-specialization/test.cpp @@ -0,0 +1,7 @@ +// RUN: clang-import-test -import %S/Inputs/T.cpp -expression %s +// XFAIL: * +void expr() { + A<int>::B b1; + A<bool>::B b2; + b1.f + b2.g; +} diff --git a/tools/clang-import-test/clang-import-test.cpp b/tools/clang-import-test/clang-import-test.cpp index 33190af4bf459d2868de7693c0ed2d14116da3fe..d7ab18478c3234fef29b296f03cde0a4401bfd7c 100644 --- a/tools/clang-import-test/clang-import-test.cpp +++ b/tools/clang-import-test/clang-import-test.cpp @@ -9,6 +9,8 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTImporter.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/ExternalASTMerger.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/SourceLocation.h" @@ -189,61 +191,18 @@ std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI, } // end namespace namespace { -class TestExternalASTSource : public ExternalASTSource { -private: - llvm::ArrayRef<std::unique_ptr<CompilerInstance>> ImportCIs; - std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ForwardImporters; - std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ReverseImporters; - -public: - TestExternalASTSource( - CompilerInstance &ExpressionCI, - llvm::ArrayRef<std::unique_ptr<CompilerInstance>> ImportCIs) - : ImportCIs(ImportCIs) { - for (const std::unique_ptr<CompilerInstance> &ImportCI : ImportCIs) { - ForwardImporters[ImportCI.get()] = llvm::make_unique<ASTImporter>( - ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), - ImportCI->getASTContext(), ImportCI->getFileManager(), - /*MinimalImport=*/true); - ReverseImporters[ImportCI.get()] = llvm::make_unique<ASTImporter>( - ImportCI->getASTContext(), ImportCI->getFileManager(), - ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), - /*MinimalImport=*/true); - } - } - - bool FindExternalVisibleDeclsByName(const DeclContext *DC, - DeclarationName Name) override { - llvm::SmallVector<NamedDecl *, 1> Decls; - - if (isa<TranslationUnitDecl>(DC)) { - for (const std::unique_ptr<CompilerInstance> &I : ImportCIs) { - DeclarationName FromName = ReverseImporters[I.get()]->Import(Name); - DeclContextLookupResult Result = - I->getASTContext().getTranslationUnitDecl()->lookup(FromName); - for (NamedDecl *FromD : Result) { - NamedDecl *D = - llvm::cast<NamedDecl>(ForwardImporters[I.get()]->Import(FromD)); - Decls.push_back(D); - } - } - } - if (Decls.empty()) { - return false; - } else { - SetExternalVisibleDeclsForName(DC, Name, Decls); - return true; - } - } -}; - + void AddExternalSource( CompilerInstance &CI, llvm::ArrayRef<std::unique_ptr<CompilerInstance>> Imports) { - ASTContext &AST = CI.getASTContext(); - auto ES = llvm::make_unique<TestExternalASTSource>(CI, Imports); - AST.setExternalSource(ES.release()); - AST.getTranslationUnitDecl()->setHasExternalVisibleStorage(); + ExternalASTMerger::ImporterEndpoint Target({CI.getASTContext(), CI.getFileManager()}); + llvm::SmallVector<ExternalASTMerger::ImporterEndpoint, 3> Sources; + for (const std::unique_ptr<CompilerInstance> &CI : Imports) { + Sources.push_back({CI->getASTContext(), CI->getFileManager()}); + } + auto ES = llvm::make_unique<ExternalASTMerger>(Target, Sources); + CI.getASTContext().setExternalSource(ES.release()); + CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(); } llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI, @@ -292,6 +251,7 @@ Parse(const std::string &Path, return std::move(CI); } } + } // end namespace int main(int argc, const char **argv) {