mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This commit adds a new type DynamicLookupInfo that provides information about how a dynamic member lookup found a particular Decl. This is needed to correctly handle KeyPath dynamic member lookups, but for now just plumb it through everywhere.
3742 lines
134 KiB
C++
3742 lines
134 KiB
C++
//===--- ClangImporter.cpp - Import Clang Modules -------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements support for loading Clang modules into Swift.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "swift/ClangImporter/ClangImporter.h"
|
|
#include "swift/ClangImporter/ClangModule.h"
|
|
#include "IAMInference.h"
|
|
#include "ImporterImpl.h"
|
|
#include "ClangDiagnosticConsumer.h"
|
|
#include "swift/Subsystems.h"
|
|
#include "swift/AST/ASTContext.h"
|
|
#include "swift/AST/DiagnosticEngine.h"
|
|
#include "swift/AST/DiagnosticsClangImporter.h"
|
|
#include "swift/AST/IRGenOptions.h"
|
|
#include "swift/AST/LinkLibrary.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/NameLookup.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/Basic/Defer.h"
|
|
#include "swift/Basic/Platform.h"
|
|
#include "swift/Basic/Range.h"
|
|
#include "swift/Basic/StringExtras.h"
|
|
#include "swift/Basic/Version.h"
|
|
#include "swift/ClangImporter/ClangImporterOptions.h"
|
|
#include "swift/Parse/Lexer.h"
|
|
#include "swift/Parse/Parser.h"
|
|
#include "swift/Config.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Mangle.h"
|
|
#include "clang/Basic/CharInfo.h"
|
|
#include "clang/Basic/IdentifierTable.h"
|
|
#include "clang/Basic/Module.h"
|
|
#include "clang/Basic/TargetInfo.h"
|
|
#include "clang/Basic/Version.h"
|
|
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
|
|
#include "clang/Frontend/FrontendActions.h"
|
|
#include "clang/Frontend/Utils.h"
|
|
#include "clang/Index/IndexingAction.h"
|
|
#include "clang/Serialization/ASTReader.h"
|
|
#include "clang/Serialization/ASTWriter.h"
|
|
#include "clang/Lex/Preprocessor.h"
|
|
#include "clang/Lex/PreprocessorOptions.h"
|
|
#include "clang/Parse/Parser.h"
|
|
#include "clang/Rewrite/Frontend/FrontendActions.h"
|
|
#include "clang/Rewrite/Frontend/Rewriters.h"
|
|
#include "clang/Sema/Lookup.h"
|
|
#include "clang/Sema/Sema.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/CrashRecoveryContext.h"
|
|
#include "llvm/Support/Memory.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
using namespace swift;
|
|
using namespace importer;
|
|
|
|
// Commonly-used Clang classes.
|
|
using clang::CompilerInstance;
|
|
using clang::CompilerInvocation;
|
|
|
|
#pragma mark Internal data structures
|
|
|
|
namespace {
|
|
class HeaderImportCallbacks : public clang::PPCallbacks {
|
|
ClangImporter::Implementation &Impl;
|
|
public:
|
|
HeaderImportCallbacks(ClangImporter::Implementation &impl)
|
|
: Impl(impl) {}
|
|
|
|
void handleImport(const clang::Module *imported) {
|
|
if (!imported)
|
|
return;
|
|
Impl.ImportedHeaderExports.push_back(
|
|
const_cast<clang::Module *>(imported));
|
|
}
|
|
|
|
void InclusionDirective(clang::SourceLocation HashLoc,
|
|
const clang::Token &IncludeTok,
|
|
StringRef FileName,
|
|
bool IsAngled,
|
|
clang::CharSourceRange FilenameRange,
|
|
const clang::FileEntry *File,
|
|
StringRef SearchPath,
|
|
StringRef RelativePath,
|
|
const clang::Module *Imported,
|
|
clang::SrcMgr::CharacteristicKind FileType) override {
|
|
handleImport(Imported);
|
|
}
|
|
|
|
void moduleImport(clang::SourceLocation ImportLoc,
|
|
clang::ModuleIdPath Path,
|
|
const clang::Module *Imported) override {
|
|
handleImport(Imported);
|
|
}
|
|
};
|
|
|
|
class PCHDeserializationCallbacks : public clang::ASTDeserializationListener {
|
|
ClangImporter::Implementation &Impl;
|
|
public:
|
|
explicit PCHDeserializationCallbacks(ClangImporter::Implementation &impl)
|
|
: Impl(impl) {}
|
|
void ModuleImportRead(clang::serialization::SubmoduleID ID,
|
|
clang::SourceLocation ImportLoc) override {
|
|
if (Impl.IsReadingBridgingPCH) {
|
|
Impl.PCHImportedSubmodules.push_back(ID);
|
|
}
|
|
}
|
|
};
|
|
|
|
class HeaderParsingASTConsumer : public clang::ASTConsumer {
|
|
SmallVector<clang::DeclGroupRef, 4> DeclGroups;
|
|
PCHDeserializationCallbacks PCHCallbacks;
|
|
public:
|
|
explicit HeaderParsingASTConsumer(ClangImporter::Implementation &impl)
|
|
: PCHCallbacks(impl) {}
|
|
void
|
|
HandleTopLevelDeclInObjCContainer(clang::DeclGroupRef decls) override {
|
|
DeclGroups.push_back(decls);
|
|
}
|
|
|
|
ArrayRef<clang::DeclGroupRef> getAdditionalParsedDecls() {
|
|
return DeclGroups;
|
|
}
|
|
|
|
clang::ASTDeserializationListener *GetASTDeserializationListener() override {
|
|
return &PCHCallbacks;
|
|
}
|
|
|
|
void reset() {
|
|
DeclGroups.clear();
|
|
}
|
|
};
|
|
|
|
class ParsingAction : public clang::ASTFrontendAction {
|
|
ASTContext &Ctx;
|
|
ClangImporter &Importer;
|
|
ClangImporter::Implementation &Impl;
|
|
const ClangImporterOptions &ImporterOpts;
|
|
std::string SwiftPCHHash;
|
|
public:
|
|
explicit ParsingAction(ASTContext &ctx,
|
|
ClangImporter &importer,
|
|
ClangImporter::Implementation &impl,
|
|
const ClangImporterOptions &importerOpts,
|
|
std::string swiftPCHHash)
|
|
: Ctx(ctx), Importer(importer), Impl(impl), ImporterOpts(importerOpts),
|
|
SwiftPCHHash(swiftPCHHash) {}
|
|
std::unique_ptr<clang::ASTConsumer>
|
|
CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override {
|
|
return llvm::make_unique<HeaderParsingASTConsumer>(Impl);
|
|
}
|
|
bool BeginSourceFileAction(clang::CompilerInstance &CI) override {
|
|
// Prefer frameworks over plain headers.
|
|
// We add search paths here instead of when building the initial invocation
|
|
// so that (a) we use the same code as search paths for imported modules,
|
|
// and (b) search paths are always added after -Xcc options.
|
|
SearchPathOptions &searchPathOpts = Ctx.SearchPathOpts;
|
|
for (const auto &framepath : searchPathOpts.FrameworkSearchPaths) {
|
|
Importer.addSearchPath(framepath.Path, /*isFramework*/true,
|
|
framepath.IsSystem);
|
|
}
|
|
|
|
for (auto path : searchPathOpts.ImportSearchPaths) {
|
|
Importer.addSearchPath(path, /*isFramework*/false, /*isSystem=*/false);
|
|
}
|
|
|
|
auto PCH = Importer.getOrCreatePCH(ImporterOpts, SwiftPCHHash);
|
|
if (PCH.hasValue()) {
|
|
Impl.getClangInstance()->getPreprocessorOpts().ImplicitPCHInclude =
|
|
PCH.getValue();
|
|
Impl.IsReadingBridgingPCH = true;
|
|
Impl.setSinglePCHImport(PCH.getValue());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class StdStringMemBuffer : public llvm::MemoryBuffer {
|
|
const std::string storage;
|
|
const std::string name;
|
|
public:
|
|
StdStringMemBuffer(std::string &&source, StringRef name)
|
|
: storage(std::move(source)), name(name.str()) {
|
|
init(storage.data(), storage.data() + storage.size(),
|
|
/*null-terminated=*/true);
|
|
}
|
|
|
|
StringRef getBufferIdentifier() const override {
|
|
return name;
|
|
}
|
|
|
|
BufferKind getBufferKind() const override {
|
|
return MemoryBuffer_Malloc;
|
|
}
|
|
};
|
|
|
|
class ZeroFilledMemoryBuffer : public llvm::MemoryBuffer {
|
|
const std::string name;
|
|
public:
|
|
explicit ZeroFilledMemoryBuffer(size_t size, StringRef name)
|
|
: name(name.str()) {
|
|
assert(size > 0);
|
|
std::error_code error;
|
|
llvm::sys::MemoryBlock memory =
|
|
llvm::sys::Memory::allocateMappedMemory(size, nullptr,
|
|
llvm::sys::Memory::MF_READ,
|
|
error);
|
|
assert(!error && "failed to allocated read-only zero-filled memory");
|
|
init(static_cast<char *>(memory.base()),
|
|
static_cast<char *>(memory.base()) + memory.size() - 1,
|
|
/*null-terminated*/true);
|
|
}
|
|
|
|
~ZeroFilledMemoryBuffer() override {
|
|
llvm::sys::MemoryBlock memory{const_cast<char *>(getBufferStart()),
|
|
getBufferSize()};
|
|
std::error_code error = llvm::sys::Memory::releaseMappedMemory(memory);
|
|
assert(!error && "failed to deallocate read-only zero-filled memory");
|
|
(void)error;
|
|
}
|
|
|
|
ZeroFilledMemoryBuffer(const ZeroFilledMemoryBuffer &) = delete;
|
|
ZeroFilledMemoryBuffer(ZeroFilledMemoryBuffer &&) = delete;
|
|
void operator=(const ZeroFilledMemoryBuffer &) = delete;
|
|
void operator=(ZeroFilledMemoryBuffer &&) = delete;
|
|
|
|
StringRef getBufferIdentifier() const override {
|
|
return name;
|
|
}
|
|
BufferKind getBufferKind() const override {
|
|
return MemoryBuffer_MMap;
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
namespace {
|
|
class BridgingPPTracker : public clang::PPCallbacks {
|
|
ClangImporter::Implementation &Impl;
|
|
|
|
public:
|
|
BridgingPPTracker(ClangImporter::Implementation &Impl)
|
|
: Impl(Impl) {}
|
|
|
|
private:
|
|
static unsigned getNumModuleIdentifiers(const clang::Module *Mod) {
|
|
unsigned Result = 1;
|
|
while (Mod->Parent) {
|
|
Mod = Mod->Parent;
|
|
++Result;
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
void InclusionDirective(clang::SourceLocation HashLoc,
|
|
const clang::Token &IncludeTok,
|
|
StringRef FileName,
|
|
bool IsAngled,
|
|
clang::CharSourceRange FilenameRange,
|
|
const clang::FileEntry *File,
|
|
StringRef SearchPath,
|
|
StringRef RelativePath,
|
|
const clang::Module *Imported,
|
|
clang::SrcMgr::CharacteristicKind FileType) override{
|
|
if (!Imported) {
|
|
if (File)
|
|
Impl.BridgeHeaderFiles.insert(File);
|
|
return;
|
|
}
|
|
// Synthesize identifier locations.
|
|
SmallVector<clang::SourceLocation, 4> IdLocs;
|
|
for (unsigned I = 0, E = getNumModuleIdentifiers(Imported); I != E; ++I)
|
|
IdLocs.push_back(HashLoc);
|
|
handleImport(HashLoc, IdLocs, Imported);
|
|
}
|
|
|
|
void moduleImport(clang::SourceLocation ImportLoc,
|
|
clang::ModuleIdPath Path,
|
|
const clang::Module *Imported) override {
|
|
if (!Imported)
|
|
return;
|
|
SmallVector<clang::SourceLocation, 4> IdLocs;
|
|
for (auto &P : Path)
|
|
IdLocs.push_back(P.second);
|
|
handleImport(ImportLoc, IdLocs, Imported);
|
|
}
|
|
|
|
void handleImport(clang::SourceLocation ImportLoc,
|
|
ArrayRef<clang::SourceLocation> IdLocs,
|
|
const clang::Module *Imported) {
|
|
clang::ASTContext &ClangCtx = Impl.getClangASTContext();
|
|
clang::ImportDecl *ClangImport = clang::ImportDecl::Create(ClangCtx,
|
|
ClangCtx.getTranslationUnitDecl(),
|
|
ImportLoc,
|
|
const_cast<clang::Module*>(Imported),
|
|
IdLocs);
|
|
Impl.BridgeHeaderTopLevelImports.push_back(ClangImport);
|
|
}
|
|
|
|
void MacroDefined(const clang::Token &MacroNameTok,
|
|
const clang::MacroDirective *MD) override {
|
|
Impl.BridgeHeaderMacros.push_back(MacroNameTok.getIdentifierInfo());
|
|
}
|
|
};
|
|
|
|
class ClangImporterDependencyCollector : public clang::DependencyCollector
|
|
{
|
|
llvm::StringSet<> ExcludedPaths;
|
|
const bool TrackSystemDeps;
|
|
|
|
public:
|
|
ClangImporterDependencyCollector(bool TrackSystemDeps)
|
|
: TrackSystemDeps(TrackSystemDeps) {}
|
|
|
|
void excludePath(StringRef filename) {
|
|
ExcludedPaths.insert(filename);
|
|
}
|
|
|
|
bool isClangImporterSpecialName(StringRef Filename) {
|
|
using ImporterImpl = ClangImporter::Implementation;
|
|
return (Filename == ImporterImpl::moduleImportBufferName
|
|
|| Filename == ImporterImpl::bridgingHeaderBufferName);
|
|
}
|
|
|
|
bool needSystemDependencies() override { return TrackSystemDeps; }
|
|
|
|
bool sawDependency(StringRef Filename, bool FromClangModule,
|
|
bool IsSystem, bool IsClangModuleFile,
|
|
bool IsMissing) override {
|
|
if (!clang::DependencyCollector::sawDependency(Filename, FromClangModule,
|
|
IsSystem, IsClangModuleFile,
|
|
IsMissing))
|
|
return false;
|
|
// Currently preserving older ClangImporter behavior of ignoring .pcm
|
|
// file dependencies, but possibly revisit?
|
|
if (IsClangModuleFile
|
|
|| isClangImporterSpecialName(Filename)
|
|
|| ExcludedPaths.count(Filename))
|
|
return false;
|
|
return true;
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
std::shared_ptr<clang::DependencyCollector>
|
|
ClangImporter::createDependencyCollector(bool TrackSystemDeps)
|
|
{
|
|
return std::make_shared<ClangImporterDependencyCollector>(TrackSystemDeps);
|
|
}
|
|
|
|
void ClangImporter::Implementation::addBridgeHeaderTopLevelDecls(
|
|
clang::Decl *D) {
|
|
if (shouldIgnoreBridgeHeaderTopLevelDecl(D))
|
|
return;
|
|
|
|
BridgeHeaderTopLevelDecls.push_back(D);
|
|
}
|
|
|
|
bool ClangImporter::Implementation::shouldIgnoreBridgeHeaderTopLevelDecl(
|
|
clang::Decl *D) {
|
|
// Ignore forward references;
|
|
if (auto *ID = dyn_cast<clang::ObjCInterfaceDecl>(D)) {
|
|
if (!ID->isThisDeclarationADefinition())
|
|
return true;
|
|
} else if (auto PD = dyn_cast<clang::ObjCProtocolDecl>(D)) {
|
|
if (!PD->isThisDeclarationADefinition())
|
|
return true;
|
|
} else if (auto TD = dyn_cast<clang::TagDecl>(D)) {
|
|
if (!TD->isThisDeclarationADefinition())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ClangImporter::ClangImporter(ASTContext &ctx,
|
|
const ClangImporterOptions &clangImporterOpts,
|
|
DependencyTracker *tracker)
|
|
: ClangModuleLoader(tracker),
|
|
Impl(*new Implementation(ctx, clangImporterOpts))
|
|
{
|
|
}
|
|
|
|
ClangImporter::~ClangImporter() {
|
|
delete &Impl;
|
|
}
|
|
|
|
void ClangImporter::setTypeResolver(LazyResolver &resolver) {
|
|
Impl.setTypeResolver(&resolver);
|
|
}
|
|
|
|
void ClangImporter::clearTypeResolver() {
|
|
Impl.setTypeResolver(nullptr);
|
|
}
|
|
|
|
#pragma mark Module loading
|
|
|
|
/// Finds the glibc.modulemap file relative to the provided resource dir.
|
|
///
|
|
/// Note that the module map used for Glibc depends on the target we're
|
|
/// compiling for, and is not included in the resource directory with the other
|
|
/// implicit module maps. It's at {freebsd|linux}/{arch}/glibc.modulemap.
|
|
static Optional<StringRef>
|
|
getGlibcModuleMapPath(StringRef resourceDir, llvm::Triple triple,
|
|
SmallVectorImpl<char> &scratch) {
|
|
if (resourceDir.empty())
|
|
return None;
|
|
|
|
scratch.append(resourceDir.begin(), resourceDir.end());
|
|
llvm::sys::path::append(
|
|
scratch,
|
|
swift::getPlatformNameForTriple(triple),
|
|
swift::getMajorArchitectureName(triple),
|
|
"glibc.modulemap");
|
|
|
|
// Only specify the module map if that file actually exists.
|
|
// It may not--for example in the case that
|
|
// `swiftc -target x86_64-unknown-linux-gnu -emit-ir` is invoked using
|
|
// a Swift compiler not built for Linux targets.
|
|
if (llvm::sys::fs::exists(scratch)) {
|
|
return StringRef(scratch.data(), scratch.size());
|
|
} else {
|
|
return None;
|
|
}
|
|
}
|
|
|
|
static void
|
|
getNormalInvocationArguments(std::vector<std::string> &invocationArgStrs,
|
|
ASTContext &ctx,
|
|
const ClangImporterOptions &importerOpts) {
|
|
const auto &LangOpts = ctx.LangOpts;
|
|
const llvm::Triple &triple = LangOpts.Target;
|
|
SearchPathOptions &searchPathOpts = ctx.SearchPathOpts;
|
|
|
|
auto languageVersion = ctx.LangOpts.EffectiveLanguageVersion;
|
|
|
|
if (llvm::sys::path::extension(importerOpts.BridgingHeader)
|
|
.endswith(file_types::getExtension(file_types::TY_PCH))) {
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {
|
|
"-include-pch", importerOpts.BridgingHeader
|
|
});
|
|
}
|
|
|
|
// If there are no shims in the resource dir, add a search path in the SDK.
|
|
SmallString<128> shimsPath(searchPathOpts.RuntimeResourcePath);
|
|
llvm::sys::path::append(shimsPath, "shims");
|
|
if (!llvm::sys::fs::exists(shimsPath)) {
|
|
shimsPath = searchPathOpts.SDKPath;
|
|
llvm::sys::path::append(shimsPath, "usr", "lib", "swift", "shims");
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {
|
|
"-isystem", shimsPath.str()
|
|
});
|
|
}
|
|
|
|
// Construct the invocation arguments for the current target.
|
|
// Add target-independent options first.
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {
|
|
// Don't emit LLVM IR.
|
|
"-fsyntax-only",
|
|
|
|
// Enable block support.
|
|
"-fblocks",
|
|
|
|
languageVersion.preprocessorDefinition("__swift__", {10000, 100, 1}),
|
|
|
|
"-fretain-comments-from-system-headers",
|
|
|
|
"-isystem", searchPathOpts.RuntimeResourcePath,
|
|
});
|
|
|
|
// Enable Position Independence. `-fPIC` is not supported on Windows, which
|
|
// is implicitly position independent.
|
|
if (!triple.isOSWindows())
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {"-fPIC"});
|
|
|
|
// Enable modules.
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {
|
|
"-fmodules",
|
|
"-Xclang", "-fmodule-feature", "-Xclang", "swift"
|
|
});
|
|
// Don't enforce strict rules when inside the debugger to work around search
|
|
// path problems caused by a module existing in both the build/install
|
|
// directory and the source directory.
|
|
if (!importerOpts.DebuggerSupport)
|
|
invocationArgStrs.push_back(
|
|
"-Werror=non-modular-include-in-framework-module");
|
|
|
|
if (LangOpts.EnableObjCInterop) {
|
|
invocationArgStrs.insert(invocationArgStrs.end(),
|
|
{"-x", "objective-c", "-std=gnu11", "-fobjc-arc"});
|
|
// TODO: Investigate whether 7.0 is a suitable default version.
|
|
if (!triple.isOSDarwin())
|
|
invocationArgStrs.insert(invocationArgStrs.end(),
|
|
{"-fobjc-runtime=ios-7.0"});
|
|
|
|
// Define macros that Swift bridging headers use.
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {
|
|
"-DSWIFT_CLASS_EXTRA=__attribute__((__annotate__("
|
|
"\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))",
|
|
"-DSWIFT_PROTOCOL_EXTRA=__attribute__((__annotate__("
|
|
"\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))",
|
|
"-DSWIFT_EXTENSION_EXTRA=__attribute__((__annotate__("
|
|
"\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))",
|
|
"-DSWIFT_ENUM_EXTRA=__attribute__((__annotate__("
|
|
"\"" SWIFT_NATIVE_ANNOTATION_STRING "\")))",
|
|
});
|
|
|
|
} else {
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {"-x", "c", "-std=gnu11"});
|
|
}
|
|
|
|
// Set C language options.
|
|
if (triple.isOSDarwin()) {
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {
|
|
// Avoid including the iso646.h header because some headers from OS X
|
|
// frameworks are broken by it.
|
|
"-D_ISO646_H_", "-D__ISO646_H",
|
|
|
|
// Request new APIs from AppKit.
|
|
"-DSWIFT_SDK_OVERLAY_APPKIT_EPOCH=2",
|
|
|
|
// Request new APIs from Foundation.
|
|
"-DSWIFT_SDK_OVERLAY_FOUNDATION_EPOCH=8",
|
|
|
|
// Request new APIs from SceneKit.
|
|
"-DSWIFT_SDK_OVERLAY2_SCENEKIT_EPOCH=3",
|
|
|
|
// Request new APIs from GameplayKit.
|
|
"-DSWIFT_SDK_OVERLAY_GAMEPLAYKIT_EPOCH=1",
|
|
|
|
// Request new APIs from SpriteKit.
|
|
"-DSWIFT_SDK_OVERLAY_SPRITEKIT_EPOCH=1",
|
|
|
|
// Request new APIs from CoreImage.
|
|
"-DSWIFT_SDK_OVERLAY_COREIMAGE_EPOCH=2",
|
|
|
|
// Request new APIs from libdispatch.
|
|
"-DSWIFT_SDK_OVERLAY_DISPATCH_EPOCH=2",
|
|
|
|
// Request new APIs from libpthread
|
|
"-DSWIFT_SDK_OVERLAY_PTHREAD_EPOCH=1",
|
|
|
|
// Request new APIs from CoreGraphics.
|
|
"-DSWIFT_SDK_OVERLAY_COREGRAPHICS_EPOCH=0",
|
|
|
|
// Request new APIs from UIKit.
|
|
"-DSWIFT_SDK_OVERLAY_UIKIT_EPOCH=2",
|
|
});
|
|
|
|
// Get the version of this compiler and pass it to C/Objective-C
|
|
// declarations.
|
|
auto V = version::Version::getCurrentCompilerVersion();
|
|
if (!V.empty()) {
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {
|
|
V.preprocessorDefinition("__SWIFT_COMPILER_VERSION",
|
|
{1000000000, /*ignored*/ 0, 1000000, 1000, 1}),
|
|
});
|
|
}
|
|
} else {
|
|
// Ideally we should turn this on for all Glibc targets that are actually
|
|
// using Glibc or a libc that respects that flag. This will cause some
|
|
// source breakage however (specifically with strerror_r()) on Linux
|
|
// without a workaround.
|
|
if (triple.isOSFuchsia() || triple.isAndroid()) {
|
|
// Many of the modern libc features are hidden behind feature macros like
|
|
// _GNU_SOURCE or _XOPEN_SOURCE.
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {
|
|
"-D_GNU_SOURCE",
|
|
});
|
|
}
|
|
|
|
if (triple.isOSWindows()) {
|
|
switch (triple.getArch()) {
|
|
default: llvm_unreachable("unsupported Windows architecture");
|
|
case llvm::Triple::arm:
|
|
case llvm::Triple::thumb:
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {"-D_ARM_"});
|
|
break;
|
|
case llvm::Triple::aarch64:
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {"-D_ARM64_"});
|
|
break;
|
|
case llvm::Triple::x86:
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {"-D_X86_"});
|
|
break;
|
|
case llvm::Triple::x86_64:
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {"-D_AMD64_"});
|
|
break;
|
|
}
|
|
}
|
|
|
|
SmallString<128> GlibcModuleMapPath;
|
|
if (auto path = getGlibcModuleMapPath(searchPathOpts.RuntimeResourcePath,
|
|
triple, GlibcModuleMapPath)) {
|
|
invocationArgStrs.push_back((Twine("-fmodule-map-file=") + *path).str());
|
|
} else {
|
|
// FIXME: Emit a warning of some kind.
|
|
}
|
|
}
|
|
|
|
if (searchPathOpts.SDKPath.empty()) {
|
|
invocationArgStrs.push_back("-Xclang");
|
|
invocationArgStrs.push_back("-nostdsysteminc");
|
|
} else {
|
|
if (triple.isWindowsMSVCEnvironment()) {
|
|
llvm::SmallString<261> path; // MAX_PATH + 1
|
|
path = searchPathOpts.SDKPath;
|
|
llvm::sys::path::append(path, "usr", "include");
|
|
llvm::sys::path::native(path);
|
|
|
|
invocationArgStrs.push_back("-isystem");
|
|
invocationArgStrs.push_back(path.str());
|
|
} else {
|
|
// On Darwin, Clang uses -isysroot to specify the include
|
|
// system root. On other targets, it seems to use --sysroot.
|
|
invocationArgStrs.push_back(triple.isOSDarwin() ? "-isysroot"
|
|
: "--sysroot");
|
|
invocationArgStrs.push_back(searchPathOpts.SDKPath);
|
|
}
|
|
}
|
|
|
|
const std::string &moduleCachePath = importerOpts.ModuleCachePath;
|
|
if (!moduleCachePath.empty()) {
|
|
invocationArgStrs.push_back("-fmodules-cache-path=");
|
|
invocationArgStrs.back().append(moduleCachePath);
|
|
}
|
|
|
|
if (!importerOpts.DisableModulesValidateSystemHeaders) {
|
|
invocationArgStrs.push_back("-fmodules-validate-system-headers");
|
|
}
|
|
|
|
if (importerOpts.DetailedPreprocessingRecord) {
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {
|
|
"-Xclang", "-detailed-preprocessing-record",
|
|
"-Xclang", "-fmodule-format=raw",
|
|
});
|
|
} else {
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {
|
|
"-Xclang", "-fmodule-format=obj",
|
|
});
|
|
}
|
|
|
|
// Enable API notes alongside headers/in frameworks.
|
|
invocationArgStrs.push_back("-fapinotes-modules");
|
|
invocationArgStrs.push_back("-fapinotes-swift-version=" +
|
|
languageVersion.asAPINotesVersionString());
|
|
invocationArgStrs.push_back("-iapinotes-modules");
|
|
invocationArgStrs.push_back((llvm::Twine(searchPathOpts.RuntimeResourcePath) +
|
|
llvm::sys::path::get_separator() +
|
|
"apinotes").str());
|
|
}
|
|
|
|
static void
|
|
getEmbedBitcodeInvocationArguments(std::vector<std::string> &invocationArgStrs,
|
|
ASTContext &ctx,
|
|
const ClangImporterOptions &importerOpts) {
|
|
invocationArgStrs.insert(invocationArgStrs.end(), {
|
|
// Backend mode.
|
|
"-fembed-bitcode",
|
|
|
|
// ...but Clang isn't doing the emission.
|
|
"-fsyntax-only",
|
|
|
|
"-x", "ir",
|
|
});
|
|
}
|
|
|
|
static void
|
|
addCommonInvocationArguments(std::vector<std::string> &invocationArgStrs,
|
|
ASTContext &ctx,
|
|
const ClangImporterOptions &importerOpts) {
|
|
using ImporterImpl = ClangImporter::Implementation;
|
|
const llvm::Triple &triple = ctx.LangOpts.Target;
|
|
SearchPathOptions &searchPathOpts = ctx.SearchPathOpts;
|
|
|
|
invocationArgStrs.push_back("-target");
|
|
invocationArgStrs.push_back(triple.str());
|
|
|
|
invocationArgStrs.push_back(ImporterImpl::moduleImportBufferName);
|
|
|
|
if (ctx.LangOpts.EnableAppExtensionRestrictions) {
|
|
invocationArgStrs.push_back("-fapplication-extension");
|
|
}
|
|
|
|
if (!importerOpts.TargetCPU.empty()) {
|
|
invocationArgStrs.push_back("-mcpu=" + importerOpts.TargetCPU);
|
|
|
|
} else if (triple.isOSDarwin()) {
|
|
// Special case: arm64 defaults to the "cyclone" CPU for Darwin,
|
|
// but Clang only detects this if we use -arch.
|
|
if (triple.getArch() == llvm::Triple::aarch64 ||
|
|
triple.getArch() == llvm::Triple::aarch64_be) {
|
|
invocationArgStrs.push_back("-mcpu=cyclone");
|
|
}
|
|
} else if (triple.getArch() == llvm::Triple::systemz) {
|
|
invocationArgStrs.push_back("-march=z196");
|
|
}
|
|
|
|
if (!importerOpts.Optimization.empty()) {
|
|
invocationArgStrs.push_back(importerOpts.Optimization);
|
|
}
|
|
|
|
const std::string &overrideResourceDir = importerOpts.OverrideResourceDir;
|
|
if (overrideResourceDir.empty()) {
|
|
llvm::SmallString<128> resourceDir(searchPathOpts.RuntimeResourcePath);
|
|
|
|
// Adjust the path to refer to our copy of the Clang resource directory
|
|
// under 'lib/swift/clang', which is either a real resource directory or a
|
|
// symlink to one inside of a full Clang installation.
|
|
//
|
|
// The rationale for looking under the Swift resource directory and not
|
|
// assuming that the Clang resource directory is located next to it is that
|
|
// Swift, when installed separately, should not need to install files in
|
|
// directories that are not "owned" by it.
|
|
llvm::sys::path::append(resourceDir, "clang");
|
|
|
|
// Set the Clang resource directory to the path we computed.
|
|
invocationArgStrs.push_back("-resource-dir");
|
|
invocationArgStrs.push_back(resourceDir.str());
|
|
} else {
|
|
invocationArgStrs.push_back("-resource-dir");
|
|
invocationArgStrs.push_back(overrideResourceDir);
|
|
}
|
|
|
|
if (!importerOpts.IndexStorePath.empty()) {
|
|
invocationArgStrs.push_back("-index-store-path");
|
|
invocationArgStrs.push_back(importerOpts.IndexStorePath);
|
|
}
|
|
|
|
invocationArgStrs.push_back("-fansi-escape-codes");
|
|
|
|
for (auto extraArg : importerOpts.ExtraArgs) {
|
|
invocationArgStrs.push_back(extraArg);
|
|
}
|
|
}
|
|
|
|
bool ClangImporter::canReadPCH(StringRef PCHFilename) {
|
|
if (!llvm::sys::fs::exists(PCHFilename))
|
|
return false;
|
|
|
|
// FIXME: The following attempts to do an initial ReadAST invocation to verify
|
|
// the PCH, without affecting the existing CompilerInstance.
|
|
// Look into combining creating the ASTReader along with verification + update
|
|
// if necessary, so that we can create and use one ASTReader in the common case
|
|
// when there is no need for update.
|
|
|
|
CompilerInstance &CI = *Impl.Instance;
|
|
auto clangDiags = CompilerInstance::createDiagnostics(
|
|
new clang::DiagnosticOptions());
|
|
|
|
// Note: Reusing the file manager is safe; this is a component that's already
|
|
// reused when building PCM files for the module cache.
|
|
clang::SourceManager clangSrcMgr(*clangDiags, CI.getFileManager());
|
|
auto FID = clangSrcMgr.createFileID(
|
|
llvm::make_unique<ZeroFilledMemoryBuffer>(1, "<main>"));
|
|
clangSrcMgr.setMainFileID(FID);
|
|
|
|
// Note: Reusing the real HeaderSearch is dangerous, but it's not easy to
|
|
// copy. We put in some effort to reset it to the way it was below.
|
|
clang::HeaderSearch &headerSearchInfo =
|
|
CI.getPreprocessor().getHeaderSearchInfo();
|
|
assert(headerSearchInfo.getExternalLookup() == nullptr &&
|
|
"already configured an ASTReader");
|
|
|
|
// Note: Reusing the PCM cache is safe because that's already shared when
|
|
// building PCM files for the module cache. Using the top-level compiler
|
|
// instance as a module loader does seem a little dangerous but does not
|
|
// appear to cause problems at the moment.
|
|
clang::Preprocessor PP(CI.getInvocation().getPreprocessorOptsPtr(),
|
|
*clangDiags,
|
|
CI.getLangOpts(),
|
|
clangSrcMgr,
|
|
headerSearchInfo,
|
|
(clang::ModuleLoader &)CI,
|
|
/*IILookup=*/nullptr,
|
|
/*OwnsHeaderSearch=*/false);
|
|
PP.Initialize(CI.getTarget());
|
|
clang::ASTContext ctx(CI.getLangOpts(), clangSrcMgr,
|
|
PP.getIdentifierTable(), PP.getSelectorTable(),
|
|
PP.getBuiltinInfo());
|
|
|
|
// Note: Reusing the PCHContainerReader or ModuleFileExtensions could be
|
|
// dangerous.
|
|
std::unique_ptr<clang::ASTReader> Reader(new clang::ASTReader(
|
|
PP, CI.getModuleCache(), &ctx, CI.getPCHContainerReader(),
|
|
CI.getFrontendOpts().ModuleFileExtensions,
|
|
CI.getHeaderSearchOpts().Sysroot,
|
|
/*DisableValidation*/ false,
|
|
/*AllowPCHWithCompilerErrors*/ false,
|
|
/*AllowConfigurationMismatch*/ false,
|
|
/*ValidateSystemInputs*/ true));
|
|
SWIFT_DEFER {
|
|
assert(headerSearchInfo.getExternalLookup() == Reader.get() ||
|
|
headerSearchInfo.getExternalLookup() == nullptr);
|
|
headerSearchInfo.SetExternalLookup(nullptr);
|
|
headerSearchInfo.SetExternalSource(nullptr);
|
|
};
|
|
ctx.InitBuiltinTypes(CI.getTarget());
|
|
|
|
auto result = Reader->ReadAST(PCHFilename,
|
|
clang::serialization::MK_PCH,
|
|
clang::SourceLocation(),
|
|
clang::ASTReader::ARR_None);
|
|
switch (result) {
|
|
case clang::ASTReader::Success:
|
|
return true;
|
|
case clang::ASTReader::Failure:
|
|
case clang::ASTReader::Missing:
|
|
case clang::ASTReader::OutOfDate:
|
|
case clang::ASTReader::VersionMismatch:
|
|
return false;
|
|
case clang::ASTReader::ConfigurationMismatch:
|
|
case clang::ASTReader::HadErrors:
|
|
assert(0 && "unexpected ASTReader failure for PCH validation");
|
|
return false;
|
|
}
|
|
llvm_unreachable("unhandled result");
|
|
}
|
|
|
|
Optional<std::string>
|
|
ClangImporter::getPCHFilename(const ClangImporterOptions &ImporterOptions,
|
|
StringRef SwiftPCHHash, bool &isExplicit) {
|
|
if (llvm::sys::path::extension(ImporterOptions.BridgingHeader)
|
|
.endswith(file_types::getExtension(file_types::TY_PCH))) {
|
|
isExplicit = true;
|
|
return ImporterOptions.BridgingHeader;
|
|
}
|
|
isExplicit = false;
|
|
|
|
const auto &BridgingHeader = ImporterOptions.BridgingHeader;
|
|
const auto &PCHOutputDir = ImporterOptions.PrecompiledHeaderOutputDir;
|
|
if (SwiftPCHHash.empty() || BridgingHeader.empty() || PCHOutputDir.empty()) {
|
|
return None;
|
|
}
|
|
|
|
SmallString<256> PCHBasename { llvm::sys::path::filename(BridgingHeader) };
|
|
llvm::sys::path::replace_extension(PCHBasename, "");
|
|
PCHBasename.append("-swift_");
|
|
PCHBasename.append(SwiftPCHHash);
|
|
PCHBasename.append("-clang_");
|
|
PCHBasename.append(getClangModuleHash());
|
|
PCHBasename.append(".pch");
|
|
SmallString<256> PCHFilename { PCHOutputDir };
|
|
llvm::sys::path::append(PCHFilename, PCHBasename);
|
|
return PCHFilename.str().str();
|
|
}
|
|
|
|
|
|
Optional<std::string>
|
|
ClangImporter::getOrCreatePCH(const ClangImporterOptions &ImporterOptions,
|
|
StringRef SwiftPCHHash) {
|
|
bool isExplicit;
|
|
auto PCHFilename = getPCHFilename(ImporterOptions, SwiftPCHHash,
|
|
isExplicit);
|
|
if (!PCHFilename.hasValue()) {
|
|
return None;
|
|
}
|
|
if (!isExplicit && !ImporterOptions.PCHDisableValidation &&
|
|
!canReadPCH(PCHFilename.getValue())) {
|
|
StringRef parentDir = llvm::sys::path::parent_path(PCHFilename.getValue());
|
|
std::error_code EC = llvm::sys::fs::create_directories(parentDir);
|
|
if (EC) {
|
|
llvm::errs() << "failed to create directory '" << parentDir << "': "
|
|
<< EC.message();
|
|
return None;
|
|
}
|
|
auto FailedToEmit = emitBridgingPCH(ImporterOptions.BridgingHeader,
|
|
PCHFilename.getValue());
|
|
if (FailedToEmit) {
|
|
return None;
|
|
}
|
|
}
|
|
|
|
return PCHFilename.getValue();
|
|
}
|
|
|
|
std::unique_ptr<ClangImporter>
|
|
ClangImporter::create(ASTContext &ctx,
|
|
const ClangImporterOptions &importerOpts,
|
|
std::string swiftPCHHash,
|
|
DependencyTracker *tracker) {
|
|
std::unique_ptr<ClangImporter> importer{
|
|
new ClangImporter(ctx, importerOpts, tracker)
|
|
};
|
|
|
|
std::vector<std::string> invocationArgStrs;
|
|
|
|
// Clang expects this to be like an actual command line. So we need to pass in
|
|
// "clang" for argv[0]
|
|
invocationArgStrs.push_back("clang");
|
|
switch (importerOpts.Mode) {
|
|
case ClangImporterOptions::Modes::Normal:
|
|
getNormalInvocationArguments(invocationArgStrs, ctx, importerOpts);
|
|
break;
|
|
case ClangImporterOptions::Modes::EmbedBitcode:
|
|
getEmbedBitcodeInvocationArguments(invocationArgStrs, ctx, importerOpts);
|
|
break;
|
|
}
|
|
addCommonInvocationArguments(invocationArgStrs, ctx, importerOpts);
|
|
|
|
if (importerOpts.DumpClangDiagnostics) {
|
|
llvm::errs() << "'";
|
|
interleave(invocationArgStrs,
|
|
[](StringRef arg) { llvm::errs() << arg; },
|
|
[] { llvm::errs() << "' '"; });
|
|
llvm::errs() << "'\n";
|
|
}
|
|
|
|
std::vector<const char *> invocationArgs;
|
|
invocationArgs.reserve(invocationArgStrs.size());
|
|
for (auto &argStr : invocationArgStrs)
|
|
invocationArgs.push_back(argStr.c_str());
|
|
|
|
if (llvm::sys::path::extension(importerOpts.BridgingHeader)
|
|
.endswith(file_types::getExtension(file_types::TY_PCH))) {
|
|
importer->Impl.setSinglePCHImport(importerOpts.BridgingHeader);
|
|
importer->Impl.IsReadingBridgingPCH = true;
|
|
if (tracker) {
|
|
// Currently ignoring dependency on bridging .pch files because they are
|
|
// temporaries; if and when they are no longer temporaries, this condition
|
|
// should be removed.
|
|
auto &coll = static_cast<ClangImporterDependencyCollector &>(
|
|
*tracker->getClangCollector());
|
|
coll.excludePath(importerOpts.BridgingHeader);
|
|
}
|
|
}
|
|
|
|
// Create a new Clang compiler invocation.
|
|
{
|
|
// Set up a temporary diagnostic client to report errors from parsing the
|
|
// command line, which may be important for Swift clients if, for example,
|
|
// they're using -Xcc options. Unfortunately this diagnostic engine has to
|
|
// use the default options because the /actual/ options haven't been parsed
|
|
// yet.
|
|
//
|
|
// The long-term client for Clang diagnostics is set up below, after the
|
|
// clang::CompilerInstance is created.
|
|
llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> tempDiagOpts{
|
|
new clang::DiagnosticOptions
|
|
};
|
|
|
|
ClangDiagnosticConsumer tempDiagClient{importer->Impl, *tempDiagOpts,
|
|
importerOpts.DumpClangDiagnostics};
|
|
llvm::IntrusiveRefCntPtr<clang::DiagnosticsEngine> tempClangDiags =
|
|
clang::CompilerInstance::createDiagnostics(tempDiagOpts.get(),
|
|
&tempDiagClient,
|
|
/*owned*/false);
|
|
|
|
importer->Impl.Invocation =
|
|
clang::createInvocationFromCommandLine(invocationArgs, tempClangDiags);
|
|
if (!importer->Impl.Invocation)
|
|
return nullptr;
|
|
}
|
|
|
|
{
|
|
// Create an almost-empty memory buffer.
|
|
auto sourceBuffer = llvm::MemoryBuffer::getMemBuffer(
|
|
"extern int __swift __attribute__((unavailable));",
|
|
Implementation::moduleImportBufferName);
|
|
clang::PreprocessorOptions &ppOpts =
|
|
importer->Impl.Invocation->getPreprocessorOpts();
|
|
ppOpts.addRemappedFile(Implementation::moduleImportBufferName,
|
|
sourceBuffer.release());
|
|
}
|
|
|
|
// Install a Clang module file extension to build Swift name lookup tables.
|
|
importer->Impl.Invocation->getFrontendOpts().ModuleFileExtensions.push_back(
|
|
std::make_shared<SwiftNameLookupExtension>(
|
|
importer->Impl.BridgingHeaderLookupTable,
|
|
importer->Impl.LookupTables, importer->Impl.SwiftContext,
|
|
importer->Impl.platformAvailability,
|
|
importer->Impl.InferImportAsMember));
|
|
|
|
// Create a compiler instance.
|
|
{
|
|
auto PCHContainerOperations =
|
|
std::make_shared<clang::PCHContainerOperations>();
|
|
PCHContainerOperations->registerWriter(
|
|
llvm::make_unique<clang::ObjectFilePCHContainerWriter>());
|
|
PCHContainerOperations->registerReader(
|
|
llvm::make_unique<clang::ObjectFilePCHContainerReader>());
|
|
importer->Impl.Instance.reset(
|
|
new clang::CompilerInstance(std::move(PCHContainerOperations)));
|
|
}
|
|
auto &instance = *importer->Impl.Instance;
|
|
instance.setInvocation(importer->Impl.Invocation);
|
|
|
|
if (tracker)
|
|
instance.addDependencyCollector(tracker->getClangCollector());
|
|
|
|
{
|
|
// Now set up the real client for Clang diagnostics---configured with proper
|
|
// options---as opposed to the temporary one we made above.
|
|
auto actualDiagClient = llvm::make_unique<ClangDiagnosticConsumer>(
|
|
importer->Impl, instance.getDiagnosticOpts(),
|
|
importerOpts.DumpClangDiagnostics);
|
|
instance.createDiagnostics(actualDiagClient.release());
|
|
}
|
|
|
|
// Set up the file manager.
|
|
{
|
|
if (!ctx.SearchPathOpts.VFSOverlayFiles.empty()) {
|
|
// If the clang instance has overlays it means the user has provided
|
|
// -ivfsoverlay options and swift -vfsoverlay options. We're going to
|
|
// clobber their file system with our own, so warn about it.
|
|
if (!instance.getHeaderSearchOpts().VFSOverlayFiles.empty()) {
|
|
ctx.Diags.diagnose(SourceLoc(), diag::clang_vfs_overlay_is_ignored);
|
|
}
|
|
instance.setVirtualFileSystem(ctx.SourceMgr.getFileSystem());
|
|
}
|
|
instance.createFileManager();
|
|
}
|
|
|
|
// Don't stop emitting messages if we ever can't load a module.
|
|
// FIXME: This is actually a general problem: any "fatal" error could mess up
|
|
// the CompilerInvocation when we're not in "show diagnostics after fatal
|
|
// error" mode.
|
|
clang::DiagnosticsEngine &clangDiags = instance.getDiagnostics();
|
|
clangDiags.setSeverity(clang::diag::err_module_not_found,
|
|
clang::diag::Severity::Error,
|
|
clang::SourceLocation());
|
|
clangDiags.setSeverity(clang::diag::err_module_not_built,
|
|
clang::diag::Severity::Error,
|
|
clang::SourceLocation());
|
|
clangDiags.setSuppressAfterFatalError(
|
|
!ctx.Diags.getShowDiagnosticsAfterFatalError());
|
|
|
|
|
|
// Create the associated action.
|
|
importer->Impl.Action.reset(new ParsingAction(ctx, *importer,
|
|
importer->Impl,
|
|
importerOpts,
|
|
swiftPCHHash));
|
|
auto *action = importer->Impl.Action.get();
|
|
|
|
// Execute the action. We effectively inline most of
|
|
// CompilerInstance::ExecuteAction here, because we need to leave the AST
|
|
// open for future module loading.
|
|
// FIXME: This has to be cleaned up on the Clang side before we can improve
|
|
// things here.
|
|
|
|
// Create the target instance.
|
|
instance.setTarget(
|
|
clang::TargetInfo::CreateTargetInfo(clangDiags,
|
|
instance.getInvocation().TargetOpts));
|
|
if (!instance.hasTarget())
|
|
return nullptr;
|
|
|
|
// Inform the target of the language options.
|
|
//
|
|
// FIXME: We shouldn't need to do this, the target should be immutable once
|
|
// created. This complexity should be lifted elsewhere.
|
|
instance.getTarget().adjust(instance.getLangOpts());
|
|
|
|
if (importerOpts.Mode == ClangImporterOptions::Modes::EmbedBitcode)
|
|
return importer;
|
|
|
|
instance.getLangOpts().NeededByPCHOrCompilationUsesPCH = true;
|
|
bool canBegin = action->BeginSourceFile(instance,
|
|
instance.getFrontendOpts().Inputs[0]);
|
|
if (!canBegin)
|
|
return nullptr; // there was an error related to the compiler arguments.
|
|
|
|
clang::Preprocessor &clangPP = instance.getPreprocessor();
|
|
clangPP.enableIncrementalProcessing();
|
|
|
|
// Setup Preprocessor callbacks before initialing the parser to make sure
|
|
// we catch implicit includes.
|
|
auto ppTracker = llvm::make_unique<BridgingPPTracker>(importer->Impl);
|
|
clangPP.addPPCallbacks(std::move(ppTracker));
|
|
|
|
instance.createModuleManager();
|
|
|
|
// Manually run the action, so that the TU stays open for additional parsing.
|
|
instance.createSema(action->getTranslationUnitKind(), nullptr);
|
|
importer->Impl.Parser.reset(new clang::Parser(clangPP, instance.getSema(),
|
|
/*SkipFunctionBodies=*/false));
|
|
|
|
clangPP.EnterMainSourceFile();
|
|
importer->Impl.Parser->Initialize();
|
|
|
|
importer->Impl.nameImporter.reset(new NameImporter(
|
|
importer->Impl.SwiftContext, importer->Impl.platformAvailability,
|
|
importer->Impl.getClangSema(), importer->Impl.InferImportAsMember));
|
|
|
|
// FIXME: These decls are not being parsed correctly since (a) some of the
|
|
// callbacks are still being added, and (b) the logic to parse them has
|
|
// changed.
|
|
clang::Parser::DeclGroupPtrTy parsed;
|
|
while (!importer->Impl.Parser->ParseTopLevelDecl(parsed)) {
|
|
for (auto *D : parsed.get()) {
|
|
importer->Impl.addBridgeHeaderTopLevelDecls(D);
|
|
|
|
if (auto named = dyn_cast<clang::NamedDecl>(D)) {
|
|
addEntryToLookupTable(*importer->Impl.BridgingHeaderLookupTable, named,
|
|
*importer->Impl.nameImporter);
|
|
}
|
|
}
|
|
}
|
|
|
|
// FIXME: This is missing implicit includes.
|
|
auto *CB = new HeaderImportCallbacks(importer->Impl);
|
|
clangPP.addPPCallbacks(std::unique_ptr<clang::PPCallbacks>(CB));
|
|
|
|
// Create the selectors we'll be looking for.
|
|
auto &clangContext = importer->Impl.Instance->getASTContext();
|
|
importer->Impl.objectAtIndexedSubscript
|
|
= clangContext.Selectors.getUnarySelector(
|
|
&clangContext.Idents.get("objectAtIndexedSubscript"));
|
|
clang::IdentifierInfo *setObjectAtIndexedSubscriptIdents[2] = {
|
|
&clangContext.Idents.get("setObject"),
|
|
&clangContext.Idents.get("atIndexedSubscript")
|
|
};
|
|
importer->Impl.setObjectAtIndexedSubscript
|
|
= clangContext.Selectors.getSelector(2, setObjectAtIndexedSubscriptIdents);
|
|
importer->Impl.objectForKeyedSubscript
|
|
= clangContext.Selectors.getUnarySelector(
|
|
&clangContext.Idents.get("objectForKeyedSubscript"));
|
|
clang::IdentifierInfo *setObjectForKeyedSubscriptIdents[2] = {
|
|
&clangContext.Idents.get("setObject"),
|
|
&clangContext.Idents.get("forKeyedSubscript")
|
|
};
|
|
importer->Impl.setObjectForKeyedSubscript
|
|
= clangContext.Selectors.getSelector(2, setObjectForKeyedSubscriptIdents);
|
|
|
|
// Set up the imported header module.
|
|
auto *importedHeaderModule = ModuleDecl::create(ctx.getIdentifier("__ObjC"), ctx);
|
|
importer->Impl.ImportedHeaderUnit =
|
|
new (ctx) ClangModuleUnit(*importedHeaderModule, importer->Impl, nullptr);
|
|
importedHeaderModule->addFile(*importer->Impl.ImportedHeaderUnit);
|
|
importedHeaderModule->setHasResolvedImports();
|
|
|
|
importer->Impl.IsReadingBridgingPCH = false;
|
|
|
|
return importer;
|
|
}
|
|
|
|
bool ClangImporter::addSearchPath(StringRef newSearchPath, bool isFramework,
|
|
bool isSystem) {
|
|
clang::FileManager &fileMgr = Impl.Instance->getFileManager();
|
|
const clang::DirectoryEntry *entry = fileMgr.getDirectory(newSearchPath);
|
|
if (!entry)
|
|
return true;
|
|
|
|
auto &headerSearchInfo = Impl.getClangPreprocessor().getHeaderSearchInfo();
|
|
auto exists = std::any_of(headerSearchInfo.search_dir_begin(),
|
|
headerSearchInfo.search_dir_end(),
|
|
[&](const clang::DirectoryLookup &lookup) -> bool {
|
|
if (isFramework)
|
|
return lookup.getFrameworkDir() == entry;
|
|
return lookup.getDir() == entry;
|
|
});
|
|
if (exists) {
|
|
// Don't bother adding a search path that's already there. Clang would have
|
|
// removed it via deduplication at the time the search path info gets built.
|
|
return false;
|
|
}
|
|
|
|
auto kind = isSystem ? clang::SrcMgr::C_System : clang::SrcMgr::C_User;
|
|
headerSearchInfo.AddSearchPath({entry, kind, isFramework},
|
|
/*isAngled=*/true);
|
|
|
|
// In addition to changing the current preprocessor directly, we still need
|
|
// to change the options structure for future module-building.
|
|
Impl.Instance->getHeaderSearchOpts().AddPath(newSearchPath,
|
|
isSystem ? clang::frontend::System : clang::frontend::Angled,
|
|
isFramework,
|
|
/*IgnoreSysRoot=*/true);
|
|
return false;
|
|
}
|
|
|
|
clang::SourceLocation
|
|
ClangImporter::Implementation::getNextIncludeLoc() {
|
|
clang::SourceManager &srcMgr = getClangInstance()->getSourceManager();
|
|
|
|
if (!DummyIncludeBuffer.isValid()) {
|
|
clang::SourceLocation includeLoc =
|
|
srcMgr.getLocForStartOfFile(srcMgr.getMainFileID());
|
|
// Picking the beginning of the main FileID as include location is also what
|
|
// the clang PCH mechanism is doing (see
|
|
// clang::ASTReader::getImportLocation()). Choose the next source location
|
|
// here to avoid having the exact same import location as the clang PCH.
|
|
// Otherwise, if we are using a PCH for bridging header, we'll have
|
|
// problems with source order comparisons of clang source locations not
|
|
// being deterministic.
|
|
includeLoc = includeLoc.getLocWithOffset(1);
|
|
DummyIncludeBuffer = srcMgr.createFileID(
|
|
llvm::make_unique<ZeroFilledMemoryBuffer>(
|
|
256*1024, StringRef(moduleImportBufferName)),
|
|
clang::SrcMgr::C_User, /*LoadedID*/0, /*LoadedOffset*/0, includeLoc);
|
|
}
|
|
|
|
clang::SourceLocation clangImportLoc =
|
|
srcMgr.getLocForStartOfFile(DummyIncludeBuffer)
|
|
.getLocWithOffset(IncludeCounter++);
|
|
assert(srcMgr.isInFileID(clangImportLoc, DummyIncludeBuffer) &&
|
|
"confused Clang's source manager with our fake locations");
|
|
return clangImportLoc;
|
|
}
|
|
|
|
bool ClangImporter::Implementation::importHeader(
|
|
ModuleDecl *adapter, StringRef headerName, SourceLoc diagLoc,
|
|
bool trackParsedSymbols,
|
|
std::unique_ptr<llvm::MemoryBuffer> sourceBuffer,
|
|
bool implicitImport) {
|
|
|
|
// Don't even try to load the bridging header if the Clang AST is in a bad
|
|
// state. It could cause a crash.
|
|
auto &clangDiags = getClangASTContext().getDiagnostics();
|
|
if (clangDiags.hasUnrecoverableErrorOccurred())
|
|
return true;
|
|
|
|
assert(adapter);
|
|
ImportedHeaderOwners.push_back(adapter);
|
|
|
|
bool hadError = clangDiags.hasErrorOccurred();
|
|
|
|
clang::SourceManager &sourceMgr = getClangInstance()->getSourceManager();
|
|
clang::FileID bufferID = sourceMgr.createFileID(std::move(sourceBuffer),
|
|
clang::SrcMgr::C_User,
|
|
/*LoadedID=*/0,
|
|
/*LoadedOffset=*/0,
|
|
getNextIncludeLoc());
|
|
auto &consumer =
|
|
static_cast<HeaderParsingASTConsumer &>(Instance->getASTConsumer());
|
|
consumer.reset();
|
|
|
|
clang::Preprocessor &pp = getClangPreprocessor();
|
|
pp.EnterSourceFile(bufferID, /*Dir=*/nullptr, /*Loc=*/{});
|
|
// Force the import to occur.
|
|
pp.LookAhead(0);
|
|
|
|
SmallVector<clang::DeclGroupRef, 16> allParsedDecls;
|
|
auto handleParsed = [&](clang::DeclGroupRef parsed) {
|
|
if (trackParsedSymbols) {
|
|
for (auto *D : parsed) {
|
|
addBridgeHeaderTopLevelDecls(D);
|
|
}
|
|
}
|
|
|
|
allParsedDecls.push_back(parsed);
|
|
};
|
|
|
|
clang::Parser::DeclGroupPtrTy parsed;
|
|
while (!Parser->ParseTopLevelDecl(parsed)) {
|
|
if (parsed)
|
|
handleParsed(parsed.get());
|
|
for (auto additionalParsedGroup : consumer.getAdditionalParsedDecls())
|
|
handleParsed(additionalParsedGroup);
|
|
consumer.reset();
|
|
}
|
|
|
|
// We're trying to discourage (and eventually deprecate) the use of implicit
|
|
// bridging-header imports triggered by IMPORTED_HEADER blocks in
|
|
// modules. There are two sub-cases to consider:
|
|
//
|
|
// #1 The implicit import actually occurred.
|
|
//
|
|
// #2 The user explicitly -import-objc-header'ed some header or PCH that
|
|
// makes the implicit import redundant.
|
|
//
|
|
// It's not obvious how to exactly differentiate these cases given the
|
|
// interface clang gives us, but we only want to warn on case #1, and the
|
|
// non-emptiness of allParsedDecls is a _definite_ sign that we're in case
|
|
// #1. So we treat that as an approximation of the condition we're after, and
|
|
// accept that we might fail to warn in the odd case where "the import
|
|
// occurred" but didn't introduce any new decls.
|
|
//
|
|
// We also want to limit (for now) the warning in case #1 to invocations that
|
|
// requested an explicit bridging header, because otherwise the warning will
|
|
// complain in a very common scenario (unit test w/o bridging header imports
|
|
// application w/ bridging header) that we don't yet have Xcode automation
|
|
// to correct. The fix would be explicitly importing on the command line.
|
|
if (implicitImport && !allParsedDecls.empty() &&
|
|
BridgingHeaderExplicitlyRequested) {
|
|
SwiftContext.Diags.diagnose(
|
|
diagLoc, diag::implicit_bridging_header_imported_from_module,
|
|
llvm::sys::path::filename(headerName), adapter->getName());
|
|
}
|
|
|
|
// We can't do this as we're parsing because we may want to resolve naming
|
|
// conflicts between the things we've parsed.
|
|
for (auto group : allParsedDecls)
|
|
for (auto *D : group)
|
|
if (auto named = dyn_cast<clang::NamedDecl>(D))
|
|
addEntryToLookupTable(*BridgingHeaderLookupTable, named,
|
|
getNameImporter());
|
|
|
|
pp.EndSourceFile();
|
|
bumpGeneration();
|
|
|
|
// Add any defined macros to the bridging header lookup table.
|
|
addMacrosToLookupTable(*BridgingHeaderLookupTable, getNameImporter());
|
|
|
|
// Finish loading any extra modules that were (transitively) imported.
|
|
handleDeferredImports();
|
|
|
|
// Wrap all Clang imports under a Swift import decl.
|
|
for (auto &Import : BridgeHeaderTopLevelImports) {
|
|
if (auto *ClangImport = Import.dyn_cast<clang::ImportDecl*>()) {
|
|
Import = createImportDecl(SwiftContext, adapter, ClangImport, {});
|
|
}
|
|
}
|
|
|
|
// Finalize the lookup table, which may fail.
|
|
finalizeLookupTable(*BridgingHeaderLookupTable, getNameImporter());
|
|
|
|
// FIXME: What do we do if there was already an error?
|
|
if (!hadError && clangDiags.hasErrorOccurred()) {
|
|
SwiftContext.Diags.diagnose(diagLoc, diag::bridging_header_error,
|
|
headerName);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ClangImporter::importHeader(StringRef header, ModuleDecl *adapter,
|
|
off_t expectedSize, time_t expectedModTime,
|
|
StringRef cachedContents, SourceLoc diagLoc) {
|
|
clang::FileManager &fileManager = Impl.Instance->getFileManager();
|
|
const clang::FileEntry *headerFile = fileManager.getFile(header,
|
|
/*OpenFile=*/true);
|
|
if (headerFile && headerFile->getSize() == expectedSize &&
|
|
headerFile->getModificationTime() == expectedModTime) {
|
|
return importBridgingHeader(header, adapter, diagLoc, false, true);
|
|
}
|
|
|
|
// If we've made it to here, this is some header other than the bridging
|
|
// header, which means we can no longer rely on one file's modification time
|
|
// to invalid code completion caches. :-(
|
|
Impl.setSinglePCHImport(None);
|
|
|
|
if (!cachedContents.empty() && cachedContents.back() == '\0')
|
|
cachedContents = cachedContents.drop_back();
|
|
std::unique_ptr<llvm::MemoryBuffer> sourceBuffer{
|
|
llvm::MemoryBuffer::getMemBuffer(cachedContents, header)
|
|
};
|
|
return Impl.importHeader(adapter, header, diagLoc, /*trackParsedSymbols=*/false,
|
|
std::move(sourceBuffer), true);
|
|
}
|
|
|
|
bool ClangImporter::importBridgingHeader(StringRef header, ModuleDecl *adapter,
|
|
SourceLoc diagLoc,
|
|
bool trackParsedSymbols,
|
|
bool implicitImport) {
|
|
if (llvm::sys::path::extension(header)
|
|
.endswith(file_types::getExtension(file_types::TY_PCH))) {
|
|
Impl.ImportedHeaderOwners.push_back(adapter);
|
|
// We already imported this with -include-pch above, so we should have
|
|
// collected a bunch of PCH-encoded module imports that we just need to
|
|
// replay in handleDeferredImports.
|
|
Impl.handleDeferredImports();
|
|
return false;
|
|
}
|
|
|
|
clang::FileManager &fileManager = Impl.Instance->getFileManager();
|
|
const clang::FileEntry *headerFile = fileManager.getFile(header,
|
|
/*OpenFile=*/true);
|
|
if (!headerFile) {
|
|
Impl.SwiftContext.Diags.diagnose(diagLoc, diag::bridging_header_missing,
|
|
header);
|
|
return true;
|
|
}
|
|
|
|
llvm::SmallString<128> importLine;
|
|
if (Impl.SwiftContext.LangOpts.EnableObjCInterop)
|
|
importLine = "#import \"";
|
|
else
|
|
importLine = "#include \"";
|
|
|
|
importLine += header;
|
|
importLine += "\"\n";
|
|
|
|
std::unique_ptr<llvm::MemoryBuffer> sourceBuffer{
|
|
llvm::MemoryBuffer::getMemBufferCopy(
|
|
importLine, Implementation::bridgingHeaderBufferName)
|
|
};
|
|
return Impl.importHeader(adapter, header, diagLoc, trackParsedSymbols,
|
|
std::move(sourceBuffer), implicitImport);
|
|
}
|
|
|
|
std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath,
|
|
off_t &fileSize,
|
|
time_t &fileModTime) {
|
|
auto invocation =
|
|
std::make_shared<clang::CompilerInvocation>(*Impl.Invocation);
|
|
|
|
invocation->getFrontendOpts().DisableFree = false;
|
|
invocation->getFrontendOpts().Inputs.clear();
|
|
invocation->getFrontendOpts().Inputs.push_back(
|
|
clang::FrontendInputFile(headerPath, clang::InputKind::ObjC));
|
|
|
|
invocation->getPreprocessorOpts().resetNonModularOptions();
|
|
|
|
clang::CompilerInstance rewriteInstance(
|
|
Impl.Instance->getPCHContainerOperations(),
|
|
&Impl.Instance->getModuleCache());
|
|
rewriteInstance.setInvocation(invocation);
|
|
rewriteInstance.createDiagnostics(new clang::IgnoringDiagConsumer);
|
|
|
|
clang::FileManager &fileManager = Impl.Instance->getFileManager();
|
|
rewriteInstance.setFileManager(&fileManager);
|
|
rewriteInstance.createSourceManager(fileManager);
|
|
rewriteInstance.setTarget(&Impl.Instance->getTarget());
|
|
|
|
std::string result;
|
|
bool success = llvm::CrashRecoveryContext().RunSafelyOnThread([&] {
|
|
// A much simpler version of clang::RewriteIncludesAction that lets us
|
|
// write to an in-memory buffer.
|
|
class RewriteIncludesAction : public clang::PreprocessorFrontendAction {
|
|
raw_ostream &OS;
|
|
|
|
void ExecuteAction() override {
|
|
clang::CompilerInstance &compiler = getCompilerInstance();
|
|
clang::RewriteIncludesInInput(compiler.getPreprocessor(), &OS,
|
|
compiler.getPreprocessorOutputOpts());
|
|
}
|
|
public:
|
|
explicit RewriteIncludesAction(raw_ostream &os) : OS(os) {}
|
|
};
|
|
|
|
llvm::raw_string_ostream os(result);
|
|
RewriteIncludesAction action(os);
|
|
rewriteInstance.ExecuteAction(action);
|
|
});
|
|
|
|
success |= !rewriteInstance.getDiagnostics().hasErrorOccurred();
|
|
if (!success) {
|
|
Impl.SwiftContext.Diags.diagnose({},
|
|
diag::could_not_rewrite_bridging_header);
|
|
return "";
|
|
}
|
|
|
|
const clang::FileEntry *fileInfo = fileManager.getFile(headerPath);
|
|
fileSize = fileInfo->getSize();
|
|
fileModTime = fileInfo->getModificationTime();
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
ClangImporter::emitBridgingPCH(StringRef headerPath,
|
|
StringRef outputPCHPath) {
|
|
auto invocation = std::make_shared<clang::CompilerInvocation>
|
|
(clang::CompilerInvocation(*Impl.Invocation));
|
|
invocation->getFrontendOpts().DisableFree = false;
|
|
invocation->getFrontendOpts().Inputs.clear();
|
|
invocation->getFrontendOpts().Inputs.push_back(
|
|
clang::FrontendInputFile(headerPath, clang::InputKind::ObjC));
|
|
invocation->getFrontendOpts().OutputFile = outputPCHPath;
|
|
invocation->getFrontendOpts().ProgramAction = clang::frontend::GeneratePCH;
|
|
invocation->getPreprocessorOpts().resetNonModularOptions();
|
|
invocation->getLangOpts()->NeededByPCHOrCompilationUsesPCH = true;
|
|
invocation->getLangOpts()->CacheGeneratedPCH = true;
|
|
|
|
clang::CompilerInstance emitInstance(
|
|
Impl.Instance->getPCHContainerOperations(),
|
|
&Impl.Instance->getModuleCache());
|
|
emitInstance.setInvocation(std::move(invocation));
|
|
emitInstance.createDiagnostics(&Impl.Instance->getDiagnosticClient(),
|
|
/*ShouldOwnClient=*/false);
|
|
|
|
clang::FileManager &fileManager = Impl.Instance->getFileManager();
|
|
emitInstance.setFileManager(&fileManager);
|
|
emitInstance.createSourceManager(fileManager);
|
|
emitInstance.setTarget(&Impl.Instance->getTarget());
|
|
|
|
std::unique_ptr<clang::FrontendAction> action;
|
|
action.reset(new clang::GeneratePCHAction());
|
|
if (!emitInstance.getFrontendOpts().IndexStorePath.empty()) {
|
|
action = clang::index::
|
|
createIndexDataRecordingAction(emitInstance.getFrontendOpts(),
|
|
std::move(action));
|
|
}
|
|
emitInstance.ExecuteAction(*action);
|
|
|
|
if (emitInstance.getDiagnostics().hasErrorOccurred()) {
|
|
Impl.SwiftContext.Diags.diagnose({},
|
|
diag::bridging_header_pch_error,
|
|
outputPCHPath, headerPath);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ClangImporter::collectSubModuleNames(
|
|
ArrayRef<std::pair<Identifier, SourceLoc>> path,
|
|
std::vector<std::string> &names) {
|
|
auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo();
|
|
|
|
// Look up the top-level module first.
|
|
clang::Module *clangModule = clangHeaderSearch.lookupModule(
|
|
path.front().first.str(), /*AllowSearch=*/true,
|
|
/*AllowExtraModuleMapSearch=*/true);
|
|
if (!clangModule)
|
|
return;
|
|
clang::Module *submodule = clangModule;
|
|
for (auto component : path.slice(1)) {
|
|
submodule = submodule->findSubmodule(component.first.str());
|
|
if (!submodule)
|
|
return;
|
|
}
|
|
for (auto sub : submodule->submodules())
|
|
names.push_back(sub->Name);
|
|
}
|
|
|
|
bool ClangImporter::isModuleImported(const clang::Module *M) {
|
|
return M->NameVisibility == clang::Module::NameVisibilityKind::AllVisible;
|
|
}
|
|
|
|
bool ClangImporter::canImportModule(std::pair<Identifier, SourceLoc> moduleID) {
|
|
// Look up the top-level module to see if it exists.
|
|
// FIXME: This only works with top-level modules.
|
|
auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo();
|
|
clang::Module *clangModule =
|
|
clangHeaderSearch.lookupModule(moduleID.first.str(), /*AllowSearch=*/true,
|
|
/*AllowExtraModuleMapSearch=*/true);
|
|
if (!clangModule) {
|
|
return false;
|
|
}
|
|
|
|
clang::Module::Requirement r;
|
|
clang::Module::UnresolvedHeaderDirective mh;
|
|
clang::Module *m;
|
|
auto &ctx = Impl.getClangASTContext();
|
|
return clangModule->isAvailable(ctx.getLangOpts(), getTargetInfo(), r, mh, m);
|
|
}
|
|
|
|
ModuleDecl *ClangImporter::loadModule(
|
|
SourceLoc importLoc,
|
|
ArrayRef<std::pair<Identifier, SourceLoc>> path) {
|
|
auto &clangContext = Impl.getClangASTContext();
|
|
auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo();
|
|
|
|
// Look up the top-level module first, to see if it exists at all.
|
|
clang::Module *clangModule = clangHeaderSearch.lookupModule(
|
|
path.front().first.str(), /*AllowSearch=*/true,
|
|
/*AllowExtraModuleMapSearch=*/true);
|
|
if (!clangModule)
|
|
return nullptr;
|
|
|
|
// Convert the Swift import path over to a Clang import path.
|
|
SmallVector<std::pair<clang::IdentifierInfo *, clang::SourceLocation>, 4>
|
|
clangPath;
|
|
for (auto component : path) {
|
|
clangPath.push_back({ &clangContext.Idents.get(component.first.str()),
|
|
Impl.exportSourceLoc(component.second) } );
|
|
}
|
|
|
|
auto &rawDiagClient = Impl.Instance->getDiagnosticClient();
|
|
auto &diagClient = static_cast<ClangDiagnosticConsumer &>(rawDiagClient);
|
|
|
|
auto loadModule = [&](clang::ModuleIdPath path,
|
|
bool makeVisible) -> clang::ModuleLoadResult {
|
|
clang::Module::NameVisibilityKind visibility =
|
|
makeVisible ? clang::Module::AllVisible : clang::Module::Hidden;
|
|
|
|
auto importRAII = diagClient.handleImport(clangPath.front().first,
|
|
importLoc);
|
|
|
|
std::string preservedIndexStorePathOption;
|
|
auto &clangFEOpts = Impl.Instance->getFrontendOpts();
|
|
if (!clangFEOpts.IndexStorePath.empty()) {
|
|
StringRef moduleName = path[0].first->getName();
|
|
// Ignore the SwiftShims module for the index data.
|
|
if (moduleName == Impl.SwiftContext.SwiftShimsModuleName.str()) {
|
|
preservedIndexStorePathOption = clangFEOpts.IndexStorePath;
|
|
clangFEOpts.IndexStorePath.clear();
|
|
}
|
|
}
|
|
|
|
clang::SourceLocation clangImportLoc = Impl.getNextIncludeLoc();
|
|
|
|
clang::ModuleLoadResult result =
|
|
Impl.Instance->loadModule(clangImportLoc, path, visibility,
|
|
/*IsInclusionDirective=*/false);
|
|
|
|
if (!preservedIndexStorePathOption.empty()) {
|
|
// Restore the -index-store-path option.
|
|
clangFEOpts.IndexStorePath = preservedIndexStorePathOption;
|
|
}
|
|
|
|
if (result && makeVisible)
|
|
Impl.getClangPreprocessor().makeModuleVisible(result, clangImportLoc);
|
|
return result;
|
|
};
|
|
|
|
// Now load the top-level module, so that we can check if the submodule
|
|
// exists without triggering a fatal error.
|
|
clangModule = loadModule(clangPath.front(), false);
|
|
if (!clangModule)
|
|
return nullptr;
|
|
|
|
// Verify that the submodule exists.
|
|
clang::Module *submodule = clangModule;
|
|
for (auto &component : path.slice(1)) {
|
|
submodule = submodule->findSubmodule(component.first.str());
|
|
|
|
// Special case: a submodule named "Foo.Private" can be moved to a top-level
|
|
// module named "Foo_Private". Clang has special support for this.
|
|
// We're limiting this to just submodules named "Private" because this will
|
|
// put the Clang AST in a fatal error state if it /doesn't/ exist.
|
|
if (!submodule && component.first.str() == "Private" &&
|
|
(&component) == (&path[1])) {
|
|
submodule = loadModule(llvm::makeArrayRef(clangPath).slice(0, 2), false);
|
|
}
|
|
|
|
if (!submodule) {
|
|
// FIXME: Specialize the error for a missing submodule?
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// Finally, load the submodule and make it visible.
|
|
clangModule = loadModule(clangPath, true);
|
|
if (!clangModule)
|
|
return nullptr;
|
|
|
|
return Impl.finishLoadingClangModule(clangModule, /*preferOverlay=*/false);
|
|
}
|
|
|
|
ModuleDecl *ClangImporter::Implementation::finishLoadingClangModule(
|
|
const clang::Module *clangModule,
|
|
bool findOverlay) {
|
|
assert(clangModule);
|
|
|
|
// Bump the generation count.
|
|
bumpGeneration();
|
|
|
|
auto &cacheEntry = ModuleWrappers[clangModule];
|
|
ModuleDecl *result;
|
|
ClangModuleUnit *wrapperUnit;
|
|
if ((wrapperUnit = cacheEntry.getPointer())) {
|
|
result = wrapperUnit->getParentModule();
|
|
if (!cacheEntry.getInt()) {
|
|
// Force load overlays for all imported modules.
|
|
// FIXME: This forces the creation of wrapper modules for all imports as
|
|
// well, and may do unnecessary work.
|
|
cacheEntry.setInt(true);
|
|
result->forAllVisibleModules({}, [&](ModuleDecl::ImportedModule import) {});
|
|
}
|
|
} else {
|
|
// Build the representation of the Clang module in Swift.
|
|
// FIXME: The name of this module could end up as a key in the ASTContext,
|
|
// but that's not correct for submodules.
|
|
Identifier name = SwiftContext.getIdentifier((*clangModule).Name);
|
|
result = ModuleDecl::create(name, SwiftContext);
|
|
// Silence error messages about testably importing a Clang module.
|
|
result->setTestingEnabled();
|
|
result->setHasResolvedImports();
|
|
|
|
wrapperUnit =
|
|
new (SwiftContext) ClangModuleUnit(*result, *this, clangModule);
|
|
result->addFile(*wrapperUnit);
|
|
cacheEntry.setPointerAndInt(wrapperUnit, true);
|
|
|
|
// Force load overlays for all imported modules.
|
|
// FIXME: This forces the creation of wrapper modules for all imports as
|
|
// well, and may do unnecessary work.
|
|
result->forAllVisibleModules({}, [](ModuleDecl::ImportedModule import) {});
|
|
}
|
|
|
|
if (clangModule->isSubModule()) {
|
|
finishLoadingClangModule(clangModule->getTopLevelModule(), true);
|
|
} else {
|
|
ModuleDecl *&loaded = SwiftContext.LoadedModules[result->getName()];
|
|
if (!loaded)
|
|
loaded = result;
|
|
}
|
|
|
|
if (findOverlay)
|
|
if (ModuleDecl *overlay = wrapperUnit->getOverlayModule())
|
|
result = overlay;
|
|
|
|
return result;
|
|
}
|
|
|
|
// Run through the set of deferred imports -- either those referenced by
|
|
// submodule ID from a bridging PCH, or those already loaded as clang::Modules
|
|
// in response to an import directive in a bridging header -- and call
|
|
// finishLoadingClangModule on each.
|
|
void ClangImporter::Implementation::handleDeferredImports()
|
|
{
|
|
clang::ASTReader &R = *Instance->getModuleManager();
|
|
llvm::SmallSet<clang::serialization::SubmoduleID, 32> seenSubmodules;
|
|
for (clang::serialization::SubmoduleID ID : PCHImportedSubmodules) {
|
|
if (!seenSubmodules.insert(ID).second)
|
|
continue;
|
|
ImportedHeaderExports.push_back(R.getSubmodule(ID));
|
|
}
|
|
PCHImportedSubmodules.clear();
|
|
for (const clang::Module *M : ImportedHeaderExports)
|
|
(void)finishLoadingClangModule(M, /*preferOverlay=*/true);
|
|
}
|
|
|
|
ModuleDecl *ClangImporter::getImportedHeaderModule() const {
|
|
return Impl.ImportedHeaderUnit->getParentModule();
|
|
}
|
|
|
|
PlatformAvailability::PlatformAvailability(LangOptions &langOpts)
|
|
: platformKind(targetPlatform(langOpts)) {
|
|
switch (platformKind) {
|
|
case PlatformKind::iOS:
|
|
case PlatformKind::iOSApplicationExtension:
|
|
case PlatformKind::tvOS:
|
|
case PlatformKind::tvOSApplicationExtension:
|
|
deprecatedAsUnavailableMessage =
|
|
"APIs deprecated as of iOS 7 and earlier are unavailable in Swift";
|
|
break;
|
|
|
|
case PlatformKind::watchOS:
|
|
case PlatformKind::watchOSApplicationExtension:
|
|
deprecatedAsUnavailableMessage = "";
|
|
break;
|
|
|
|
case PlatformKind::OSX:
|
|
case PlatformKind::OSXApplicationExtension:
|
|
deprecatedAsUnavailableMessage =
|
|
"APIs deprecated as of macOS 10.9 and earlier are unavailable in Swift";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool PlatformAvailability::isPlatformRelevant(StringRef name) const {
|
|
switch (platformKind) {
|
|
case PlatformKind::OSX:
|
|
return name == "macos";
|
|
case PlatformKind::OSXApplicationExtension:
|
|
return name == "macos" || name == "macos_app_extension";
|
|
|
|
case PlatformKind::iOS:
|
|
return name == "ios";
|
|
case PlatformKind::iOSApplicationExtension:
|
|
return name == "ios" || name == "ios_app_extension";
|
|
|
|
case PlatformKind::tvOS:
|
|
return name == "tvos";
|
|
case PlatformKind::tvOSApplicationExtension:
|
|
return name == "tvos" || name == "tvos_app_extension";
|
|
|
|
case PlatformKind::watchOS:
|
|
return name == "watchos";
|
|
case PlatformKind::watchOSApplicationExtension:
|
|
return name == "watchos" || name == "watchos_app_extension";
|
|
|
|
case PlatformKind::none:
|
|
return false;
|
|
}
|
|
|
|
llvm_unreachable("Unexpected platform");
|
|
}
|
|
|
|
bool PlatformAvailability::treatDeprecatedAsUnavailable(
|
|
const clang::Decl *clangDecl, const llvm::VersionTuple &version) const {
|
|
assert(!version.empty() && "Must provide version when deprecated");
|
|
unsigned major = version.getMajor();
|
|
Optional<unsigned> minor = version.getMinor();
|
|
|
|
switch (platformKind) {
|
|
case PlatformKind::OSX:
|
|
// Anything deprecated in OSX 10.9.x and earlier is unavailable in Swift.
|
|
return major < 10 ||
|
|
(major == 10 && (!minor.hasValue() || minor.getValue() <= 9));
|
|
|
|
case PlatformKind::iOS:
|
|
case PlatformKind::iOSApplicationExtension:
|
|
case PlatformKind::tvOS:
|
|
case PlatformKind::tvOSApplicationExtension:
|
|
// Anything deprecated in iOS 7.x and earlier is unavailable in Swift.
|
|
return major <= 7;
|
|
|
|
case PlatformKind::watchOS:
|
|
case PlatformKind::watchOSApplicationExtension:
|
|
// No deprecation filter on watchOS
|
|
return false;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ClangImporter::Implementation::Implementation(ASTContext &ctx,
|
|
const ClangImporterOptions &opts)
|
|
: SwiftContext(ctx),
|
|
ImportForwardDeclarations(opts.ImportForwardDeclarations),
|
|
InferImportAsMember(opts.InferImportAsMember),
|
|
DisableSwiftBridgeAttr(opts.DisableSwiftBridgeAttr),
|
|
BridgingHeaderExplicitlyRequested(!opts.BridgingHeader.empty()),
|
|
DisableOverlayModules(opts.DisableOverlayModules),
|
|
IsReadingBridgingPCH(false),
|
|
CurrentVersion(ImportNameVersion::fromOptions(ctx.LangOpts)),
|
|
BridgingHeaderLookupTable(new SwiftLookupTable(nullptr)),
|
|
platformAvailability(ctx.LangOpts),
|
|
nameImporter() {}
|
|
|
|
ClangImporter::Implementation::~Implementation() {
|
|
#ifndef NDEBUG
|
|
SwiftContext.SourceMgr.verifyAllBuffers();
|
|
#endif
|
|
}
|
|
|
|
ClangModuleUnit *ClangImporter::Implementation::getWrapperForModule(
|
|
const clang::Module *underlying) {
|
|
auto &cacheEntry = ModuleWrappers[underlying];
|
|
if (ClangModuleUnit *cached = cacheEntry.getPointer())
|
|
return cached;
|
|
|
|
// FIXME: Handle hierarchical names better.
|
|
Identifier name = SwiftContext.getIdentifier(underlying->Name);
|
|
auto wrapper = ModuleDecl::create(name, SwiftContext);
|
|
// Silence error messages about testably importing a Clang module.
|
|
wrapper->setTestingEnabled();
|
|
wrapper->setHasResolvedImports();
|
|
|
|
auto file = new (SwiftContext) ClangModuleUnit(*wrapper, *this,
|
|
underlying);
|
|
wrapper->addFile(*file);
|
|
cacheEntry.setPointer(file);
|
|
|
|
return file;
|
|
}
|
|
|
|
ClangModuleUnit *ClangImporter::Implementation::getClangModuleForDecl(
|
|
const clang::Decl *D,
|
|
bool allowForwardDeclaration) {
|
|
auto maybeModule = getClangSubmoduleForDecl(D, allowForwardDeclaration);
|
|
if (!maybeModule)
|
|
return nullptr;
|
|
if (!maybeModule.getValue())
|
|
return ImportedHeaderUnit;
|
|
|
|
// Get the parent module because currently we don't represent submodules with
|
|
// ClangModuleUnit.
|
|
auto *M = maybeModule.getValue()->getTopLevelModule();
|
|
|
|
return getWrapperForModule(M);
|
|
}
|
|
|
|
#pragma mark Source locations
|
|
clang::SourceLocation
|
|
ClangImporter::Implementation::exportSourceLoc(SourceLoc loc) {
|
|
// FIXME: Implement!
|
|
return clang::SourceLocation();
|
|
}
|
|
|
|
SourceLoc
|
|
ClangImporter::Implementation::importSourceLoc(clang::SourceLocation loc) {
|
|
// FIXME: Implement!
|
|
return SourceLoc();
|
|
}
|
|
|
|
SourceRange
|
|
ClangImporter::Implementation::importSourceRange(clang::SourceRange loc) {
|
|
// FIXME: Implement!
|
|
return SourceRange();
|
|
}
|
|
|
|
#pragma mark Importing names
|
|
|
|
clang::DeclarationName
|
|
ClangImporter::Implementation::exportName(Identifier name) {
|
|
// FIXME: When we start dealing with C++, we can map over some operator
|
|
// names.
|
|
if (name.empty() || name.isOperator())
|
|
return clang::DeclarationName();
|
|
|
|
// Map the identifier. If it's some kind of keyword, it can't be mapped.
|
|
auto ident = &Instance->getASTContext().Idents.get(name.str());
|
|
if (ident->getTokenID() != clang::tok::identifier)
|
|
return clang::DeclarationName();
|
|
|
|
return ident;
|
|
}
|
|
|
|
Identifier
|
|
ClangImporter::Implementation::importIdentifier(
|
|
const clang::IdentifierInfo *identifier,
|
|
StringRef removePrefix)
|
|
{
|
|
if (!identifier) return Identifier();
|
|
|
|
StringRef name = identifier->getName();
|
|
// Remove the prefix, if any.
|
|
if (!removePrefix.empty()) {
|
|
if (name.startswith(removePrefix)) {
|
|
name = name.slice(removePrefix.size(), name.size());
|
|
}
|
|
}
|
|
|
|
// Get the Swift identifier.
|
|
return SwiftContext.getIdentifier(name);
|
|
}
|
|
|
|
ObjCSelector ClangImporter::Implementation::importSelector(
|
|
clang::Selector selector) {
|
|
auto &ctx = SwiftContext;
|
|
|
|
// Handle zero-argument selectors directly.
|
|
if (selector.isUnarySelector()) {
|
|
Identifier name;
|
|
if (auto id = selector.getIdentifierInfoForSlot(0))
|
|
name = ctx.getIdentifier(id->getName());
|
|
return ObjCSelector(ctx, 0, name);
|
|
}
|
|
|
|
SmallVector<Identifier, 2> pieces;
|
|
for (auto i = 0u, n = selector.getNumArgs(); i != n; ++i) {
|
|
Identifier piece;
|
|
if (auto id = selector.getIdentifierInfoForSlot(i))
|
|
piece = ctx.getIdentifier(id->getName());
|
|
pieces.push_back(piece);
|
|
}
|
|
|
|
return ObjCSelector(ctx, pieces.size(), pieces);
|
|
}
|
|
|
|
clang::Selector
|
|
ClangImporter::Implementation::exportSelector(DeclName name,
|
|
bool allowSimpleName) {
|
|
if (!allowSimpleName && name.isSimpleName())
|
|
return {};
|
|
|
|
clang::ASTContext &ctx = getClangASTContext();
|
|
|
|
SmallVector<clang::IdentifierInfo *, 8> pieces;
|
|
pieces.push_back(exportName(name.getBaseIdentifier()).getAsIdentifierInfo());
|
|
|
|
auto argNames = name.getArgumentNames();
|
|
if (argNames.empty())
|
|
return ctx.Selectors.getNullarySelector(pieces.front());
|
|
|
|
if (!argNames.front().empty())
|
|
return {};
|
|
argNames = argNames.slice(1);
|
|
|
|
for (Identifier argName : argNames)
|
|
pieces.push_back(exportName(argName).getAsIdentifierInfo());
|
|
|
|
return ctx.Selectors.getSelector(pieces.size(), pieces.data());
|
|
}
|
|
|
|
clang::Selector
|
|
ClangImporter::Implementation::exportSelector(ObjCSelector selector) {
|
|
SmallVector<clang::IdentifierInfo *, 4> pieces;
|
|
for (auto piece : selector.getSelectorPieces())
|
|
pieces.push_back(exportName(piece).getAsIdentifierInfo());
|
|
return getClangASTContext().Selectors.getSelector(selector.getNumArgs(),
|
|
pieces.data());
|
|
}
|
|
|
|
/// Determine whether the given method potentially conflicts with the
|
|
/// setter for a property in the given protocol.
|
|
static bool
|
|
isPotentiallyConflictingSetter(const clang::ObjCProtocolDecl *proto,
|
|
const clang::ObjCMethodDecl *method) {
|
|
auto sel = method->getSelector();
|
|
if (sel.getNumArgs() != 1)
|
|
return false;
|
|
|
|
clang::IdentifierInfo *setterID = sel.getIdentifierInfoForSlot(0);
|
|
if (!setterID || !setterID->getName().startswith("set"))
|
|
return false;
|
|
|
|
for (auto *prop : proto->properties()) {
|
|
if (prop->getSetterName() == sel)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool importer::shouldSuppressDeclImport(const clang::Decl *decl) {
|
|
if (auto objcMethod = dyn_cast<clang::ObjCMethodDecl>(decl)) {
|
|
// First check if we're actually in a Swift class.
|
|
auto dc = decl->getDeclContext();
|
|
if (hasNativeSwiftDecl(cast<clang::ObjCContainerDecl>(dc)))
|
|
return true;
|
|
|
|
// If this member is a method that is a getter or setter for a
|
|
// property, don't add it into the table. property names and
|
|
// getter names (by choosing to only have a property).
|
|
//
|
|
// Note that this is suppressed for certain accessibility declarations,
|
|
// which are imported as getter/setter pairs and not properties.
|
|
if (objcMethod->isPropertyAccessor()) {
|
|
// Suppress the import of this method when the corresponding
|
|
// property is not suppressed.
|
|
return !shouldSuppressDeclImport(
|
|
objcMethod->findPropertyDecl(/*CheckOverrides=*/false));
|
|
}
|
|
|
|
// If the method was declared within a protocol, check that it
|
|
// does not conflict with the setter of a property.
|
|
if (auto proto = dyn_cast<clang::ObjCProtocolDecl>(dc))
|
|
return isPotentiallyConflictingSetter(proto, objcMethod);
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
if (auto objcProperty = dyn_cast<clang::ObjCPropertyDecl>(decl)) {
|
|
// First check if we're actually in a Swift class.
|
|
auto dc = objcProperty->getDeclContext();
|
|
if (hasNativeSwiftDecl(cast<clang::ObjCContainerDecl>(dc)))
|
|
return true;
|
|
|
|
// Suppress certain properties; import them as getter/setter pairs instead.
|
|
if (shouldImportPropertyAsAccessors(objcProperty))
|
|
return true;
|
|
|
|
// Check whether there is a superclass method for the getter that
|
|
// is *not* suppressed, in which case we will need to suppress
|
|
// this property.
|
|
auto objcClass = dyn_cast<clang::ObjCInterfaceDecl>(dc);
|
|
if (!objcClass) {
|
|
if (auto objcCategory = dyn_cast<clang::ObjCCategoryDecl>(dc)) {
|
|
// If the enclosing category is invalid, suppress this declaration.
|
|
if (objcCategory->isInvalidDecl()) return true;
|
|
|
|
objcClass = objcCategory->getClassInterface();
|
|
}
|
|
}
|
|
|
|
if (objcClass) {
|
|
if (auto objcSuperclass = objcClass->getSuperClass()) {
|
|
auto getterMethod =
|
|
objcSuperclass->lookupMethod(objcProperty->getGetterName(),
|
|
objcProperty->isInstanceProperty());
|
|
if (getterMethod && !shouldSuppressDeclImport(getterMethod))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#pragma mark Name lookup
|
|
const clang::TypedefNameDecl *
|
|
ClangImporter::Implementation::lookupTypedef(clang::DeclarationName name) {
|
|
clang::Sema &sema = Instance->getSema();
|
|
clang::LookupResult lookupResult(sema, name,
|
|
clang::SourceLocation(),
|
|
clang::Sema::LookupOrdinaryName);
|
|
|
|
if (sema.LookupName(lookupResult, /*scope=*/nullptr)) {
|
|
for (auto decl : lookupResult) {
|
|
if (auto typedefDecl =
|
|
dyn_cast<clang::TypedefNameDecl>(decl->getUnderlyingDecl()))
|
|
return typedefDecl;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static bool isDeclaredInModule(const ClangModuleUnit *ModuleFilter,
|
|
const Decl *VD) {
|
|
auto ContainingUnit = VD->getDeclContext()->getModuleScopeContext();
|
|
return ModuleFilter == ContainingUnit;
|
|
}
|
|
|
|
static const clang::Module *
|
|
getClangOwningModule(ClangNode Node, const clang::ASTContext &ClangCtx) {
|
|
assert(!Node.getAsModule() && "not implemented for modules");
|
|
|
|
if (const clang::Decl *D = Node.getAsDecl()) {
|
|
auto ExtSource = ClangCtx.getExternalSource();
|
|
assert(ExtSource);
|
|
return ExtSource->getModule(D->getOwningModuleID());
|
|
}
|
|
|
|
if (const clang::ModuleMacro *M = Node.getAsModuleMacro())
|
|
return M->getOwningModule();
|
|
|
|
// A locally-defined MacroInfo does not have an owning module.
|
|
assert(Node.getAsMacroInfo());
|
|
return nullptr;
|
|
}
|
|
|
|
static const clang::Module *
|
|
getClangTopLevelOwningModule(ClangNode Node,
|
|
const clang::ASTContext &ClangCtx) {
|
|
const clang::Module *OwningModule = getClangOwningModule(Node, ClangCtx);
|
|
if (!OwningModule)
|
|
return nullptr;
|
|
return OwningModule->getTopLevelModule();
|
|
}
|
|
|
|
static bool isVisibleFromModule(const ClangModuleUnit *ModuleFilter,
|
|
ValueDecl *VD) {
|
|
assert(ModuleFilter);
|
|
|
|
auto ContainingUnit = VD->getDeclContext()->getModuleScopeContext();
|
|
if (ModuleFilter == ContainingUnit)
|
|
return true;
|
|
|
|
// The rest of this function is looking to see if the Clang entity that
|
|
// caused VD to be imported has redeclarations in the filter module.
|
|
auto Wrapper = dyn_cast<ClangModuleUnit>(ContainingUnit);
|
|
if (!Wrapper)
|
|
return false;
|
|
|
|
auto ClangNode = VD->getClangNode();
|
|
if (!ClangNode) {
|
|
// If we synthesized a ValueDecl, it won't have a Clang node. Find the
|
|
// associated declaration that /does/ have a Clang node, and use that.
|
|
auto *SynthesizedTypeAttr =
|
|
VD->getAttrs().getAttribute<ClangImporterSynthesizedTypeAttr>();
|
|
assert(SynthesizedTypeAttr);
|
|
|
|
switch (SynthesizedTypeAttr->getKind()) {
|
|
case ClangImporterSynthesizedTypeAttr::Kind::NSErrorWrapper:
|
|
case ClangImporterSynthesizedTypeAttr::Kind::NSErrorWrapperAnon: {
|
|
ASTContext &Ctx = ContainingUnit->getASTContext();
|
|
auto LookupFlags =
|
|
NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions;
|
|
auto WrapperStruct = cast<StructDecl>(VD);
|
|
TinyPtrVector<ValueDecl *> LookupResults =
|
|
WrapperStruct->lookupDirect(Ctx.Id_Code, LookupFlags);
|
|
assert(!LookupResults.empty() && "imported error enum without Code");
|
|
|
|
auto CodeEnumIter = llvm::find_if(LookupResults,
|
|
[&](ValueDecl *Member) -> bool {
|
|
return Member->getDeclContext() == WrapperStruct;
|
|
});
|
|
assert(CodeEnumIter != LookupResults.end() &&
|
|
"could not find Code enum in wrapper struct");
|
|
assert((*CodeEnumIter)->hasClangNode());
|
|
ClangNode = (*CodeEnumIter)->getClangNode();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Macros can be "redeclared" by putting an equivalent definition in two
|
|
// different modules. (We don't actually check the equivalence.)
|
|
// FIXME: We're also not checking if the redeclaration is in /this/ module.
|
|
if (ClangNode.getAsMacro())
|
|
return true;
|
|
|
|
const clang::Decl *D = ClangNode.castAsDecl();
|
|
auto &ClangASTContext = ModuleFilter->getClangASTContext();
|
|
|
|
// We don't handle Clang submodules; pop everything up to the top-level
|
|
// module.
|
|
auto OwningClangModule = getClangTopLevelOwningModule(ClangNode,
|
|
ClangASTContext);
|
|
if (OwningClangModule == ModuleFilter->getClangModule())
|
|
return true;
|
|
|
|
// Handle redeclarable Clang decls by checking each redeclaration.
|
|
bool IsTagDecl = isa<clang::TagDecl>(D);
|
|
if (!(IsTagDecl || isa<clang::FunctionDecl>(D) || isa<clang::VarDecl>(D) ||
|
|
isa<clang::TypedefNameDecl>(D))) {
|
|
return false;
|
|
}
|
|
|
|
for (auto Redeclaration : D->redecls()) {
|
|
if (Redeclaration == D)
|
|
continue;
|
|
|
|
// For enums, structs, and unions, only count definitions when looking to
|
|
// see what other modules they appear in.
|
|
if (IsTagDecl)
|
|
if (!cast<clang::TagDecl>(Redeclaration)->isCompleteDefinition())
|
|
continue;
|
|
|
|
auto OwningClangModule = getClangTopLevelOwningModule(Redeclaration,
|
|
ClangASTContext);
|
|
if (OwningClangModule == ModuleFilter->getClangModule())
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
namespace {
|
|
class ClangVectorDeclConsumer : public clang::VisibleDeclConsumer {
|
|
std::vector<clang::NamedDecl *> results;
|
|
public:
|
|
ClangVectorDeclConsumer() = default;
|
|
|
|
void FoundDecl(clang::NamedDecl *ND, clang::NamedDecl *Hiding,
|
|
clang::DeclContext *Ctx, bool InBaseClass) override {
|
|
if (!ND->getIdentifier())
|
|
return;
|
|
|
|
if (ND->isModulePrivate())
|
|
return;
|
|
|
|
results.push_back(ND);
|
|
}
|
|
|
|
llvm::MutableArrayRef<clang::NamedDecl *> getResults() {
|
|
return results;
|
|
}
|
|
};
|
|
|
|
class FilteringVisibleDeclConsumer : public swift::VisibleDeclConsumer {
|
|
swift::VisibleDeclConsumer &NextConsumer;
|
|
const ClangModuleUnit *ModuleFilter;
|
|
|
|
public:
|
|
FilteringVisibleDeclConsumer(swift::VisibleDeclConsumer &consumer,
|
|
const ClangModuleUnit *CMU)
|
|
: NextConsumer(consumer), ModuleFilter(CMU) {
|
|
assert(CMU);
|
|
}
|
|
|
|
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo) override {
|
|
if (isVisibleFromModule(ModuleFilter, VD))
|
|
NextConsumer.foundDecl(VD, Reason, dynamicLookupInfo);
|
|
}
|
|
};
|
|
|
|
class FilteringDeclaredDeclConsumer : public swift::VisibleDeclConsumer {
|
|
swift::VisibleDeclConsumer &NextConsumer;
|
|
const ClangModuleUnit *ModuleFilter;
|
|
|
|
public:
|
|
FilteringDeclaredDeclConsumer(swift::VisibleDeclConsumer &consumer,
|
|
const ClangModuleUnit *CMU)
|
|
: NextConsumer(consumer), ModuleFilter(CMU) {
|
|
assert(CMU);
|
|
}
|
|
|
|
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo) override {
|
|
if (isDeclaredInModule(ModuleFilter, VD))
|
|
NextConsumer.foundDecl(VD, Reason, dynamicLookupInfo);
|
|
}
|
|
};
|
|
|
|
/// A hack to hide particular types in the "Darwin" module on Apple platforms.
|
|
class DarwinLegacyFilterDeclConsumer : public swift::VisibleDeclConsumer {
|
|
swift::VisibleDeclConsumer &NextConsumer;
|
|
clang::ASTContext &ClangASTContext;
|
|
|
|
bool shouldDiscard(ValueDecl *VD) {
|
|
if (!VD->hasClangNode())
|
|
return false;
|
|
|
|
const clang::Module *clangModule = getClangOwningModule(VD->getClangNode(),
|
|
ClangASTContext);
|
|
if (!clangModule)
|
|
return false;
|
|
|
|
if (clangModule->Name == "MacTypes") {
|
|
if (!VD->hasName() || VD->getBaseName().isSpecial())
|
|
return true;
|
|
return llvm::StringSwitch<bool>(VD->getBaseName().getIdentifier().str())
|
|
.Cases("OSErr", "OSStatus", "OptionBits", false)
|
|
.Cases("FourCharCode", "OSType", false)
|
|
.Case("Boolean", false)
|
|
.Case("kUnknownType", false)
|
|
.Cases("UTF32Char", "UniChar", "UTF16Char", "UTF8Char", false)
|
|
.Case("ProcessSerialNumber", false)
|
|
.Default(true);
|
|
}
|
|
|
|
if (clangModule->Parent &&
|
|
clangModule->Parent->Name == "CarbonCore") {
|
|
return llvm::StringSwitch<bool>(clangModule->Name)
|
|
.Cases("BackupCore", "DiskSpaceRecovery", "MacErrors", false)
|
|
.Case("UnicodeUtilities", false)
|
|
.Default(true);
|
|
}
|
|
|
|
if (clangModule->Parent &&
|
|
clangModule->Parent->Name == "OSServices") {
|
|
// Note that this is a list of things to /drop/ rather than to /keep/.
|
|
// We're more likely to see new, modern headers added to OSServices.
|
|
return llvm::StringSwitch<bool>(clangModule->Name)
|
|
.Cases("IconStorage", "KeychainCore", "Power", true)
|
|
.Cases("SecurityCore", "SystemSound", true)
|
|
.Cases("WSMethodInvocation", "WSProtocolHandler", "WSTypes", true)
|
|
.Default(false);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
DarwinLegacyFilterDeclConsumer(swift::VisibleDeclConsumer &consumer,
|
|
clang::ASTContext &clangASTContext)
|
|
: NextConsumer(consumer), ClangASTContext(clangASTContext) {}
|
|
|
|
static bool needsFiltering(const clang::Module *topLevelModule) {
|
|
return topLevelModule && (topLevelModule->Name == "Darwin" ||
|
|
topLevelModule->Name == "CoreServices");
|
|
}
|
|
|
|
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo dynamicLookupInfo) override {
|
|
if (!shouldDiscard(VD))
|
|
NextConsumer.foundDecl(VD, Reason, dynamicLookupInfo);
|
|
}
|
|
};
|
|
|
|
} // unnamed namespace
|
|
|
|
/// Translate a MacroDefinition to a ClangNode, either a ModuleMacro for
|
|
/// a definition imported from a module or a MacroInfo for a macro defined
|
|
/// locally.
|
|
ClangNode getClangNodeForMacroDefinition(clang::MacroDefinition &M) {
|
|
if (!M.getModuleMacros().empty())
|
|
return ClangNode(M.getModuleMacros().back()->getMacroInfo());
|
|
if (auto *MD = M.getLocalDirective())
|
|
return ClangNode(MD->getMacroInfo());
|
|
return ClangNode();
|
|
}
|
|
|
|
void ClangImporter::lookupBridgingHeaderDecls(
|
|
llvm::function_ref<bool(ClangNode)> filter,
|
|
llvm::function_ref<void(Decl*)> receiver) const {
|
|
for (auto &Import : Impl.BridgeHeaderTopLevelImports) {
|
|
auto ImportD = Import.get<ImportDecl*>();
|
|
if (filter(ImportD->getClangDecl()))
|
|
receiver(ImportD);
|
|
}
|
|
for (auto *ClangD : Impl.BridgeHeaderTopLevelDecls) {
|
|
if (filter(ClangD)) {
|
|
if (auto *ND = dyn_cast<clang::NamedDecl>(ClangD)) {
|
|
if (Decl *imported = Impl.importDeclReal(ND, Impl.CurrentVersion))
|
|
receiver(imported);
|
|
}
|
|
}
|
|
}
|
|
|
|
auto &ClangPP = Impl.getClangPreprocessor();
|
|
for (clang::IdentifierInfo *II : Impl.BridgeHeaderMacros) {
|
|
auto MD = ClangPP.getMacroDefinition(II);
|
|
if (auto macroNode = getClangNodeForMacroDefinition(MD)) {
|
|
if (filter(macroNode)) {
|
|
auto MI = macroNode.getAsMacro();
|
|
Identifier Name = Impl.getNameImporter().importMacroName(II, MI);
|
|
if (Decl *imported = Impl.importMacro(Name, macroNode))
|
|
receiver(imported);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ClangImporter::lookupDeclsFromHeader(StringRef Filename,
|
|
llvm::function_ref<bool(ClangNode)> filter,
|
|
llvm::function_ref<void(Decl*)> receiver) const {
|
|
const clang::FileEntry *File =
|
|
getClangPreprocessor().getFileManager().getFile(Filename);
|
|
if (!File)
|
|
return true;
|
|
|
|
auto &ClangCtx = getClangASTContext();
|
|
auto &ClangSM = ClangCtx.getSourceManager();
|
|
auto &ClangPP = getClangPreprocessor();
|
|
|
|
// Look up the header in the includes of the bridging header.
|
|
if (Impl.BridgeHeaderFiles.count(File)) {
|
|
auto headerFilter = [&](ClangNode ClangN) -> bool {
|
|
if (ClangN.isNull())
|
|
return false;
|
|
|
|
auto ClangLoc = ClangSM.getFileLoc(ClangN.getLocation());
|
|
if (ClangLoc.isInvalid())
|
|
return false;
|
|
|
|
if (ClangSM.getFileEntryForID(ClangSM.getFileID(ClangLoc)) != File)
|
|
return false;
|
|
|
|
return filter(ClangN);
|
|
};
|
|
|
|
lookupBridgingHeaderDecls(headerFilter, receiver);
|
|
return false;
|
|
}
|
|
|
|
clang::FileID FID = ClangSM.translateFile(File);
|
|
if (FID.isInvalid())
|
|
return false;
|
|
|
|
// Look up the header in the ASTReader.
|
|
if (ClangSM.isLoadedFileID(FID)) {
|
|
// Decls.
|
|
SmallVector<clang::Decl *, 32> Decls;
|
|
unsigned Length = ClangSM.getFileIDSize(FID);
|
|
ClangCtx.getExternalSource()->FindFileRegionDecls(FID, 0, Length, Decls);
|
|
for (auto *ClangD : Decls) {
|
|
if (Impl.shouldIgnoreBridgeHeaderTopLevelDecl(ClangD))
|
|
continue;
|
|
if (filter(ClangD)) {
|
|
if (auto *ND = dyn_cast<clang::NamedDecl>(ClangD)) {
|
|
if (Decl *imported = Impl.importDeclReal(ND, Impl.CurrentVersion))
|
|
receiver(imported);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Macros.
|
|
if (auto *ppRec = ClangPP.getPreprocessingRecord()) {
|
|
clang::SourceLocation B = ClangSM.getLocForStartOfFile(FID);
|
|
clang::SourceLocation E = ClangSM.getLocForEndOfFile(FID);
|
|
clang::SourceRange R(B, E);
|
|
const auto &Entities = ppRec->getPreprocessedEntitiesInRange(R);
|
|
for (auto I = Entities.begin(), E = Entities.end(); I != E; ++I) {
|
|
if (!ppRec->isEntityInFileID(I, FID))
|
|
continue;
|
|
clang::PreprocessedEntity *PPE = *I;
|
|
if (!PPE)
|
|
continue;
|
|
if (auto *MDR = dyn_cast<clang::MacroDefinitionRecord>(PPE)) {
|
|
auto *II = const_cast<clang::IdentifierInfo*>(MDR->getName());
|
|
auto MD = ClangPP.getMacroDefinition(II);
|
|
if (auto macroNode = getClangNodeForMacroDefinition(MD)) {
|
|
if (filter(macroNode)) {
|
|
auto MI = macroNode.getAsMacro();
|
|
Identifier Name = Impl.getNameImporter().importMacroName(II, MI);
|
|
if (Decl *imported = Impl.importMacro(Name, macroNode))
|
|
receiver(imported);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// FIXME: Module imports inside that header.
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true; // no info found about that header.
|
|
}
|
|
|
|
void ClangImporter::lookupValue(DeclName name, VisibleDeclConsumer &consumer){
|
|
Impl.forEachLookupTable([&](SwiftLookupTable &table) -> bool {
|
|
Impl.lookupValue(table, name, consumer);
|
|
return false;
|
|
});
|
|
}
|
|
|
|
void
|
|
ClangImporter::lookupTypeDecl(StringRef rawName, ClangTypeKind kind,
|
|
llvm::function_ref<void(TypeDecl*)> receiver) {
|
|
clang::DeclarationName clangName(
|
|
&Impl.Instance->getASTContext().Idents.get(rawName));
|
|
|
|
clang::Sema::LookupNameKind lookupKind;
|
|
switch (kind) {
|
|
case ClangTypeKind::Typedef:
|
|
lookupKind = clang::Sema::LookupOrdinaryName;
|
|
break;
|
|
case ClangTypeKind::Tag:
|
|
lookupKind = clang::Sema::LookupTagName;
|
|
break;
|
|
case ClangTypeKind::ObjCProtocol:
|
|
lookupKind = clang::Sema::LookupObjCProtocolName;
|
|
break;
|
|
}
|
|
|
|
// Perform name lookup into the global scope.
|
|
auto &sema = Impl.Instance->getSema();
|
|
clang::LookupResult lookupResult(sema, clangName, clang::SourceLocation(),
|
|
lookupKind);
|
|
if (sema.LookupName(lookupResult, /*Scope=*/nullptr)) {
|
|
for (auto clangDecl : lookupResult) {
|
|
if (!isa<clang::TypeDecl>(clangDecl) &&
|
|
!isa<clang::ObjCContainerDecl>(clangDecl) &&
|
|
!isa<clang::ObjCCompatibleAliasDecl>(clangDecl)) {
|
|
continue;
|
|
}
|
|
auto *imported = Impl.importDecl(clangDecl, Impl.CurrentVersion);
|
|
if (auto *importedType = dyn_cast_or_null<TypeDecl>(imported))
|
|
receiver(importedType);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClangImporter::lookupRelatedEntity(
|
|
StringRef rawName, ClangTypeKind kind, StringRef relatedEntityKind,
|
|
llvm::function_ref<void(TypeDecl*)> receiver) {
|
|
using CISTAttr = ClangImporterSynthesizedTypeAttr;
|
|
if (relatedEntityKind ==
|
|
CISTAttr::manglingNameForKind(CISTAttr::Kind::NSErrorWrapper) ||
|
|
relatedEntityKind ==
|
|
CISTAttr::manglingNameForKind(CISTAttr::Kind::NSErrorWrapperAnon)) {
|
|
auto underlyingKind = ClangTypeKind::Tag;
|
|
if (relatedEntityKind ==
|
|
CISTAttr::manglingNameForKind(CISTAttr::Kind::NSErrorWrapperAnon)) {
|
|
underlyingKind = ClangTypeKind::Typedef;
|
|
}
|
|
lookupTypeDecl(rawName, underlyingKind,
|
|
[this, receiver] (const TypeDecl *foundType) {
|
|
auto *enumDecl =
|
|
dyn_cast_or_null<clang::EnumDecl>(foundType->getClangDecl());
|
|
if (!enumDecl)
|
|
return;
|
|
if (!Impl.getEnumInfo(enumDecl).isErrorEnum())
|
|
return;
|
|
auto *enclosingType =
|
|
dyn_cast<NominalTypeDecl>(foundType->getDeclContext());
|
|
if (!enclosingType)
|
|
return;
|
|
receiver(enclosingType);
|
|
});
|
|
}
|
|
}
|
|
|
|
void ClangModuleUnit::lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath,
|
|
VisibleDeclConsumer &consumer,
|
|
NLKind lookupKind) const {
|
|
// FIXME: Ignore submodules, which are empty for now.
|
|
if (clangModule && clangModule->isSubModule())
|
|
return;
|
|
|
|
// FIXME: Respect the access path.
|
|
FilteringVisibleDeclConsumer filterConsumer(consumer, this);
|
|
|
|
DarwinLegacyFilterDeclConsumer darwinFilterConsumer(filterConsumer,
|
|
getClangASTContext());
|
|
|
|
swift::VisibleDeclConsumer *actualConsumer = &filterConsumer;
|
|
if (lookupKind == NLKind::UnqualifiedLookup &&
|
|
DarwinLegacyFilterDeclConsumer::needsFiltering(clangModule)) {
|
|
actualConsumer = &darwinFilterConsumer;
|
|
}
|
|
|
|
// Find the corresponding lookup table.
|
|
if (auto lookupTable = owner.findLookupTable(clangModule)) {
|
|
// Search it.
|
|
owner.lookupVisibleDecls(*lookupTable, *actualConsumer);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
class VectorDeclPtrConsumer : public swift::VisibleDeclConsumer {
|
|
public:
|
|
SmallVectorImpl<Decl *> &Results;
|
|
explicit VectorDeclPtrConsumer(SmallVectorImpl<Decl *> &Decls)
|
|
: Results(Decls) {}
|
|
|
|
void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason,
|
|
DynamicLookupInfo) override {
|
|
Results.push_back(VD);
|
|
}
|
|
};
|
|
} // unnamed namespace
|
|
|
|
void ClangModuleUnit::getTopLevelDecls(SmallVectorImpl<Decl*> &results) const {
|
|
VectorDeclPtrConsumer consumer(results);
|
|
FilteringDeclaredDeclConsumer filterConsumer(consumer, this);
|
|
DarwinLegacyFilterDeclConsumer darwinFilterConsumer(filterConsumer,
|
|
getClangASTContext());
|
|
|
|
const clang::Module *topLevelModule =
|
|
clangModule ? clangModule->getTopLevelModule() : nullptr;
|
|
|
|
swift::VisibleDeclConsumer *actualConsumer = &filterConsumer;
|
|
if (DarwinLegacyFilterDeclConsumer::needsFiltering(topLevelModule))
|
|
actualConsumer = &darwinFilterConsumer;
|
|
|
|
// Find the corresponding lookup table.
|
|
if (auto lookupTable = owner.findLookupTable(topLevelModule)) {
|
|
// Search it.
|
|
owner.lookupVisibleDecls(*lookupTable, *actualConsumer);
|
|
|
|
// Add the extensions produced by importing categories.
|
|
for (auto category : lookupTable->categories()) {
|
|
if (auto extension = cast_or_null<ExtensionDecl>(
|
|
owner.importDecl(category, owner.CurrentVersion)))
|
|
results.push_back(extension);
|
|
}
|
|
|
|
auto findEnclosingExtension = [](Decl *importedDecl) -> ExtensionDecl * {
|
|
for (auto importedDC = importedDecl->getDeclContext();
|
|
!importedDC->isModuleContext();
|
|
importedDC = importedDC->getParent()) {
|
|
if (auto ext = dyn_cast<ExtensionDecl>(importedDC))
|
|
return ext;
|
|
}
|
|
return nullptr;
|
|
};
|
|
// Retrieve all of the globals that will be mapped to members.
|
|
|
|
// FIXME: Since we don't represent Clang submodules as Swift
|
|
// modules, we're getting everything.
|
|
llvm::SmallPtrSet<ExtensionDecl *, 8> knownExtensions;
|
|
for (auto entry : lookupTable->allGlobalsAsMembers()) {
|
|
auto decl = entry.get<clang::NamedDecl *>();
|
|
auto importedDecl = owner.importDecl(decl, owner.CurrentVersion);
|
|
if (!importedDecl) continue;
|
|
|
|
// Find the enclosing extension, if there is one.
|
|
ExtensionDecl *ext = findEnclosingExtension(importedDecl);
|
|
if (ext && knownExtensions.insert(ext).second)
|
|
results.push_back(ext);
|
|
|
|
// If this is a compatibility typealias, the canonical type declaration
|
|
// may exist in another extension.
|
|
auto alias = dyn_cast<TypeAliasDecl>(importedDecl);
|
|
if (!alias || !alias->isCompatibilityAlias()) continue;
|
|
|
|
auto aliasedTy = alias->getUnderlyingTypeLoc().getType();
|
|
ext = nullptr;
|
|
importedDecl = nullptr;
|
|
|
|
// Note: We can't use getAnyGeneric() here because `aliasedTy`
|
|
// might be typealias.
|
|
if (auto Ty = dyn_cast<TypeAliasType>(aliasedTy.getPointer()))
|
|
importedDecl = Ty->getDecl();
|
|
else if (auto Ty = dyn_cast<AnyGenericType>(aliasedTy.getPointer()))
|
|
importedDecl = Ty->getDecl();
|
|
if (!importedDecl) continue;
|
|
|
|
ext = findEnclosingExtension(importedDecl);
|
|
if (ext && knownExtensions.insert(ext).second)
|
|
results.push_back(ext);
|
|
}
|
|
}
|
|
}
|
|
|
|
ImportDecl *swift::createImportDecl(ASTContext &Ctx,
|
|
DeclContext *DC,
|
|
ClangNode ClangN,
|
|
ArrayRef<clang::Module *> Exported) {
|
|
auto *ImportedMod = ClangN.getClangModule();
|
|
assert(ImportedMod);
|
|
SmallVector<std::pair<swift::Identifier, swift::SourceLoc>, 4> AccessPath;
|
|
auto *TmpMod = ImportedMod;
|
|
while (TmpMod) {
|
|
AccessPath.push_back({ Ctx.getIdentifier(TmpMod->Name), SourceLoc() });
|
|
TmpMod = TmpMod->Parent;
|
|
}
|
|
std::reverse(AccessPath.begin(), AccessPath.end());
|
|
|
|
bool IsExported = false;
|
|
for (auto *ExportedMod : Exported) {
|
|
if (ImportedMod == ExportedMod) {
|
|
IsExported = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto *ID = ImportDecl::create(Ctx, DC, SourceLoc(),
|
|
ImportKind::Module, SourceLoc(), AccessPath,
|
|
ClangN);
|
|
if (IsExported)
|
|
ID->getAttrs().add(new (Ctx) ExportedAttr(/*IsImplicit=*/false));
|
|
return ID;
|
|
}
|
|
|
|
static void getImportDecls(ClangModuleUnit *ClangUnit, const clang::Module *M,
|
|
SmallVectorImpl<Decl *> &Results) {
|
|
assert(M);
|
|
SmallVector<clang::Module *, 1> Exported;
|
|
M->getExportedModules(Exported);
|
|
|
|
ASTContext &Ctx = ClangUnit->getASTContext();
|
|
|
|
for (auto *ImportedMod : M->Imports) {
|
|
auto *ID = createImportDecl(Ctx, ClangUnit, ImportedMod, Exported);
|
|
Results.push_back(ID);
|
|
}
|
|
}
|
|
|
|
void ClangModuleUnit::getDisplayDecls(SmallVectorImpl<Decl*> &results) const {
|
|
if (clangModule)
|
|
getImportDecls(const_cast<ClangModuleUnit *>(this), clangModule, results);
|
|
getTopLevelDecls(results);
|
|
}
|
|
|
|
void ClangModuleUnit::lookupValue(ModuleDecl::AccessPathTy accessPath,
|
|
DeclName name, NLKind lookupKind,
|
|
SmallVectorImpl<ValueDecl*> &results) const {
|
|
if (!ModuleDecl::matchesAccessPath(accessPath, name))
|
|
return;
|
|
|
|
// FIXME: Ignore submodules, which are empty for now.
|
|
if (clangModule && clangModule->isSubModule())
|
|
return;
|
|
|
|
VectorDeclConsumer vectorWriter(results);
|
|
FilteringVisibleDeclConsumer filteringConsumer(vectorWriter, this);
|
|
|
|
DarwinLegacyFilterDeclConsumer darwinFilterConsumer(filteringConsumer,
|
|
getClangASTContext());
|
|
|
|
swift::VisibleDeclConsumer *consumer = &filteringConsumer;
|
|
if (lookupKind == NLKind::UnqualifiedLookup &&
|
|
DarwinLegacyFilterDeclConsumer::needsFiltering(clangModule)) {
|
|
consumer = &darwinFilterConsumer;
|
|
}
|
|
|
|
// Find the corresponding lookup table.
|
|
if (auto lookupTable = owner.findLookupTable(clangModule)) {
|
|
// Search it.
|
|
owner.lookupValue(*lookupTable, name, *consumer);
|
|
}
|
|
}
|
|
|
|
/// Determine whether the given Clang entry is visible.
|
|
///
|
|
/// FIXME: this is an elaborate hack to badly reflect Clang's
|
|
/// submodule visibility into Swift.
|
|
static bool isVisibleClangEntry(clang::ASTContext &ctx,
|
|
SwiftLookupTable::SingleEntry entry) {
|
|
if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) {
|
|
// For a declaration, check whether the declaration is hidden.
|
|
if (!clangDecl->isHidden()) return true;
|
|
|
|
// Is any redeclaration visible?
|
|
for (auto redecl : clangDecl->redecls()) {
|
|
if (!cast<clang::NamedDecl>(redecl)->isHidden()) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// If it's a macro from a module, check whether the module has been imported.
|
|
if (auto moduleMacro = entry.dyn_cast<clang::ModuleMacro *>()) {
|
|
clang::Module *module = moduleMacro->getOwningModule();
|
|
return module->NameVisibility == clang::Module::AllVisible;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TypeDecl *
|
|
ClangModuleUnit::lookupNestedType(Identifier name,
|
|
const NominalTypeDecl *baseType) const {
|
|
// Special case for error code enums: try looking directly into the struct
|
|
// first. But only if it looks like a synthesized error wrapped struct.
|
|
if (name == getASTContext().Id_Code && !baseType->hasClangNode() &&
|
|
isa<StructDecl>(baseType) && !baseType->hasLazyMembers() &&
|
|
baseType->isChildContextOf(this)) {
|
|
auto *mutableBase = const_cast<NominalTypeDecl *>(baseType);
|
|
auto flags = OptionSet<NominalTypeDecl::LookupDirectFlags>();
|
|
flags |= NominalTypeDecl::LookupDirectFlags::IgnoreNewExtensions;
|
|
auto codeEnum = mutableBase->lookupDirect(name, flags);
|
|
// Double-check that we actually have a good result. It's possible what we
|
|
// found is /not/ a synthesized error struct, but just something that looks
|
|
// like it. But if we still found a good result we should return that.
|
|
if (codeEnum.size() == 1 && isa<TypeDecl>(codeEnum.front()))
|
|
return cast<TypeDecl>(codeEnum.front());
|
|
if (codeEnum.size() > 1)
|
|
return nullptr;
|
|
// Otherwise, fall back and try via lookup table.
|
|
}
|
|
|
|
auto lookupTable = owner.findLookupTable(clangModule);
|
|
if (!lookupTable)
|
|
return nullptr;
|
|
|
|
auto baseTypeContext = owner.getEffectiveClangContext(baseType);
|
|
if (!baseTypeContext)
|
|
return nullptr;
|
|
|
|
auto &clangCtx = owner.getClangASTContext();
|
|
|
|
// FIXME: This is very similar to what's in Implementation::lookupValue and
|
|
// Implementation::loadAllMembers.
|
|
SmallVector<TypeDecl *, 2> results;
|
|
for (auto entry : lookupTable->lookup(SerializedSwiftName(name.str()),
|
|
baseTypeContext)) {
|
|
// If the entry is not visible, skip it.
|
|
if (!isVisibleClangEntry(clangCtx, entry)) continue;
|
|
|
|
auto clangDecl = entry.dyn_cast<clang::NamedDecl *>();
|
|
auto clangTypeDecl = dyn_cast_or_null<clang::TypeDecl>(clangDecl);
|
|
if (!clangTypeDecl)
|
|
continue;
|
|
|
|
clangTypeDecl = cast<clang::TypeDecl>(clangTypeDecl->getMostRecentDecl());
|
|
|
|
bool anyMatching = false;
|
|
TypeDecl *originalDecl = nullptr;
|
|
owner.forEachDistinctName(clangTypeDecl,
|
|
[&](ImportedName newName,
|
|
ImportNameVersion nameVersion) -> bool {
|
|
if (anyMatching)
|
|
return true;
|
|
if (!newName.getDeclName().isSimpleName(name))
|
|
return true;
|
|
|
|
auto decl = dyn_cast_or_null<TypeDecl>(
|
|
owner.importDeclReal(clangTypeDecl, nameVersion));
|
|
if (!decl)
|
|
return false;
|
|
|
|
if (!originalDecl)
|
|
originalDecl = decl;
|
|
else if (originalDecl == decl)
|
|
return true;
|
|
|
|
auto *importedContext = decl->getDeclContext()->getSelfNominalTypeDecl();
|
|
if (importedContext != baseType)
|
|
return true;
|
|
|
|
assert(decl->getFullName().matchesRef(name) &&
|
|
"importFullName behaved differently from importDecl");
|
|
results.push_back(decl);
|
|
anyMatching = true;
|
|
return true;
|
|
});
|
|
}
|
|
|
|
if (results.size() != 1) {
|
|
// It's possible that two types were import-as-member'd onto the same base
|
|
// type with the same name. In this case, fall back to regular lookup.
|
|
return nullptr;
|
|
}
|
|
|
|
return results.front();
|
|
}
|
|
|
|
void ClangImporter::loadExtensions(NominalTypeDecl *nominal,
|
|
unsigned previousGeneration) {
|
|
// Determine the effective Clang context for this Swift nominal type.
|
|
auto effectiveClangContext = Impl.getEffectiveClangContext(nominal);
|
|
if (!effectiveClangContext) return;
|
|
|
|
// For an Objective-C class, import all of the visible categories.
|
|
if (auto objcClass = dyn_cast_or_null<clang::ObjCInterfaceDecl>(
|
|
effectiveClangContext.getAsDeclContext())) {
|
|
// Simply importing the categories adds them to the list of extensions.
|
|
for (auto I = objcClass->visible_categories_begin(),
|
|
E = objcClass->visible_categories_end();
|
|
I != E; ++I) {
|
|
Impl.importDeclReal(*I, Impl.CurrentVersion);
|
|
}
|
|
}
|
|
|
|
// Dig through each of the Swift lookup tables, creating extensions
|
|
// where needed.
|
|
auto &clangCtx = Impl.getClangASTContext();
|
|
(void)Impl.forEachLookupTable([&](SwiftLookupTable &table) -> bool {
|
|
// FIXME: If we already looked at this for this generation,
|
|
// skip.
|
|
|
|
for (auto entry : table.lookupGlobalsAsMembers(effectiveClangContext)) {
|
|
// If the entry is not visible, skip it.
|
|
if (!isVisibleClangEntry(clangCtx, entry)) continue;
|
|
|
|
if (auto decl = entry.dyn_cast<clang::NamedDecl *>()) {
|
|
// Import the context of this declaration, which has the
|
|
// side effect of creating instantiations.
|
|
(void)Impl.importDeclContextOf(decl, effectiveClangContext);
|
|
} else {
|
|
llvm_unreachable("Macros cannot be imported as members.");
|
|
}
|
|
}
|
|
|
|
return false;
|
|
});
|
|
}
|
|
|
|
void ClangImporter::loadObjCMethods(
|
|
ClassDecl *classDecl,
|
|
ObjCSelector selector,
|
|
bool isInstanceMethod,
|
|
unsigned previousGeneration,
|
|
llvm::TinyPtrVector<AbstractFunctionDecl *> &methods) {
|
|
// If we're currently looking for this selector, don't load any Objective-C
|
|
// methods.
|
|
if (Impl.ActiveSelectors.count({selector, isInstanceMethod}))
|
|
return;
|
|
|
|
const auto *objcClass =
|
|
dyn_cast_or_null<clang::ObjCInterfaceDecl>(classDecl->getClangDecl());
|
|
if (!objcClass)
|
|
return;
|
|
|
|
// Collect the set of visible Objective-C methods with this selector.
|
|
clang::Selector clangSelector = Impl.exportSelector(selector);
|
|
SmallVector<clang::ObjCMethodDecl *, 4> objcMethods;
|
|
auto &sema = Impl.Instance->getSema();
|
|
sema.CollectMultipleMethodsInGlobalPool(clangSelector, objcMethods,
|
|
isInstanceMethod,
|
|
/*CheckTheOther=*/false);
|
|
|
|
// Check whether this method is in the class we care about.
|
|
SmallVector<AbstractFunctionDecl *, 4> foundMethods;
|
|
for (auto objcMethod : objcMethods) {
|
|
// Find the owner of this method and determine whether it is the class
|
|
// we're looking for.
|
|
if (objcMethod->getClassInterface() != objcClass)
|
|
continue;
|
|
|
|
// If we found a property accessor, import the property.
|
|
if (objcMethod->isPropertyAccessor())
|
|
(void)Impl.importDecl(objcMethod->findPropertyDecl(true),
|
|
Impl.CurrentVersion);
|
|
|
|
if (auto method = dyn_cast_or_null<AbstractFunctionDecl>(
|
|
Impl.importDecl(objcMethod, Impl.CurrentVersion))) {
|
|
foundMethods.push_back(method);
|
|
}
|
|
}
|
|
|
|
// If we didn't find anything, we're done.
|
|
if (foundMethods.empty())
|
|
return;
|
|
|
|
// If we did find something, it might be a duplicate of something we found
|
|
// earlier, because we aren't tracking generation counts for Clang modules.
|
|
// Filter out the duplicates.
|
|
// FIXME: We shouldn't need to do this.
|
|
llvm::SmallPtrSet<AbstractFunctionDecl *, 4> known;
|
|
known.insert(methods.begin(), methods.end());
|
|
for (auto method : foundMethods) {
|
|
if (known.insert(method).second)
|
|
methods.push_back(method);
|
|
}
|
|
}
|
|
|
|
void
|
|
ClangModuleUnit::lookupClassMember(ModuleDecl::AccessPathTy accessPath,
|
|
DeclName name,
|
|
SmallVectorImpl<ValueDecl*> &results) const {
|
|
// FIXME: Ignore submodules, which are empty for now.
|
|
if (clangModule && clangModule->isSubModule())
|
|
return;
|
|
|
|
VectorDeclConsumer consumer(results);
|
|
|
|
// Find the corresponding lookup table.
|
|
if (auto lookupTable = owner.findLookupTable(clangModule)) {
|
|
// Search it.
|
|
owner.lookupObjCMembers(*lookupTable, name, consumer);
|
|
}
|
|
}
|
|
|
|
void ClangModuleUnit::lookupClassMembers(ModuleDecl::AccessPathTy accessPath,
|
|
VisibleDeclConsumer &consumer) const {
|
|
// FIXME: Ignore submodules, which are empty for now.
|
|
if (clangModule && clangModule->isSubModule())
|
|
return;
|
|
|
|
// Find the corresponding lookup table.
|
|
if (auto lookupTable = owner.findLookupTable(clangModule)) {
|
|
// Search it.
|
|
owner.lookupAllObjCMembers(*lookupTable, consumer);
|
|
}
|
|
}
|
|
|
|
void ClangModuleUnit::lookupObjCMethods(
|
|
ObjCSelector selector,
|
|
SmallVectorImpl<AbstractFunctionDecl *> &results) const {
|
|
// FIXME: Ignore submodules, which are empty for now.
|
|
if (clangModule && clangModule->isSubModule())
|
|
return;
|
|
|
|
// Map the selector into a Clang selector.
|
|
auto clangSelector = owner.exportSelector(selector);
|
|
if (clangSelector.isNull()) return;
|
|
|
|
// Collect all of the Objective-C methods with this selector.
|
|
SmallVector<clang::ObjCMethodDecl *, 8> objcMethods;
|
|
auto &clangSema = owner.getClangSema();
|
|
clangSema.CollectMultipleMethodsInGlobalPool(clangSelector,
|
|
objcMethods,
|
|
/*InstanceFirst=*/true,
|
|
/*CheckTheOther=*/false);
|
|
clangSema.CollectMultipleMethodsInGlobalPool(clangSelector,
|
|
objcMethods,
|
|
/*InstanceFirst=*/false,
|
|
/*CheckTheOther=*/false);
|
|
|
|
// Import the methods.
|
|
auto &clangCtx = clangSema.getASTContext();
|
|
for (auto objcMethod : objcMethods) {
|
|
// Verify that this method came from this module.
|
|
auto owningClangModule = getClangTopLevelOwningModule(objcMethod, clangCtx);
|
|
if (owningClangModule != clangModule) continue;
|
|
|
|
// If we found a property accessor, import the property.
|
|
if (objcMethod->isPropertyAccessor())
|
|
(void)owner.importDecl(objcMethod->findPropertyDecl(true),
|
|
owner.CurrentVersion);
|
|
|
|
// Import it.
|
|
// FIXME: Retrying a failed import works around recursion bugs in the Clang
|
|
// importer.
|
|
auto imported =
|
|
owner.importDecl(objcMethod, owner.CurrentVersion);
|
|
if (!imported)
|
|
imported = owner.importDecl(objcMethod, owner.CurrentVersion);
|
|
if (!imported) continue;
|
|
|
|
if (auto func = dyn_cast<AbstractFunctionDecl>(imported))
|
|
results.push_back(func);
|
|
|
|
// If there is an alternate declaration, also look at it.
|
|
for (auto alternate : owner.getAlternateDecls(imported)) {
|
|
if (auto func = dyn_cast<AbstractFunctionDecl>(alternate))
|
|
results.push_back(func);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClangModuleUnit::collectLinkLibraries(
|
|
ModuleDecl::LinkLibraryCallback callback) const {
|
|
if (!clangModule)
|
|
return;
|
|
|
|
// Skip this lib name in favor of export_as name.
|
|
if (clangModule->UseExportAsModuleLinkName)
|
|
return;
|
|
|
|
for (auto clangLinkLib : clangModule->LinkLibraries) {
|
|
LibraryKind kind;
|
|
if (clangLinkLib.IsFramework)
|
|
kind = LibraryKind::Framework;
|
|
else
|
|
kind = LibraryKind::Library;
|
|
|
|
callback(LinkLibrary(clangLinkLib.Library, kind));
|
|
}
|
|
}
|
|
|
|
StringRef ClangModuleUnit::getFilename() const {
|
|
if (!clangModule) {
|
|
StringRef SinglePCH = owner.getSinglePCHImport();
|
|
if (SinglePCH.empty())
|
|
return "<imports>";
|
|
else
|
|
return SinglePCH;
|
|
}
|
|
if (const clang::FileEntry *F = clangModule->getASTFile())
|
|
if (!F->getName().empty())
|
|
return F->getName();
|
|
return StringRef();
|
|
}
|
|
|
|
clang::TargetInfo &ClangImporter::getTargetInfo() const {
|
|
return Impl.Instance->getTarget();
|
|
}
|
|
|
|
clang::ASTContext &ClangImporter::getClangASTContext() const {
|
|
return Impl.getClangASTContext();
|
|
}
|
|
|
|
clang::Preprocessor &ClangImporter::getClangPreprocessor() const {
|
|
return Impl.getClangPreprocessor();
|
|
}
|
|
|
|
const clang::CompilerInstance &ClangImporter::getClangInstance() const {
|
|
return *Impl.Instance;
|
|
}
|
|
|
|
const clang::Module *ClangImporter::getClangOwningModule(ClangNode Node) const {
|
|
return Impl.getClangOwningModule(Node);
|
|
}
|
|
|
|
const clang::Module *
|
|
ClangImporter::Implementation::getClangOwningModule(ClangNode Node) const {
|
|
return ::getClangOwningModule(Node, getClangASTContext());
|
|
}
|
|
|
|
bool ClangImporter::hasTypedef(const clang::Decl *typeDecl) const {
|
|
return Impl.DeclsWithSuperfluousTypedefs.count(typeDecl);
|
|
}
|
|
|
|
clang::Sema &ClangImporter::getClangSema() const {
|
|
return Impl.getClangSema();
|
|
}
|
|
|
|
clang::CodeGenOptions &ClangImporter::getClangCodeGenOpts() const {
|
|
return Impl.getClangCodeGenOpts();
|
|
}
|
|
|
|
std::string ClangImporter::getClangModuleHash() const {
|
|
return Impl.Invocation->getModuleHash(Impl.Instance->getDiagnostics());
|
|
}
|
|
|
|
Decl *ClangImporter::importDeclCached(const clang::NamedDecl *ClangDecl) {
|
|
return Impl.importDeclCached(ClangDecl, Impl.CurrentVersion);
|
|
}
|
|
|
|
void ClangImporter::printStatistics() const {
|
|
Impl.Instance->getModuleManager()->PrintStats();
|
|
}
|
|
|
|
void ClangImporter::verifyAllModules() {
|
|
#ifndef NDEBUG
|
|
if (Impl.VerifiedDeclsCounter == Impl.ImportedDecls.size())
|
|
return;
|
|
|
|
// Collect the Decls before verifying them; the act of verifying may cause
|
|
// more decls to be imported and modify the map while we are iterating it.
|
|
size_t verifiedCounter = Impl.ImportedDecls.size();
|
|
SmallVector<Decl *, 8> Decls;
|
|
for (auto &I : Impl.ImportedDecls)
|
|
if (I.first.second == Impl.CurrentVersion)
|
|
if (Decl *D = I.second)
|
|
Decls.push_back(D);
|
|
|
|
for (auto D : Decls)
|
|
verify(D);
|
|
|
|
Impl.VerifiedDeclsCounter = verifiedCounter;
|
|
#endif
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ClangModule Implementation
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
ClangModuleUnit::ClangModuleUnit(ModuleDecl &M,
|
|
ClangImporter::Implementation &owner,
|
|
const clang::Module *clangModule)
|
|
: LoadedFile(FileUnitKind::ClangModule, M), owner(owner),
|
|
clangModule(clangModule) {
|
|
// Capture the file metadata before it goes away.
|
|
if (clangModule)
|
|
ASTSourceDescriptor = {*clangModule};
|
|
}
|
|
|
|
Optional<clang::ExternalASTSource::ASTSourceDescriptor>
|
|
ClangModuleUnit::getASTSourceDescriptor() const {
|
|
if (clangModule) {
|
|
assert(ASTSourceDescriptor.getModuleOrNull() == clangModule);
|
|
return ASTSourceDescriptor;
|
|
}
|
|
return None;
|
|
}
|
|
|
|
bool ClangModuleUnit::hasClangModule(ModuleDecl *M) {
|
|
for (auto F : M->getFiles()) {
|
|
if (isa<ClangModuleUnit>(F))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClangModuleUnit::isTopLevel() const {
|
|
return !clangModule || !clangModule->isSubModule();
|
|
}
|
|
|
|
bool ClangModuleUnit::isSystemModule() const {
|
|
return clangModule && clangModule->IsSystem;
|
|
}
|
|
|
|
clang::ASTContext &ClangModuleUnit::getClangASTContext() const {
|
|
return owner.getClangASTContext();
|
|
}
|
|
|
|
StringRef ClangModuleUnit::getExportedModuleName() const {
|
|
if (clangModule && !clangModule->ExportAsModule.empty())
|
|
return clangModule->ExportAsModule;
|
|
|
|
return getParentModule()->getName().str();
|
|
}
|
|
|
|
ModuleDecl *ClangModuleUnit::getOverlayModule() const {
|
|
if (!clangModule)
|
|
return nullptr;
|
|
|
|
if (owner.DisableOverlayModules)
|
|
return nullptr;
|
|
|
|
if (!isTopLevel()) {
|
|
// FIXME: Is this correct for submodules?
|
|
auto topLevel = clangModule->getTopLevelModule();
|
|
auto wrapper = owner.getWrapperForModule(topLevel);
|
|
return wrapper->getOverlayModule();
|
|
}
|
|
|
|
if (!overlayModule.getInt()) {
|
|
// FIXME: Include proper source location.
|
|
ModuleDecl *M = getParentModule();
|
|
ASTContext &Ctx = M->getASTContext();
|
|
auto overlay = Ctx.getModule(ModuleDecl::AccessPathTy({M->getName(),
|
|
SourceLoc()}));
|
|
if (overlay == M) {
|
|
overlay = nullptr;
|
|
} else {
|
|
auto &sharedModuleRef = Ctx.LoadedModules[M->getName()];
|
|
assert(!sharedModuleRef || sharedModuleRef == overlay ||
|
|
sharedModuleRef == M);
|
|
sharedModuleRef = overlay;
|
|
}
|
|
|
|
auto mutableThis = const_cast<ClangModuleUnit *>(this);
|
|
mutableThis->overlayModule.setPointerAndInt(overlay, true);
|
|
}
|
|
|
|
return overlayModule.getPointer();
|
|
}
|
|
|
|
void ClangModuleUnit::getImportedModules(
|
|
SmallVectorImpl<ModuleDecl::ImportedModule> &imports,
|
|
ModuleDecl::ImportFilter filter) const {
|
|
// Bail out if we /only/ want ImplementationOnly imports; Clang modules never
|
|
// have any of these.
|
|
if (filter.containsOnly(ModuleDecl::ImportFilterKind::ImplementationOnly))
|
|
return;
|
|
|
|
if (filter.contains(ModuleDecl::ImportFilterKind::Private))
|
|
if (auto stdlib = owner.getStdlibModule())
|
|
imports.push_back({ModuleDecl::AccessPathTy(), stdlib});
|
|
|
|
SmallVector<clang::Module *, 8> imported;
|
|
if (!clangModule) {
|
|
// This is the special "imported headers" module.
|
|
if (filter.contains(ModuleDecl::ImportFilterKind::Public)) {
|
|
imported.append(owner.ImportedHeaderExports.begin(),
|
|
owner.ImportedHeaderExports.end());
|
|
}
|
|
|
|
} else {
|
|
clangModule->getExportedModules(imported);
|
|
|
|
if (filter.contains(ModuleDecl::ImportFilterKind::Private)) {
|
|
// Copy in any modules that are imported but not exported.
|
|
llvm::SmallPtrSet<clang::Module *, 8> knownModules(imported.begin(),
|
|
imported.end());
|
|
if (!filter.contains(ModuleDecl::ImportFilterKind::Public)) {
|
|
// Remove the exported ones now that we're done with them.
|
|
imported.clear();
|
|
}
|
|
llvm::copy_if(clangModule->Imports, std::back_inserter(imported),
|
|
[&](clang::Module *mod) {
|
|
return !knownModules.insert(mod).second;
|
|
});
|
|
|
|
// FIXME: The parent module isn't exactly a private import, but it is
|
|
// needed for link dependencies.
|
|
if (clangModule->Parent)
|
|
imported.push_back(clangModule->Parent);
|
|
}
|
|
}
|
|
|
|
auto topLevelOverlay = getOverlayModule();
|
|
for (auto importMod : imported) {
|
|
auto wrapper = owner.getWrapperForModule(importMod);
|
|
|
|
auto actualMod = wrapper->getOverlayModule();
|
|
if (!actualMod) {
|
|
// HACK: Deal with imports of submodules by importing the top-level module
|
|
// as well.
|
|
auto importTopLevel = importMod->getTopLevelModule();
|
|
if (importTopLevel != importMod) {
|
|
if (!clangModule || importTopLevel != clangModule->getTopLevelModule()){
|
|
auto topLevelWrapper = owner.getWrapperForModule(importTopLevel);
|
|
imports.push_back({ ModuleDecl::AccessPathTy(),
|
|
topLevelWrapper->getParentModule() });
|
|
}
|
|
}
|
|
actualMod = wrapper->getParentModule();
|
|
} else if (actualMod == topLevelOverlay) {
|
|
actualMod = wrapper->getParentModule();
|
|
}
|
|
|
|
assert(actualMod && "Missing imported overlay");
|
|
imports.push_back({ModuleDecl::AccessPathTy(), actualMod});
|
|
}
|
|
}
|
|
|
|
void ClangModuleUnit::getImportedModulesForLookup(
|
|
SmallVectorImpl<ModuleDecl::ImportedModule> &imports) const {
|
|
|
|
// Reuse our cached list of imports if we have one.
|
|
if (!importedModulesForLookup.empty()) {
|
|
imports.append(importedModulesForLookup.begin(),
|
|
importedModulesForLookup.end());
|
|
return;
|
|
}
|
|
|
|
size_t firstImport = imports.size();
|
|
|
|
SmallVector<clang::Module *, 8> imported;
|
|
const clang::Module *topLevel;
|
|
ModuleDecl *topLevelOverlay = getOverlayModule();
|
|
if (!clangModule) {
|
|
// This is the special "imported headers" module.
|
|
imported.append(owner.ImportedHeaderExports.begin(),
|
|
owner.ImportedHeaderExports.end());
|
|
topLevel = nullptr;
|
|
} else {
|
|
clangModule->getExportedModules(imported);
|
|
topLevel = clangModule->getTopLevelModule();
|
|
}
|
|
|
|
if (imported.empty())
|
|
return;
|
|
|
|
SmallPtrSet<clang::Module *, 32> seen{imported.begin(), imported.end()};
|
|
SmallVector<clang::Module *, 8> tmpBuf;
|
|
llvm::SmallSetVector<clang::Module *, 8> topLevelImported;
|
|
|
|
// Get the transitive set of top-level imports. That is, if a particular
|
|
// import is a top-level import, add it. Otherwise, keep searching.
|
|
while (!imported.empty()) {
|
|
clang::Module *next = imported.pop_back_val();
|
|
|
|
// HACK: Deal with imports of submodules by importing the top-level module
|
|
// as well, unless it's the top-level module we're currently in.
|
|
clang::Module *nextTopLevel = next->getTopLevelModule();
|
|
if (nextTopLevel != topLevel) {
|
|
topLevelImported.insert(nextTopLevel);
|
|
|
|
// Don't continue looking through submodules of modules that have
|
|
// overlays. The overlay might shadow things.
|
|
auto wrapper = owner.getWrapperForModule(nextTopLevel);
|
|
if (wrapper->getOverlayModule())
|
|
continue;
|
|
}
|
|
|
|
// Only look through the current module if it's not top-level.
|
|
if (nextTopLevel == next)
|
|
continue;
|
|
|
|
next->getExportedModules(tmpBuf);
|
|
for (clang::Module *nextImported : tmpBuf) {
|
|
if (seen.insert(nextImported).second)
|
|
imported.push_back(nextImported);
|
|
}
|
|
tmpBuf.clear();
|
|
}
|
|
|
|
for (auto importMod : topLevelImported) {
|
|
auto wrapper = owner.getWrapperForModule(importMod);
|
|
|
|
auto actualMod = wrapper->getOverlayModule();
|
|
if (!actualMod || actualMod == topLevelOverlay)
|
|
actualMod = wrapper->getParentModule();
|
|
|
|
assert(actualMod && "Missing imported overlay");
|
|
imports.push_back({ModuleDecl::AccessPathTy(), actualMod});
|
|
}
|
|
|
|
// Cache our results for use next time.
|
|
auto importsToCache = llvm::makeArrayRef(imports).slice(firstImport);
|
|
importedModulesForLookup = getASTContext().AllocateCopy(importsToCache);
|
|
}
|
|
|
|
void ClangImporter::getMangledName(raw_ostream &os,
|
|
const clang::NamedDecl *clangDecl) const {
|
|
if (!Impl.Mangler)
|
|
Impl.Mangler.reset(Impl.getClangASTContext().createMangleContext());
|
|
|
|
Impl.Mangler->mangleName(clangDecl, os);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Swift lookup tables
|
|
// ---------------------------------------------------------------------------
|
|
|
|
SwiftLookupTable *ClangImporter::Implementation::findLookupTable(
|
|
const clang::Module *clangModule) {
|
|
// If the Clang module is null, use the bridging header lookup table.
|
|
if (!clangModule)
|
|
return BridgingHeaderLookupTable.get();
|
|
|
|
// Submodules share lookup tables with their parents.
|
|
if (clangModule->isSubModule())
|
|
return findLookupTable(clangModule->getTopLevelModule());
|
|
|
|
// Look for a Clang module with this name.
|
|
auto known = LookupTables.find(clangModule->Name);
|
|
if (known == LookupTables.end()) return nullptr;
|
|
|
|
return known->second.get();
|
|
}
|
|
|
|
bool ClangImporter::Implementation::forEachLookupTable(
|
|
llvm::function_ref<bool(SwiftLookupTable &table)> fn) {
|
|
// Visit the bridging header's lookup table.
|
|
if (fn(*BridgingHeaderLookupTable)) return true;
|
|
|
|
// Collect and sort the set of module names.
|
|
SmallVector<StringRef, 4> moduleNames;
|
|
for (const auto &entry : LookupTables) {
|
|
moduleNames.push_back(entry.first);
|
|
}
|
|
llvm::array_pod_sort(moduleNames.begin(), moduleNames.end());
|
|
|
|
// Visit the lookup tables.
|
|
for (auto moduleName : moduleNames) {
|
|
if (fn(*LookupTables[moduleName])) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ClangImporter::Implementation::lookupValue(
|
|
SwiftLookupTable &table, DeclName name,
|
|
VisibleDeclConsumer &consumer) {
|
|
auto &clangCtx = getClangASTContext();
|
|
auto clangTU = clangCtx.getTranslationUnitDecl();
|
|
|
|
for (auto entry : table.lookup(name.getBaseName(), clangTU)) {
|
|
// If the entry is not visible, skip it.
|
|
if (!isVisibleClangEntry(clangCtx, entry)) continue;
|
|
|
|
ValueDecl *decl;
|
|
// If it's a Clang declaration, try to import it.
|
|
if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) {
|
|
decl = cast_or_null<ValueDecl>(
|
|
importDeclReal(clangDecl->getMostRecentDecl(), CurrentVersion));
|
|
if (!decl) continue;
|
|
} else if (!name.isSpecial()) {
|
|
// Try to import a macro.
|
|
if (auto modMacro = entry.dyn_cast<clang::ModuleMacro *>())
|
|
decl = importMacro(name.getBaseIdentifier(), modMacro);
|
|
else if (auto clangMacro = entry.dyn_cast<clang::MacroInfo *>())
|
|
decl = importMacro(name.getBaseIdentifier(), clangMacro);
|
|
else
|
|
llvm_unreachable("new kind of lookup table entry");
|
|
if (!decl) continue;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
// If we found a declaration from the standard library, make sure
|
|
// it does not show up in the lookup results for the imported
|
|
// module.
|
|
if (decl->getDeclContext()->isModuleScopeContext() &&
|
|
decl->getModuleContext() == getStdlibModule())
|
|
continue;
|
|
|
|
// If the name matched, report this result.
|
|
bool anyMatching = false;
|
|
if (decl->getFullName().matchesRef(name) &&
|
|
decl->getDeclContext()->isModuleScopeContext()) {
|
|
consumer.foundDecl(decl, DeclVisibilityKind::VisibleAtTopLevel);
|
|
anyMatching = true;
|
|
}
|
|
|
|
// If there is an alternate declaration and the name matches,
|
|
// report this result.
|
|
for (auto alternate : getAlternateDecls(decl)) {
|
|
if (alternate->getFullName().matchesRef(name) &&
|
|
alternate->getDeclContext()->isModuleScopeContext()) {
|
|
consumer.foundDecl(alternate, DeclVisibilityKind::VisibleAtTopLevel);
|
|
anyMatching = true;
|
|
}
|
|
}
|
|
|
|
// If we have a declaration and nothing matched so far, try the names used
|
|
// in other versions of Swift.
|
|
if (!anyMatching) {
|
|
if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) {
|
|
const clang::NamedDecl *recentClangDecl =
|
|
clangDecl->getMostRecentDecl();
|
|
|
|
CurrentVersion.forEachOtherImportNameVersion(
|
|
[&](ImportNameVersion nameVersion) {
|
|
if (anyMatching)
|
|
return;
|
|
|
|
// Check to see if the name and context match what we expect.
|
|
ImportedName newName = importFullName(recentClangDecl, nameVersion);
|
|
if (!newName.getDeclName().matchesRef(name))
|
|
return;
|
|
|
|
const clang::DeclContext *clangDC =
|
|
newName.getEffectiveContext().getAsDeclContext();
|
|
if (!clangDC || !clangDC->isFileContext())
|
|
return;
|
|
|
|
// Then try to import the decl under the alternate name.
|
|
auto alternateNamedDecl =
|
|
cast_or_null<ValueDecl>(importDeclReal(recentClangDecl,
|
|
nameVersion));
|
|
if (!alternateNamedDecl || alternateNamedDecl == decl)
|
|
return;
|
|
assert(alternateNamedDecl->getFullName().matchesRef(name) &&
|
|
"importFullName behaved differently from importDecl");
|
|
if (alternateNamedDecl->getDeclContext()->isModuleScopeContext()) {
|
|
consumer.foundDecl(alternateNamedDecl,
|
|
DeclVisibilityKind::VisibleAtTopLevel);
|
|
anyMatching = true;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClangImporter::Implementation::lookupVisibleDecls(
|
|
SwiftLookupTable &table,
|
|
VisibleDeclConsumer &consumer) {
|
|
// Retrieve and sort all of the base names in this particular table.
|
|
auto baseNames = table.allBaseNames();
|
|
llvm::array_pod_sort(baseNames.begin(), baseNames.end());
|
|
|
|
// Look for namespace-scope entities with each base name.
|
|
for (auto baseName : baseNames) {
|
|
lookupValue(table, baseName.toDeclBaseName(SwiftContext), consumer);
|
|
}
|
|
}
|
|
|
|
void ClangImporter::Implementation::lookupObjCMembers(
|
|
SwiftLookupTable &table,
|
|
DeclName name,
|
|
VisibleDeclConsumer &consumer) {
|
|
auto &clangCtx = getClangASTContext();
|
|
|
|
for (auto clangDecl : table.lookupObjCMembers(name.getBaseName())) {
|
|
// If the entry is not visible, skip it.
|
|
if (!isVisibleClangEntry(clangCtx, clangDecl)) continue;
|
|
|
|
forEachDistinctName(clangDecl,
|
|
[&](ImportedName importedName,
|
|
ImportNameVersion nameVersion) -> bool {
|
|
// Import the declaration.
|
|
auto decl =
|
|
cast_or_null<ValueDecl>(importDeclReal(clangDecl, nameVersion));
|
|
if (!decl)
|
|
return false;
|
|
|
|
// If the name we found matches, report the declaration.
|
|
// FIXME: If we didn't need to check alternate decls here, we could avoid
|
|
// importing the member at all by checking importedName ahead of time.
|
|
if (decl->getFullName().matchesRef(name)) {
|
|
consumer.foundDecl(decl, DeclVisibilityKind::DynamicLookup,
|
|
DynamicLookupInfo::AnyObject);
|
|
}
|
|
|
|
// Check for an alternate declaration; if its name matches,
|
|
// report it.
|
|
for (auto alternate : getAlternateDecls(decl)) {
|
|
if (alternate->getFullName().matchesRef(name)) {
|
|
consumer.foundDecl(alternate, DeclVisibilityKind::DynamicLookup,
|
|
DynamicLookupInfo::AnyObject);
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
}
|
|
|
|
void ClangImporter::Implementation::lookupAllObjCMembers(
|
|
SwiftLookupTable &table,
|
|
VisibleDeclConsumer &consumer) {
|
|
// Retrieve and sort all of the base names in this particular table.
|
|
auto baseNames = table.allBaseNames();
|
|
llvm::array_pod_sort(baseNames.begin(), baseNames.end());
|
|
|
|
// Look for Objective-C members with each base name.
|
|
for (auto baseName : baseNames) {
|
|
lookupObjCMembers(table, baseName.toDeclBaseName(SwiftContext), consumer);
|
|
}
|
|
}
|
|
|
|
Optional<TinyPtrVector<ValueDecl *>>
|
|
ClangImporter::Implementation::loadNamedMembers(
|
|
const IterableDeclContext *IDC, DeclBaseName N, uint64_t contextData) {
|
|
|
|
auto *D = IDC->getDecl();
|
|
auto *DC = cast<DeclContext>(D);
|
|
auto *CD = D->getClangDecl();
|
|
auto *CDC = cast<clang::DeclContext>(CD);
|
|
assert(CD && "loadNamedMembers on a Decl without a clangDecl");
|
|
|
|
auto *nominal = DC->getSelfNominalTypeDecl();
|
|
auto effectiveClangContext = getEffectiveClangContext(nominal);
|
|
|
|
// FIXME: The legacy of mirroring protocol members rears its ugly head,
|
|
// and as a result we have to bail on any @interface or @category that
|
|
// has a declared protocol conformance.
|
|
if (auto *ID = dyn_cast<clang::ObjCInterfaceDecl>(CD)) {
|
|
if (ID->protocol_begin() != ID->protocol_end())
|
|
return None;
|
|
}
|
|
if (auto *CCD = dyn_cast<clang::ObjCCategoryDecl>(CD)) {
|
|
if (CCD->protocol_begin() != CCD->protocol_end())
|
|
return None;
|
|
}
|
|
|
|
// Also bail out if there are any global-as-member mappings for this type; we
|
|
// can support some of them lazily but the full set of idioms seems
|
|
// prohibitively complex (also they're not stored in by-name lookup, for
|
|
// reasons unclear).
|
|
if (forEachLookupTable([&](SwiftLookupTable &table) -> bool {
|
|
return (!table.lookupGlobalsAsMembers(effectiveClangContext).empty());
|
|
}))
|
|
return None;
|
|
|
|
// There are 3 cases:
|
|
//
|
|
// - The decl is from a bridging header, CMO is Some(nullptr)
|
|
// which denotes the __ObjC Swift module and its associated
|
|
// BridgingHeaderLookupTable.
|
|
//
|
|
// - The decl is from a clang module, CMO is Some(M) for non-null
|
|
// M and we can use the table for that module.
|
|
//
|
|
// - The decl is a forward declaration, CMO is None, which should
|
|
// never be the case if we got here (someone is asking for members).
|
|
//
|
|
// findLookupTable, below, handles the first two cases; we assert on the
|
|
// third.
|
|
|
|
auto CMO = getClangSubmoduleForDecl(CD);
|
|
assert(CMO && "loadNamedMembers on a forward-declared Decl");
|
|
|
|
auto table = findLookupTable(*CMO);
|
|
assert(table && "clang module without lookup table");
|
|
|
|
clang::ASTContext &clangCtx = getClangASTContext();
|
|
|
|
assert(isa<clang::ObjCContainerDecl>(CD));
|
|
|
|
TinyPtrVector<ValueDecl *> Members;
|
|
for (auto entry : table->lookup(SerializedSwiftName(N),
|
|
effectiveClangContext)) {
|
|
if (!entry.is<clang::NamedDecl *>()) continue;
|
|
auto member = entry.get<clang::NamedDecl *>();
|
|
if (!isVisibleClangEntry(clangCtx, member)) continue;
|
|
|
|
// Skip Decls from different clang::DeclContexts
|
|
if (member->getDeclContext() != CDC) continue;
|
|
|
|
SmallVector<Decl*, 4> tmp;
|
|
insertMembersAndAlternates(member, tmp);
|
|
for (auto *TD : tmp) {
|
|
if (auto *V = dyn_cast<ValueDecl>(TD)) {
|
|
// Skip ValueDecls if they import under different names.
|
|
if (V->getBaseName() == N) {
|
|
Members.push_back(V);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Members;
|
|
}
|
|
|
|
|
|
EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext(
|
|
const NominalTypeDecl *nominal) {
|
|
// If we have a Clang declaration, look at it to determine the
|
|
// effective Clang context.
|
|
if (auto constClangDecl = nominal->getClangDecl()) {
|
|
auto clangDecl = const_cast<clang::Decl *>(constClangDecl);
|
|
if (auto dc = dyn_cast<clang::DeclContext>(clangDecl))
|
|
return EffectiveClangContext(dc);
|
|
if (auto typedefName = dyn_cast<clang::TypedefNameDecl>(clangDecl))
|
|
return EffectiveClangContext(typedefName);
|
|
|
|
return EffectiveClangContext();
|
|
}
|
|
|
|
// If it's an @objc entity, go look for it.
|
|
// Note that we're stepping lightly here to avoid computing isObjC()
|
|
// too early.
|
|
if (isa<ClassDecl>(nominal) &&
|
|
(nominal->getAttrs().hasAttribute<ObjCAttr>() ||
|
|
(!nominal->getParentSourceFile() && nominal->isObjC()))) {
|
|
// Map the name. If we can't represent the Swift name in Clang.
|
|
// FIXME: We should be using the Objective-C name here!
|
|
auto clangName = exportName(nominal->getName());
|
|
if (!clangName)
|
|
return EffectiveClangContext();
|
|
|
|
// Perform name lookup into the global scope.
|
|
auto &sema = Instance->getSema();
|
|
clang::LookupResult lookupResult(sema, clangName,
|
|
clang::SourceLocation(),
|
|
clang::Sema::LookupOrdinaryName);
|
|
if (sema.LookupName(lookupResult, /*Scope=*/nullptr)) {
|
|
// FIXME: Filter based on access path? C++ access control?
|
|
for (auto clangDecl : lookupResult) {
|
|
if (auto objcClass = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl))
|
|
return EffectiveClangContext(objcClass);
|
|
|
|
/// FIXME: Other type declarations should also be okay?
|
|
}
|
|
}
|
|
}
|
|
|
|
return EffectiveClangContext();
|
|
}
|
|
|
|
void ClangImporter::dumpSwiftLookupTables() {
|
|
Impl.dumpSwiftLookupTables();
|
|
}
|
|
|
|
void ClangImporter::Implementation::dumpSwiftLookupTables() {
|
|
// Sort the module names so we can print in a deterministic order.
|
|
SmallVector<StringRef, 4> moduleNames;
|
|
for (const auto &lookupTable : LookupTables) {
|
|
moduleNames.push_back(lookupTable.first);
|
|
}
|
|
array_pod_sort(moduleNames.begin(), moduleNames.end());
|
|
|
|
// Print out the lookup tables for the various modules.
|
|
for (auto moduleName : moduleNames) {
|
|
llvm::errs() << "<<" << moduleName << " lookup table>>\n";
|
|
LookupTables[moduleName]->deserializeAll();
|
|
LookupTables[moduleName]->dump();
|
|
}
|
|
|
|
llvm::errs() << "<<Bridging header lookup table>>\n";
|
|
BridgingHeaderLookupTable->dump();
|
|
}
|
|
|
|
DeclName ClangImporter::
|
|
importName(const clang::NamedDecl *D,
|
|
clang::DeclarationName preferredName) {
|
|
return Impl.importFullName(D, Impl.CurrentVersion, preferredName).
|
|
getDeclName();
|
|
}
|
|
|
|
bool ClangImporter::isInOverlayModuleForImportedModule(
|
|
const DeclContext *overlayDC,
|
|
const DeclContext *importedDC) {
|
|
overlayDC = overlayDC->getModuleScopeContext();
|
|
importedDC = importedDC->getModuleScopeContext();
|
|
|
|
auto importedClangModuleUnit = dyn_cast<ClangModuleUnit>(importedDC);
|
|
if (!importedClangModuleUnit || !importedClangModuleUnit->getClangModule())
|
|
return false;
|
|
|
|
auto overlayModule = overlayDC->getParentModule();
|
|
if (overlayModule == importedClangModuleUnit->getOverlayModule())
|
|
return true;
|
|
|
|
// Is this a private module that's re-exported to the public (overlay) name?
|
|
auto clangModule =
|
|
importedClangModuleUnit->getClangModule()->getTopLevelModule();
|
|
return !clangModule->ExportAsModule.empty() &&
|
|
clangModule->ExportAsModule == overlayModule->getName().str();
|
|
}
|