mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
719 lines
28 KiB
C++
719 lines
28 KiB
C++
//===--- PrintAsClang.cpp - Emit a header file for a Swift AST ------------===//
|
||
//
|
||
// 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
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "swift/PrintAsClang/PrintAsClang.h"
|
||
|
||
#include "ClangSyntaxPrinter.h"
|
||
#include "ModuleContentsWriter.h"
|
||
#include "SwiftToClangInteropContext.h"
|
||
|
||
#include "swift/AST/ASTContext.h"
|
||
#include "swift/AST/AttrKind.h"
|
||
#include "swift/AST/Module.h"
|
||
#include "swift/AST/PrettyStackTrace.h"
|
||
#include "swift/Basic/Assertions.h"
|
||
#include "swift/Basic/Version.h"
|
||
#include "swift/ClangImporter/ClangImporter.h"
|
||
#include "swift/Frontend/FrontendOptions.h"
|
||
|
||
#include "clang/Basic/FileManager.h"
|
||
#include "clang/Basic/Module.h"
|
||
#include "clang/Lex/HeaderSearch.h"
|
||
|
||
#include "llvm/Support/FormatVariadic.h"
|
||
#include "llvm/Support/Path.h"
|
||
#include "llvm/Support/raw_ostream.h"
|
||
|
||
using namespace swift;
|
||
|
||
static void emitCxxConditional(raw_ostream &out,
|
||
llvm::function_ref<void()> cxxCase,
|
||
llvm::function_ref<void()> cCase = {}) {
|
||
out << "#if defined(__cplusplus)\n";
|
||
cxxCase();
|
||
if (cCase) {
|
||
out << "#else\n";
|
||
cCase();
|
||
}
|
||
out << "#endif\n";
|
||
}
|
||
|
||
static void emitObjCConditional(raw_ostream &out,
|
||
llvm::function_ref<void()> objcCase,
|
||
llvm::function_ref<void()> nonObjCCase = {}) {
|
||
out << "#if defined(__OBJC__)\n";
|
||
objcCase();
|
||
if (nonObjCCase) {
|
||
out << "#else\n";
|
||
nonObjCCase();
|
||
}
|
||
out << "#endif // defined(__OBJC__)\n";
|
||
}
|
||
|
||
static void emitExternC(raw_ostream &out,
|
||
llvm::function_ref<void()> cCase) {
|
||
emitCxxConditional(out, [&] {
|
||
out << "extern \"C\" {\n";
|
||
});
|
||
cCase();
|
||
emitCxxConditional(out, [&] {
|
||
out << "} // extern \"C\"\n";
|
||
});
|
||
}
|
||
|
||
static void writePtrauthPrologue(raw_ostream &os, ASTContext &ctx) {
|
||
emitCxxConditional(os, [&]() {
|
||
ClangSyntaxPrinter(ctx, os).printIgnoredDiagnosticBlock(
|
||
"non-modular-include-in-framework-module", [&] {
|
||
os << "#if defined(__arm64e__) && __has_include(<ptrauth.h>)\n";
|
||
os << "# include <ptrauth.h>\n";
|
||
os << "#else\n";
|
||
ClangSyntaxPrinter(ctx, os).printIgnoredDiagnosticBlock(
|
||
"reserved-macro-identifier", [&]() {
|
||
os << "# ifndef "
|
||
"__ptrauth_swift_value_witness_function_pointer\n";
|
||
os << "# define "
|
||
"__ptrauth_swift_value_witness_function_pointer(x)\n";
|
||
os << "# endif\n";
|
||
os << "# ifndef __ptrauth_swift_class_method_pointer\n";
|
||
os << "# define __ptrauth_swift_class_method_pointer(x)\n";
|
||
os << "# endif\n";
|
||
});
|
||
os << "#endif\n";
|
||
});
|
||
});
|
||
}
|
||
|
||
static void writePrologue(raw_ostream &out, ASTContext &ctx,
|
||
StringRef macroGuard) {
|
||
|
||
out << "// Generated by "
|
||
<< version::getSwiftFullVersion(ctx.LangOpts.EffectiveLanguageVersion)
|
||
<< "\n"
|
||
// Guard against recursive definition.
|
||
<< "#ifndef " << macroGuard << "\n"
|
||
<< "#define " << macroGuard
|
||
<< "\n"
|
||
"#pragma clang diagnostic push\n"
|
||
"#pragma clang diagnostic ignored \"-Wgcc-compat\"\n"
|
||
"\n"
|
||
"#if !defined(__has_include)\n"
|
||
"# define __has_include(x) 0\n"
|
||
"#endif\n"
|
||
"#if !defined(__has_attribute)\n"
|
||
"# define __has_attribute(x) 0\n"
|
||
"#endif\n"
|
||
"#if !defined(__has_feature)\n"
|
||
"# define __has_feature(x) 0\n"
|
||
"#endif\n"
|
||
"#if !defined(__has_warning)\n"
|
||
"# define __has_warning(x) 0\n"
|
||
"#endif\n"
|
||
"\n"
|
||
"#if __has_include(<swift/objc-prologue.h>)\n"
|
||
"# include <swift/objc-prologue.h>\n"
|
||
"#endif\n"
|
||
"\n"
|
||
"#pragma clang diagnostic ignored \"-Wauto-import\"\n";
|
||
emitObjCConditional(out,
|
||
[&] { out << "#include <Foundation/Foundation.h>\n"; });
|
||
emitCxxConditional(
|
||
out,
|
||
[&] {
|
||
out << "#include <cstdint>\n"
|
||
"#include <cstddef>\n"
|
||
"#include <cstdbool>\n"
|
||
"#include <cstring>\n";
|
||
out << "#include <stdlib.h>\n";
|
||
out << "#include <new>\n";
|
||
out << "#include <type_traits>\n";
|
||
},
|
||
[&] {
|
||
out << "#include <stdint.h>\n"
|
||
"#include <stddef.h>\n"
|
||
"#include <stdbool.h>\n"
|
||
"#include <string.h>\n";
|
||
});
|
||
writePtrauthPrologue(out, ctx);
|
||
out << "\n"
|
||
"#if !defined(SWIFT_TYPEDEFS)\n"
|
||
"# define SWIFT_TYPEDEFS 1\n"
|
||
"# if __has_include(<uchar.h>)\n"
|
||
"# include <uchar.h>\n"
|
||
"# elif !defined(__cplusplus)\n"
|
||
"typedef unsigned char char8_t;\n"
|
||
"typedef uint_least16_t char16_t;\n"
|
||
"typedef uint_least32_t char32_t;\n"
|
||
"# endif\n"
|
||
#define MAP_SIMD_TYPE(C_TYPE, SCALAR_TYPE, _) \
|
||
"typedef " #SCALAR_TYPE " swift_" #C_TYPE "2" \
|
||
" __attribute__((__ext_vector_type__(2)));\n" \
|
||
"typedef " #SCALAR_TYPE " swift_" #C_TYPE "3" \
|
||
" __attribute__((__ext_vector_type__(3)));\n" \
|
||
"typedef " #SCALAR_TYPE " swift_" #C_TYPE "4" \
|
||
" __attribute__((__ext_vector_type__(4)));\n"
|
||
#include "swift/ClangImporter/SIMDMappedTypes.def"
|
||
"#endif\n"
|
||
"\n";
|
||
|
||
#define CLANG_MACRO_BODY(NAME, BODY) \
|
||
out << "#if !defined(" NAME ")\n" \
|
||
BODY "\n" \
|
||
"#endif\n";
|
||
|
||
#define CLANG_MACRO(NAME, ARGS, VALUE) CLANG_MACRO_BODY(NAME, "# define " NAME ARGS " " VALUE)
|
||
|
||
#define CLANG_MACRO_ALTERNATIVE(NAME, ARGS, CONDITION, VALUE, ALTERNATIVE) CLANG_MACRO_BODY(NAME, \
|
||
"# if " CONDITION "\n" \
|
||
"# define " NAME ARGS " " VALUE "\n" \
|
||
"# else\n" \
|
||
"# define " NAME ARGS " " ALTERNATIVE "\n" \
|
||
"# endif")
|
||
|
||
#define CLANG_MACRO_OBJC(NAME, ARGS, VALUE) \
|
||
out << "#if defined(__OBJC__)\n" \
|
||
"#if !defined(" NAME ")\n" \
|
||
"# define " NAME ARGS " " VALUE "\n" \
|
||
"#endif\n" \
|
||
"#endif\n";
|
||
|
||
#define CLANG_MACRO_CXX(NAME, ARGS, VALUE, ALTERNATIVE) \
|
||
out << "#if defined(__cplusplus)\n" \
|
||
"# define " NAME ARGS " " VALUE "\n" \
|
||
"#else\n" \
|
||
"# define " NAME ARGS " " ALTERNATIVE "\n" \
|
||
"#endif\n";
|
||
|
||
#define CLANG_MACRO_CXX_BODY(NAME, BODY) \
|
||
out << "#if defined(__cplusplus)\n" \
|
||
BODY "\n" \
|
||
"#endif\n";
|
||
|
||
#include "swift/PrintAsClang/ClangMacros.def"
|
||
|
||
// SWIFT_IMPORT_STDLIB_SYMBOL's expansion can't be calculated in the
|
||
// preprocessor, so write its definition here
|
||
auto emitMacro = [&](StringRef name, StringRef value = "") {
|
||
out << "#if !defined(" << name << ")\n";
|
||
out << "# define " << name << " " << value << "\n";
|
||
out << "#endif\n";
|
||
};
|
||
if (ctx.getStdlibModule()->isStaticLibrary()) {
|
||
emitMacro("SWIFT_IMPORT_STDLIB_SYMBOL");
|
||
} else {
|
||
out << "#if defined(_WIN32)\n";
|
||
emitMacro("SWIFT_IMPORT_STDLIB_SYMBOL", "__declspec(dllimport)");
|
||
out << "#else\n";
|
||
emitMacro("SWIFT_IMPORT_STDLIB_SYMBOL");
|
||
out << "#endif\n";
|
||
}
|
||
|
||
static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4,
|
||
"need to add SIMD typedefs here if max elements is increased");
|
||
|
||
// For C compilers which don’t support nullability attributes, ignore them;
|
||
// for ones which do, suppress warnings about them being an extension.
|
||
out << "#if !__has_feature(nullability)\n"
|
||
"# define _Nonnull\n"
|
||
"# define _Nullable\n"
|
||
"# define _Null_unspecified\n"
|
||
"#elif !defined(__OBJC__)\n"
|
||
"# pragma clang diagnostic ignored \"-Wnullability-extension\"\n"
|
||
"#endif\n"
|
||
"#if !__has_feature(nullability_nullable_result)\n"
|
||
"# define _Nullable_result _Nullable\n"
|
||
"#endif\n";
|
||
}
|
||
|
||
static int compareImportModulesByName(const ImportModuleTy *left,
|
||
const ImportModuleTy *right, bool isCxx) {
|
||
auto *leftSwiftModule = left->dyn_cast<ModuleDecl *>();
|
||
auto *rightSwiftModule = right->dyn_cast<ModuleDecl *>();
|
||
|
||
if (leftSwiftModule && !rightSwiftModule)
|
||
return -compareImportModulesByName(right, left, isCxx);
|
||
|
||
if (leftSwiftModule && rightSwiftModule)
|
||
return leftSwiftModule->getName().compare(rightSwiftModule->getName());
|
||
|
||
auto *leftClangModule = cast<const clang::Module *>(*left);
|
||
assert((isCxx || leftClangModule->isSubModule()) &&
|
||
"top-level modules should use a normal swift::ModuleDecl");
|
||
if (rightSwiftModule) {
|
||
// Because the Clang module is a submodule, its full name will never be
|
||
// equal to a Swift module's name, even if the top-level name is the same;
|
||
// it will always come before or after.
|
||
if (leftClangModule->getTopLevelModuleName() <
|
||
rightSwiftModule->getName().str()) {
|
||
return -1;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
auto *rightClangModule = cast<const clang::Module *>(*right);
|
||
assert((isCxx || rightClangModule->isSubModule()) &&
|
||
"top-level modules should use a normal swift::ModuleDecl");
|
||
|
||
SmallVector<StringRef, 8> leftReversePath(
|
||
ModuleDecl::ReverseFullNameIterator(leftClangModule), {});
|
||
SmallVector<StringRef, 8> rightReversePath(
|
||
ModuleDecl::ReverseFullNameIterator(rightClangModule), {});
|
||
|
||
assert(leftReversePath != rightReversePath &&
|
||
"distinct Clang modules should not have the same full name");
|
||
if (std::lexicographical_compare(leftReversePath.rbegin(),
|
||
leftReversePath.rend(),
|
||
rightReversePath.rbegin(),
|
||
rightReversePath.rend())) {
|
||
return -1;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
// Makes the provided path absolute and removes any "." or ".." segments from
|
||
// the path
|
||
static llvm::SmallString<128> normalizePath(const llvm::StringRef path) {
|
||
llvm::SmallString<128> result = path;
|
||
llvm::sys::path::remove_dots(result, /* remove_dot_dot */ true);
|
||
llvm::sys::fs::make_absolute(result);
|
||
return result;
|
||
}
|
||
|
||
// Collect the set of header includes needed to import the given Clang module
|
||
// into an ObjectiveC program. Modeled after collectModuleHeaderIncludes in the
|
||
// Clang frontend (FrontendAction.cpp)
|
||
// Augment requiredTextualIncludes with the set of headers required.
|
||
static void collectClangModuleHeaderIncludes(
|
||
const clang::Module *clangModule, clang::FileManager &fileManager,
|
||
llvm::SmallSet<llvm::SmallString<128>, 10> &requiredTextualIncludes,
|
||
llvm::SmallSet<const clang::Module *, 10> &visitedModules,
|
||
const llvm::SmallSet<llvm::SmallString<128>, 10> &includeDirs,
|
||
const llvm::StringRef cwd) {
|
||
|
||
if (!visitedModules.insert(clangModule).second)
|
||
return;
|
||
|
||
auto addHeader = [&](llvm::StringRef headerPath,
|
||
llvm::StringRef pathRelativeToRootModuleDir) {
|
||
if (!clangModule->Directory)
|
||
return;
|
||
|
||
llvm::SmallString<128> textualInclude = normalizePath(headerPath);
|
||
llvm::SmallString<128> containingSearchDirPath;
|
||
|
||
for (auto &includeDir : includeDirs) {
|
||
if (textualInclude.str().starts_with(includeDir)) {
|
||
if (includeDir.size() > containingSearchDirPath.size()) {
|
||
containingSearchDirPath = includeDir;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!containingSearchDirPath.empty()) {
|
||
llvm::SmallString<128> prefixToRemove =
|
||
llvm::formatv("{0}/", containingSearchDirPath);
|
||
llvm::sys::path::replace_path_prefix(textualInclude, prefixToRemove, "");
|
||
} else {
|
||
// If we cannot find find the module map on the search path,
|
||
// fallback to including the header using the provided path relative
|
||
// to the module map
|
||
textualInclude = pathRelativeToRootModuleDir;
|
||
}
|
||
|
||
if (clangModule->getTopLevelModule()->IsFramework) {
|
||
llvm::SmallString<32> frameworkName =
|
||
clangModule->getTopLevelModuleName();
|
||
llvm::SmallString<64> oldFrameworkPrefix =
|
||
llvm::formatv("{0}.framework/Headers", frameworkName);
|
||
llvm::sys::path::replace_path_prefix(textualInclude, oldFrameworkPrefix,
|
||
frameworkName);
|
||
}
|
||
|
||
requiredTextualIncludes.insert(textualInclude);
|
||
};
|
||
|
||
if (std::optional<clang::Module::Header> umbrellaHeader =
|
||
clangModule->getUmbrellaHeaderAsWritten()) {
|
||
addHeader(umbrellaHeader->Entry.getFileEntry().tryGetRealPathName(),
|
||
umbrellaHeader->PathRelativeToRootModuleDirectory);
|
||
} else if (std::optional<clang::Module::DirectoryName> umbrellaDir =
|
||
clangModule->getUmbrellaDirAsWritten()) {
|
||
SmallString<128> nativeUmbrellaDirPath;
|
||
std::error_code errorCode;
|
||
llvm::sys::path::native(umbrellaDir->Entry.getName(),
|
||
nativeUmbrellaDirPath);
|
||
llvm::vfs::FileSystem &fileSystem = fileManager.getVirtualFileSystem();
|
||
for (llvm::vfs::recursive_directory_iterator
|
||
dir(fileSystem, nativeUmbrellaDirPath, errorCode),
|
||
end;
|
||
dir != end && !errorCode; dir.increment(errorCode)) {
|
||
|
||
if (llvm::StringSwitch<bool>(llvm::sys::path::extension(dir->path()))
|
||
.Cases(".h", ".H", ".hh", ".hpp", true)
|
||
.Default(false)) {
|
||
|
||
// Compute path to the header relative to the root of the module
|
||
// (location of the module map) First compute the relative path from
|
||
// umbrella directory to header file
|
||
SmallVector<StringRef> pathComponents;
|
||
auto pathIt = llvm::sys::path::rbegin(dir->path());
|
||
|
||
for (int i = 0; i != dir.level() + 1; ++i, ++pathIt)
|
||
pathComponents.push_back(*pathIt);
|
||
// Then append this to the path from module root to umbrella dir
|
||
SmallString<128> relativeHeaderPath;
|
||
if (umbrellaDir->PathRelativeToRootModuleDirectory != ".")
|
||
relativeHeaderPath += umbrellaDir->PathRelativeToRootModuleDirectory;
|
||
|
||
for (auto it = pathComponents.rbegin(), end = pathComponents.rend();
|
||
it != end; ++it) {
|
||
llvm::sys::path::append(relativeHeaderPath, *it);
|
||
}
|
||
|
||
addHeader(dir->path(), relativeHeaderPath);
|
||
}
|
||
}
|
||
} else {
|
||
for (clang::Module::HeaderKind headerKind :
|
||
{clang::Module::HK_Normal, clang::Module::HK_Textual}) {
|
||
for (const clang::Module::Header &header :
|
||
clangModule->getHeaders(headerKind)) {
|
||
addHeader(header.Entry.getFileEntry().tryGetRealPathName(),
|
||
header.PathRelativeToRootModuleDirectory);
|
||
}
|
||
}
|
||
for (auto submodule : clangModule->submodules()) {
|
||
if (submodule->IsExplicit)
|
||
continue;
|
||
|
||
collectClangModuleHeaderIncludes(submodule, fileManager,
|
||
requiredTextualIncludes, visitedModules,
|
||
includeDirs, cwd);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
writeImports(raw_ostream &out, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
|
||
ModuleDecl &M, StringRef bridgingHeader,
|
||
const FrontendOptions &frontendOpts,
|
||
clang::HeaderSearch &clangHeaderSearchInfo,
|
||
const llvm::StringMap<StringRef> &exposedModuleHeaderNames,
|
||
bool useCxxImport = false,
|
||
bool useNonModularIncludes = false) {
|
||
useNonModularIncludes |= frontendOpts.EmitClangHeaderWithNonModularIncludes;
|
||
|
||
// Note: we can't use has_feature(modules) as it's always enabled in C++20
|
||
// mode.
|
||
out << "#if __has_feature(objc_modules)\n";
|
||
|
||
out << "#if __has_warning(\"-Watimport-in-framework-header\")\n"
|
||
<< "#pragma clang diagnostic ignored \"-Watimport-in-framework-header\"\n"
|
||
<< "#endif\n";
|
||
|
||
// Sort alphabetically for determinism and consistency.
|
||
SmallVector<ImportModuleTy, 8> sortedImports{imports.begin(),
|
||
imports.end()};
|
||
std::stable_sort(
|
||
sortedImports.begin(), sortedImports.end(),
|
||
[&](const ImportModuleTy &left, const ImportModuleTy &right) -> bool {
|
||
return compareImportModulesByName(&left, &right, useCxxImport) < 0;
|
||
});
|
||
|
||
auto isUnderlyingModule = [&M, bridgingHeader](ModuleDecl *import) -> bool {
|
||
if (bridgingHeader.empty())
|
||
return import != &M && import->getName() == M.getName();
|
||
|
||
return import->isClangHeaderImportModule();
|
||
};
|
||
|
||
clang::FileSystemOptions fileSystemOptions;
|
||
clang::FileManager fileManager{fileSystemOptions};
|
||
|
||
llvm::SmallSet<llvm::SmallString<128>, 10>
|
||
requiredTextualIncludes; // Only included without modules.
|
||
llvm::SmallVector<StringRef, 1> textualIncludes; // always included.
|
||
llvm::SmallSet<const clang::Module *, 10> visitedModules;
|
||
llvm::SmallSet<llvm::SmallString<128>, 10> includeDirs;
|
||
|
||
llvm::vfs::FileSystem &fileSystem = fileManager.getVirtualFileSystem();
|
||
llvm::ErrorOr<std::string> cwd = fileSystem.getCurrentWorkingDirectory();
|
||
|
||
if (useNonModularIncludes) {
|
||
assert(cwd && "Access to current working directory required");
|
||
|
||
for (auto searchDir = clangHeaderSearchInfo.search_dir_begin();
|
||
searchDir != clangHeaderSearchInfo.search_dir_end(); ++searchDir) {
|
||
includeDirs.insert(normalizePath(searchDir->getName()));
|
||
}
|
||
|
||
const clang::Module *foundationModule = clangHeaderSearchInfo.lookupModule(
|
||
"Foundation", clang::SourceLocation(), false, false);
|
||
const clang::Module *darwinModule = clangHeaderSearchInfo.lookupModule(
|
||
"Darwin", clang::SourceLocation(), false, false);
|
||
|
||
std::function<void(const clang::Module *)>
|
||
collectTransitiveSubmoduleClosure;
|
||
collectTransitiveSubmoduleClosure = [&](const clang::Module *module) {
|
||
if (!module)
|
||
return;
|
||
|
||
visitedModules.insert(module);
|
||
for (auto submodule : module->submodules()) {
|
||
collectTransitiveSubmoduleClosure(submodule);
|
||
}
|
||
};
|
||
|
||
collectTransitiveSubmoduleClosure(foundationModule);
|
||
collectTransitiveSubmoduleClosure(darwinModule);
|
||
}
|
||
|
||
// Track printed names to handle overlay modules.
|
||
llvm::SmallPtrSet<Identifier, 8> seenImports;
|
||
bool includeUnderlying = false;
|
||
StringRef importDirective =
|
||
useCxxImport ? "#pragma clang module import" : "@import";
|
||
StringRef importDirectiveLineEnd = useCxxImport ? "\n" : ";\n";
|
||
for (auto import : sortedImports) {
|
||
if (auto *swiftModule = import.dyn_cast<ModuleDecl *>()) {
|
||
if (useCxxImport) {
|
||
// Do not import Swift modules into the C++ section of the generated
|
||
// header unless explicitly exposed.
|
||
auto it = exposedModuleHeaderNames.find(swiftModule->getName().str());
|
||
if (it != exposedModuleHeaderNames.end())
|
||
textualIncludes.push_back(it->getValue());
|
||
continue;
|
||
}
|
||
auto Name = swiftModule->getName();
|
||
if (isUnderlyingModule(swiftModule)) {
|
||
includeUnderlying = true;
|
||
continue;
|
||
}
|
||
if (seenImports.insert(Name).second) {
|
||
out << importDirective << ' ' << Name.str() << importDirectiveLineEnd;
|
||
if (useNonModularIncludes) {
|
||
if (const clang::Module *underlyingClangModule =
|
||
swiftModule->findUnderlyingClangModule()) {
|
||
collectClangModuleHeaderIncludes(
|
||
underlyingClangModule, fileManager, requiredTextualIncludes,
|
||
visitedModules, includeDirs, cwd.get());
|
||
} else if ((underlyingClangModule =
|
||
clangHeaderSearchInfo.lookupModule(
|
||
Name.str(), clang::SourceLocation(), true,
|
||
true))) {
|
||
collectClangModuleHeaderIncludes(
|
||
underlyingClangModule, fileManager, requiredTextualIncludes,
|
||
visitedModules, includeDirs, cwd.get());
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
const auto *clangModule = cast<const clang::Module *>(import);
|
||
assert((useCxxImport || clangModule->isSubModule()) &&
|
||
"top-level modules should use a normal swift::ModuleDecl");
|
||
out << importDirective << ' ';
|
||
ModuleDecl::ReverseFullNameIterator(clangModule).printForward(out);
|
||
out << importDirectiveLineEnd;
|
||
if (useNonModularIncludes) {
|
||
collectClangModuleHeaderIncludes(
|
||
clangModule, fileManager, requiredTextualIncludes, visitedModules,
|
||
includeDirs, cwd.get());
|
||
}
|
||
}
|
||
}
|
||
|
||
if (useNonModularIncludes && !requiredTextualIncludes.empty()) {
|
||
out << "#elif defined(__OBJC__)\n";
|
||
for (auto header : requiredTextualIncludes)
|
||
out << "#import <" << header << ">\n";
|
||
out << "#else\n";
|
||
for (auto header : requiredTextualIncludes)
|
||
out << "#include <" << header << ">\n";
|
||
}
|
||
out << "#endif\n\n";
|
||
for (const auto header : textualIncludes) {
|
||
out << "#include <" << header << ">\n";
|
||
}
|
||
|
||
if (includeUnderlying) {
|
||
if (bridgingHeader.empty())
|
||
out << "#import <" << M.getName().str() << '/' << M.getName().str()
|
||
<< ".h>\n\n";
|
||
else {
|
||
out << "#if defined(__OBJC__)\n";
|
||
out << "#import \"" << bridgingHeader << "\"\n";
|
||
out << "#else\n";
|
||
out << "#include \"" << bridgingHeader << "\"\n";
|
||
out << "#endif\n\n";
|
||
}
|
||
}
|
||
}
|
||
|
||
static void writePostImportPrologue(raw_ostream &os, ModuleDecl &M) {
|
||
os << "#pragma clang diagnostic ignored \"-Wproperty-attribute-mismatch\"\n"
|
||
"#pragma clang diagnostic ignored \"-Wduplicate-method-arg\"\n"
|
||
"#if __has_warning(\"-Wpragma-clang-attribute\")\n"
|
||
"# pragma clang diagnostic ignored \"-Wpragma-clang-attribute\"\n"
|
||
"#endif\n"
|
||
"#pragma clang diagnostic ignored \"-Wunknown-pragmas\"\n"
|
||
"#pragma clang diagnostic ignored \"-Wnullability\"\n"
|
||
"#pragma clang diagnostic ignored "
|
||
"\"-Wdollar-in-identifier-extension\"\n"
|
||
"#pragma clang diagnostic ignored "
|
||
"\"-Wunsafe-buffer-usage\"\n"
|
||
"\n"
|
||
"#if __has_attribute(external_source_symbol)\n"
|
||
"# pragma push_macro(\"any\")\n"
|
||
"# undef any\n"
|
||
"# pragma clang attribute push("
|
||
"__attribute__((external_source_symbol(language=\"Swift\", "
|
||
"defined_in=\""
|
||
<< M.getNameStr()
|
||
<< "\",generated_declaration))), "
|
||
"apply_to=any(function,enum,objc_interface,objc_category,"
|
||
"objc_protocol))\n"
|
||
"# pragma pop_macro(\"any\")\n"
|
||
"#endif\n\n";
|
||
}
|
||
|
||
static void writeObjCEpilogue(raw_ostream &os) {
|
||
// Pop out of `external_source_symbol` attribute
|
||
// before emitting the C++ section as the C++ section
|
||
// might include other files in it.
|
||
os << "#if __has_attribute(external_source_symbol)\n"
|
||
"# pragma clang attribute pop\n"
|
||
"#endif\n";
|
||
}
|
||
|
||
static void writeEpilogue(raw_ostream &os) {
|
||
os << "#pragma clang diagnostic pop\n"
|
||
// For the macro guard against recursive definition
|
||
"#endif\n";
|
||
}
|
||
|
||
static std::string computeMacroGuard(const ModuleDecl *M) {
|
||
return (llvm::Twine(M->getNameStr().upper()) + "_SWIFT_H").str();
|
||
}
|
||
|
||
bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M,
|
||
StringRef bridgingHeader,
|
||
const FrontendOptions &frontendOpts,
|
||
const IRGenOptions &irGenOpts,
|
||
clang::HeaderSearch &clangHeaderSearchInfo) {
|
||
llvm::PrettyStackTraceString trace("While generating Clang header");
|
||
|
||
SwiftToClangInteropContext interopContext(*M, irGenOpts);
|
||
writePrologue(os, M->getASTContext(), computeMacroGuard(M));
|
||
|
||
// C content (@c)
|
||
std::string moduleContentsScratch;
|
||
{
|
||
SmallPtrSet<ImportModuleTy, 8> imports;
|
||
llvm::raw_string_ostream cModuleContents{moduleContentsScratch};
|
||
printModuleContentsAsC(cModuleContents, imports, *M, interopContext,
|
||
frontendOpts.ClangHeaderMinAccess);
|
||
|
||
llvm::StringMap<StringRef> exposedModuleHeaderNames;
|
||
writeImports(os, imports, *M, bridgingHeader, frontendOpts,
|
||
clangHeaderSearchInfo, exposedModuleHeaderNames,
|
||
/*useCxxImport=*/false, /*useNonModularIncludes*/true);
|
||
|
||
emitExternC(os, [&] { os << "\n" << cModuleContents.str(); });
|
||
moduleContentsScratch.clear();
|
||
}
|
||
|
||
// Objective-C content
|
||
SmallPtrSet<ImportModuleTy, 8> imports;
|
||
llvm::raw_string_ostream objcModuleContents{moduleContentsScratch};
|
||
printModuleContentsAsObjC(objcModuleContents, imports, *M, interopContext,
|
||
frontendOpts.ClangHeaderMinAccess);
|
||
emitObjCConditional(os, [&] {
|
||
llvm::StringMap<StringRef> exposedModuleHeaderNames;
|
||
writeImports(os, imports, *M, bridgingHeader, frontendOpts,
|
||
clangHeaderSearchInfo, exposedModuleHeaderNames);
|
||
});
|
||
writePostImportPrologue(os, *M);
|
||
emitObjCConditional(os, [&] { os << "\n" << objcModuleContents.str(); });
|
||
writeObjCEpilogue(os);
|
||
|
||
// C++ content
|
||
emitCxxConditional(os, [&] {
|
||
// FIXME: Expose Swift with @expose by default.
|
||
bool enableCxx = frontendOpts.ClangHeaderExposedDecls.has_value() ||
|
||
M->DeclContext::getASTContext().LangOpts.EnableCXXInterop;
|
||
if (!enableCxx)
|
||
return;
|
||
|
||
llvm::StringSet<> exposedModules;
|
||
for (const auto &mod : frontendOpts.clangHeaderExposedImports)
|
||
exposedModules.insert(mod.moduleName);
|
||
|
||
// Include the shim header only in the C++ mode.
|
||
ClangSyntaxPrinter(M->getASTContext(), os).printIncludeForShimHeader(
|
||
"_SwiftCxxInteroperability.h");
|
||
|
||
// Explicit @expose attribute is required only when the user specifies
|
||
// -clang-header-expose-decls flag.
|
||
// FIXME: should we detect any presence of @expose and require it then?
|
||
bool requiresExplicitExpose =
|
||
frontendOpts.ClangHeaderExposedDecls.has_value() &&
|
||
(*frontendOpts.ClangHeaderExposedDecls ==
|
||
FrontendOptions::ClangHeaderExposeBehavior::HasExposeAttr ||
|
||
*frontendOpts.ClangHeaderExposedDecls ==
|
||
FrontendOptions::ClangHeaderExposeBehavior::
|
||
HasExposeAttrOrImplicitDeps);
|
||
// Swift stdlib dependencies are emitted into the same header when
|
||
// -clang-header-expose-decls flag is not specified, or when it allows
|
||
// implicit dependency emission.
|
||
bool addStdlibDepsInline =
|
||
!frontendOpts.ClangHeaderExposedDecls.has_value() ||
|
||
*frontendOpts.ClangHeaderExposedDecls ==
|
||
FrontendOptions::ClangHeaderExposeBehavior::
|
||
HasExposeAttrOrImplicitDeps ||
|
||
*frontendOpts.ClangHeaderExposedDecls ==
|
||
FrontendOptions::ClangHeaderExposeBehavior::AllPublic;
|
||
|
||
std::string moduleContentsBuf;
|
||
llvm::raw_string_ostream moduleContents{moduleContentsBuf};
|
||
auto deps = printModuleContentsAsCxx(
|
||
moduleContents, *M, interopContext,
|
||
frontendOpts.ClangHeaderMinAccess.value_or(AccessLevel::Public),
|
||
/*requiresExposedAttribute=*/requiresExplicitExpose, exposedModules);
|
||
// FIXME: In ObjC++ mode, we do not need to reimport duplicate modules.
|
||
llvm::StringMap<StringRef> exposedModuleHeaderNames;
|
||
for (const auto &mod : frontendOpts.clangHeaderExposedImports)
|
||
exposedModuleHeaderNames.insert({mod.moduleName, mod.headerName});
|
||
writeImports(os, deps.imports, *M, bridgingHeader, frontendOpts,
|
||
clangHeaderSearchInfo, exposedModuleHeaderNames,
|
||
/*useCxxImport=*/true);
|
||
// Embed the standard library directly.
|
||
if (addStdlibDepsInline && deps.dependsOnStandardLibrary) {
|
||
assert(!M->isStdlibModule());
|
||
SwiftToClangInteropContext interopContext(
|
||
*M->getASTContext().getStdlibModule(), irGenOpts);
|
||
auto macroGuard = computeMacroGuard(M->getASTContext().getStdlibModule());
|
||
os << "#ifndef " << macroGuard << "\n";
|
||
os << "#define " << macroGuard << "\n";
|
||
printModuleContentsAsCxx(os, *M->getASTContext().getStdlibModule(),
|
||
interopContext, AccessLevel::Public,
|
||
/*requiresExposedAttribute=*/true,
|
||
exposedModules);
|
||
os << "#endif // " << macroGuard << "\n";
|
||
}
|
||
|
||
os << moduleContents.str();
|
||
});
|
||
writeEpilogue(os);
|
||
|
||
return false;
|
||
}
|