Files
swift-mirror/lib/ClangImporter/ClangImporter.cpp
Duncan P. N. Exon Smith 43e771e55b Reapply ClangImporter changes to leverage Clang's module cache
This reapplies four commits for the ClangImporter.  They broke the build
on master because the associated commits on swift-clang's
swift-5.1-branch never made it to stable due to automerger woes.  This
time I'll push everything through manually.

These are the commits being reapplied:

- a42bc62397 "ClangImporter: Share a module cache between CompilerInstances"
- 9eed34235b "ClangImporter: Adjust to r355777, PCMCache => ModuleCache"
- da8a1c57e8 "ClangImporter: Use the new CacheGeneratedPCH in Clang"
- 2134b17a20 "ClangImporter: Test that PCHs are actually cached in memory"

rdar://problem/48545366
2019-03-20 09:59:58 -07:00

3754 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
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;
}
}
// 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.
SmallString<128> GlibcModuleMapPath;
GlibcModuleMapPath = searchPathOpts.RuntimeResourcePath;
// Running without a resource directory is not a supported configuration.
assert(!GlibcModuleMapPath.empty());
llvm::sys::path::append(
GlibcModuleMapPath,
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(GlibcModuleMapPath)) {
invocationArgStrs.push_back(
(Twine("-fmodule-map-file=") + GlibcModuleMapPath).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,
/*preferAdapter=*/false);
}
ModuleDecl *ClangImporter::Implementation::finishLoadingClangModule(
const clang::Module *clangModule,
bool findAdapter) {
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 adapter modules 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 adapter modules 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 (findAdapter)
if (ModuleDecl *adapter = wrapperUnit->getAdapterModule())
result = adapter;
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, /*preferAdapter=*/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()),
DisableAdapterModules(opts.DisableAdapterModules),
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) override {
if (isVisibleFromModule(ModuleFilter, VD))
NextConsumer.foundDecl(VD, Reason);
}
};
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) override {
if (isDeclaredInModule(ModuleFilter, VD))
NextConsumer.foundDecl(VD, Reason);
}
};
/// 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) override {
if (!shouldDiscard(VD))
NextConsumer.foundDecl(VD, Reason);
}
};
} // 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) 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();
}
std::string ClangModuleUnit::getExportedModuleName() const {
if (clangModule && !clangModule->ExportAsModule.empty())
return clangModule->ExportAsModule;
return getParentModule()->getName().str();
}
ModuleDecl *ClangModuleUnit::getAdapterModule() const {
if (!clangModule)
return nullptr;
if (owner.DisableAdapterModules)
return nullptr;
if (!isTopLevel()) {
// FIXME: Is this correct for submodules?
auto topLevel = clangModule->getTopLevelModule();
auto wrapper = owner.getWrapperForModule(topLevel);
return wrapper->getAdapterModule();
}
if (!adapterModule.getInt()) {
// FIXME: Include proper source location.
ModuleDecl *M = getParentModule();
ASTContext &Ctx = M->getASTContext();
auto adapter = Ctx.getModule(ModuleDecl::AccessPathTy({M->getName(),
SourceLoc()}));
if (adapter == M) {
adapter = nullptr;
} else {
auto &sharedModuleRef = Ctx.LoadedModules[M->getName()];
assert(!sharedModuleRef || sharedModuleRef == adapter ||
sharedModuleRef == M);
sharedModuleRef = adapter;
}
auto mutableThis = const_cast<ClangModuleUnit *>(this);
mutableThis->adapterModule.setPointerAndInt(adapter, true);
}
return adapterModule.getPointer();
}
void ClangModuleUnit::getImportedModules(
SmallVectorImpl<ModuleDecl::ImportedModule> &imports,
ModuleDecl::ImportFilter filter) const {
switch (filter) {
case ModuleDecl::ImportFilter::All:
case ModuleDecl::ImportFilter::Private:
if (auto stdlib = owner.getStdlibModule())
imports.push_back({ModuleDecl::AccessPathTy(), stdlib});
break;
case ModuleDecl::ImportFilter::Public:
break;
}
SmallVector<clang::Module *, 8> imported;
if (!clangModule) {
// This is the special "imported headers" module.
switch (filter) {
case ModuleDecl::ImportFilter::All:
case ModuleDecl::ImportFilter::Public:
imported.append(owner.ImportedHeaderExports.begin(),
owner.ImportedHeaderExports.end());
break;
case ModuleDecl::ImportFilter::Private:
break;
}
} else {
clangModule->getExportedModules(imported);
switch (filter) {
case ModuleDecl::ImportFilter::All: {
llvm::SmallPtrSet<clang::Module *, 8> knownModules;
imported.append(clangModule->Imports.begin(), clangModule->Imports.end());
imported.erase(std::remove_if(imported.begin(), imported.end(),
[&](clang::Module *mod) -> bool {
return !knownModules.insert(mod).second;
}),
imported.end());
// 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);
break;
}
case ModuleDecl::ImportFilter::Private: {
llvm::SmallPtrSet<clang::Module *, 8> knownModules(imported.begin(),
imported.end());
SmallVector<clang::Module *, 8> privateImports;
std::copy_if(clangModule->Imports.begin(), clangModule->Imports.end(),
std::back_inserter(privateImports), [&](clang::Module *mod) {
return knownModules.count(mod) == 0;
});
imported.swap(privateImports);
// 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);
break;
}
case ModuleDecl::ImportFilter::Public:
break;
}
}
auto topLevelAdapter = getAdapterModule();
for (auto importMod : imported) {
auto wrapper = owner.getWrapperForModule(importMod);
auto actualMod = wrapper->getAdapterModule();
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 == topLevelAdapter) {
actualMod = wrapper->getParentModule();
}
assert(actualMod && "Missing imported adapter module");
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 *topLevelAdapter = getAdapterModule();
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->getAdapterModule())
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->getAdapterModule();
if (!actualMod || actualMod == topLevelAdapter)
actualMod = wrapper->getParentModule();
assert(actualMod && "Missing imported adapter module");
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);
}
// 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);
}
}
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->getAdapterModule())
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();
}