Index: cfe/trunk/include/clang/AST/ExternalASTMerger.h =================================================================== --- cfe/trunk/include/clang/AST/ExternalASTMerger.h +++ cfe/trunk/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 Forward; + std::unique_ptr Reverse; + }; + +private: + std::vector Importers; + +public: + struct ImporterEndpoint { + ASTContext &AST; + FileManager &FM; + }; + ExternalASTMerger(const ImporterEndpoint &Target, + llvm::ArrayRef Sources); + + bool FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) override; + + void + FindExternalLexicalDecls(const DeclContext *DC, + llvm::function_ref IsKindWeWant, + SmallVectorImpl &Result) override; +}; + +} // end namespace clang + +#endif Index: cfe/trunk/lib/AST/CMakeLists.txt =================================================================== --- cfe/trunk/lib/AST/CMakeLists.txt +++ cfe/trunk/lib/AST/CMakeLists.txt @@ -31,6 +31,7 @@ ExprConstant.cpp ExprCXX.cpp ExprObjC.cpp + ExternalASTMerger.cpp ExternalASTSource.cpp InheritViz.cpp ItaniumCXXABI.cpp Index: cfe/trunk/lib/AST/ExternalASTMerger.cpp =================================================================== --- cfe/trunk/lib/AST/ExternalASTMerger.cpp +++ cfe/trunk/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 struct Source { + T t; + Source(T &&t) : t(std::move(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; + +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(To)) { + ToTag->setHasExternalLexicalStorage(); + } else if (auto ToNamespace = dyn_cast(To)) { + ToNamespace->setHasExternalVisibleStorage(); + } + return ASTImporter::Imported(From, To); + } +}; + +Source +LookupSameContext(Source SourceTU, const DeclContext *DC, + ASTImporter &ReverseImporter) { + 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(); + // 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(SearchResultDecl); + } +} + +bool IsForwardDeclaration(Decl *D) { + assert(!isa(D)); // TODO handle this case + if (auto TD = dyn_cast(D)) { + return !TD->isThisDeclarationADefinition(); + } else if (auto FD = dyn_cast(D)) { + return !FD->isThisDeclarationADefinition(); + } else { + return false; + } +} + +void ForEachMatchingDC( + const DeclContext *DC, + llvm::ArrayRef Importers, + std::function SourceDC)> + Callback) { + for (const ExternalASTMerger::ImporterPair &IP : Importers) { + Source SourceTU( + IP.Forward->getFromContext().getTranslationUnitDecl()); + Source SourceDC = + LookupSameContext(SourceTU, DC, *IP.Reverse); + if (SourceDC.get()) { + Callback(IP, SourceDC); + } + } +} + +bool HasDeclOfSameType(llvm::ArrayRef 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 Sources) { + for (const ImporterEndpoint &S : Sources) { + Importers.push_back( + {llvm::make_unique(Target.AST, Target.FM, S.AST, S.FM), + llvm::make_unique(S.AST, S.FM, Target.AST, Target.FM, + /*MinimalImport=*/true)}); + } +} + +bool ExternalASTMerger::FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) { + llvm::SmallVector Decls; + llvm::SmallVector CompleteDecls; + llvm::SmallVector 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 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 DeclsToReport = + CompleteDecls.empty() ? ForwardDecls : CompleteDecls; + + if (DeclsToReport.empty()) { + return false; + } + + Decls.reserve(DeclsToReport.size()); + for (const Candidate &C : DeclsToReport) { + 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, Importers, [DC, IsKindWeWant](const ImporterPair &IP, + Source SourceDC) { + for (Source SourceDecl : SourceDC.get()->decls()) { + if (IsKindWeWant(SourceDecl.get()->getKind())) { + Decl *ImportedDecl = + IP.Forward->Import(const_cast(SourceDecl.get())); + assert(ImportedDecl->getDeclContext() == DC); + } + } + }); +} Index: cfe/trunk/test/Import/forward-declared-struct/Inputs/S1.c =================================================================== --- cfe/trunk/test/Import/forward-declared-struct/Inputs/S1.c +++ cfe/trunk/test/Import/forward-declared-struct/Inputs/S1.c @@ -0,0 +1 @@ +struct S; Index: cfe/trunk/test/Import/forward-declared-struct/Inputs/S2.c =================================================================== --- cfe/trunk/test/Import/forward-declared-struct/Inputs/S2.c +++ cfe/trunk/test/Import/forward-declared-struct/Inputs/S2.c @@ -0,0 +1,3 @@ +struct S { + int a; +}; Index: cfe/trunk/test/Import/forward-declared-struct/test.c =================================================================== --- cfe/trunk/test/Import/forward-declared-struct/test.c +++ cfe/trunk/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; +} Index: cfe/trunk/test/Import/member-in-struct/Inputs/S.c =================================================================== --- cfe/trunk/test/Import/member-in-struct/Inputs/S.c +++ cfe/trunk/test/Import/member-in-struct/Inputs/S.c @@ -0,0 +1,3 @@ +struct S { + int a; +}; Index: cfe/trunk/test/Import/member-in-struct/test.c =================================================================== --- cfe/trunk/test/Import/member-in-struct/test.c +++ cfe/trunk/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; +} Index: cfe/trunk/test/Import/multiple-forward-declarations/Inputs/S1.c =================================================================== --- cfe/trunk/test/Import/multiple-forward-declarations/Inputs/S1.c +++ cfe/trunk/test/Import/multiple-forward-declarations/Inputs/S1.c @@ -0,0 +1 @@ +struct S; Index: cfe/trunk/test/Import/multiple-forward-declarations/Inputs/S2.c =================================================================== --- cfe/trunk/test/Import/multiple-forward-declarations/Inputs/S2.c +++ cfe/trunk/test/Import/multiple-forward-declarations/Inputs/S2.c @@ -0,0 +1 @@ +struct S; Index: cfe/trunk/test/Import/multiple-forward-declarations/test.c =================================================================== --- cfe/trunk/test/Import/multiple-forward-declarations/test.c +++ cfe/trunk/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; +} Index: cfe/trunk/test/Import/overloaded-function/Inputs/F1.c =================================================================== --- cfe/trunk/test/Import/overloaded-function/Inputs/F1.c +++ cfe/trunk/test/Import/overloaded-function/Inputs/F1.c @@ -0,0 +1 @@ +void f(int arg) { } Index: cfe/trunk/test/Import/overloaded-function/Inputs/F2.c =================================================================== --- cfe/trunk/test/Import/overloaded-function/Inputs/F2.c +++ cfe/trunk/test/Import/overloaded-function/Inputs/F2.c @@ -0,0 +1,4 @@ +struct S { int a; }; + +void f(const char *arg) { } +void f(S arg) { } Index: cfe/trunk/test/Import/overloaded-function/test.c =================================================================== --- cfe/trunk/test/Import/overloaded-function/test.c +++ cfe/trunk/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); +} Index: cfe/trunk/test/Import/struct-in-namespace/Inputs/N1.cpp =================================================================== --- cfe/trunk/test/Import/struct-in-namespace/Inputs/N1.cpp +++ cfe/trunk/test/Import/struct-in-namespace/Inputs/N1.cpp @@ -0,0 +1,11 @@ +namespace N { + struct S { + int a; + }; +} + +namespace N { + struct T { + int b; + }; +} Index: cfe/trunk/test/Import/struct-in-namespace/Inputs/N2.cpp =================================================================== --- cfe/trunk/test/Import/struct-in-namespace/Inputs/N2.cpp +++ cfe/trunk/test/Import/struct-in-namespace/Inputs/N2.cpp @@ -0,0 +1,5 @@ +namespace N { + struct U { + int c; + }; +} Index: cfe/trunk/test/Import/struct-in-namespace/Inputs/N3.cpp =================================================================== --- cfe/trunk/test/Import/struct-in-namespace/Inputs/N3.cpp +++ cfe/trunk/test/Import/struct-in-namespace/Inputs/N3.cpp @@ -0,0 +1,5 @@ +namespace M { + struct V { + int d; + }; +} Index: cfe/trunk/test/Import/struct-in-namespace/test.cpp =================================================================== --- cfe/trunk/test/Import/struct-in-namespace/test.cpp +++ cfe/trunk/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; +} Index: cfe/trunk/test/Import/template-specialization/Inputs/T.cpp =================================================================== --- cfe/trunk/test/Import/template-specialization/Inputs/T.cpp +++ cfe/trunk/test/Import/template-specialization/Inputs/T.cpp @@ -0,0 +1,14 @@ +template struct A { +}; + +template <> struct A { + struct B { + int f; + }; +}; + +template <> struct A { + struct B { + int g; + }; +}; Index: cfe/trunk/test/Import/template-specialization/test.cpp =================================================================== --- cfe/trunk/test/Import/template-specialization/test.cpp +++ cfe/trunk/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::B b1; + A::B b2; + b1.f + b2.g; +} Index: cfe/trunk/tools/clang-import-test/clang-import-test.cpp =================================================================== --- cfe/trunk/tools/clang-import-test/clang-import-test.cpp +++ cfe/trunk/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 @@ } // end namespace namespace { -class TestExternalASTSource : public ExternalASTSource { -private: - llvm::ArrayRef> ImportCIs; - std::map> ForwardImporters; - std::map> ReverseImporters; - -public: - TestExternalASTSource( - CompilerInstance &ExpressionCI, - llvm::ArrayRef> ImportCIs) - : ImportCIs(ImportCIs) { - for (const std::unique_ptr &ImportCI : ImportCIs) { - ForwardImporters[ImportCI.get()] = llvm::make_unique( - ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), - ImportCI->getASTContext(), ImportCI->getFileManager(), - /*MinimalImport=*/true); - ReverseImporters[ImportCI.get()] = llvm::make_unique( - ImportCI->getASTContext(), ImportCI->getFileManager(), - ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), - /*MinimalImport=*/true); - } - } - - bool FindExternalVisibleDeclsByName(const DeclContext *DC, - DeclarationName Name) override { - llvm::SmallVector Decls; - - if (isa(DC)) { - for (const std::unique_ptr &I : ImportCIs) { - DeclarationName FromName = ReverseImporters[I.get()]->Import(Name); - DeclContextLookupResult Result = - I->getASTContext().getTranslationUnitDecl()->lookup(FromName); - for (NamedDecl *FromD : Result) { - NamedDecl *D = - llvm::cast(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> Imports) { - ASTContext &AST = CI.getASTContext(); - auto ES = llvm::make_unique(CI, Imports); - AST.setExternalSource(ES.release()); - AST.getTranslationUnitDecl()->setHasExternalVisibleStorage(); + ExternalASTMerger::ImporterEndpoint Target({CI.getASTContext(), CI.getFileManager()}); + llvm::SmallVector Sources; + for (const std::unique_ptr &CI : Imports) { + Sources.push_back({CI->getASTContext(), CI->getFileManager()}); + } + auto ES = llvm::make_unique(Target, Sources); + CI.getASTContext().setExternalSource(ES.release()); + CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(); } llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI, @@ -292,6 +251,7 @@ return std::move(CI); } } + } // end namespace int main(int argc, const char **argv) {