Files
swift-mirror/lib/PrintAsClang/ModuleContentsWriter.cpp
Anthony Latsis bda6edb85c AST: Rename GenericContext::isGeneric to hasGenericParamList
`isGeneric` is a misleading name because this method checks for the
existence of a `GenericParamList`, which is not implied by genericity.
2025-11-11 15:55:16 +00:00

1271 lines
45 KiB
C++

//===--- ModuleContentsWriter.cpp - Walk module decls to print ObjC/C++ ---===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 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 "ModuleContentsWriter.h"
#include "ClangSyntaxPrinter.h"
#include "DeclAndTypePrinter.h"
#include "OutputLanguageMode.h"
#include "PrimitiveTypeMapping.h"
#include "PrintClangValueType.h"
#include "PrintSwiftToClangCoreScaffold.h"
#include "SwiftToClangInteropContext.h"
#include "swift/AST/AttrKind.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticsSema.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/Module.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/SwiftNameTranslation.h"
#include "swift/AST/TypeDeclFinder.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/Feature.h"
#include "swift/Basic/SourceManager.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/Strings.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/Module.h"
#include "llvm/Support/raw_ostream.h"
using namespace swift;
using namespace swift::objc_translation;
using DelayedMemberSet = DeclAndTypePrinter::DelayedMemberSet;
/// Returns true if \p decl represents an <os/object.h> type.
static bool isOSObjectType(const clang::Decl *decl) {
auto *named = dyn_cast_or_null<clang::NamedDecl>(decl);
if (!named)
return false;
return !DeclAndTypePrinter::maybeGetOSObjectBaseName(named).empty();
}
namespace {
class ReferencedTypeFinder : public TypeDeclFinder {
friend TypeDeclFinder;
llvm::function_ref<void(ReferencedTypeFinder &, const TypeDecl *)> Callback;
bool NeedsDefinition = false;
explicit ReferencedTypeFinder(decltype(Callback) callback)
: Callback(callback) {}
Action visitNominalType(NominalType *nominal) override {
Callback(*this, nominal->getDecl());
return Action::SkipNode;
}
Action visitTypeAliasType(TypeAliasType *aliasTy) override {
if (aliasTy->getDecl()->hasClangNode() &&
!aliasTy->getDecl()->isCompatibilityAlias()) {
Callback(*this, aliasTy->getDecl());
} else {
Type(aliasTy->getSinglyDesugaredType()).walk(*this);
}
return Action::SkipNode;
}
/// Returns true if \p paramTy has any constraints other than being
/// class-bound ("conforms to" AnyObject).
static bool isConstrained(GenericSignature sig,
GenericTypeParamType *paramTy) {
auto existentialTy = sig->getExistentialType(paramTy);
return !(existentialTy->isAny() || existentialTy->isAnyObject());
}
Action visitBoundGenericType(BoundGenericType *boundGeneric) override {
auto *decl = boundGeneric->getDecl();
NeedsDefinition = true;
Callback(*this, decl);
NeedsDefinition = false;
bool isObjCGeneric = decl->hasClangNode();
auto sig = decl->getGenericSignature();
for_each(boundGeneric->getGenericArgs(),
sig.getInnermostGenericParams(),
[&](Type argTy, GenericTypeParamType *paramTy) {
// FIXME: I think there's a bug here with recursive generic types.
if (isObjCGeneric && isConstrained(sig, paramTy))
NeedsDefinition = true;
argTy.walk(*this);
NeedsDefinition = false;
});
return Action::SkipNode;
}
public:
bool needsDefinition() const {
return NeedsDefinition;
}
static void walk(Type ty, decltype(Callback) callback) {
ty.walk(ReferencedTypeFinder(callback));
}
};
namespace compare_detail {
enum : int {
Ascending = -1,
Equivalent = 0,
Descending = 1,
};
static StringRef getNameString(const Decl *D) {
if (auto VD = dyn_cast<ValueDecl>(D))
return VD->getBaseName().userFacingName();
if (auto ED = dyn_cast<ExtensionDecl>(D)) {
auto baseClass = ED->getSelfClassDecl();
if (!baseClass)
return ED->getExtendedNominal()->getName().str();
return baseClass->getName().str();
}
llvm_unreachable("unknown top-level ObjC decl");
}
static std::string getLocString(const Decl *afd) {
std::string res;
llvm::raw_string_ostream os(res);
afd->getLoc().print(os, afd->getASTContext().SourceMgr);
return std::move(os.str());
}
static std::string getMangledNameString(const Decl *D) {
auto VD = dyn_cast<ValueDecl>(D);
if (!VD && isa<ExtensionDecl>(D))
VD = cast<ExtensionDecl>(D)->getExtendedNominal();
if (!VD)
return std::string();
Mangle::ASTMangler mangler(VD->getASTContext());
return mangler.mangleAnyDecl(VD, /*addPrefix*/ true);
}
static std::string getTypeString(const ValueDecl *VD) {
return VD->getInterfaceType().getString();
}
static std::string getGenericSignatureString(const Decl *VD) {
if (auto gc = VD->getAsGenericContext())
return gc->getGenericSignature().getAsString();
return "";
}
enum class TypeMatch {
Disfavored,
Neutral,
Favored
};
/// Implements a type check where one declaration must belong to \p FavoredType
/// and the other must belong to \p DisfavoredType , with \p FavoredType
/// sorting later (printing first).
template <typename FavoredType, typename DisfavoredType>
TypeMatch areTypes(Decl *D) {
if (isa<FavoredType>(D))
return TypeMatch::Favored;
if (isa<DisfavoredType>(D))
return TypeMatch::Disfavored;
return TypeMatch::Neutral;
}
/// Implements a type check where one declaration must belong to \p FavoredType
/// and the other must not.
template <typename FavoredType>
TypeMatch isType(Decl *D) {
if (isa<FavoredType>(D))
return TypeMatch::Favored;
return TypeMatch::Disfavored;
}
static int reverseCompare(TypeMatch lhs, TypeMatch rhs) {
if (rhs == TypeMatch::Disfavored && lhs == TypeMatch::Favored)
return Descending;
if (lhs == TypeMatch::Disfavored && rhs == TypeMatch::Favored)
return Ascending;
return Equivalent;
}
static int reverseCompare(bool lhs, bool rhs) {
if (!rhs && lhs)
return Descending;
if (rhs && !lhs)
return Ascending;
return Equivalent;
}
template<typename T,
typename std::enable_if<std::is_integral<T>::value>::type * = nullptr>
static int reverseCompare(const T &lhs, const T &rhs) {
if (lhs != rhs)
return lhs < rhs ? Descending : Ascending;
return Equivalent;
}
static int reverseCompare(StringRef lhs, StringRef rhs) {
return rhs.compare(lhs);
}
static int lastDitchSort(Decl *lhs, Decl *rhs, bool suppressDiagnostic) {
int result = reverseCompare(reinterpret_cast<uintptr_t>(lhs),
reinterpret_cast<uintptr_t>(rhs));
// Sorting with yourself shouldn't happen (but implement consistent behavior
// if this assert is disabled).
ASSERT(result != Equivalent && "sorting should not compare decl to itself");
// Warn that this isn't stable across different compilations.
if (!suppressDiagnostic) {
auto earlier = (result == Ascending) ? lhs : rhs;
auto later = (result == Ascending) ? rhs : lhs;
earlier->diagnose(diag::objc_header_sorting_arbitrary, earlier, later);
later->diagnose(diag::objc_header_sorting_arbitrary_other, earlier, later);
earlier->diagnose(diag::objc_header_sorting_arbitrary_please_report);
}
return result;
}
} // end namespace compare_detail
/// Comparator for use with \c llvm::array_pod_sort() . This sorts decls into
/// reverse order since they will be pushed onto a stack.
static int reverseCompareDecls(Decl * const *lhs, Decl * const *rhs) {
using namespace compare_detail;
assert(*lhs != *rhs && "duplicate top-level decl");
/// Run the LHS and RHS expressions through an appropriate overload of
/// `compare_detail::reverseCompare()`, returning if they are unequal or
/// continuing if they are equal.
#define COMPARE(LHS, RHS) do {\
int result = reverseCompare((LHS), (RHS)); \
if (result != 0) \
return result; \
} while (0)
// When we visit a function, we might also generate a thunk that calls into the
// implementation of structs/enums to get the opaque pointers. To avoid
// referencing these methods before we see the definition for the generated
// classes, we want to visit function definitions last.
COMPARE((areTypes<NominalTypeDecl, AbstractFunctionDecl>(*lhs)),
(areTypes<NominalTypeDecl, AbstractFunctionDecl>(*rhs)));
// Sort by names.
COMPARE(getNameString(*lhs), getNameString(*rhs));
// Two overloaded functions can have the same name when emitting C++.
if (isa<AbstractFunctionDecl>(*rhs) && isa<AbstractFunctionDecl>(*lhs)) {
// Sort top level functions with the same C++ name by their location to
// have stable sorting that depends on users source but not on the
// compiler invocation.
// FIXME: This is pretty suspect; PrintAsClang sometimes operates on
// serialized modules which don't have SourceLocs, so this sort
// rule may be applied in some steps of a build but not others.
if ((*rhs)->getLoc().isValid() && (*lhs)->getLoc().isValid()) {
COMPARE(getLocString(*lhs), getLocString(*rhs));
}
}
// A function and a global variable can have the same name in C++,
// even when the variable might not actually be emitted by the emitter.
// In that case, order the function before the variable.
COMPARE((areTypes<AbstractFunctionDecl, VarDecl>(*lhs)),
(areTypes<AbstractFunctionDecl, VarDecl>(*rhs)));
// Prefer value decls to extensions.
COMPARE(isType<ValueDecl>(*lhs), isType<ValueDecl>(*rhs));
// Last-ditch ValueDecl tiebreaker: Compare mangled names. This captures
// *tons* of context and detail missed by the previous checks, but the
// resulting sort makes little sense to humans.
// FIXME: It'd be nice to share the mangler or even memoize mangled names,
// but we'd have to stop using `llvm::array_pod_sort()` so that we
// could capture some outside state.
COMPARE(getMangledNameString(*lhs), getMangledNameString(*rhs));
// Mangled names ought to distinguish all value decls, leaving only
// extensions of the same nominal type beyond this point.
if (!isa<ExtensionDecl>(*lhs) || !isa<ExtensionDecl>(*rhs))
return lastDitchSort(*lhs, *rhs, /*suppressDiagnostic=*/false);
// Break ties in extensions by putting smaller extensions last (in reverse
// order).
auto lhsMembers = cast<ExtensionDecl>(*lhs)->getAllMembers();
auto rhsMembers = cast<ExtensionDecl>(*rhs)->getAllMembers();
COMPARE(lhsMembers.size(), rhsMembers.size());
// Or the extension with fewer protocols.
auto lhsProtos = cast<ExtensionDecl>(*lhs)->getLocalProtocols();
auto rhsProtos = cast<ExtensionDecl>(*rhs)->getLocalProtocols();
COMPARE(lhsProtos.size(), rhsProtos.size());
// If that fails, arbitrarily pick the extension whose protocols are
// alphabetically first.
{
auto mismatch =
std::mismatch(lhsProtos.begin(), lhsProtos.end(), rhsProtos.begin(),
[] (const ProtocolDecl *nextLHSProto,
const ProtocolDecl *nextRHSProto) {
return nextLHSProto->getName() == nextRHSProto->getName();
});
if (mismatch.first != lhsProtos.end()) {
COMPARE(getNameString(*mismatch.first), getNameString(*mismatch.second));
}
}
// Still nothing? Fine, we'll look for a difference between the members.
{
// First pass: compare names
for (auto pair : llvm::zip_equal(lhsMembers, rhsMembers)) {
auto *lhsMember = dyn_cast<ValueDecl>(std::get<0>(pair)),
*rhsMember = dyn_cast<ValueDecl>(std::get<1>(pair));
if (!lhsMember && !rhsMember)
continue;
COMPARE((bool)lhsMember, (bool)rhsMember);
ASSERT(lhsMember && rhsMember);
COMPARE(getNameString(lhsMember), getNameString(rhsMember));
}
// Second pass: compare other traits.
for (auto pair : llvm::zip_equal(lhsMembers, rhsMembers)) {
auto *lhsMember = dyn_cast<ValueDecl>(std::get<0>(pair)),
*rhsMember = dyn_cast<ValueDecl>(std::get<1>(pair));
if (!lhsMember || !rhsMember)
continue;
COMPARE(getTypeString(lhsMember), getTypeString(rhsMember));
COMPARE(getGenericSignatureString(lhsMember),
getGenericSignatureString(rhsMember));
COMPARE(getMangledNameString(lhsMember), getMangledNameString(rhsMember));
}
}
// Tough customer. Maybe they have different generic signatures?
COMPARE(getGenericSignatureString(*lhs), getGenericSignatureString(*rhs));
// Nothing, sadly.
bool bothEmpty = lhsMembers.empty() && rhsMembers.empty()
&& lhsProtos.empty() && rhsProtos.empty();
return lastDitchSort(*lhs, *rhs, /*suppressDiagnostic=*/bothEmpty);
#undef COMPARE
}
class ModuleWriter {
enum class EmissionState { NotYetDefined = 0, DefinitionRequested, Defined };
raw_ostream &os;
SmallPtrSetImpl<ImportModuleTy> &imports;
ModuleDecl &M;
llvm::DenseMap<const TypeDecl *, std::pair<EmissionState, bool>> seenTypes;
llvm::DenseSet<const clang::Type *> seenClangTypes;
std::vector<const Decl *> declsToWrite;
DelayedMemberSet objcDelayedMembers;
CxxDeclEmissionScope topLevelEmissionScope;
PrimitiveTypeMapping typeMapping;
std::string outOfLineDefinitions;
llvm::raw_string_ostream outOfLineDefinitionsOS;
DeclAndTypePrinter printer;
OutputLanguageMode outputLangMode;
bool dependsOnStdlib = false;
public:
ModuleWriter(raw_ostream &os, raw_ostream &prologueOS,
llvm::SmallPtrSetImpl<ImportModuleTy> &imports, ModuleDecl &mod,
SwiftToClangInteropContext &interopContext, AccessLevel access,
bool requiresExposedAttribute, llvm::StringSet<> &exposedModules,
OutputLanguageMode outputLang)
: os(os), imports(imports), M(mod),
outOfLineDefinitionsOS(outOfLineDefinitions),
printer(M, os, prologueOS, outOfLineDefinitionsOS, objcDelayedMembers,
topLevelEmissionScope, typeMapping, interopContext, access,
requiresExposedAttribute, exposedModules, outputLang),
outputLangMode(outputLang) {}
PrimitiveTypeMapping &getTypeMapping() { return typeMapping; }
/// Returns true if a Stdlib dependency was seen during the emission of this module.
bool isStdlibRequired() const {
return dependsOnStdlib;
}
/// Returns true if we added the decl's module to the import set, false if
/// the decl is a local decl.
///
/// The standard library is special-cased: we assume that any types from it
/// will be handled explicitly rather than needing an explicit @import.
bool addImport(const Decl *D) {
ModuleDecl *otherModule = D->getModuleContext();
if (otherModule == &M)
return false;
if (otherModule->isStdlibModule()) {
dependsOnStdlib = true;
return true;
} else if (otherModule->isBuiltinModule())
return true;
// Don't need a module for SIMD types in C.
if (otherModule->getName() == M.getASTContext().Id_simd)
return true;
// If there's a Clang node, see if it comes from an explicit submodule.
// Import that instead, looking through any implicit submodules.
if (auto clangNode = D->getClangNode()) {
auto importer =
static_cast<ClangImporter *>(M.getASTContext().getClangModuleLoader());
if (const auto *clangModule = importer->getClangOwningModule(clangNode)) {
while (clangModule && !clangModule->IsExplicit)
clangModule = clangModule->Parent;
if (clangModule) {
imports.insert(clangModule);
return true;
}
}
}
if (outputLangMode == OutputLanguageMode::Cxx) {
// Do not expose compiler private '_ObjC' module.
if (otherModule->getName().str() == CLANG_HEADER_MODULE_NAME)
return true;
// Add C++ module imports in C++ mode explicitly, to ensure that their
// import is always emitted in the header.
if (D->hasClangNode()) {
if (auto *clangMod = otherModule->findUnderlyingClangModule())
imports.insert(clangMod);
}
}
if (isa<EnumDecl>(D) && !D->hasClangNode() &&
outputLangMode != OutputLanguageMode::Cxx) {
// We don't want to add an import for a @c or @objc enum declared
// in Swift. We either do nothing for special enums like Optional as
// done in the prologue here, or we forward declare them.
return false;
}
imports.insert(otherModule);
return true;
}
bool hasBeenRequested(const TypeDecl *D) const {
return seenTypes.lookup(D).first >= EmissionState::DefinitionRequested;
}
bool tryRequire(const TypeDecl *D) {
if (addImport(D)) {
seenTypes[D] = { EmissionState::Defined, true };
return true;
}
auto &state = seenTypes[D];
return state.first == EmissionState::Defined;
}
bool require(const TypeDecl *D) { return requireTypes(D, declsToWrite); }
template <typename T>
bool requireTypes(const TypeDecl *D, T &types) {
if (addImport(D)) {
seenTypes[D] = { EmissionState::Defined, true };
return true;
}
auto &state = seenTypes[D];
switch (state.first) {
case EmissionState::NotYetDefined:
case EmissionState::DefinitionRequested:
state.first = EmissionState::DefinitionRequested;
types.push_back(D);
return false;
case EmissionState::Defined:
return true;
}
llvm_unreachable("Unhandled EmissionState in switch.");
}
void forwardDeclare(const NominalTypeDecl *NTD,
llvm::function_ref<void(void)> Printer) {
if (NTD->getModuleContext()->isStdlibModule()) {
if (outputLangMode != OutputLanguageMode::Cxx ||
!printer.shouldInclude(NTD))
return;
}
auto &state = seenTypes[NTD];
if (state.second)
return;
Printer();
state.second = true;
}
bool forwardDeclare(const ClassDecl *CD) {
if (!CD->isObjC() ||
CD->getForeignClassKind() == ClassDecl::ForeignKind::CFType ||
isOSObjectType(CD->getClangDecl())) {
return false;
}
forwardDeclare(CD, [&]{ os << "@class " << getNameForObjC(CD) << ";\n"; });
return true;
}
void forwardDeclare(const ProtocolDecl *PD) {
assert(PD->isObjC() ||
*PD->getKnownProtocolKind() == KnownProtocolKind::Error);
forwardDeclare(PD, [&]{
os << "@protocol " << getNameForObjC(PD) << ";\n";
});
}
void forwardDeclare(const EnumDecl *ED) {
assert(ED->isCCompatibleEnum() || ED->hasClangNode());
forwardDeclare(ED, [&]{
// Forward declare in a way to be compatible with older C standards.
os << "typedef SWIFT_ENUM_FWD_DECL(";
printer.print(ED->getRawType());
os << ", " << getNameForObjC(ED) << ")\n";
});
}
void emitReferencedClangTypeMetadata(const TypeDecl *typeDecl) {
const auto *clangDecl = typeDecl->getClangDecl();
if (const auto *objCInt = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl)) {
auto clangType = clangDecl->getASTContext()
.getObjCInterfaceType(objCInt)
.getCanonicalType();
auto it = seenClangTypes.insert(clangType.getTypePtr());
if (it.second)
ClangValueTypePrinter::printClangTypeSwiftGenericTraits(os, typeDecl, &M,
printer);
return;
}
if (!isa<clang::TypeDecl>(clangDecl))
return;
// Get the underlying clang type from a type alias decl or record decl.
auto clangType = clangDecl->getASTContext()
.getTypeDeclType(cast<clang::TypeDecl>(clangDecl))
.getCanonicalType();
if (!isa<clang::RecordType>(clangType.getTypePtr()))
return;
auto it = seenClangTypes.insert(clangType.getTypePtr());
if (it.second)
ClangValueTypePrinter::printClangTypeSwiftGenericTraits(os, typeDecl, &M,
printer);
}
void forwardDeclareCxxValueTypeIfNeeded(const NominalTypeDecl *NTD) {
forwardDeclare(NTD, [&]() {
ClangValueTypePrinter::forwardDeclType(os, NTD, printer);
});
}
void forwardDeclareType(const TypeDecl *TD) {
if (outputLangMode == OutputLanguageMode::Cxx) {
if (isa<StructDecl>(TD) || isa<EnumDecl>(TD) || isa<ClassDecl>(TD)) {
auto *NTD = cast<NominalTypeDecl>(TD);
if (!addImport(NTD))
forwardDeclareCxxValueTypeIfNeeded(NTD);
else if (isa<StructDecl>(TD) && NTD->hasClangNode())
emitReferencedClangTypeMetadata(NTD);
else if (const auto *cd = dyn_cast<ClassDecl>(TD))
if ((cd->isObjC() && cd->getClangDecl()) ||
cd->isForeignReferenceType())
emitReferencedClangTypeMetadata(NTD);
} else if (auto TAD = dyn_cast<TypeAliasDecl>(TD)) {
if (TAD->hasClangNode())
emitReferencedClangTypeMetadata(TAD);
}
return;
}
if (auto CD = dyn_cast<ClassDecl>(TD)) {
if (!forwardDeclare(CD)) {
(void)addImport(CD);
}
} else if (auto PD = dyn_cast<ProtocolDecl>(TD)) {
if (!PD->isMarkerProtocol())
forwardDeclare(PD);
} else if (auto TAD = dyn_cast<TypeAliasDecl>(TD)) {
bool imported = false;
if (TAD->hasClangNode())
imported = addImport(TD);
assert((imported || !TAD->hasGenericParamList()) &&
"referencing non-imported generic typealias?");
} else if (addImport(TD)) {
return;
} else if (auto ED = dyn_cast<EnumDecl>(TD)) {
// Treat this after addImport to filter out special enums from the stdlib.
forwardDeclare(ED);
} else if (isa<GenericTypeParamDecl>(TD)) {
llvm_unreachable("should not see generic parameters here");
} else if (isa<AssociatedTypeDecl>(TD)) {
llvm_unreachable("should not see associated types here");
} else if (isa<StructDecl>(TD) &&
TD->getModuleContext()->isStdlibModule()) {
// stdlib has some @_cdecl functions with structs.
return;
} else {
assert(false && "unknown local type decl");
}
}
bool forwardDeclareMemberTypes(ArrayRef<Decl *> members,
const Decl *container) {
PrettyStackTraceDecl
entry("printing forward declarations needed by members of", container);
switch (container->getKind()) {
case DeclKind::Class:
case DeclKind::Protocol:
case DeclKind::Extension:
break;
case DeclKind::Struct:
case DeclKind::Enum:
if (outputLangMode == OutputLanguageMode::Cxx)
break;
LLVM_FALLTHROUGH;
default:
llvm_unreachable("unexpected container kind");
}
bool hadAnyDelayedMembers = false;
SmallVector<const ValueDecl *, 4> nestedTypes;
for (auto member : members) {
PrettyStackTraceDecl loopEntry("printing for member", member);
auto VD = dyn_cast<ValueDecl>(member);
if (!VD || !printer.shouldInclude(VD))
continue;
// Catch nested types and emit their definitions /after/ this class.
if (const auto *TD = dyn_cast<TypeDecl>(VD)) {
if (outputLangMode == OutputLanguageMode::Cxx) {
if (!isa<TypeAliasDecl>(TD) && !isStringNestedType(VD, "UTF8View") &&
!isStringNestedType(VD, "Index")) {
forwardDeclareType(TD);
requireTypes(TD, nestedTypes);
}
} else {
// Don't emit nested types that are just implicitly @objc.
// You should have to opt into this, since they are even less
// namespaced than usual.
if (std::any_of(VD->getAttrs().begin(), VD->getAttrs().end(),
[](const DeclAttribute *attr) {
return isa<ObjCAttr>(attr) && !attr->isImplicit();
})) {
nestedTypes.push_back(VD);
}
}
continue;
}
addImportsForReferencedAvailabilityDomains(member);
bool needsToBeIndividuallyDelayed = false;
ReferencedTypeFinder::walk(VD->getInterfaceType(),
[&](ReferencedTypeFinder &finder,
const TypeDecl *TD) {
PrettyStackTraceDecl
entry("walking its interface type, currently at", TD);
if (TD == container)
return;
// Bridge, if necessary.
if (outputLangMode != OutputLanguageMode::Cxx)
TD = printer.getObjCTypeDecl(TD);
if (finder.needsDefinition() && isa<NominalTypeDecl>(TD)) {
// We can delay individual members of classes; do so if necessary.
if (isa<ClassDecl>(container)) {
if (!tryRequire(TD)) {
needsToBeIndividuallyDelayed = true;
hadAnyDelayedMembers = true;
}
return;
}
// Extensions can always be delayed wholesale.
if (isa<ExtensionDecl>(container)) {
if (!require(TD))
hadAnyDelayedMembers = true;
return;
}
// Protocols should be delayed wholesale unless we might have a cycle.
if (auto *proto = dyn_cast<ProtocolDecl>(container)) {
if (!hasBeenRequested(proto) || !hasBeenRequested(TD)) {
if (!require(TD))
hadAnyDelayedMembers = true;
return;
}
}
// Otherwise, we have a cyclic dependency. Give up and continue with
// regular forward-declarations even though this will lead to an
// error; there's nothing we can do here.
// FIXME: It would be nice to diagnose this.
}
forwardDeclareType(TD);
});
if (needsToBeIndividuallyDelayed) {
assert(isa<ClassDecl>(container));
objcDelayedMembers.insert(VD);
}
}
declsToWrite.insert(declsToWrite.end()-1, nestedTypes.rbegin(),
nestedTypes.rend());
// Separate forward declarations from the class itself.
return !hadAnyDelayedMembers;
}
void addImportsForReferencedAvailabilityDomains(const Decl *D) {
for (auto attr : D->getSemanticAvailableAttrs()) {
if (auto *domainDecl = attr.getDomain().getDecl()) {
if (domainDecl->hasClangNode())
addImport(domainDecl);
}
}
}
bool writeClass(const ClassDecl *CD) {
if (addImport(CD))
return true;
if (seenTypes[CD].first == EmissionState::Defined)
return true;
bool allRequirementsSatisfied = true;
const ClassDecl *superclass = nullptr;
if ((superclass = CD->getSuperclassDecl())) {
allRequirementsSatisfied &= require(superclass);
}
if (outputLangMode != OutputLanguageMode::Cxx) {
for (auto proto :
CD->getLocalProtocols(ConformanceLookupKind::OnlyExplicit))
if (printer.shouldInclude(proto))
allRequirementsSatisfied &= require(proto);
}
if (!allRequirementsSatisfied)
return false;
(void)forwardDeclareMemberTypes(CD->getAllMembers(), CD);
for (const auto *ed :
printer.getInteropContext().getExtensionsForNominalType(CD)) {
(void)forwardDeclareMemberTypes(ed->getAllMembers(), CD);
}
auto [it, inserted] =
seenTypes.try_emplace(CD, EmissionState::NotYetDefined, false);
if (outputLangMode == OutputLanguageMode::Cxx &&
(inserted || !it->second.second))
ClangValueTypePrinter::forwardDeclType(os, CD, printer);
addImportsForReferencedAvailabilityDomains(CD);
it->second = {EmissionState::Defined, true};
printer.print(CD);
return true;
}
bool writeFunc(const FuncDecl *FD) {
if (addImport(FD))
return true;
PrettyStackTraceDecl entry(
"printing forward declarations needed by function", FD);
ReferencedTypeFinder::walk(
FD->getInterfaceType(),
[&](ReferencedTypeFinder &finder, const TypeDecl *TD) {
PrettyStackTraceDecl entry("walking its interface type, currently at",
TD);
forwardDeclareType(TD);
});
addImportsForReferencedAvailabilityDomains(FD);
printer.print(FD);
return true;
}
bool writeStruct(const StructDecl *SD) {
if (addImport(SD))
return true;
if (outputLangMode == OutputLanguageMode::Cxx) {
(void)forwardDeclareMemberTypes(SD->getAllMembers(), SD);
for (const auto *ed :
printer.getInteropContext().getExtensionsForNominalType(SD)) {
(void)forwardDeclareMemberTypes(ed->getAllMembers(), SD);
}
forwardDeclareCxxValueTypeIfNeeded(SD);
}
addImportsForReferencedAvailabilityDomains(SD);
printer.print(SD);
return true;
}
bool writeProtocol(const ProtocolDecl *PD) {
if (addImport(PD))
return true;
if (seenTypes[PD].first == EmissionState::Defined)
return true;
bool allRequirementsSatisfied = true;
for (auto proto : PD->getInheritedProtocols()) {
if (printer.shouldInclude(proto)) {
assert(proto->isObjC());
allRequirementsSatisfied &= require(proto);
}
}
if (!allRequirementsSatisfied)
return false;
if (!forwardDeclareMemberTypes(PD->getAllMembers(), PD))
return false;
addImportsForReferencedAvailabilityDomains(PD);
seenTypes[PD] = { EmissionState::Defined, true };
printer.print(PD);
return true;
}
bool writeExtension(const ExtensionDecl *ED) {
if (printer.isEmptyExtensionDecl(ED))
return true;
bool allRequirementsSatisfied = true;
const ClassDecl *CD = ED->getSelfClassDecl();
allRequirementsSatisfied &= require(CD);
for (auto proto : ED->getLocalProtocols())
if (printer.shouldInclude(proto))
allRequirementsSatisfied &= require(proto);
if (!allRequirementsSatisfied)
return false;
// This isn't rolled up into the previous set of requirements because
// it /also/ prints forward declarations, and the header is a little
// prettier if those are as close as possible to the necessary extension.
if (!forwardDeclareMemberTypes(ED->getAllMembers(), ED))
return false;
addImportsForReferencedAvailabilityDomains(ED);
printer.print(ED);
return true;
}
bool writeEnum(const EnumDecl *ED) {
if (addImport(ED))
return true;
if (outputLangMode == OutputLanguageMode::Cxx) {
forwardDeclareMemberTypes(ED->getAllMembers(), ED);
for (const auto *ed :
printer.getInteropContext().getExtensionsForNominalType(ED)) {
(void)forwardDeclareMemberTypes(ed->getAllMembers(), ED);
}
forwardDeclareCxxValueTypeIfNeeded(ED);
}
if (seenTypes[ED].first == EmissionState::Defined)
return true;
addImportsForReferencedAvailabilityDomains(ED);
seenTypes[ED] = {EmissionState::Defined, true};
printer.print(ED);
ASTContext &ctx = M.getASTContext();
SmallVector<ProtocolConformance *, 1> conformances;
auto errorTypeProto = ctx.getProtocol(KnownProtocolKind::Error);
if (outputLangMode == OutputLanguageMode::ObjC
&& ED->lookupConformance(errorTypeProto, conformances)) {
bool hasDomainCase = std::any_of(ED->getAllElements().begin(),
ED->getAllElements().end(),
[](const EnumElementDecl *elem) {
return elem->getBaseIdentifier().str() == "Domain";
});
if (!hasDomainCase) {
os << "static NSString * _Nonnull const " << getNameForObjC(ED)
<< "Domain = @\"" << getErrorDomainStringForObjC(ED) << "\";\n";
}
}
return true;
}
void write() {
SmallVector<Decl *, 64> decls;
M.getTopLevelDeclsWithAuxiliaryDecls(decls);
llvm::DenseSet<const ValueDecl *> removedValueDecls;
auto newEnd =
std::remove_if(decls.begin(), decls.end(),
[this, &removedValueDecls](const Decl *D) -> bool {
if (auto VD = dyn_cast<ValueDecl>(D)) {
auto shouldRemove = !printer.shouldInclude(VD);
if (shouldRemove)
removedValueDecls.insert(VD);
return shouldRemove;
}
if (auto ED = dyn_cast<ExtensionDecl>(D)) {
if (outputLangMode == OutputLanguageMode::Cxx)
return false;
auto baseClass = ED->getSelfClassDecl();
return !baseClass ||
!printer.shouldInclude(baseClass) ||
baseClass->isForeign();
}
return true;
});
decls.erase(newEnd, decls.end());
if (M.isStdlibModule()) {
llvm::SmallVector<Decl *, 2> nestedAdds;
for (const auto *d : decls) {
auto *ext = dyn_cast<ExtensionDecl>(d);
if (!ext ||
ext->getExtendedNominal() != M.getASTContext().getStringDecl())
continue;
for (auto *m : ext->getAllMembers()) {
if (auto *sd = dyn_cast<StructDecl>(m)) {
if (sd->getBaseIdentifier().str() == "UTF8View" ||
sd->getBaseIdentifier().str() == "Index") {
nestedAdds.push_back(sd);
}
}
}
}
decls.append(nestedAdds);
}
// REVERSE sort the decls, since we are going to copy them onto a stack.
llvm::array_pod_sort(decls.begin(), decls.end(), &reverseCompareDecls);
assert(declsToWrite.empty());
declsToWrite.assign(decls.begin(), decls.end());
if (outputLangMode == OutputLanguageMode::Cxx) {
for (const Decl *D : declsToWrite) {
if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
const auto *type = ED->getExtendedNominal();
if (isa<StructDecl>(type) || isa<EnumDecl>(type))
printer.getInteropContext().recordExtensions(type, ED);
}
}
}
while (!declsToWrite.empty()) {
const Decl *D = declsToWrite.back();
bool success = true;
auto posBefore = os.tell();
if (auto ED = dyn_cast<EnumDecl>(D)) {
success = writeEnum(ED);
} else if (auto CD = dyn_cast<ClassDecl>(D)) {
success = writeClass(CD);
} else if (outputLangMode == OutputLanguageMode::Cxx) {
if (auto FD = dyn_cast<FuncDecl>(D))
success = writeFunc(FD);
else if (auto SD = dyn_cast<StructDecl>(D))
success = writeStruct(SD);
else if (auto *vd = dyn_cast<ValueDecl>(D))
topLevelEmissionScope.additionalUnrepresentableDeclarations.push_back(
vd);
} else if (isa<ValueDecl>(D)) {
if (auto PD = dyn_cast<ProtocolDecl>(D))
success = writeProtocol(PD);
else if (auto ED = dyn_cast<FuncDecl>(D))
success = writeFunc(ED);
else
llvm_unreachable("unknown top-level ObjC value decl");
} else if (auto ED = dyn_cast<ExtensionDecl>(D)) {
success = writeExtension(ED);
} else {
llvm_unreachable("unknown top-level ObjC decl");
}
if (success) {
assert(declsToWrite.back() == D);
// If we actually wrote something to the file, add a newline after it.
// (As opposed to, for instance, an extension we decided to skip.)
if (posBefore != os.tell())
os << "\n";
declsToWrite.pop_back();
}
}
if (outputLangMode == OutputLanguageMode::ObjC)
if (!objcDelayedMembers.empty()) {
auto groupBegin = objcDelayedMembers.begin();
for (auto i = groupBegin, e = objcDelayedMembers.end(); i != e; ++i) {
if ((*i)->getDeclContext() != (*groupBegin)->getDeclContext()) {
printer.printAdHocCategory(make_range(groupBegin, i));
groupBegin = i;
}
}
printer.printAdHocCategory(
make_range(groupBegin, objcDelayedMembers.end()));
}
// Print any out of line definitions.
os << outOfLineDefinitionsOS.str();
// In C++ section, emit unavailable stubs for top value level
// declarations that couldn't be represented in C++.
if (outputLangMode != OutputLanguageMode::Cxx)
return;
auto &emissionScope = topLevelEmissionScope;
auto removedVDList = std::vector<const ValueDecl *>(
removedValueDecls.begin(), removedValueDecls.end());
for (const auto *removedVD :
emissionScope.additionalUnrepresentableDeclarations)
removedVDList.push_back(removedVD);
// Do not report internal/private decls as unavailable.
// @objc declarations are emitted in the Objective-C section, so do not
// report them as unavailable. Also skip underscored decls from the standard
// library. Also skip structs from the standard library, they can cause
// ambiguities because of the arithmetic types that conflict with types we
// already have in `swift::` namespace. Also skip `Error` protocol from
// stdlib, we have experimental support for it. Also skip explicitly exluded
// declarations.
removedVDList.erase(
llvm::remove_if(
removedVDList,
[&](const ValueDecl *vd) {
return !printer.isVisible(vd) || vd->isObjC() ||
(vd->isStdlibDecl() && !vd->getName().isSpecial() &&
vd->getBaseIdentifier().hasUnderscoredNaming()) ||
(vd->isStdlibDecl() && isa<StructDecl>(vd)) ||
(vd->isStdlibDecl() &&
vd->getASTContext().getErrorDecl() == vd) ||
swift::hasExposeNotCxxAttr(vd);
}),
removedVDList.end());
// Sort the unavaiable decls by their name and kind.
llvm::sort(removedVDList, [](const ValueDecl *lhs, const ValueDecl *rhs) {
auto getSortKey = [](const ValueDecl *vd) {
std::string sortKey;
llvm::raw_string_ostream os(sortKey);
vd->getName().print(os);
os << ' ' << (unsigned)vd->getDescriptiveKind();
return std::move(os.str());
};
return getSortKey(lhs) < getSortKey(rhs);
});
for (const auto *vd : removedVDList) {
assert(!vd->isObjC());
os << "\n";
auto emitStubComment = [&]() {
// Emit a generic comment for an handled declaration.
os << "// Unavailable in C++: Swift "
<< vd->getDescriptiveKindName(vd->getDescriptiveKind()) << " '";
vd->getName().print(os);
os << "'.\n";
};
// Do not emit a C++ declaration with a specific C++ name more than once.
auto cxxName = cxx_translation::getNameForCxx(vd);
if (emissionScope.emittedDeclarationNames.contains(cxxName)) {
emitStubComment();
continue;
}
emissionScope.emittedDeclarationNames.insert(cxxName);
// Emit an unavailable stub for a Swift type.
if (auto *nmtd = dyn_cast<NominalTypeDecl>(vd)) {
auto representation = cxx_translation::getDeclRepresentation(
vd, [this](const NominalTypeDecl *decl) {
return printer.isZeroSized(decl);
});
if (nmtd->hasGenericParamList()) {
auto genericSignature =
nmtd->getGenericSignature().getCanonicalSignature();
ClangSyntaxPrinter(nmtd->getASTContext(), os).printGenericSignature(genericSignature);
}
os << "class ";
ClangSyntaxPrinter(nmtd->getASTContext(), os).printBaseName(vd);
os << " { } SWIFT_UNAVAILABLE_MSG(\"";
auto diag =
representation.isUnsupported() && representation.error.has_value()
? cxx_translation::diagnoseRepresenationError(
*representation.error, const_cast<ValueDecl *>(vd))
: Diagnostic(
vd->isStdlibDecl() ? diag::unexposed_other_decl_in_cxx
: diag::unsupported_other_decl_in_cxx,
const_cast<ValueDecl *>(vd));
// Emit a specific unavailable message when we know why a decl can't be
// exposed, or a generic message otherwise.
auto diagString =
M.getASTContext().Diags.getFormatStringForDiagnostic(diag.getID());
DiagnosticEngine::formatDiagnosticText(os, diagString, diag.getArgs(),
DiagnosticFormatOptions());
os << "\");\n";
continue;
}
// FIXME: Emit an unavailable stub for a function / function overload set
// / variable.
// FIXME: Note unrepresented type aliases too.
emitStubComment();
}
}
};
} // end anonymous namespace
static AccessLevel getRequiredAccess(const ModuleDecl &M,
std::optional<AccessLevel> minAccess) {
if (minAccess)
return *minAccess;
return M.isExternallyConsumed() ? AccessLevel::Public : AccessLevel::Internal;
}
void swift::printModuleContentsAsObjC(
raw_ostream &os, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
ModuleDecl &M, SwiftToClangInteropContext &interopContext,
std::optional<AccessLevel> minAccess) {
llvm::raw_null_ostream prologueOS;
llvm::StringSet<> exposedModules;
ModuleWriter(os, prologueOS, imports, M, interopContext,
getRequiredAccess(M, minAccess),
/*requiresExposedAttribute=*/false, exposedModules,
OutputLanguageMode::ObjC)
.write();
}
void swift::printModuleContentsAsC(
raw_ostream &os, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
ModuleDecl &M, SwiftToClangInteropContext &interopContext,
std::optional<AccessLevel> minAccess) {
llvm::raw_null_ostream prologueOS;
llvm::StringSet<> exposedModules;
ModuleWriter(os, prologueOS, imports, M, interopContext,
getRequiredAccess(M, minAccess),
/*requiresExposedAttribute=*/false, exposedModules,
OutputLanguageMode::C)
.write();
}
EmittedClangHeaderDependencyInfo swift::printModuleContentsAsCxx(
raw_ostream &os, ModuleDecl &M, SwiftToClangInteropContext &interopContext,
AccessLevel minAccess, bool requiresExposedAttribute,
llvm::StringSet<> &exposedModules) {
std::string moduleContentsBuf;
llvm::raw_string_ostream moduleOS{moduleContentsBuf};
std::string modulePrologueBuf;
llvm::raw_string_ostream prologueOS{modulePrologueBuf};
EmittedClangHeaderDependencyInfo info;
auto &context = M.getASTContext();
// Define the `SWIFT_SYMBOL` macro.
os << "#ifdef SWIFT_SYMBOL\n";
os << "#undef SWIFT_SYMBOL\n";
os << "#endif\n";
os << "#define SWIFT_SYMBOL(usrValue) SWIFT_SYMBOL_MODULE_USR(\"";
ClangSyntaxPrinter(context, os).printBaseName(&M);
os << "\", usrValue)\n";
// FIXME: Use getRequiredAccess once @expose is supported.
ModuleWriter writer(moduleOS, prologueOS, info.imports, M, interopContext,
minAccess, requiresExposedAttribute, exposedModules,
OutputLanguageMode::Cxx);
writer.write();
info.dependsOnStandardLibrary = writer.isStdlibRequired();
if (M.isStdlibModule()) {
// Embed additional STL includes.
os << "#ifndef SWIFT_CXX_INTEROP_HIDE_STL_OVERLAY\n";
os << "#include <string>\n";
os << "#endif\n";
os << "#include <new>\n";
if (context.LangOpts.hasFeature(Feature::Embedded))
os << "#define __EmbeddedSwift__\n";
// Embed an overlay for the standard library.
ClangSyntaxPrinter(context, moduleOS)
.printIncludeForShimHeader("_SwiftStdlibCxxOverlay.h");
// Ignore typos in Swift stdlib doc comments.
os << "#pragma clang diagnostic push\n";
os << "#pragma clang diagnostic ignored \"-Wdocumentation\"\n";
}
os << "#ifndef SWIFT_PRINTED_CORE\n";
os << "#define SWIFT_PRINTED_CORE\n";
printSwiftToClangCoreScaffold(interopContext, context,
writer.getTypeMapping(), os);
os << "#endif\n";
// FIXME: refactor.
if (!prologueOS.str().empty()) {
// FIXME: This is a workaround for prologue being emitted outside of
// __cplusplus.
if (!M.isStdlibModule())
os << "#endif\n";
os << "#ifdef __cplusplus\n";
os << "namespace ";
ClangSyntaxPrinter(context, os).printBaseName(&M);
os << " SWIFT_PRIVATE_ATTR";
ClangSyntaxPrinter(context, os).printSymbolUSRAttribute(&M);
os << " {\n";
os << "namespace " << cxx_synthesis::getCxxImplNamespaceName() << " {\n";
os << "extern \"C\" {\n";
os << "#endif\n\n";
os << prologueOS.str();
if (!M.isStdlibModule())
os << "\n#ifdef __cplusplus\n";
os << "}\n";
os << "}\n";
os << "}\n";
}
os << "#pragma clang diagnostic push\n";
os << "#pragma clang diagnostic ignored \"-Wreserved-identifier\"\n";
// Construct a C++ namespace for the module.
ClangSyntaxPrinter(context, os)
.printNamespace(
[&](raw_ostream &os) {
ClangSyntaxPrinter(context, os).printBaseName(&M);
},
[&](raw_ostream &os) { os << moduleOS.str(); },
ClangSyntaxPrinter::NamespaceTrivia::AttributeSwiftPrivate, &M);
os << "#pragma clang diagnostic pop\n";
if (M.isStdlibModule()) {
os << "#pragma clang diagnostic pop\n";
}
os << "#undef SWIFT_SYMBOL\n";
return info;
}