[PATCH] D38208: Add support for remembering origins to ExternalASTMerger
Sean Callanan via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 25 17:15:30 PDT 2017
Updated the diff. Unfortunately reviews.llvm.org is down, so I'll try
updating that page in the morning.
Sean
On 9/25/17 5:04 AM, Bruno Cardoso Lopes via Phabricator wrote:
> bruno added inline comments.
>
>
> ================
> Comment at: lib/AST/ExternalASTMerger.cpp:79
> + return cast<DeclContext>(SearchResultDecl)->getPrimaryContext();
> + else
> + return nullptr; // This type of lookup is unsupported
> ----------------
> No need for `else` here.
>
>
> ================
> Comment at: lib/AST/ExternalASTMerger.cpp:173
> +ASTImporter &ExternalASTMerger::ImporterForOrigin(ASTContext &OriginContext) {
> + for (const std::unique_ptr<ASTImporter> &I : Importers) {
> + if (&I->getFromContext() == &OriginContext)
> ----------------
> No need for curly braces here.
>
>
> ================
> Comment at: lib/AST/ExternalASTMerger.cpp:189
> +bool ExternalASTMerger::HasImporterForOrigin(ASTContext &OriginContext) {
> + for (const std::unique_ptr<ASTImporter> &I : Importers) {
> + if (&I->getFromContext() == &OriginContext)
> ----------------
> No need for curly braces here.
>
>
> ================
> Comment at: lib/AST/ExternalASTMerger.cpp:289
> + LookupSameContext(Origin.AST->getTranslationUnitDecl(), ToDC, Reverse);
> + if (!FoundFromDC || !IsSameDC(FoundFromDC.get(), Origin.DC)) {
> + if (LoggingEnabled())
> ----------------
> You can probably simplify this to something like:
>
>
> ```
> bool RecordOrigin = !FoundFromDC || !IsSameDC(FoundFromDC.get(), Origin.DC);
>
> if (RecordOrigin)
> RecordOriginImpl(ToDC, Origin, Importer);
>
> if (LoggingEnabled()) {
> logs() << "(ExternalASTMerger*)" << (void*)this
> << " decided";
>
> if (!RecordOrigin)
> logs() << " NOT";
>
> logs() << " to record origin (DeclContext*)" << (void*)Origin.DC
> << ", (ASTContext*)" << (void*)&Origin.AST
> << "\n";
> }
> ```
>
>
> ================
> Comment at: lib/AST/ExternalASTMerger.cpp:380
> +
> + if (Candidates.empty()) {
> + return false;
> ----------------
> No need for curly braces here.
>
>
> ================
> Comment at: tools/clang-import-test/clang-import-test.cpp:329
> CG.GetModule()->print(llvm::outs(), nullptr);
> - if (CI->getDiagnosticClient().getNumErrors()) {
> + if (CI.getDiagnosticClient().getNumErrors()) {
> return llvm::make_error<llvm::StringError>(
> ----------------
> No need for curly braces here
>
>
> Repository:
> rL LLVM
>
> https://reviews.llvm.org/D38208
>
>
>
-------------- next part --------------
Index: include/clang/AST/ExternalASTMerger.h
===================================================================
--- include/clang/AST/ExternalASTMerger.h (revision 313996)
+++ include/clang/AST/ExternalASTMerger.h (working copy)
@@ -1,51 +1,176 @@
//===--- 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"
+#include "llvm/Support/raw_ostream.h"
namespace clang {
+/// ExternalASTSource implementation that merges information from several
+/// ASTContexts.
+///
+/// ExtermalASTMerger maintains a vector of ASTImporters that it uses to import
+/// (potentially incomplete) Decls and DeclContexts from the source ASTContexts
+/// in response to ExternalASTSource API calls.
+///
+/// When lookup occurs in the resulting imported DeclContexts, the original
+/// DeclContexts need to be queried. Roughly, there are three cases here:
+///
+/// - The DeclContext of origin can be found by simple name lookup. In this
+/// case, no additional state is required.
+///
+/// - The DeclContext of origin is different from what would be found by name
+/// lookup. In this case, Origins contains an entry overriding lookup and
+/// specifying the correct pair of DeclContext/ASTContext.
+///
+/// - The DeclContext of origin was determined by another ExterenalASTMerger.
+/// (This is possible when the source ASTContext for one of the Importers has
+/// its own ExternalASTMerger). The origin must be properly forwarded in this
+/// case.
+///
+/// ExternalASTMerger's job is to maintain the data structures necessary to
+/// allow this. The data structures themselves can be extracted (read-only) and
+/// copied for re-use.
class ExternalASTMerger : public ExternalASTSource {
public:
- struct ImporterPair {
- std::unique_ptr<ASTImporter> Forward;
- std::unique_ptr<ASTImporter> Reverse;
+ /// A single origin for a DeclContext. Unlike Decls, DeclContexts do
+ /// not allow their containing ASTContext to be determined in all cases.
+ struct DCOrigin {
+ DeclContext *DC;
+ ASTContext *AST;
};
+ typedef std::map<const DeclContext *, DCOrigin> OriginMap;
+ typedef std::vector<std::unique_ptr<ASTImporter>> ImporterVector;
private:
- std::vector<ImporterPair> Importers;
+ /// One importer exists for each source.
+ ImporterVector Importers;
+ /// Overrides in case name lookup would return nothing or would return
+ /// the wrong thing.
+ OriginMap Origins;
+ /// The installed log stream.
+ llvm::raw_ostream *LogStream;
public:
- struct ImporterEndpoint {
+ /// The target for an ExternalASTMerger.
+ ///
+ /// ASTImporters require both ASTContext and FileManager to be able to
+ /// import SourceLocations properly.
+ struct ImporterTarget {
ASTContext &AST;
FileManager &FM;
};
- ExternalASTMerger(const ImporterEndpoint &Target,
- llvm::ArrayRef<ImporterEndpoint> Sources);
+ /// A source for an ExternalASTMerger.
+ ///
+ /// ASTImporters require both ASTContext and FileManager to be able to
+ /// import SourceLocations properly. Additionally, when import occurs for
+ /// a DeclContext whose origin has been overridden, then this
+ /// ExternalASTMerger must be able to determine that.
+ struct ImporterSource {
+ ASTContext &AST;
+ FileManager &FM;
+ const OriginMap &OM;
+ };
+private:
+ /// The target for this ExtenralASTMerger.
+ ImporterTarget Target;
+
+public:
+ ExternalASTMerger(const ImporterTarget &Target,
+ llvm::ArrayRef<ImporterSource> Sources);
+
+ /// Add a set of ASTContexts as possible origins.
+ ///
+ /// Usually the set will be initialized in the constructor, but long-lived
+ /// ExternalASTMergers may neeed to import from new sources (for example,
+ /// newly-parsed source files).
+ ///
+ /// Ensures that Importers does not gain duplicate entries as a result.
+ void AddSources(llvm::ArrayRef<ImporterSource> Sources);
+
+ /// Remove a set of ASTContexts as possible origins.
+ ///
+ /// Sometimes an origin goes away (for example, if a source file gets
+ /// superseded by a newer version).
+ ///
+ /// The caller is responsible for ensuring that this doesn't leave
+ /// DeclContexts that can't be completed.
+ void RemoveSources(llvm::ArrayRef<ImporterSource> Sources);
+
+ /// Implementation of the ExternalASTSource API.
bool FindExternalVisibleDeclsByName(const DeclContext *DC,
DeclarationName Name) override;
+ /// Implementation of the ExternalASTSource API.
void
FindExternalLexicalDecls(const DeclContext *DC,
llvm::function_ref<bool(Decl::Kind)> IsKindWeWant,
SmallVectorImpl<Decl *> &Result) override;
+
+ /// Implementation of the ExternalASTSource API.
+ void CompleteType(TagDecl *Tag) override;
+
+ /// Implementation of the ExternalASTSource API.
+ void CompleteType(ObjCInterfaceDecl *Interface) override;
+
+ /// Returns true if DC can be found in any source AST context.
+ bool CanComplete(DeclContext *DC);
+
+ /// Records an origin in Origins only if name lookup would find
+ /// something different or nothing at all.
+ void MaybeRecordOrigin(const DeclContext *ToDC, DCOrigin Origin);
+
+ /// Regardless of any checks, override the Origin for a DeclContext.
+ void ForceRecordOrigin(const DeclContext *ToDC, DCOrigin Origin);
+
+ /// Get a read-only view of the Origins map, for use in constructing
+ /// an ImporterSource for another ExternalASTMerger.
+ const OriginMap &GetOrigins() { return Origins; }
+
+ /// Returns true if Importers contains an ASTImporter whose source is
+ /// OriginContext.
+ bool HasImporterForOrigin(ASTContext &OriginContext);
+
+ /// Returns a reference to the ASTRImporter from Importers whose origin
+ /// is OriginContext. This allows manual import of ASTs while preserving the
+ /// OriginMap correctly.
+ ASTImporter &ImporterForOrigin(ASTContext &OriginContext);
+
+ /// Sets the current log stream.
+ void SetLogStream(llvm::raw_string_ostream &Stream) { LogStream = &Stream; }
+private:
+ /// Records and origin in Origins.
+ void RecordOriginImpl(const DeclContext *ToDC, DCOrigin Origin,
+ ASTImporter &importer);
+
+ /// Performs an action for every DeclContext that is identified as
+ /// corresponding (either by forced origin or by name lookup) to DC.
+ template <typename CallbackType>
+ void ForEachMatchingDC(const DeclContext *DC, CallbackType Callback);
+
+public:
+ /// Log something if there is a logging callback installed.
+ llvm::raw_ostream &logs() { return *LogStream; }
+
+ /// True if the log stream is not llvm::nulls();
+ bool LoggingEnabled() { return LogStream != &llvm::nulls(); }
};
} // end namespace clang
#endif
Index: lib/AST/ExternalASTMerger.cpp
===================================================================
--- lib/AST/ExternalASTMerger.cpp (revision 313996)
+++ lib/AST/ExternalASTMerger.cpp (working copy)
@@ -1,186 +1,402 @@
//===- 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 <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;
-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();
- ToTag->setMustBuildLookupTable();
- } else if (auto ToNamespace = dyn_cast<NamespaceDecl>(To)) {
- ToNamespace->setHasExternalVisibleStorage();
- } else if (auto ToContainer = dyn_cast<ObjCContainerDecl>(To)) {
- ToContainer->setHasExternalLexicalStorage();
- ToContainer->setMustBuildLookupTable();
- }
- return ASTImporter::Imported(From, To);
- }
-};
+/// 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);
+ 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
+ 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];
- return dyn_cast<DeclContext>(SearchResultDecl);
+ if (isa<DeclContext>(SearchResultDecl) &&
+ SearchResultDecl->getKind() == DC->getDeclKind())
+ return cast<DeclContext>(SearchResultDecl)->getPrimaryContext();
+ return nullptr; // This type of lookup is unsupported
}
}
-bool IsForwardDeclaration(Decl *D) {
- if (auto TD = dyn_cast<TagDecl>(D)) {
- return !TD->isThisDeclarationADefinition();
- } else if (auto FD = dyn_cast<FunctionDecl>(D)) {
- return !FD->isThisDeclarationADefinition();
- } else if (auto OID = dyn_cast<ObjCInterfaceDecl>(D)) {
- return OID->isThisDeclarationADefinition();
- } else {
- return false;
- }
-}
+/// 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;
-template <typename CallbackType>
-void ForEachMatchingDC(
- const DeclContext *DC,
- llvm::ArrayRef<ExternalASTMerger::ImporterPair> Importers,
- CallbackType Callback) {
- for (const ExternalASTMerger::ImporterPair &IP : Importers) {
- Source<TranslationUnitDecl *> SourceTU =
- IP.Forward->getFromContext().getTranslationUnitDecl();
- if (auto SourceDC = LookupSameContext(SourceTU, DC, *IP.Reverse))
- Callback(IP, SourceDC);
+ 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<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->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->setMustBuildLookupTable();
+ assert(Parent.CanComplete(ToContainer));
+ }
+ return ASTImporter::Imported(From, To);
}
-}
+ 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
-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)});
- }
+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!");
}
-bool ExternalASTMerger::FindExternalVisibleDeclsByName(const DeclContext *DC,
- DeclarationName Name) {
- llvm::SmallVector<NamedDecl *, 1> Decls;
- llvm::SmallVector<Candidate, 4> CompleteDecls;
- llvm::SmallVector<Candidate, 4> ForwardDecls;
+namespace {
+LazyASTImporter &LazyImporterForOrigin(ExternalASTMerger &Merger,
+ ASTContext &OriginContext) {
+ return static_cast<LazyASTImporter &>(
+ Merger.ImporterForOrigin(OriginContext));
+}
+}
- auto FilterFoundDecl = [&CompleteDecls, &ForwardDecls](const Candidate &C) {
- if (IsForwardDeclaration(C.first.get())) {
- if (!HasDeclOfSameType(ForwardDecls, C)) {
- ForwardDecls.push_back(C);
+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;
}
- } else {
- CompleteDecls.push_back(C);
}
- };
+ if (!DidCallback && LoggingEnabled())
+ logs() << "(ExternalASTMerger*)" << (void*)this
+ << " asserting for (DeclContext*)" << (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.Imported(SourceTag, Tag);
+ Forward.ImportDefinition(SourceTag);
+ Tag->setCompleteDefinition(SourceTag->isCompleteDefinition());
+ return true;
+ });
+}
+
+void ExternalASTMerger::CompleteType(ObjCInterfaceDecl *Interface) {
+ assert(Interface->hasExternalLexicalStorage());
ForEachMatchingDC(
- DC, Importers,
- [&](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()));
- }
+ 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.Imported(SourceInterface, Interface);
+ Forward.ImportDefinition(SourceInterface);
+ return true;
});
+}
- llvm::ArrayRef<Candidate> DeclsToReport =
- CompleteDecls.empty() ? ForwardDecls : CompleteDecls;
+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;
+}
- if (DeclsToReport.empty()) {
- return false;
+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::Imported(cast<Decl>(Origin.DC), const_cast<Decl*>(cast<Decl>(ToDC)));
+}
+
+ExternalASTMerger::ExternalASTMerger(const ImporterTarget &Target,
+ llvm::ArrayRef<ImporterSource> Sources) : LogStream(&llvm::nulls()), Target(Target) {
+ AddSources(Sources);
+}
+
+void ExternalASTMerger::AddSources(llvm::ArrayRef<ImporterSource> Sources) {
+ for (const ImporterSource &S : Sources) {
+ assert(&S.AST != &Target.AST);
+ Importers.push_back(llvm::make_unique<LazyASTImporter>(
+ *this, Target.AST, Target.FM, S.AST, S.FM, S.OM));
}
+}
- Decls.reserve(DeclsToReport.size());
- for (const Candidate &C : DeclsToReport) {
+void ExternalASTMerger::RemoveSources(llvm::ArrayRef<ImporterSource> 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<ASTImporter> &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<const DeclContext *, DCOrigin> 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<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 {
+ 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<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,
- [&](const ImporterPair &IP, Source<const DeclContext *> SourceDC) {
- for (const Decl *SourceDecl : SourceDC.get()->decls()) {
- if (IsKindWeWant(SourceDecl->getKind())) {
- Decl *ImportedDecl =
- IP.Forward->Import(const_cast<Decl *>(SourceDecl));
- assert(ImportedDecl->getDeclContext() == DC);
- (void)ImportedDecl;
- }
- }
- });
+ ForEachMatchingDC(DC, [&](ASTImporter &Forward, ASTImporter &Reverse,
+ Source<const DeclContext *> SourceDC) -> bool {
+ for (const Decl *SourceDecl : SourceDC.get()->decls()) {
+ if (IsKindWeWant(SourceDecl->getKind())) {
+ Decl *ImportedDecl = Forward.Import(const_cast<Decl *>(SourceDecl));
+ assert(!ImportedDecl || IsSameDC(ImportedDecl->getDeclContext(), DC));
+ (void)ImportedDecl;
+ }
+ }
+ return false;
+ });
}
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp (revision 313996)
+++ lib/Sema/SemaType.cpp (working copy)
@@ -6312,1393 +6312,1397 @@
switch (Attr.getKind()) {
default:
llvm_unreachable("not a calling convention attribute");
case AttributeList::AT_CDecl:
return AttributedType::attr_cdecl;
case AttributeList::AT_FastCall:
return AttributedType::attr_fastcall;
case AttributeList::AT_StdCall:
return AttributedType::attr_stdcall;
case AttributeList::AT_ThisCall:
return AttributedType::attr_thiscall;
case AttributeList::AT_RegCall:
return AttributedType::attr_regcall;
case AttributeList::AT_Pascal:
return AttributedType::attr_pascal;
case AttributeList::AT_SwiftCall:
return AttributedType::attr_swiftcall;
case AttributeList::AT_VectorCall:
return AttributedType::attr_vectorcall;
case AttributeList::AT_Pcs: {
// The attribute may have had a fixit applied where we treated an
// identifier as a string literal. The contents of the string are valid,
// but the form may not be.
StringRef Str;
if (Attr.isArgExpr(0))
Str = cast<StringLiteral>(Attr.getArgAsExpr(0))->getString();
else
Str = Attr.getArgAsIdent(0)->Ident->getName();
return llvm::StringSwitch<AttributedType::Kind>(Str)
.Case("aapcs", AttributedType::attr_pcs)
.Case("aapcs-vfp", AttributedType::attr_pcs_vfp);
}
case AttributeList::AT_IntelOclBicc:
return AttributedType::attr_inteloclbicc;
case AttributeList::AT_MSABI:
return AttributedType::attr_ms_abi;
case AttributeList::AT_SysVABI:
return AttributedType::attr_sysv_abi;
case AttributeList::AT_PreserveMost:
return AttributedType::attr_preserve_most;
case AttributeList::AT_PreserveAll:
return AttributedType::attr_preserve_all;
}
llvm_unreachable("unexpected attribute kind!");
}
/// Process an individual function attribute. Returns true to
/// indicate that the attribute was handled, false if it wasn't.
static bool handleFunctionTypeAttr(TypeProcessingState &state,
AttributeList &attr,
QualType &type) {
Sema &S = state.getSema();
FunctionTypeUnwrapper unwrapped(S, type);
if (attr.getKind() == AttributeList::AT_NoReturn) {
if (S.CheckNoReturnAttr(attr))
return true;
// Delay if this is not a function type.
if (!unwrapped.isFunctionType())
return false;
// Otherwise we can process right away.
FunctionType::ExtInfo EI = unwrapped.get()->getExtInfo().withNoReturn(true);
type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
return true;
}
// ns_returns_retained is not always a type attribute, but if we got
// here, we're treating it as one right now.
if (attr.getKind() == AttributeList::AT_NSReturnsRetained) {
if (attr.getNumArgs()) return true;
// Delay if this is not a function type.
if (!unwrapped.isFunctionType())
return false;
// Check whether the return type is reasonable.
if (S.checkNSReturnsRetainedReturnType(attr.getLoc(),
unwrapped.get()->getReturnType()))
return true;
// Only actually change the underlying type in ARC builds.
QualType origType = type;
if (state.getSema().getLangOpts().ObjCAutoRefCount) {
FunctionType::ExtInfo EI
= unwrapped.get()->getExtInfo().withProducesResult(true);
type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
}
type = S.Context.getAttributedType(AttributedType::attr_ns_returns_retained,
origType, type);
return true;
}
if (attr.getKind() == AttributeList::AT_AnyX86NoCallerSavedRegisters) {
if (S.CheckNoCallerSavedRegsAttr(attr))
return true;
// Delay if this is not a function type.
if (!unwrapped.isFunctionType())
return false;
FunctionType::ExtInfo EI =
unwrapped.get()->getExtInfo().withNoCallerSavedRegs(true);
type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
return true;
}
if (attr.getKind() == AttributeList::AT_Regparm) {
unsigned value;
if (S.CheckRegparmAttr(attr, value))
return true;
// Delay if this is not a function type.
if (!unwrapped.isFunctionType())
return false;
// Diagnose regparm with fastcall.
const FunctionType *fn = unwrapped.get();
CallingConv CC = fn->getCallConv();
if (CC == CC_X86FastCall) {
S.Diag(attr.getLoc(), diag::err_attributes_are_not_compatible)
<< FunctionType::getNameForCallConv(CC)
<< "regparm";
attr.setInvalid();
return true;
}
FunctionType::ExtInfo EI =
unwrapped.get()->getExtInfo().withRegParm(value);
type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
return true;
}
// Delay if the type didn't work out to a function.
if (!unwrapped.isFunctionType()) return false;
// Otherwise, a calling convention.
CallingConv CC;
if (S.CheckCallingConvAttr(attr, CC))
return true;
const FunctionType *fn = unwrapped.get();
CallingConv CCOld = fn->getCallConv();
AttributedType::Kind CCAttrKind = getCCTypeAttrKind(attr);
if (CCOld != CC) {
// Error out on when there's already an attribute on the type
// and the CCs don't match.
const AttributedType *AT = S.getCallingConvAttributedType(type);
if (AT && AT->getAttrKind() != CCAttrKind) {
S.Diag(attr.getLoc(), diag::err_attributes_are_not_compatible)
<< FunctionType::getNameForCallConv(CC)
<< FunctionType::getNameForCallConv(CCOld);
attr.setInvalid();
return true;
}
}
// Diagnose use of variadic functions with calling conventions that
// don't support them (e.g. because they're callee-cleanup).
// We delay warning about this on unprototyped function declarations
// until after redeclaration checking, just in case we pick up a
// prototype that way. And apparently we also "delay" warning about
// unprototyped function types in general, despite not necessarily having
// much ability to diagnose it later.
if (!supportsVariadicCall(CC)) {
const FunctionProtoType *FnP = dyn_cast<FunctionProtoType>(fn);
if (FnP && FnP->isVariadic()) {
unsigned DiagID = diag::err_cconv_varargs;
// stdcall and fastcall are ignored with a warning for GCC and MS
// compatibility.
bool IsInvalid = true;
if (CC == CC_X86StdCall || CC == CC_X86FastCall) {
DiagID = diag::warn_cconv_varargs;
IsInvalid = false;
}
S.Diag(attr.getLoc(), DiagID) << FunctionType::getNameForCallConv(CC);
if (IsInvalid) attr.setInvalid();
return true;
}
}
// Also diagnose fastcall with regparm.
if (CC == CC_X86FastCall && fn->getHasRegParm()) {
S.Diag(attr.getLoc(), diag::err_attributes_are_not_compatible)
<< "regparm" << FunctionType::getNameForCallConv(CC_X86FastCall);
attr.setInvalid();
return true;
}
// Modify the CC from the wrapped function type, wrap it all back, and then
// wrap the whole thing in an AttributedType as written. The modified type
// might have a different CC if we ignored the attribute.
QualType Equivalent;
if (CCOld == CC) {
Equivalent = type;
} else {
auto EI = unwrapped.get()->getExtInfo().withCallingConv(CC);
Equivalent =
unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI));
}
type = S.Context.getAttributedType(CCAttrKind, type, Equivalent);
return true;
}
bool Sema::hasExplicitCallingConv(QualType &T) {
QualType R = T.IgnoreParens();
while (const AttributedType *AT = dyn_cast<AttributedType>(R)) {
if (AT->isCallingConv())
return true;
R = AT->getModifiedType().IgnoreParens();
}
return false;
}
void Sema::adjustMemberFunctionCC(QualType &T, bool IsStatic, bool IsCtorOrDtor,
SourceLocation Loc) {
FunctionTypeUnwrapper Unwrapped(*this, T);
const FunctionType *FT = Unwrapped.get();
bool IsVariadic = (isa<FunctionProtoType>(FT) &&
cast<FunctionProtoType>(FT)->isVariadic());
CallingConv CurCC = FT->getCallConv();
CallingConv ToCC = Context.getDefaultCallingConvention(IsVariadic, !IsStatic);
if (CurCC == ToCC)
return;
// MS compiler ignores explicit calling convention attributes on structors. We
// should do the same.
if (Context.getTargetInfo().getCXXABI().isMicrosoft() && IsCtorOrDtor) {
// Issue a warning on ignored calling convention -- except of __stdcall.
// Again, this is what MS compiler does.
if (CurCC != CC_X86StdCall)
Diag(Loc, diag::warn_cconv_structors)
<< FunctionType::getNameForCallConv(CurCC);
// Default adjustment.
} else {
// Only adjust types with the default convention. For example, on Windows
// we should adjust a __cdecl type to __thiscall for instance methods, and a
// __thiscall type to __cdecl for static methods.
CallingConv DefaultCC =
Context.getDefaultCallingConvention(IsVariadic, IsStatic);
if (CurCC != DefaultCC || DefaultCC == ToCC)
return;
if (hasExplicitCallingConv(T))
return;
}
FT = Context.adjustFunctionType(FT, FT->getExtInfo().withCallingConv(ToCC));
QualType Wrapped = Unwrapped.wrap(*this, FT);
T = Context.getAdjustedType(T, Wrapped);
}
/// HandleVectorSizeAttribute - this attribute is only applicable to integral
/// and float scalars, although arrays, pointers, and function return values are
/// allowed in conjunction with this construct. Aggregates with this attribute
/// are invalid, even if they are of the same size as a corresponding scalar.
/// The raw attribute should contain precisely 1 argument, the vector size for
/// the variable, measured in bytes. If curType and rawAttr are well formed,
/// this routine will return a new vector type.
static void HandleVectorSizeAttr(QualType& CurType, const AttributeList &Attr,
Sema &S) {
// Check the attribute arguments.
if (Attr.getNumArgs() != 1) {
S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments)
<< Attr.getName() << 1;
Attr.setInvalid();
return;
}
Expr *sizeExpr = static_cast<Expr *>(Attr.getArgAsExpr(0));
llvm::APSInt vecSize(32);
if (sizeExpr->isTypeDependent() || sizeExpr->isValueDependent() ||
!sizeExpr->isIntegerConstantExpr(vecSize, S.Context)) {
S.Diag(Attr.getLoc(), diag::err_attribute_argument_type)
<< Attr.getName() << AANT_ArgumentIntegerConstant
<< sizeExpr->getSourceRange();
Attr.setInvalid();
return;
}
// The base type must be integer (not Boolean or enumeration) or float, and
// can't already be a vector.
if (!CurType->isBuiltinType() || CurType->isBooleanType() ||
(!CurType->isIntegerType() && !CurType->isRealFloatingType())) {
S.Diag(Attr.getLoc(), diag::err_attribute_invalid_vector_type) << CurType;
Attr.setInvalid();
return;
}
unsigned typeSize = static_cast<unsigned>(S.Context.getTypeSize(CurType));
// vecSize is specified in bytes - convert to bits.
unsigned vectorSize = static_cast<unsigned>(vecSize.getZExtValue() * 8);
// the vector size needs to be an integral multiple of the type size.
if (vectorSize % typeSize) {
S.Diag(Attr.getLoc(), diag::err_attribute_invalid_size)
<< sizeExpr->getSourceRange();
Attr.setInvalid();
return;
}
if (VectorType::isVectorSizeTooLarge(vectorSize / typeSize)) {
S.Diag(Attr.getLoc(), diag::err_attribute_size_too_large)
<< sizeExpr->getSourceRange();
Attr.setInvalid();
return;
}
if (vectorSize == 0) {
S.Diag(Attr.getLoc(), diag::err_attribute_zero_size)
<< sizeExpr->getSourceRange();
Attr.setInvalid();
return;
}
// Success! Instantiate the vector type, the number of elements is > 0, and
// not required to be a power of 2, unlike GCC.
CurType = S.Context.getVectorType(CurType, vectorSize/typeSize,
VectorType::GenericVector);
}
/// \brief Process the OpenCL-like ext_vector_type attribute when it occurs on
/// a type.
static void HandleExtVectorTypeAttr(QualType &CurType,
const AttributeList &Attr,
Sema &S) {
// check the attribute arguments.
if (Attr.getNumArgs() != 1) {
S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments)
<< Attr.getName() << 1;
return;
}
Expr *sizeExpr;
// Special case where the argument is a template id.
if (Attr.isArgIdent(0)) {
CXXScopeSpec SS;
SourceLocation TemplateKWLoc;
UnqualifiedId id;
id.setIdentifier(Attr.getArgAsIdent(0)->Ident, Attr.getLoc());
ExprResult Size = S.ActOnIdExpression(S.getCurScope(), SS, TemplateKWLoc,
id, false, false);
if (Size.isInvalid())
return;
sizeExpr = Size.get();
} else {
sizeExpr = Attr.getArgAsExpr(0);
}
// Create the vector type.
QualType T = S.BuildExtVectorType(CurType, sizeExpr, Attr.getLoc());
if (!T.isNull())
CurType = T;
}
static bool isPermittedNeonBaseType(QualType &Ty,
VectorType::VectorKind VecKind, Sema &S) {
const BuiltinType *BTy = Ty->getAs<BuiltinType>();
if (!BTy)
return false;
llvm::Triple Triple = S.Context.getTargetInfo().getTriple();
// Signed poly is mathematically wrong, but has been baked into some ABIs by
// now.
bool IsPolyUnsigned = Triple.getArch() == llvm::Triple::aarch64 ||
Triple.getArch() == llvm::Triple::aarch64_be;
if (VecKind == VectorType::NeonPolyVector) {
if (IsPolyUnsigned) {
// AArch64 polynomial vectors are unsigned and support poly64.
return BTy->getKind() == BuiltinType::UChar ||
BTy->getKind() == BuiltinType::UShort ||
BTy->getKind() == BuiltinType::ULong ||
BTy->getKind() == BuiltinType::ULongLong;
} else {
// AArch32 polynomial vector are signed.
return BTy->getKind() == BuiltinType::SChar ||
BTy->getKind() == BuiltinType::Short;
}
}
// Non-polynomial vector types: the usual suspects are allowed, as well as
// float64_t on AArch64.
bool Is64Bit = Triple.getArch() == llvm::Triple::aarch64 ||
Triple.getArch() == llvm::Triple::aarch64_be;
if (Is64Bit && BTy->getKind() == BuiltinType::Double)
return true;
return BTy->getKind() == BuiltinType::SChar ||
BTy->getKind() == BuiltinType::UChar ||
BTy->getKind() == BuiltinType::Short ||
BTy->getKind() == BuiltinType::UShort ||
BTy->getKind() == BuiltinType::Int ||
BTy->getKind() == BuiltinType::UInt ||
BTy->getKind() == BuiltinType::Long ||
BTy->getKind() == BuiltinType::ULong ||
BTy->getKind() == BuiltinType::LongLong ||
BTy->getKind() == BuiltinType::ULongLong ||
BTy->getKind() == BuiltinType::Float ||
BTy->getKind() == BuiltinType::Half;
}
/// HandleNeonVectorTypeAttr - The "neon_vector_type" and
/// "neon_polyvector_type" attributes are used to create vector types that
/// are mangled according to ARM's ABI. Otherwise, these types are identical
/// to those created with the "vector_size" attribute. Unlike "vector_size"
/// the argument to these Neon attributes is the number of vector elements,
/// not the vector size in bytes. The vector width and element type must
/// match one of the standard Neon vector types.
static void HandleNeonVectorTypeAttr(QualType& CurType,
const AttributeList &Attr, Sema &S,
VectorType::VectorKind VecKind) {
// Target must have NEON
if (!S.Context.getTargetInfo().hasFeature("neon")) {
S.Diag(Attr.getLoc(), diag::err_attribute_unsupported) << Attr.getName();
Attr.setInvalid();
return;
}
// Check the attribute arguments.
if (Attr.getNumArgs() != 1) {
S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments)
<< Attr.getName() << 1;
Attr.setInvalid();
return;
}
// The number of elements must be an ICE.
Expr *numEltsExpr = static_cast<Expr *>(Attr.getArgAsExpr(0));
llvm::APSInt numEltsInt(32);
if (numEltsExpr->isTypeDependent() || numEltsExpr->isValueDependent() ||
!numEltsExpr->isIntegerConstantExpr(numEltsInt, S.Context)) {
S.Diag(Attr.getLoc(), diag::err_attribute_argument_type)
<< Attr.getName() << AANT_ArgumentIntegerConstant
<< numEltsExpr->getSourceRange();
Attr.setInvalid();
return;
}
// Only certain element types are supported for Neon vectors.
if (!isPermittedNeonBaseType(CurType, VecKind, S)) {
S.Diag(Attr.getLoc(), diag::err_attribute_invalid_vector_type) << CurType;
Attr.setInvalid();
return;
}
// The total size of the vector must be 64 or 128 bits.
unsigned typeSize = static_cast<unsigned>(S.Context.getTypeSize(CurType));
unsigned numElts = static_cast<unsigned>(numEltsInt.getZExtValue());
unsigned vecSize = typeSize * numElts;
if (vecSize != 64 && vecSize != 128) {
S.Diag(Attr.getLoc(), diag::err_attribute_bad_neon_vector_size) << CurType;
Attr.setInvalid();
return;
}
CurType = S.Context.getVectorType(CurType, numElts, VecKind);
}
/// Handle OpenCL Access Qualifier Attribute.
static void HandleOpenCLAccessAttr(QualType &CurType, const AttributeList &Attr,
Sema &S) {
// OpenCL v2.0 s6.6 - Access qualifier can be used only for image and pipe type.
if (!(CurType->isImageType() || CurType->isPipeType())) {
S.Diag(Attr.getLoc(), diag::err_opencl_invalid_access_qualifier);
Attr.setInvalid();
return;
}
if (const TypedefType* TypedefTy = CurType->getAs<TypedefType>()) {
QualType PointeeTy = TypedefTy->desugar();
S.Diag(Attr.getLoc(), diag::err_opencl_multiple_access_qualifiers);
std::string PrevAccessQual;
switch (cast<BuiltinType>(PointeeTy.getTypePtr())->getKind()) {
#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \
case BuiltinType::Id: \
PrevAccessQual = #Access; \
break;
#include "clang/Basic/OpenCLImageTypes.def"
default:
assert(0 && "Unable to find corresponding image type.");
}
S.Diag(TypedefTy->getDecl()->getLocStart(),
diag::note_opencl_typedef_access_qualifier) << PrevAccessQual;
} else if (CurType->isPipeType()) {
if (Attr.getSemanticSpelling() == OpenCLAccessAttr::Keyword_write_only) {
QualType ElemType = CurType->getAs<PipeType>()->getElementType();
CurType = S.Context.getWritePipeType(ElemType);
}
}
}
static void processTypeAttrs(TypeProcessingState &state, QualType &type,
TypeAttrLocation TAL, AttributeList *attrs) {
// Scan through and apply attributes to this type where it makes sense. Some
// attributes (such as __address_space__, __vector_size__, etc) apply to the
// type, but others can be present in the type specifiers even though they
// apply to the decl. Here we apply type attributes and ignore the rest.
bool hasOpenCLAddressSpace = false;
while (attrs) {
AttributeList &attr = *attrs;
attrs = attr.getNext(); // reset to the next here due to early loop continue
// stmts
// Skip attributes that were marked to be invalid.
if (attr.isInvalid())
continue;
if (attr.isCXX11Attribute()) {
// [[gnu::...]] attributes are treated as declaration attributes, so may
// not appertain to a DeclaratorChunk, even if we handle them as type
// attributes.
if (attr.getScopeName() && attr.getScopeName()->isStr("gnu")) {
if (TAL == TAL_DeclChunk) {
state.getSema().Diag(attr.getLoc(),
diag::warn_cxx11_gnu_attribute_on_type)
<< attr.getName();
continue;
}
} else if (TAL != TAL_DeclChunk) {
// Otherwise, only consider type processing for a C++11 attribute if
// it's actually been applied to a type.
continue;
}
}
// If this is an attribute we can handle, do so now,
// otherwise, add it to the FnAttrs list for rechaining.
switch (attr.getKind()) {
default:
// A C++11 attribute on a declarator chunk must appertain to a type.
if (attr.isCXX11Attribute() && TAL == TAL_DeclChunk) {
state.getSema().Diag(attr.getLoc(), diag::err_attribute_not_type_attr)
<< attr.getName();
attr.setUsedAsTypeAttr();
}
break;
case AttributeList::UnknownAttribute:
if (attr.isCXX11Attribute() && TAL == TAL_DeclChunk)
state.getSema().Diag(attr.getLoc(),
diag::warn_unknown_attribute_ignored)
<< attr.getName();
break;
case AttributeList::IgnoredAttribute:
break;
case AttributeList::AT_MayAlias:
// FIXME: This attribute needs to actually be handled, but if we ignore
// it it breaks large amounts of Linux software.
attr.setUsedAsTypeAttr();
break;
case AttributeList::AT_OpenCLPrivateAddressSpace:
case AttributeList::AT_OpenCLGlobalAddressSpace:
case AttributeList::AT_OpenCLLocalAddressSpace:
case AttributeList::AT_OpenCLConstantAddressSpace:
case AttributeList::AT_OpenCLGenericAddressSpace:
case AttributeList::AT_AddressSpace:
HandleAddressSpaceTypeAttribute(type, attr, state.getSema());
attr.setUsedAsTypeAttr();
hasOpenCLAddressSpace = true;
break;
OBJC_POINTER_TYPE_ATTRS_CASELIST:
if (!handleObjCPointerTypeAttr(state, attr, type))
distributeObjCPointerTypeAttr(state, attr, type);
attr.setUsedAsTypeAttr();
break;
case AttributeList::AT_VectorSize:
HandleVectorSizeAttr(type, attr, state.getSema());
attr.setUsedAsTypeAttr();
break;
case AttributeList::AT_ExtVectorType:
HandleExtVectorTypeAttr(type, attr, state.getSema());
attr.setUsedAsTypeAttr();
break;
case AttributeList::AT_NeonVectorType:
HandleNeonVectorTypeAttr(type, attr, state.getSema(),
VectorType::NeonVector);
attr.setUsedAsTypeAttr();
break;
case AttributeList::AT_NeonPolyVectorType:
HandleNeonVectorTypeAttr(type, attr, state.getSema(),
VectorType::NeonPolyVector);
attr.setUsedAsTypeAttr();
break;
case AttributeList::AT_OpenCLAccess:
HandleOpenCLAccessAttr(type, attr, state.getSema());
attr.setUsedAsTypeAttr();
break;
MS_TYPE_ATTRS_CASELIST:
if (!handleMSPointerTypeQualifierAttr(state, attr, type))
attr.setUsedAsTypeAttr();
break;
NULLABILITY_TYPE_ATTRS_CASELIST:
// Either add nullability here or try to distribute it. We
// don't want to distribute the nullability specifier past any
// dependent type, because that complicates the user model.
if (type->canHaveNullability() || type->isDependentType() ||
type->isArrayType() ||
!distributeNullabilityTypeAttr(state, type, attr)) {
unsigned endIndex;
if (TAL == TAL_DeclChunk)
endIndex = state.getCurrentChunkIndex();
else
endIndex = state.getDeclarator().getNumTypeObjects();
bool allowOnArrayType =
state.getDeclarator().isPrototypeContext() &&
!hasOuterPointerLikeChunk(state.getDeclarator(), endIndex);
if (state.getSema().checkNullabilityTypeSpecifier(
type,
mapNullabilityAttrKind(attr.getKind()),
attr.getLoc(),
attr.isContextSensitiveKeywordAttribute(),
allowOnArrayType)) {
attr.setInvalid();
}
attr.setUsedAsTypeAttr();
}
break;
case AttributeList::AT_ObjCKindOf:
// '__kindof' must be part of the decl-specifiers.
switch (TAL) {
case TAL_DeclSpec:
break;
case TAL_DeclChunk:
case TAL_DeclName:
state.getSema().Diag(attr.getLoc(),
diag::err_objc_kindof_wrong_position)
<< FixItHint::CreateRemoval(attr.getLoc())
<< FixItHint::CreateInsertion(
state.getDeclarator().getDeclSpec().getLocStart(), "__kindof ");
break;
}
// Apply it regardless.
if (state.getSema().checkObjCKindOfType(type, attr.getLoc()))
attr.setInvalid();
attr.setUsedAsTypeAttr();
break;
FUNCTION_TYPE_ATTRS_CASELIST:
attr.setUsedAsTypeAttr();
// Never process function type attributes as part of the
// declaration-specifiers.
if (TAL == TAL_DeclSpec)
distributeFunctionTypeAttrFromDeclSpec(state, attr, type);
// Otherwise, handle the possible delays.
else if (!handleFunctionTypeAttr(state, attr, type))
distributeFunctionTypeAttr(state, attr, type);
break;
}
}
// If address space is not set, OpenCL 2.0 defines non private default
// address spaces for some cases:
// OpenCL 2.0, section 6.5:
// The address space for a variable at program scope or a static variable
// inside a function can either be __global or __constant, but defaults to
// __global if not specified.
// (...)
// Pointers that are declared without pointing to a named address space point
// to the generic address space.
if (state.getSema().getLangOpts().OpenCLVersion >= 200 &&
!hasOpenCLAddressSpace && type.getAddressSpace() == 0 &&
(TAL == TAL_DeclSpec || TAL == TAL_DeclChunk)) {
Declarator &D = state.getDeclarator();
if (state.getCurrentChunkIndex() > 0 &&
(D.getTypeObject(state.getCurrentChunkIndex() - 1).Kind ==
DeclaratorChunk::Pointer ||
D.getTypeObject(state.getCurrentChunkIndex() - 1).Kind ==
DeclaratorChunk::BlockPointer)) {
type = state.getSema().Context.getAddrSpaceQualType(
type, LangAS::opencl_generic);
} else if (state.getCurrentChunkIndex() == 0 &&
D.getContext() == Declarator::FileContext &&
!D.isFunctionDeclarator() && !D.isFunctionDefinition() &&
D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef &&
!type->isSamplerT())
type = state.getSema().Context.getAddrSpaceQualType(
type, LangAS::opencl_global);
else if (state.getCurrentChunkIndex() == 0 &&
D.getContext() == Declarator::BlockContext &&
D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)
type = state.getSema().Context.getAddrSpaceQualType(
type, LangAS::opencl_global);
}
}
void Sema::completeExprArrayBound(Expr *E) {
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) {
if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
if (isTemplateInstantiation(Var->getTemplateSpecializationKind())) {
SourceLocation PointOfInstantiation = E->getExprLoc();
if (MemberSpecializationInfo *MSInfo =
Var->getMemberSpecializationInfo()) {
// If we don't already have a point of instantiation, this is it.
if (MSInfo->getPointOfInstantiation().isInvalid()) {
MSInfo->setPointOfInstantiation(PointOfInstantiation);
// This is a modification of an existing AST node. Notify
// listeners.
if (ASTMutationListener *L = getASTMutationListener())
L->StaticDataMemberInstantiated(Var);
}
} else {
VarTemplateSpecializationDecl *VarSpec =
cast<VarTemplateSpecializationDecl>(Var);
if (VarSpec->getPointOfInstantiation().isInvalid())
VarSpec->setPointOfInstantiation(PointOfInstantiation);
}
InstantiateVariableDefinition(PointOfInstantiation, Var);
// Update the type to the newly instantiated definition's type both
// here and within the expression.
if (VarDecl *Def = Var->getDefinition()) {
DRE->setDecl(Def);
QualType T = Def->getType();
DRE->setType(T);
// FIXME: Update the type on all intervening expressions.
E->setType(T);
}
// We still go on to try to complete the type independently, as it
// may also require instantiations or diagnostics if it remains
// incomplete.
}
}
}
}
/// \brief Ensure that the type of the given expression is complete.
///
/// This routine checks whether the expression \p E has a complete type. If the
/// expression refers to an instantiable construct, that instantiation is
/// performed as needed to complete its type. Furthermore
/// Sema::RequireCompleteType is called for the expression's type (or in the
/// case of a reference type, the referred-to type).
///
/// \param E The expression whose type is required to be complete.
/// \param Diagnoser The object that will emit a diagnostic if the type is
/// incomplete.
///
/// \returns \c true if the type of \p E is incomplete and diagnosed, \c false
/// otherwise.
bool Sema::RequireCompleteExprType(Expr *E, TypeDiagnoser &Diagnoser) {
QualType T = E->getType();
// Incomplete array types may be completed by the initializer attached to
// their definitions. For static data members of class templates and for
// variable templates, we need to instantiate the definition to get this
// initializer and complete the type.
if (T->isIncompleteArrayType()) {
completeExprArrayBound(E);
T = E->getType();
}
// FIXME: Are there other cases which require instantiating something other
// than the type to complete the type of an expression?
return RequireCompleteType(E->getExprLoc(), T, Diagnoser);
}
bool Sema::RequireCompleteExprType(Expr *E, unsigned DiagID) {
BoundTypeDiagnoser<> Diagnoser(DiagID);
return RequireCompleteExprType(E, Diagnoser);
}
/// @brief Ensure that the type T is a complete type.
///
/// This routine checks whether the type @p T is complete in any
/// context where a complete type is required. If @p T is a complete
/// type, returns false. If @p T is a class template specialization,
/// this routine then attempts to perform class template
/// instantiation. If instantiation fails, or if @p T is incomplete
/// and cannot be completed, issues the diagnostic @p diag (giving it
/// the type @p T) and returns true.
///
/// @param Loc The location in the source that the incomplete type
/// diagnostic should refer to.
///
/// @param T The type that this routine is examining for completeness.
///
/// @returns @c true if @p T is incomplete and a diagnostic was emitted,
/// @c false otherwise.
bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
TypeDiagnoser &Diagnoser) {
if (RequireCompleteTypeImpl(Loc, T, &Diagnoser))
return true;
if (const TagType *Tag = T->getAs<TagType>()) {
if (!Tag->getDecl()->isCompleteDefinitionRequired()) {
Tag->getDecl()->setCompleteDefinitionRequired();
Consumer.HandleTagDeclRequiredDefinition(Tag->getDecl());
}
}
return false;
}
bool Sema::hasStructuralCompatLayout(Decl *D, Decl *Suggested) {
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
if (!Suggested)
return false;
// FIXME: Add a specific mode for C11 6.2.7/1 in StructuralEquivalenceContext
// and isolate from other C++ specific checks.
StructuralEquivalenceContext Ctx(
D->getASTContext(), Suggested->getASTContext(), NonEquivalentDecls,
false /*StrictTypeSpelling*/, true /*Complain*/,
true /*ErrorOnTagTypeMismatch*/);
return Ctx.IsStructurallyEquivalent(D, Suggested);
}
/// \brief Determine whether there is any declaration of \p D that was ever a
/// definition (perhaps before module merging) and is currently visible.
/// \param D The definition of the entity.
/// \param Suggested Filled in with the declaration that should be made visible
/// in order to provide a definition of this entity.
/// \param OnlyNeedComplete If \c true, we only need the type to be complete,
/// not defined. This only matters for enums with a fixed underlying
/// type, since in all other cases, a type is complete if and only if it
/// is defined.
bool Sema::hasVisibleDefinition(NamedDecl *D, NamedDecl **Suggested,
bool OnlyNeedComplete) {
// Easy case: if we don't have modules, all declarations are visible.
if (!getLangOpts().Modules && !getLangOpts().ModulesLocalVisibility)
return true;
// If this definition was instantiated from a template, map back to the
// pattern from which it was instantiated.
if (isa<TagDecl>(D) && cast<TagDecl>(D)->isBeingDefined()) {
// We're in the middle of defining it; this definition should be treated
// as visible.
return true;
} else if (auto *RD = dyn_cast<CXXRecordDecl>(D)) {
if (auto *Pattern = RD->getTemplateInstantiationPattern())
RD = Pattern;
D = RD->getDefinition();
} else if (auto *ED = dyn_cast<EnumDecl>(D)) {
if (auto *Pattern = ED->getTemplateInstantiationPattern())
ED = Pattern;
if (OnlyNeedComplete && ED->isFixed()) {
// If the enum has a fixed underlying type, and we're only looking for a
// complete type (not a definition), any visible declaration of it will
// do.
*Suggested = nullptr;
for (auto *Redecl : ED->redecls()) {
if (isVisible(Redecl))
return true;
if (Redecl->isThisDeclarationADefinition() ||
(Redecl->isCanonicalDecl() && !*Suggested))
*Suggested = Redecl;
}
return false;
}
D = ED->getDefinition();
} else if (auto *FD = dyn_cast<FunctionDecl>(D)) {
if (auto *Pattern = FD->getTemplateInstantiationPattern())
FD = Pattern;
D = FD->getDefinition();
} else if (auto *VD = dyn_cast<VarDecl>(D)) {
if (auto *Pattern = VD->getTemplateInstantiationPattern())
VD = Pattern;
D = VD->getDefinition();
}
assert(D && "missing definition for pattern of instantiated definition");
*Suggested = D;
if (isVisible(D))
return true;
// The external source may have additional definitions of this entity that are
// visible, so complete the redeclaration chain now and ask again.
if (auto *Source = Context.getExternalSource()) {
Source->CompleteRedeclChain(D);
return isVisible(D);
}
return false;
}
/// Locks in the inheritance model for the given class and all of its bases.
static void assignInheritanceModel(Sema &S, CXXRecordDecl *RD) {
RD = RD->getMostRecentDecl();
if (!RD->hasAttr<MSInheritanceAttr>()) {
MSInheritanceAttr::Spelling IM;
switch (S.MSPointerToMemberRepresentationMethod) {
case LangOptions::PPTMK_BestCase:
IM = RD->calculateInheritanceModel();
break;
case LangOptions::PPTMK_FullGeneralitySingleInheritance:
IM = MSInheritanceAttr::Keyword_single_inheritance;
break;
case LangOptions::PPTMK_FullGeneralityMultipleInheritance:
IM = MSInheritanceAttr::Keyword_multiple_inheritance;
break;
case LangOptions::PPTMK_FullGeneralityVirtualInheritance:
IM = MSInheritanceAttr::Keyword_unspecified_inheritance;
break;
}
RD->addAttr(MSInheritanceAttr::CreateImplicit(
S.getASTContext(), IM,
/*BestCase=*/S.MSPointerToMemberRepresentationMethod ==
LangOptions::PPTMK_BestCase,
S.ImplicitMSInheritanceAttrLoc.isValid()
? S.ImplicitMSInheritanceAttrLoc
: RD->getSourceRange()));
S.Consumer.AssignInheritanceModel(RD);
}
}
/// \brief The implementation of RequireCompleteType
bool Sema::RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
TypeDiagnoser *Diagnoser) {
// FIXME: Add this assertion to make sure we always get instantiation points.
// assert(!Loc.isInvalid() && "Invalid location in RequireCompleteType");
// FIXME: Add this assertion to help us flush out problems with
// checking for dependent types and type-dependent expressions.
//
// assert(!T->isDependentType() &&
// "Can't ask whether a dependent type is complete");
// We lock in the inheritance model once somebody has asked us to ensure
// that a pointer-to-member type is complete.
if (Context.getTargetInfo().getCXXABI().isMicrosoft()) {
if (const MemberPointerType *MPTy = T->getAs<MemberPointerType>()) {
if (!MPTy->getClass()->isDependentType()) {
(void)isCompleteType(Loc, QualType(MPTy->getClass(), 0));
assignInheritanceModel(*this, MPTy->getMostRecentCXXRecordDecl());
}
}
}
NamedDecl *Def = nullptr;
bool Incomplete = T->isIncompleteType(&Def);
// Check that any necessary explicit specializations are visible. For an
// enum, we just need the declaration, so don't check this.
if (Def && !isa<EnumDecl>(Def))
checkSpecializationVisibility(Loc, Def);
// If we have a complete type, we're done.
if (!Incomplete) {
// If we know about the definition but it is not visible, complain.
NamedDecl *SuggestedDef = nullptr;
if (Def &&
!hasVisibleDefinition(Def, &SuggestedDef, /*OnlyNeedComplete*/true)) {
// If the user is going to see an error here, recover by making the
// definition visible.
bool TreatAsComplete = Diagnoser && !isSFINAEContext();
if (Diagnoser)
diagnoseMissingImport(Loc, SuggestedDef, MissingImportKind::Definition,
/*Recover*/TreatAsComplete);
return !TreatAsComplete;
}
return false;
}
const TagType *Tag = T->getAs<TagType>();
const ObjCInterfaceType *IFace = T->getAs<ObjCInterfaceType>();
// If there's an unimported definition of this type in a module (for
// instance, because we forward declared it, then imported the definition),
// import that definition now.
//
// FIXME: What about other cases where an import extends a redeclaration
// chain for a declaration that can be accessed through a mechanism other
// than name lookup (eg, referenced in a template, or a variable whose type
// could be completed by the module)?
//
// FIXME: Should we map through to the base array element type before
// checking for a tag type?
if (Tag || IFace) {
NamedDecl *D =
Tag ? static_cast<NamedDecl *>(Tag->getDecl()) : IFace->getDecl();
// Avoid diagnosing invalid decls as incomplete.
if (D->isInvalidDecl())
return true;
// Give the external AST source a chance to complete the type.
if (auto *Source = Context.getExternalSource()) {
- if (Tag)
- Source->CompleteType(Tag->getDecl());
- else
- Source->CompleteType(IFace->getDecl());
-
+ if (Tag) {
+ TagDecl *TagD = Tag->getDecl();
+ if (TagD->hasExternalLexicalStorage())
+ Source->CompleteType(TagD);
+ } else {
+ ObjCInterfaceDecl *IFaceD = IFace->getDecl();
+ if (IFaceD->hasExternalLexicalStorage())
+ Source->CompleteType(IFace->getDecl());
+ }
// If the external source completed the type, go through the motions
// again to ensure we're allowed to use the completed type.
if (!T->isIncompleteType())
return RequireCompleteTypeImpl(Loc, T, Diagnoser);
}
}
// If we have a class template specialization or a class member of a
// class template specialization, or an array with known size of such,
// try to instantiate it.
QualType MaybeTemplate = T;
while (const ConstantArrayType *Array
= Context.getAsConstantArrayType(MaybeTemplate))
MaybeTemplate = Array->getElementType();
if (const RecordType *Record = MaybeTemplate->getAs<RecordType>()) {
bool Instantiated = false;
bool Diagnosed = false;
if (ClassTemplateSpecializationDecl *ClassTemplateSpec
= dyn_cast<ClassTemplateSpecializationDecl>(Record->getDecl())) {
if (ClassTemplateSpec->getSpecializationKind() == TSK_Undeclared) {
Diagnosed = InstantiateClassTemplateSpecialization(
Loc, ClassTemplateSpec, TSK_ImplicitInstantiation,
/*Complain=*/Diagnoser);
Instantiated = true;
}
} else if (CXXRecordDecl *Rec
= dyn_cast<CXXRecordDecl>(Record->getDecl())) {
CXXRecordDecl *Pattern = Rec->getInstantiatedFromMemberClass();
if (!Rec->isBeingDefined() && Pattern) {
MemberSpecializationInfo *MSI = Rec->getMemberSpecializationInfo();
assert(MSI && "Missing member specialization information?");
// This record was instantiated from a class within a template.
if (MSI->getTemplateSpecializationKind() !=
TSK_ExplicitSpecialization) {
Diagnosed = InstantiateClass(Loc, Rec, Pattern,
getTemplateInstantiationArgs(Rec),
TSK_ImplicitInstantiation,
/*Complain=*/Diagnoser);
Instantiated = true;
}
}
}
if (Instantiated) {
// Instantiate* might have already complained that the template is not
// defined, if we asked it to.
if (Diagnoser && Diagnosed)
return true;
// If we instantiated a definition, check that it's usable, even if
// instantiation produced an error, so that repeated calls to this
// function give consistent answers.
if (!T->isIncompleteType())
return RequireCompleteTypeImpl(Loc, T, Diagnoser);
}
}
// FIXME: If we didn't instantiate a definition because of an explicit
// specialization declaration, check that it's visible.
if (!Diagnoser)
return true;
Diagnoser->diagnose(*this, Loc, T);
// If the type was a forward declaration of a class/struct/union
// type, produce a note.
if (Tag && !Tag->getDecl()->isInvalidDecl())
Diag(Tag->getDecl()->getLocation(),
Tag->isBeingDefined() ? diag::note_type_being_defined
: diag::note_forward_declaration)
<< QualType(Tag, 0);
// If the Objective-C class was a forward declaration, produce a note.
if (IFace && !IFace->getDecl()->isInvalidDecl())
Diag(IFace->getDecl()->getLocation(), diag::note_forward_class);
// If we have external information that we can use to suggest a fix,
// produce a note.
if (ExternalSource)
ExternalSource->MaybeDiagnoseMissingCompleteType(Loc, T);
return true;
}
bool Sema::RequireCompleteType(SourceLocation Loc, QualType T,
unsigned DiagID) {
BoundTypeDiagnoser<> Diagnoser(DiagID);
return RequireCompleteType(Loc, T, Diagnoser);
}
/// \brief Get diagnostic %select index for tag kind for
/// literal type diagnostic message.
/// WARNING: Indexes apply to particular diagnostics only!
///
/// \returns diagnostic %select index.
static unsigned getLiteralDiagFromTagKind(TagTypeKind Tag) {
switch (Tag) {
case TTK_Struct: return 0;
case TTK_Interface: return 1;
case TTK_Class: return 2;
default: llvm_unreachable("Invalid tag kind for literal type diagnostic!");
}
}
/// @brief Ensure that the type T is a literal type.
///
/// This routine checks whether the type @p T is a literal type. If @p T is an
/// incomplete type, an attempt is made to complete it. If @p T is a literal
/// type, or @p AllowIncompleteType is true and @p T is an incomplete type,
/// returns false. Otherwise, this routine issues the diagnostic @p PD (giving
/// it the type @p T), along with notes explaining why the type is not a
/// literal type, and returns true.
///
/// @param Loc The location in the source that the non-literal type
/// diagnostic should refer to.
///
/// @param T The type that this routine is examining for literalness.
///
/// @param Diagnoser Emits a diagnostic if T is not a literal type.
///
/// @returns @c true if @p T is not a literal type and a diagnostic was emitted,
/// @c false otherwise.
bool Sema::RequireLiteralType(SourceLocation Loc, QualType T,
TypeDiagnoser &Diagnoser) {
assert(!T->isDependentType() && "type should not be dependent");
QualType ElemType = Context.getBaseElementType(T);
if ((isCompleteType(Loc, ElemType) || ElemType->isVoidType()) &&
T->isLiteralType(Context))
return false;
Diagnoser.diagnose(*this, Loc, T);
if (T->isVariableArrayType())
return true;
const RecordType *RT = ElemType->getAs<RecordType>();
if (!RT)
return true;
const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
// A partially-defined class type can't be a literal type, because a literal
// class type must have a trivial destructor (which can't be checked until
// the class definition is complete).
if (RequireCompleteType(Loc, ElemType, diag::note_non_literal_incomplete, T))
return true;
// If the class has virtual base classes, then it's not an aggregate, and
// cannot have any constexpr constructors or a trivial default constructor,
// so is non-literal. This is better to diagnose than the resulting absence
// of constexpr constructors.
if (RD->getNumVBases()) {
Diag(RD->getLocation(), diag::note_non_literal_virtual_base)
<< getLiteralDiagFromTagKind(RD->getTagKind()) << RD->getNumVBases();
for (const auto &I : RD->vbases())
Diag(I.getLocStart(), diag::note_constexpr_virtual_base_here)
<< I.getSourceRange();
} else if (!RD->isAggregate() && !RD->hasConstexprNonCopyMoveConstructor() &&
!RD->hasTrivialDefaultConstructor()) {
Diag(RD->getLocation(), diag::note_non_literal_no_constexpr_ctors) << RD;
} else if (RD->hasNonLiteralTypeFieldsOrBases()) {
for (const auto &I : RD->bases()) {
if (!I.getType()->isLiteralType(Context)) {
Diag(I.getLocStart(),
diag::note_non_literal_base_class)
<< RD << I.getType() << I.getSourceRange();
return true;
}
}
for (const auto *I : RD->fields()) {
if (!I->getType()->isLiteralType(Context) ||
I->getType().isVolatileQualified()) {
Diag(I->getLocation(), diag::note_non_literal_field)
<< RD << I << I->getType()
<< I->getType().isVolatileQualified();
return true;
}
}
} else if (!RD->hasTrivialDestructor()) {
// All fields and bases are of literal types, so have trivial destructors.
// If this class's destructor is non-trivial it must be user-declared.
CXXDestructorDecl *Dtor = RD->getDestructor();
assert(Dtor && "class has literal fields and bases but no dtor?");
if (!Dtor)
return true;
Diag(Dtor->getLocation(), Dtor->isUserProvided() ?
diag::note_non_literal_user_provided_dtor :
diag::note_non_literal_nontrivial_dtor) << RD;
if (!Dtor->isUserProvided())
SpecialMemberIsTrivial(Dtor, CXXDestructor, /*Diagnose*/true);
}
return true;
}
bool Sema::RequireLiteralType(SourceLocation Loc, QualType T, unsigned DiagID) {
BoundTypeDiagnoser<> Diagnoser(DiagID);
return RequireLiteralType(Loc, T, Diagnoser);
}
/// \brief Retrieve a version of the type 'T' that is elaborated by Keyword
/// and qualified by the nested-name-specifier contained in SS.
QualType Sema::getElaboratedType(ElaboratedTypeKeyword Keyword,
const CXXScopeSpec &SS, QualType T) {
if (T.isNull())
return T;
NestedNameSpecifier *NNS;
if (SS.isValid())
NNS = SS.getScopeRep();
else {
if (Keyword == ETK_None)
return T;
NNS = nullptr;
}
return Context.getElaboratedType(Keyword, NNS, T);
}
QualType Sema::BuildTypeofExprType(Expr *E, SourceLocation Loc) {
ExprResult ER = CheckPlaceholderExpr(E);
if (ER.isInvalid()) return QualType();
E = ER.get();
if (!getLangOpts().CPlusPlus && E->refersToBitField())
Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield) << 2;
if (!E->isTypeDependent()) {
QualType T = E->getType();
if (const TagType *TT = T->getAs<TagType>())
DiagnoseUseOfDecl(TT->getDecl(), E->getExprLoc());
}
return Context.getTypeOfExprType(E);
}
/// getDecltypeForExpr - Given an expr, will return the decltype for
/// that expression, according to the rules in C++11
/// [dcl.type.simple]p4 and C++11 [expr.lambda.prim]p18.
static QualType getDecltypeForExpr(Sema &S, Expr *E) {
if (E->isTypeDependent())
return S.Context.DependentTy;
// C++11 [dcl.type.simple]p4:
// The type denoted by decltype(e) is defined as follows:
//
// - if e is an unparenthesized id-expression or an unparenthesized class
// member access (5.2.5), decltype(e) is the type of the entity named
// by e. If there is no such entity, or if e names a set of overloaded
// functions, the program is ill-formed;
//
// We apply the same rules for Objective-C ivar and property references.
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
if (const ValueDecl *VD = dyn_cast<ValueDecl>(DRE->getDecl()))
return VD->getType();
} else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
if (const FieldDecl *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
return FD->getType();
} else if (const ObjCIvarRefExpr *IR = dyn_cast<ObjCIvarRefExpr>(E)) {
return IR->getDecl()->getType();
} else if (const ObjCPropertyRefExpr *PR = dyn_cast<ObjCPropertyRefExpr>(E)) {
if (PR->isExplicitProperty())
return PR->getExplicitProperty()->getType();
} else if (auto *PE = dyn_cast<PredefinedExpr>(E)) {
return PE->getType();
}
// C++11 [expr.lambda.prim]p18:
// Every occurrence of decltype((x)) where x is a possibly
// parenthesized id-expression that names an entity of automatic
// storage duration is treated as if x were transformed into an
// access to a corresponding data member of the closure type that
// would have been declared if x were an odr-use of the denoted
// entity.
using namespace sema;
if (S.getCurLambda()) {
if (isa<ParenExpr>(E)) {
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParens())) {
if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
QualType T = S.getCapturedDeclRefType(Var, DRE->getLocation());
if (!T.isNull())
return S.Context.getLValueReferenceType(T);
}
}
}
}
// C++11 [dcl.type.simple]p4:
// [...]
QualType T = E->getType();
switch (E->getValueKind()) {
// - otherwise, if e is an xvalue, decltype(e) is T&&, where T is the
// type of e;
case VK_XValue: T = S.Context.getRValueReferenceType(T); break;
// - otherwise, if e is an lvalue, decltype(e) is T&, where T is the
// type of e;
case VK_LValue: T = S.Context.getLValueReferenceType(T); break;
// - otherwise, decltype(e) is the type of e.
case VK_RValue: break;
}
return T;
}
QualType Sema::BuildDecltypeType(Expr *E, SourceLocation Loc,
bool AsUnevaluated) {
ExprResult ER = CheckPlaceholderExpr(E);
if (ER.isInvalid()) return QualType();
E = ER.get();
if (AsUnevaluated && CodeSynthesisContexts.empty() &&
E->HasSideEffects(Context, false)) {
// The expression operand for decltype is in an unevaluated expression
// context, so side effects could result in unintended consequences.
Diag(E->getExprLoc(), diag::warn_side_effects_unevaluated_context);
}
return Context.getDecltypeType(E, getDecltypeForExpr(*this, E));
}
QualType Sema::BuildUnaryTransformType(QualType BaseType,
UnaryTransformType::UTTKind UKind,
SourceLocation Loc) {
switch (UKind) {
case UnaryTransformType::EnumUnderlyingType:
if (!BaseType->isDependentType() && !BaseType->isEnumeralType()) {
Diag(Loc, diag::err_only_enums_have_underlying_types);
return QualType();
} else {
QualType Underlying = BaseType;
if (!BaseType->isDependentType()) {
// The enum could be incomplete if we're parsing its definition or
// recovering from an error.
NamedDecl *FwdDecl = nullptr;
if (BaseType->isIncompleteType(&FwdDecl)) {
Diag(Loc, diag::err_underlying_type_of_incomplete_enum) << BaseType;
Diag(FwdDecl->getLocation(), diag::note_forward_declaration) << FwdDecl;
return QualType();
}
EnumDecl *ED = BaseType->getAs<EnumType>()->getDecl();
assert(ED && "EnumType has no EnumDecl");
DiagnoseUseOfDecl(ED, Loc);
Underlying = ED->getIntegerType();
assert(!Underlying.isNull());
}
return Context.getUnaryTransformType(BaseType, Underlying,
UnaryTransformType::EnumUnderlyingType);
}
}
llvm_unreachable("unknown unary transform type");
}
QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) {
if (!T->isDependentType()) {
// FIXME: It isn't entirely clear whether incomplete atomic types
// are allowed or not; for simplicity, ban them for the moment.
if (RequireCompleteType(Loc, T, diag::err_atomic_specifier_bad_type, 0))
return QualType();
int DisallowedKind = -1;
if (T->isArrayType())
DisallowedKind = 1;
else if (T->isFunctionType())
DisallowedKind = 2;
else if (T->isReferenceType())
DisallowedKind = 3;
else if (T->isAtomicType())
DisallowedKind = 4;
else if (T.hasQualifiers())
DisallowedKind = 5;
else if (!T.isTriviallyCopyableType(Context))
// Some other non-trivially-copyable type (probably a C++ class)
DisallowedKind = 6;
if (DisallowedKind != -1) {
Diag(Loc, diag::err_atomic_specifier_bad_type) << DisallowedKind << T;
return QualType();
}
// FIXME: Do we need any handling for ARC here?
}
// Build the pointer type.
return Context.getAtomicType(T);
}
Index: test/Import/extern-c-function/Inputs/F.cpp
===================================================================
--- test/Import/extern-c-function/Inputs/F.cpp (revision 311017)
+++ test/Import/extern-c-function/Inputs/F.cpp (working copy)
@@ -1 +1,3 @@
-void f(int arg) { }
+extern "C" {
+ void f(int arg);
+}
Index: test/Import/extern-c-function/test.cpp
===================================================================
--- test/Import/extern-c-function/test.cpp (revision 311017)
+++ test/Import/extern-c-function/test.cpp (working copy)
@@ -1,7 +1,4 @@
-// RUN: clang-import-test -import %S/Inputs/F1.c -import %S/Inputs/F2.c -expression %s
+// RUN: clang-import-test -import %S/Inputs/F.cpp -expression %s
void expr() {
f(2);
- f("world");
- S s;
- f(s);
-}
+}
\ No newline at end of file
Index: test/Import/forward-declared-objc-class/Inputs/S1.m
===================================================================
--- test/Import/forward-declared-objc-class/Inputs/S1.m (revision 311468)
+++ test/Import/forward-declared-objc-class/Inputs/S1.m (working copy)
@@ -1 +1 @@
-struct S;
+ at class MyClass;
\ No newline at end of file
Index: test/Import/forward-declared-objc-class/Inputs/S2.m
===================================================================
--- test/Import/forward-declared-objc-class/Inputs/S2.m (revision 311468)
+++ test/Import/forward-declared-objc-class/Inputs/S2.m (working copy)
@@ -1,3 +1,6 @@
-struct S {
- int a;
+ at interface MyClass {
+ int j;
};
++(MyClass*)fromInteger:(int)_j;
+-(int)getInteger;
+ at end
\ No newline at end of file
Index: test/Import/forward-declared-objc-class/Inputs/S3.m
===================================================================
--- test/Import/forward-declared-objc-class/Inputs/S3.m (revision 311468)
+++ test/Import/forward-declared-objc-class/Inputs/S3.m (working copy)
@@ -1 +1 @@
-struct S;
+ at class MyClass;
\ No newline at end of file
Index: test/Import/forward-declared-objc-class/test.m
===================================================================
--- test/Import/forward-declared-objc-class/test.m (revision 311468)
+++ test/Import/forward-declared-objc-class/test.m (working copy)
@@ -1,5 +1,6 @@
-// RUN: clang-import-test -import %S/Inputs/S1.c --import %S/Inputs/S2.c -expression %s
+// RUN: clang-import-test -x objective-c++ -import %S/Inputs/S1.m --import %S/Inputs/S2.m --import %S/Inputs/S3.m -expression %s
void expr() {
- struct S MyS;
- MyS.a = 3;
+ MyClass *c = [MyClass fromInteger:3];
+ const int i = [c getInteger];
+ const int j = c->j;
}
Index: test/Import/forward-declared-struct/Inputs/S3.c
===================================================================
Index: test/Import/forward-declared-struct/test.c
===================================================================
--- test/Import/forward-declared-struct/test.c (revision 313996)
+++ test/Import/forward-declared-struct/test.c (working copy)
@@ -1,5 +1,5 @@
-// RUN: clang-import-test -import %S/Inputs/S1.c --import %S/Inputs/S2.c -expression %s
+// RUN: clang-import-test -import %S/Inputs/S1.c --import %S/Inputs/S2.c --import %S/Inputs/S3.c -expression %s
void expr() {
struct S MyS;
MyS.a = 3;
}
Index: test/Import/local-struct/test.cpp
===================================================================
--- test/Import/local-struct/test.cpp (revision 313996)
+++ test/Import/local-struct/test.cpp (working copy)
@@ -1,8 +1,7 @@
// RUN: clang-import-test -dump-ir -import %S/Inputs/Callee.cpp -expression %s | FileCheck %s
-// XFAIL: *
// CHECK: %struct.S = type { i
-// CHECK: %struct.S.0 = type { i1 }
+// CHECK: %struct.S.0 = type { i
void foo() {
return Bar().bar(3, true);
}
Index: test/Import/local-struct-use-origins/Inputs/Callee.cpp
===================================================================
Index: test/Import/local-struct-use-origins/test.cpp
===================================================================
--- test/Import/local-struct-use-origins/test.cpp (revision 310656)
+++ test/Import/local-struct-use-origins/test.cpp (working copy)
@@ -1,8 +1,7 @@
-// RUN: clang-import-test -dump-ir -import %S/Inputs/Callee.cpp -expression %s | FileCheck %s
-// XFAIL: *
+// RUN: clang-import-test -dump-ir -use-origins -import %S/Inputs/Callee.cpp -expression %s | FileCheck %s
// CHECK: %struct.S = type { i
-// CHECK: %struct.S.0 = type { i1 }
+// CHECK: %struct.S.0 = type { i
void foo() {
return Bar().bar(3, true);
}
Index: test/Import/objc-definitions-in-expression/Inputs/S.m
===================================================================
Index: test/Import/objc-definitions-in-expression/test.m
===================================================================
--- test/Import/objc-definitions-in-expression/test.m (revision 311468)
+++ test/Import/objc-definitions-in-expression/test.m (working copy)
@@ -1,5 +1,21 @@
// RUN: clang-import-test -x objective-c++ -import %S/Inputs/S.m -expression %s
+ at class D;
+
+ at interface B {
+ int x;
+ int y;
+}
+ at end
+
+ at interface D : B {
+ int z;
+}
+-(int)n;
+ at end
+
void expr() {
C *c;
int i = [c m];
+ D *d;
+ int j = [d n] + d->x;
}
Index: test/Import/struct-and-var/Inputs/S1.cpp
===================================================================
--- test/Import/struct-and-var/Inputs/S1.cpp (revision 310656)
+++ test/Import/struct-and-var/Inputs/S1.cpp (working copy)
@@ -1,6 +1 @@
-class T;
-
-class S {
- T *t;
- int a;
-};
+int F;
Index: test/Import/struct-and-var/Inputs/S2.cpp
===================================================================
--- test/Import/struct-and-var/Inputs/S2.cpp (revision 310656)
+++ test/Import/struct-and-var/Inputs/S2.cpp (working copy)
@@ -1,7 +1,3 @@
-class U {
- int b;
+struct F {
+ int a;
};
-
-class T {
- U u;
-};
Index: test/Import/struct-and-var/test.cpp
===================================================================
--- test/Import/struct-and-var/test.cpp (revision 310656)
+++ test/Import/struct-and-var/test.cpp (working copy)
@@ -1,7 +1,5 @@
// RUN: clang-import-test --import %S/Inputs/S1.cpp --import %S/Inputs/S2.cpp -expression %s
void expr() {
- S MyS;
- T MyT;
- MyS.a = 3;
- MyT.u.b = 2;
+ struct F f;
+ int x = f.a;
}
Index: test/Import/template/Inputs/T.cpp
===================================================================
--- test/Import/template/Inputs/T.cpp (revision 310656)
+++ test/Import/template/Inputs/T.cpp (working copy)
@@ -1,14 +1,5 @@
template <typename T> struct A {
-};
-
-template <> struct A<int> {
struct B {
- int f;
+ T f;
};
};
-
-template <> struct A<bool> {
- struct B {
- int g;
- };
-};
Index: test/Import/template/test.cpp
===================================================================
--- test/Import/template/test.cpp (revision 310656)
+++ test/Import/template/test.cpp (working copy)
@@ -1,7 +1,4 @@
// 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;
+ A<int>::B b;
}
Index: tools/clang-import-test/clang-import-test.cpp
===================================================================
--- tools/clang-import-test/clang-import-test.cpp (revision 313996)
+++ tools/clang-import-test/clang-import-test.cpp (working copy)
@@ -1,344 +1,378 @@
//===-- import-test.cpp - ASTImporter/ExternalASTSource testbed -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Driver/Types.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Parse/ParseAST.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Signals.h"
#include <memory>
#include <string>
using namespace clang;
static llvm::cl::opt<std::string> Expression(
"expression", llvm::cl::Required,
llvm::cl::desc("Path to a file containing the expression to parse"));
static llvm::cl::list<std::string>
Imports("import", llvm::cl::ZeroOrMore,
llvm::cl::desc("Path to a file containing declarations to import"));
static llvm::cl::opt<bool>
Direct("direct", llvm::cl::Optional,
- llvm::cl::desc("Use the parsed declarations without indirection"));
+ llvm::cl::desc("Use the parsed declarations without indirection"));
+static llvm::cl::opt<bool>
+ UseOrigins("use-origins", llvm::cl::Optional,
+ llvm::cl::desc("Use DeclContext origin information for more accurate lookups"));
+
static llvm::cl::list<std::string>
ClangArgs("Xcc", llvm::cl::ZeroOrMore,
llvm::cl::desc("Argument to pass to the CompilerInvocation"),
llvm::cl::CommaSeparated);
static llvm::cl::opt<std::string>
Input("x", llvm::cl::Optional,
llvm::cl::desc("The language to parse (default: c++)"),
llvm::cl::init("c++"));
-static llvm::cl::opt<bool>
-DumpAST("dump-ast", llvm::cl::init(false),
- llvm::cl::desc("Dump combined AST"));
+static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false),
+ llvm::cl::desc("Dump combined AST"));
-static llvm::cl::opt<bool>
-DumpIR("dump-ir", llvm::cl::init(false),
- llvm::cl::desc("Dump IR from final parse"));
+static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(false),
+ llvm::cl::desc("Dump IR from final parse"));
namespace init_convenience {
class TestDiagnosticConsumer : public DiagnosticConsumer {
private:
std::unique_ptr<TextDiagnosticBuffer> Passthrough;
const LangOptions *LangOpts = nullptr;
public:
TestDiagnosticConsumer()
: Passthrough(llvm::make_unique<TextDiagnosticBuffer>()) {}
virtual void BeginSourceFile(const LangOptions &LangOpts,
const Preprocessor *PP = nullptr) override {
this->LangOpts = &LangOpts;
return Passthrough->BeginSourceFile(LangOpts, PP);
}
virtual void EndSourceFile() override {
this->LangOpts = nullptr;
Passthrough->EndSourceFile();
}
virtual bool IncludeInDiagnosticCounts() const override {
return Passthrough->IncludeInDiagnosticCounts();
}
private:
static void PrintSourceForLocation(const SourceLocation &Loc,
SourceManager &SM) {
const char *LocData = SM.getCharacterData(Loc, /*Invalid=*/nullptr);
unsigned LocColumn =
SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1;
FileID FID = SM.getFileID(Loc);
llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, Loc, /*Invalid=*/nullptr);
assert(LocData >= Buffer->getBufferStart() &&
LocData < Buffer->getBufferEnd());
const char *LineBegin = LocData - LocColumn;
assert(LineBegin >= Buffer->getBufferStart());
const char *LineEnd = nullptr;
for (LineEnd = LineBegin; *LineEnd != '\n' && *LineEnd != '\r' &&
LineEnd < Buffer->getBufferEnd();
++LineEnd)
;
llvm::StringRef LineString(LineBegin, LineEnd - LineBegin);
llvm::errs() << LineString << '\n';
llvm::errs().indent(LocColumn);
llvm::errs() << '^';
llvm::errs() << '\n';
}
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const Diagnostic &Info) override {
if (Info.hasSourceManager() && LangOpts) {
SourceManager &SM = Info.getSourceManager();
if (Info.getLocation().isValid()) {
Info.getLocation().print(llvm::errs(), SM);
llvm::errs() << ": ";
}
SmallString<16> DiagText;
Info.FormatDiagnostic(DiagText);
llvm::errs() << DiagText << '\n';
if (Info.getLocation().isValid()) {
PrintSourceForLocation(Info.getLocation(), SM);
}
for (const CharSourceRange &Range : Info.getRanges()) {
bool Invalid = true;
StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid);
if (!Invalid) {
llvm::errs() << Ref << '\n';
}
}
}
DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
}
};
-std::unique_ptr<CompilerInstance>
-BuildCompilerInstance() {
+std::unique_ptr<CompilerInstance> BuildCompilerInstance() {
auto Ins = llvm::make_unique<CompilerInstance>();
auto DC = llvm::make_unique<TestDiagnosticConsumer>();
const bool ShouldOwnClient = true;
Ins->createDiagnostics(DC.release(), ShouldOwnClient);
auto Inv = llvm::make_unique<CompilerInvocation>();
std::vector<const char *> ClangArgv(ClangArgs.size());
std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
[](const std::string &s) -> const char * { return s.data(); });
CompilerInvocation::CreateFromArgs(*Inv, ClangArgv.data(),
&ClangArgv.data()[ClangArgv.size()],
Ins->getDiagnostics());
{
using namespace driver::types;
ID Id = lookupTypeForTypeSpecifier(Input.c_str());
assert(Id != TY_INVALID);
if (isCXX(Id)) {
Inv->getLangOpts()->CPlusPlus = true;
Inv->getLangOpts()->CPlusPlus11 = true;
Inv->getHeaderSearchOpts().UseLibcxx = true;
}
if (isObjC(Id)) {
Inv->getLangOpts()->ObjC1 = 1;
Inv->getLangOpts()->ObjC2 = 1;
}
}
Inv->getLangOpts()->Bool = true;
Inv->getLangOpts()->WChar = true;
Inv->getLangOpts()->Blocks = true;
Inv->getLangOpts()->DebuggerSupport = true;
Inv->getLangOpts()->SpellChecking = false;
Inv->getLangOpts()->ThreadsafeStatics = false;
Inv->getLangOpts()->AccessControl = false;
Inv->getLangOpts()->DollarIdents = true;
Inv->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo);
Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple();
Ins->setInvocation(std::move(Inv));
TargetInfo *TI = TargetInfo::CreateTargetInfo(
Ins->getDiagnostics(), Ins->getInvocation().TargetOpts);
Ins->setTarget(TI);
Ins->getTarget().adjust(Ins->getLangOpts());
Ins->createFileManager();
Ins->createSourceManager(Ins->getFileManager());
Ins->createPreprocessor(TU_Complete);
return Ins;
}
std::unique_ptr<ASTContext>
BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) {
auto AST = llvm::make_unique<ASTContext>(
CI.getLangOpts(), CI.getSourceManager(),
CI.getPreprocessor().getIdentifierTable(), ST, BC);
AST->InitBuiltinTypes(CI.getTarget());
return AST;
}
std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
llvm::LLVMContext &LLVMCtx) {
StringRef ModuleName("$__module");
return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen(
CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(),
CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx));
}
} // end namespace
namespace {
-
-void AddExternalSource(
- CompilerInstance &CI,
- llvm::ArrayRef<std::unique_ptr<CompilerInstance>> Imports) {
- 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()});
+
+/// A container for a CompilerInstance (possibly with an ExternalASTMerger
+/// attached to its ASTContext).
+///
+/// Provides an accessor for the DeclContext origins associated with the
+/// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is
+/// attached).
+///
+/// This is the main unit of parsed source code maintained by clang-import-test.
+struct CIAndOrigins {
+ using OriginMap = clang::ExternalASTMerger::OriginMap;
+ std::unique_ptr<CompilerInstance> CI;
+
+ ASTContext &getASTContext() { return CI->getASTContext(); }
+ FileManager &getFileManager() { return CI->getFileManager(); }
+ const OriginMap &getOriginMap() {
+ static const OriginMap EmptyOriginMap;
+ if (ExternalASTSource *Source = CI->getASTContext().getExternalSource())
+ return static_cast<ExternalASTMerger *>(Source)->GetOrigins();
+ return EmptyOriginMap;
}
+ DiagnosticConsumer &getDiagnosticClient() {
+ return CI->getDiagnosticClient();
+ }
+ CompilerInstance &getCompilerInstance() { return *CI; }
+};
+
+void AddExternalSource(CIAndOrigins &CI,
+ llvm::MutableArrayRef<CIAndOrigins> Imports) {
+ ExternalASTMerger::ImporterTarget Target(
+ {CI.getASTContext(), CI.getFileManager()});
+ llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
+ for (CIAndOrigins &Import : Imports)
+ Sources.push_back(
+ {Import.getASTContext(), Import.getFileManager(), Import.getOriginMap()});
auto ES = llvm::make_unique<ExternalASTMerger>(Target, Sources);
CI.getASTContext().setExternalSource(ES.release());
CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
}
-std::unique_ptr<CompilerInstance> BuildIndirect(std::unique_ptr<CompilerInstance> &CI) {
- std::unique_ptr<CompilerInstance> IndirectCI =
- init_convenience::BuildCompilerInstance();
+CIAndOrigins BuildIndirect(CIAndOrigins &CI) {
+ CIAndOrigins IndirectCI{init_convenience::BuildCompilerInstance()};
auto ST = llvm::make_unique<SelectorTable>();
auto BC = llvm::make_unique<Builtin::Context>();
- std::unique_ptr<ASTContext> AST =
- init_convenience::BuildASTContext(*IndirectCI, *ST, *BC);
- IndirectCI->setASTContext(AST.release());
- AddExternalSource(*IndirectCI, CI);
+ std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext(
+ IndirectCI.getCompilerInstance(), *ST, *BC);
+ IndirectCI.getCompilerInstance().setASTContext(AST.release());
+ AddExternalSource(IndirectCI, CI);
return IndirectCI;
}
llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
ASTConsumer &Consumer) {
SourceManager &SM = CI.getSourceManager();
const FileEntry *FE = CI.getFileManager().getFile(Path);
if (!FE) {
return llvm::make_error<llvm::StringError>(
llvm::Twine("Couldn't open ", Path), std::error_code());
}
SM.setMainFileID(SM.createFileID(FE, SourceLocation(), SrcMgr::C_User));
ParseAST(CI.getPreprocessor(), &Consumer, CI.getASTContext());
return llvm::Error::success();
}
-llvm::Expected<std::unique_ptr<CompilerInstance>>
-Parse(const std::string &Path,
- llvm::ArrayRef<std::unique_ptr<CompilerInstance>> Imports,
- bool ShouldDumpAST, bool ShouldDumpIR) {
- std::unique_ptr<CompilerInstance> CI =
- init_convenience::BuildCompilerInstance();
+llvm::Expected<CIAndOrigins> Parse(const std::string &Path,
+ llvm::MutableArrayRef<CIAndOrigins> Imports,
+ bool ShouldDumpAST, bool ShouldDumpIR) {
+ CIAndOrigins CI{init_convenience::BuildCompilerInstance()};
auto ST = llvm::make_unique<SelectorTable>();
auto BC = llvm::make_unique<Builtin::Context>();
std::unique_ptr<ASTContext> AST =
- init_convenience::BuildASTContext(*CI, *ST, *BC);
- CI->setASTContext(AST.release());
+ init_convenience::BuildASTContext(CI.getCompilerInstance(), *ST, *BC);
+ CI.getCompilerInstance().setASTContext(AST.release());
if (Imports.size())
- AddExternalSource(*CI, Imports);
+ AddExternalSource(CI, Imports);
std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers;
auto LLVMCtx = llvm::make_unique<llvm::LLVMContext>();
- ASTConsumers.push_back(init_convenience::BuildCodeGen(*CI, *LLVMCtx));
- auto &CG = *static_cast<CodeGenerator*>(ASTConsumers.back().get());
+ ASTConsumers.push_back(
+ init_convenience::BuildCodeGen(CI.getCompilerInstance(), *LLVMCtx));
+ auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get());
if (ShouldDumpAST)
ASTConsumers.push_back(CreateASTDumper("", true, false, false));
- CI->getDiagnosticClient().BeginSourceFile(CI->getLangOpts(),
- &CI->getPreprocessor());
+ CI.getDiagnosticClient().BeginSourceFile(
+ CI.getCompilerInstance().getLangOpts(),
+ &CI.getCompilerInstance().getPreprocessor());
MultiplexConsumer Consumers(std::move(ASTConsumers));
- Consumers.Initialize(CI->getASTContext());
+ Consumers.Initialize(CI.getASTContext());
- if (llvm::Error PE = ParseSource(Path, *CI, Consumers)) {
+ if (llvm::Error PE = ParseSource(Path, CI.getCompilerInstance(), Consumers))
return std::move(PE);
- }
- CI->getDiagnosticClient().EndSourceFile();
+ CI.getDiagnosticClient().EndSourceFile();
if (ShouldDumpIR)
CG.GetModule()->print(llvm::outs(), nullptr);
- if (CI->getDiagnosticClient().getNumErrors()) {
+ if (CI.getDiagnosticClient().getNumErrors())
return llvm::make_error<llvm::StringError>(
"Errors occured while parsing the expression.", std::error_code());
- } else {
- return std::move(CI);
- }
+ return std::move(CI);
}
+void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) {
+ llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
+ for (CIAndOrigins &Import : Imports)
+ Sources.push_back(
+ {Import.getASTContext(), Import.getFileManager(), Import.getOriginMap()});
+ ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource();
+ auto *Merger = static_cast<ExternalASTMerger *>(Source);
+ Merger->RemoveSources(Sources);
+}
+
} // end namespace
int main(int argc, const char **argv) {
const bool DisableCrashReporting = true;
llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
llvm::cl::ParseCommandLineOptions(argc, argv);
- std::vector<std::unique_ptr<CompilerInstance>> ImportCIs;
+ std::vector<CIAndOrigins> ImportCIs;
for (auto I : Imports) {
- llvm::Expected<std::unique_ptr<CompilerInstance>> ImportCI =
- Parse(I, {}, false, false);
+ llvm::Expected<CIAndOrigins> ImportCI = Parse(I, {}, false, false);
if (auto E = ImportCI.takeError()) {
llvm::errs() << llvm::toString(std::move(E));
exit(-1);
- } else {
- ImportCIs.push_back(std::move(*ImportCI));
}
+ ImportCIs.push_back(std::move(*ImportCI));
}
- std::vector<std::unique_ptr<CompilerInstance>> IndirectCIs;
- if (!Direct) {
+ std::vector<CIAndOrigins> IndirectCIs;
+ if (!Direct || UseOrigins) {
for (auto &ImportCI : ImportCIs) {
- std::unique_ptr<CompilerInstance> IndirectCI = BuildIndirect(ImportCI);
+ CIAndOrigins IndirectCI = BuildIndirect(ImportCI);
IndirectCIs.push_back(std::move(IndirectCI));
}
}
- llvm::Expected<std::unique_ptr<CompilerInstance>> ExpressionCI =
- Parse(Expression, Direct ? ImportCIs : IndirectCIs, DumpAST, DumpIR);
+ if (UseOrigins)
+ for (auto &ImportCI : ImportCIs)
+ IndirectCIs.push_back(std::move(ImportCI));
+ llvm::Expected<CIAndOrigins> ExpressionCI =
+ Parse(Expression, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs,
+ DumpAST, DumpIR);
if (auto E = ExpressionCI.takeError()) {
llvm::errs() << llvm::toString(std::move(E));
exit(-1);
- } else {
- return 0;
}
+ Forget(*ExpressionCI, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs);
+ return 0;
}
-
More information about the cfe-commits
mailing list