//===-- PrintAsObjC.cpp - Emit a header file for a Swift AST --------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/PrintAsObjC/PrintAsObjC.h" #include "swift/Strings.h" #include "swift/AST/AST.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/TypeVisitor.h" #include "swift/AST/Comment.h" #include "swift/Basic/Version.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/Frontend/Frontend.h" #include "swift/Frontend/PrintingDiagnosticConsumer.h" #include "swift/IDE/CommentConversion.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/Basic/Module.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" using namespace swift; #ifndef SWIFT_DISABLE_OBJC_GENERICS static bool isNSObject(Type type) { if (auto classDecl = type->getClassOrBoundGenericClass()) { return !classDecl->getName().empty() && classDecl->getName().str() == "NSObject" && !classDecl->getModuleContext()->Name.empty() && classDecl->getModuleContext()->Name.str() == "ObjectiveC"; } return false; } #endif namespace { // Key struct for the special names map. struct SpecialName { Identifier module, parent, type; }; } namespace llvm { template<> struct DenseMapInfo { using IdentifierInfo = DenseMapInfo; static inline SpecialName getEmptyKey() { return {IdentifierInfo::getEmptyKey(), IdentifierInfo::getEmptyKey(), IdentifierInfo::getEmptyKey()}; } static inline SpecialName getTombstoneKey() { return {IdentifierInfo::getTombstoneKey(), IdentifierInfo::getTombstoneKey(), IdentifierInfo::getTombstoneKey()}; } static unsigned getHashValue(SpecialName key) { return llvm::hash_combine(IdentifierInfo::getHashValue(key.module), IdentifierInfo::getHashValue(key.parent), IdentifierInfo::getHashValue(key.type)); } static bool isEqual(SpecialName a, SpecialName b) { return a.module == b.module && a.parent == b.parent && a.type == b.type; } }; } namespace { class ObjCPrinter : private DeclVisitor, private TypeVisitor> { friend ASTVisitor; friend TypeVisitor; friend struct llvm::DenseMapInfo; llvm::DenseMap> specialNames; Identifier ID_CFTypeRef; ASTContext &ctx; raw_ostream &os; SmallVector openFunctionTypes; Accessibility minRequiredAccess; bool protocolMembersOptional = false; friend ASTVisitor; friend TypeVisitor; public: explicit ObjCPrinter(ASTContext &context, raw_ostream &out, Accessibility access) : ctx(context), os(out), minRequiredAccess(access) {} void print(const Decl *D) { visit(const_cast(D)); } bool shouldInclude(const ValueDecl *VD) { return VD->isObjC() && VD->getFormalAccess() >= minRequiredAccess && !(isa(VD) && cast(VD)->hasStubImplementation()); } private: using ASTVisitor::visit; /// Prints a protocol adoption list: <NSCoding, NSCopying> /// /// This method filters out non-ObjC protocols, along with the special /// AnyObject protocol. void printProtocols(ArrayRef protos) { SmallVector protosToPrint; std::copy_if(protos.begin(), protos.end(), std::back_inserter(protosToPrint), [this](const ProtocolDecl *PD) -> bool { if (!shouldInclude(PD)) return false; auto knownProtocol = PD->getKnownProtocolKind(); if (!knownProtocol) return true; return *knownProtocol != KnownProtocolKind::AnyObject; }); if (protosToPrint.empty()) return; os << " <"; interleave(protosToPrint, [this](const ProtocolDecl *PD) { if (PD->hasClangNode()) { SmallString<64> buf; os << PD->getObjCRuntimeName(buf); } else { os << PD->getName().str(); } }, [this] { os << ", "; }); os << ">"; } /// Prints the members of a class, extension, or protocol. void printMembers(DeclRange members) { for (auto member : members) { auto VD = dyn_cast(member); if (!VD || !shouldInclude(VD) || isa(VD)) continue; if (auto FD = dyn_cast(VD)) if (FD->isAccessor()) continue; if (VD->getAttrs().hasAttribute() != protocolMembersOptional) { protocolMembersOptional = VD->getAttrs().hasAttribute(); os << (protocolMembersOptional ? "@optional\n" : "@required\n"); } visit(VD); } } void printDocumentationComment(Decl *D) { llvm::markup::MarkupContext MC; auto DC = getDocComment(MC, D); if (DC.hasValue()) ide::getDocumentationCommentAsDoxygen(DC.getValue(), os); } // Ignore other declarations. void visitDecl(Decl *D) {} void visitClassDecl(ClassDecl *CD) { printDocumentationComment(CD); llvm::SmallString<32> scratch; os << "SWIFT_CLASS(\"" << CD->getObjCRuntimeName(scratch) << "\")\n" << "@interface " << CD->getName(); if (Type superTy = CD->getSuperclass()) os << " : " << superTy->getClassOrBoundGenericClass()->getName(); printProtocols(CD->getLocalProtocols(ConformanceLookupKind::OnlyExplicit)); os << "\n"; printMembers(CD->getMembers()); os << "@end\n"; } void visitExtensionDecl(ExtensionDecl *ED) { auto baseClass = ED->getExtendedType()->getClassOrBoundGenericClass(); os << "@interface " << baseClass->getName() << " (SWIFT_EXTENSION(" << ED->getModuleContext()->Name << "))"; printProtocols(ED->getLocalProtocols(ConformanceLookupKind::OnlyExplicit)); os << "\n"; printMembers(ED->getMembers()); os << "@end\n"; } void visitProtocolDecl(ProtocolDecl *PD) { printDocumentationComment(PD); llvm::SmallString<32> scratch; os << "SWIFT_PROTOCOL(\"" << PD->getObjCRuntimeName(scratch) << "\")\n" << "@protocol " << PD->getName(); printProtocols(PD->getInheritedProtocols(nullptr)); os << "\n"; assert(!protocolMembersOptional && "protocols start required"); printMembers(PD->getMembers()); protocolMembersOptional = false; os << "@end\n"; } void visitEnumDecl(EnumDecl *ED) { printDocumentationComment(ED); llvm::SmallString<32> scratch; os << "typedef SWIFT_ENUM("; print(ED->getRawType(), OTK_None); os << ", " << ED->getName() << ") {\n"; for (auto Elt : ED->getAllElements()) { printDocumentationComment(Elt); // Print the cases as the concatenation of the enum name with the case // name. os << " " << ED->getName() << Elt->getName(); if (auto ILE = cast_or_null(Elt->getRawValueExpr())) { os << " = "; if (ILE->isNegative()) os << "-"; os << ILE->getDigitsText(); } os << ",\n"; } os << "};\n"; } void printSingleMethodParam(StringRef selectorPiece, const Pattern *param, const clang::ParmVarDecl *clangParam, bool isNSUIntegerSubscript, bool isLastPiece) { os << selectorPiece << ":("; if ((isNSUIntegerSubscript && isLastPiece) || (clangParam && isNSUInteger(clangParam->getType()))) { os << "NSUInteger"; } else { this->print(param->getType(), OTK_None); } os << ")"; if (isa(param)) os << "_"; else os << cast(param)->getBodyName(); } template static const T *findClangBase(const T *member) { while (member) { if (member->getClangDecl()) return member; member = member->getOverriddenDecl(); } return nullptr; } /// Returns true if \p clangTy is the typedef for NSUInteger. bool isNSUInteger(clang::QualType clangTy) { const auto *typedefTy = dyn_cast(clangTy); if (!typedefTy) return false; const clang::IdentifierInfo *nameII = typedefTy->getDecl()->getIdentifier(); if (!nameII) return false; if (nameII->getName() != "NSUInteger") return false; return true; } void printAbstractFunction(AbstractFunctionDecl *AFD, bool isClassMethod, bool isNSUIntegerSubscript = false) { printDocumentationComment(AFD); if (isClassMethod) os << "+ ("; else os << "- ("; const clang::ObjCMethodDecl *clangMethod = nullptr; if (!isNSUIntegerSubscript) { if (const AbstractFunctionDecl *clangBase = findClangBase(AFD)) { clangMethod = dyn_cast_or_null(clangBase->getClangDecl()); } } Type rawMethodTy = AFD->getType()->castTo()->getResult(); auto methodTy = rawMethodTy->castTo(); // A foreign error convention can affect the result type as seen in // Objective-C. Optional errorConvention = AFD->getForeignErrorConvention(); Type resultTy = methodTy->getResult(); if (errorConvention) { switch (errorConvention->getKind()) { case ForeignErrorConvention::ZeroResult: case ForeignErrorConvention::NonZeroResult: // The error convention provides the result type. resultTy = errorConvention->getResultType(); break; case ForeignErrorConvention::NilResult: // Errors are propagated via 'nil' returns. resultTy = OptionalType::get(resultTy); break; case ForeignErrorConvention::NonNilError: break; } } // Constructors and methods returning DynamicSelf return // instancetype. if (isa(AFD) || (isa(AFD) && cast(AFD)->hasDynamicSelf())) { if (errorConvention && errorConvention->stripsResultOptionality()) { printNullability(OTK_Optional, NullabilityPrintKind::ContextSensitive); } else if (auto ctor = dyn_cast(AFD)) { printNullability(ctor->getFailability(), NullabilityPrintKind::ContextSensitive); } else { auto func = cast(AFD); OptionalTypeKind optionalKind; (void)func->getResultType()->getAnyOptionalObjectType(optionalKind); printNullability(optionalKind, NullabilityPrintKind::ContextSensitive); } os << "instancetype"; } else if (resultTy->isVoid() && AFD->getAttrs().hasAttribute()) { os << "IBAction"; } else if (clangMethod && isNSUInteger(clangMethod->getReturnType())) { os << "NSUInteger"; } else { print(resultTy, OTK_None); } os << ")"; auto bodyPatterns = AFD->getBodyParamPatterns(); assert(bodyPatterns.size() == 2 && "not an ObjC-compatible method"); llvm::SmallString<128> selectorBuf; ArrayRef selectorPieces = AFD->getObjCSelector().getSelectorPieces(); const TuplePattern *paramTuple = dyn_cast(bodyPatterns.back()); const ParenPattern *paramParen = dyn_cast(bodyPatterns.back()); assert((paramTuple || paramParen) && "Bad body parameters?"); unsigned paramIndex = 0; for (unsigned i = 0, n = selectorPieces.size(); i != n; ++i) { if (i > 0) os << ' '; // Retrieve the selector piece. StringRef piece = selectorPieces[i].empty() ? StringRef("") : selectorPieces[i].str(); // If we have an error convention and this is the error // parameter, print it. if (errorConvention && i == errorConvention->getErrorParameterIndex()) { os << piece << ":("; print(errorConvention->getErrorParameterType(), OTK_None); os << ")error"; continue; } // Zero-parameter initializers with a long selector. if (isa(AFD) && cast(AFD)->isObjCZeroParameterWithLongSelector()) { os << piece; continue; } // Single-parameter methods. if (paramParen) { assert(paramIndex == 0); auto clangParam = clangMethod ? clangMethod->parameters()[0] : nullptr; printSingleMethodParam(piece, paramParen->getSemanticsProvidingPattern(), clangParam, isNSUIntegerSubscript, i == n-1); paramIndex = 1; continue; } // Zero-parameter methods. if (paramTuple->getNumElements() == 0) { assert(paramIndex == 0); os << piece; paramIndex = 1; continue; } // Multi-parameter methods. const clang::ParmVarDecl *clangParam = nullptr; if (clangMethod) clangParam = clangMethod->parameters()[paramIndex]; const TuplePatternElt ¶m = paramTuple->getElements()[paramIndex]; auto pattern = param.getPattern()->getSemanticsProvidingPattern(); printSingleMethodParam(piece, pattern, clangParam, isNSUIntegerSubscript, i == n-1); ++paramIndex; } // Swift designated initializers are Objective-C designated initializers. if (auto ctor = dyn_cast(AFD)) { if (ctor->isDesignatedInit() && !isa(ctor->getDeclContext())) { os << " OBJC_DESIGNATED_INITIALIZER"; } } os << ";\n"; } void visitFuncDecl(FuncDecl *FD) { assert(FD->getDeclContext()->isTypeContext() && "cannot handle free functions right now"); printAbstractFunction(FD, FD->isStatic()); } void visitConstructorDecl(ConstructorDecl *CD) { printAbstractFunction(CD, false); } bool maybePrintIBOutletCollection(Type ty) { if (auto unwrapped = ty->getAnyOptionalObjectType()) ty = unwrapped; auto genericTy = ty->getAs(); if (!genericTy || genericTy->getDecl() != ctx.getArrayDecl()) return false; assert(genericTy->getGenericArgs().size() == 1); auto argTy = genericTy->getGenericArgs().front(); if (auto classDecl = argTy->getClassOrBoundGenericClass()) os << "IBOutletCollection(" << classDecl->getName() << ") "; else os << "IBOutletCollection(id) "; return true; } bool isCFTypeRef(Type ty) { if (ID_CFTypeRef.empty()) ID_CFTypeRef = ctx.getIdentifier("CFTypeRef"); while (auto aliasTy = dyn_cast(ty.getPointer())) { const TypeAliasDecl *TAD = aliasTy->getDecl(); if (TAD->hasClangNode() && TAD->getName() == ID_CFTypeRef) return true; } return false; } void visitVarDecl(VarDecl *VD) { assert(VD->getDeclContext()->isTypeContext() && "cannot handle global variables right now"); printDocumentationComment(VD); if (VD->isStatic()) { // Objective-C doesn't have class properties. Just print the accessors. printAbstractFunction(VD->getGetter(), true); if (auto setter = VD->getSetter()) printAbstractFunction(setter, true); return; } // For now, never promise atomicity. os << "@property (nonatomic"; bool isSettable = VD->isSettable(nullptr); if (isSettable && ctx.LangOpts.EnableAccessControl) isSettable = (VD->getSetterAccessibility() >= minRequiredAccess); if (!isSettable) os << ", readonly"; // Print the ownership semantics, if relevant. // We treat "unowned" as "assign" (even though it's more like // "safe_unretained") because we want people to think twice about // allowing that object to disappear. // FIXME: Handle the "Unmanaged" wrapper struct. Type ty = VD->getType(); if (auto weakTy = ty->getAs()) { auto innerTy = weakTy->getReferentType()->getAnyOptionalObjectType(); auto innerClass = innerTy->getClassOrBoundGenericClass(); if ((innerClass && !innerClass->isForeign()) || (innerTy->isObjCExistentialType() && !isCFTypeRef(innerTy))) { os << ", weak"; } } else if (ty->is()) { os << ", assign"; } else if (ty->is()) { os << ", unsafe_unretained"; } else { Type copyTy = ty; if (auto unwrappedTy = copyTy->getAnyOptionalObjectType()) copyTy = unwrappedTy; if (auto nominal = copyTy->getStructOrBoundGenericStruct()) { if (nominal == ctx.getArrayDecl() || nominal == ctx.getDictionaryDecl() || nominal == ctx.getSetDecl() || nominal == ctx.getStringDecl()) { os << ", copy"; } } else if (auto fnTy = copyTy->getAs()) { switch (fnTy->getRepresentation()) { case FunctionTypeRepresentation::Block: case FunctionTypeRepresentation::Swift: os << ", copy"; break; case FunctionTypeRepresentation::Thin: case FunctionTypeRepresentation::CFunctionPointer: break; } } } // Even though Swift doesn't use custom accessor names, we need to be // consistent when an Objective-C property is overridden. // FIXME: Will we ever need to do this for properties that /don't/ come // from Objective-C? const VarDecl *base = findClangBase(VD); if (base) { llvm::SmallString<64> buffer; os << ", getter=" << VD->getObjCGetterSelector().getString(buffer); if (VD->isSettable(nullptr)) { buffer.clear(); os << ", setter=" << VD->getObjCSetterSelector().getString(buffer); } } os << ") "; if (VD->getAttrs().hasAttribute()) { if (!maybePrintIBOutletCollection(ty)) os << "IBOutlet "; } clang::QualType clangTy; if (base) if (auto prop = dyn_cast(base->getClangDecl())) clangTy = prop->getType(); if (!clangTy.isNull() && isNSUInteger(clangTy)) { os << "NSUInteger " << VD->getName().str(); } else { print(ty, OTK_None, VD->getObjCPropertyName().str()); } os << ";\n"; } void visitSubscriptDecl(SubscriptDecl *SD) { assert(SD->isInstanceMember() && "static subscripts not supported"); bool isNSUIntegerSubscript = false; if (auto clangBase = findClangBase(SD)) { const auto *clangGetter = cast(clangBase->getClangDecl()); const auto *indexParam = clangGetter->parameters().front(); isNSUIntegerSubscript = isNSUInteger(indexParam->getType()); } printAbstractFunction(SD->getGetter(), false, isNSUIntegerSubscript); if (auto setter = SD->getSetter()) printAbstractFunction(setter, false, isNSUIntegerSubscript); } /// Visit part of a type, such as the base of a pointer type. /// /// If a full type is being printed, use print() instead. void visitPart(Type ty, Optional optionalKind) { TypeVisitor::visit(ty, optionalKind); } /// Where nullability information should be printed. enum class NullabilityPrintKind { Before, After, ContextSensitive, }; void printNullability(Optional kind, NullabilityPrintKind printKind = NullabilityPrintKind::After) { if (!kind) return; if (printKind == NullabilityPrintKind::After) os << ' '; switch (*kind) { case OTK_None: if (printKind == NullabilityPrintKind::ContextSensitive) os << "SWIFT_NULLABILITY(nonnull)"; else os << "__nonnull"; break; case OTK_Optional: if (printKind == NullabilityPrintKind::ContextSensitive) os << "SWIFT_NULLABILITY(nullable)"; else os << "__nullable"; break; case OTK_ImplicitlyUnwrappedOptional: if (printKind == NullabilityPrintKind::ContextSensitive) os << "SWIFT_NULLABILITY(null_unspecified)"; else os << "__null_unspecified"; break; } if (printKind != NullabilityPrintKind::After) os << ' '; } /// If "name" is one of the standard library types used to map in Clang /// primitives and basic types, print out the appropriate spelling and /// return true. /// /// This handles typealiases and structs provided by the standard library /// for interfacing with C and Objective-C. bool printIfKnownTypeName(Identifier moduleName, Identifier parentName, Identifier name, Optional optionalKind) { if (specialNames.empty()) { #define MAP(SWIFT_NAME, CLANG_REPR, NEEDS_NULLABILITY) \ specialNames[{ctx.StdlibModuleName, Identifier(), \ ctx.getIdentifier(#SWIFT_NAME)}] = \ { CLANG_REPR, NEEDS_NULLABILITY } MAP(CBool, "bool", false); MAP(CChar, "char", false); MAP(CWideChar, "wchar_t", false); MAP(CChar16, "char16_t", false); MAP(CChar32, "char32_t", false); MAP(CSignedChar, "signed char", false); MAP(CShort, "short", false); MAP(CInt, "int", false); MAP(CLong, "long", false); MAP(CLongLong, "long long", false); MAP(CUnsignedChar, "unsigned char", false); MAP(CUnsignedShort, "unsigned short", false); MAP(CUnsignedInt, "unsigned int", false); MAP(CUnsignedLong, "unsigned long", false); MAP(CUnsignedLongLong, "unsigned long long", false); MAP(CFloat, "float", false); MAP(CDouble, "double", false); MAP(Int8, "int8_t", false); MAP(Int16, "int16_t", false); MAP(Int32, "int32_t", false); MAP(Int64, "int64_t", false); MAP(UInt8, "uint8_t", false); MAP(UInt16, "uint16_t", false); MAP(UInt32, "uint32_t", false); MAP(UInt64, "uint64_t", false); MAP(Float, "float", false); MAP(Double, "double", false); MAP(Float32, "float", false); MAP(Float64, "double", false); MAP(Int, "NSInteger", false); MAP(UInt, "NSUInteger", false); MAP(Bool, "BOOL", false); MAP(COpaquePointer, "void *", true); MAP(CMutableVoidPointer, "void *", true); MAP(CConstVoidPointer, "void const *", true); Identifier ID_ObjectiveC = ctx.Id_ObjectiveC; specialNames[{ID_ObjectiveC, Identifier(), ctx.getIdentifier("ObjCBool")}] = { "BOOL", false}; specialNames[{ID_ObjectiveC, Identifier(), ctx.getIdentifier("Selector")}] = { "SEL", true }; specialNames[{ID_ObjectiveC, Identifier(), ctx.getIdentifier("NSZone")}] = { "NSZone *", true }; // Use typedefs we set up for SIMD vector types. #define MAP_SIMD_TYPE(_, __, PARENT_NAME) \ specialNames[{ctx.Id_simd, ctx.getIdentifier(#PARENT_NAME), \ ctx.getIdentifier("Vector2")}] \ = { "swift_" #PARENT_NAME "_Vector2", false }; \ specialNames[{ctx.Id_simd, ctx.getIdentifier(#PARENT_NAME), \ ctx.getIdentifier("Vector3")}] \ = { "swift_" #PARENT_NAME "_Vector3", false }; \ specialNames[{ctx.Id_simd, ctx.getIdentifier(#PARENT_NAME), \ ctx.getIdentifier("Vector4")}] \ = { "swift_" #PARENT_NAME "_Vector4", false }; #include "swift/ClangImporter/SIMDMappedTypes.def" static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4, "must add or remove special name mappings if max number of " "SIMD elements is changed"); } auto iter = specialNames.find({moduleName, parentName, name}); if (iter == specialNames.end()) return false; os << iter->second.first; if (iter->second.second) { // FIXME: Selectors and pointers should be nullable. printNullability(OptionalTypeKind::OTK_ImplicitlyUnwrappedOptional); } return true; } void visitType(TypeBase *Ty, Optional optionalKind) { assert(Ty->getDesugaredType() == Ty && "unhandled sugared type"); os << "/* "; Ty->print(os); os << " */"; } bool isClangObjectPointerType(const clang::TypeDecl *clangTypeDecl) const { auto &clangASTContext = ctx.getClangModuleLoader()->getClangASTContext(); clang::QualType clangTy = clangASTContext.getTypeDeclType(clangTypeDecl); return clangTy->isObjCRetainableType(); } bool isClangPointerType(const clang::TypeDecl *clangTypeDecl) const { auto &clangASTContext = ctx.getClangModuleLoader()->getClangASTContext(); clang::QualType clangTy = clangASTContext.getTypeDeclType(clangTypeDecl); return clangTy->isPointerType(); } void visitNameAliasType(NameAliasType *aliasTy, Optional optionalKind) { const TypeAliasDecl *alias = aliasTy->getDecl(); if (printIfKnownTypeName(alias->getModuleContext()->Name, Identifier(), alias->getName(), optionalKind)) return; if (alias->hasClangNode()) { auto *clangTypeDecl = cast(alias->getClangDecl()); os << clangTypeDecl->getName(); // Print proper nullability for CF types, but __null_unspecified for // all other non-object Clang pointer types. if (aliasTy->hasReferenceSemantics() || isClangObjectPointerType(clangTypeDecl)) { printNullability(optionalKind); } else if (isClangPointerType(clangTypeDecl)) { printNullability(OptionalTypeKind::OTK_ImplicitlyUnwrappedOptional); } return; } if (alias->isObjC()) { os << alias->getName(); return; } visitPart(alias->getUnderlyingType(), optionalKind); } void maybePrintTagKeyword(const NominalTypeDecl *NTD) { if (isa(NTD) && !NTD->hasClangNode()) { os << "enum "; return; } auto clangDecl = dyn_cast_or_null(NTD->getClangDecl()); if (!clangDecl) return; if (clangDecl->getTypedefNameForAnonDecl()) return; auto importer = static_cast(ctx.getClangModuleLoader()); if (importer->hasTypedef(clangDecl)) return; os << clangDecl->getKindName() << " "; } void visitStructType(StructType *ST, Optional optionalKind) { const StructDecl *SD = ST->getStructOrBoundGenericStruct(); if (SD == ctx.getStringDecl()) { os << "NSString *"; printNullability(optionalKind); return; } Identifier parentName{}; if (auto parentType = SD->getParent()->getDeclaredTypeOfContext()) parentName = parentType->getAnyNominal()->getName(); if (printIfKnownTypeName(SD->getModuleContext()->Name, parentName, SD->getName(), optionalKind)) return; maybePrintTagKeyword(SD); os << SD->getName(); } /// Determine whether the given type contains a type that cannot be printed /// in Objective-C. static bool containsNonObjCType(Type type) { return type.findIf([&](Type type) -> bool { if (auto classDecl = type->getClassOrBoundGenericClass()) { return classDecl->isForeign() || !classDecl->isObjC(); } return false; }); } /// If \p BGT represents a generic struct used to import Clang types, print /// it out. bool printIfKnownGenericStruct(const BoundGenericStructType *BGT, Optional optionalKind) { StructDecl *SD = BGT->getDecl(); if (!SD->getModuleContext()->isStdlibModule()) return false; if (SD == ctx.getArrayDecl()) { #ifndef SWIFT_DISABLE_OBJC_GENERICS if (!BGT->getGenericArgs()[0]->isAnyObject() && !containsNonObjCType(BGT->getGenericArgs()[0])) { os << "NS_ARRAY("; visitPart(BGT->getGenericArgs()[0], None); os << ")"; } else { os << "NSArray *"; } #else os << "NSArray *"; #endif printNullability(optionalKind); return true; } if (SD == ctx.getDictionaryDecl()) { #ifndef SWIFT_DISABLE_OBJC_GENERICS if ((!isNSObject(BGT->getGenericArgs()[0]) || !BGT->getGenericArgs()[1]->isAnyObject()) && !containsNonObjCType(BGT->getGenericArgs()[0]) && !containsNonObjCType(BGT->getGenericArgs()[1])) { os << "NS_DICTIONARY("; visitPart(BGT->getGenericArgs()[0], None); os << ", "; visitPart(BGT->getGenericArgs()[1], None); os << ")"; } else { os << "NSDictionary *"; } #else os << "NSDictionary *"; #endif printNullability(optionalKind); return true; } if (SD == ctx.getSetDecl()) { #ifndef SWIFT_DISABLE_OBJC_GENERICS if (!isNSObject(BGT->getGenericArgs()[0]) && !containsNonObjCType(BGT->getGenericArgs()[0])) { os << "NS_SET("; visitPart(BGT->getGenericArgs()[0], None); os << ")"; } else { os << "NSSet *"; } #else os << "NSSet *"; #endif printNullability(optionalKind); return true; } if (SD == ctx.getCFunctionPointerDecl()) { assert(BGT->getGenericArgs().size() == 1); auto FT = BGT->getGenericArgs()[0]->castTo(); printFunctionType(FT, '*', OTK_ImplicitlyUnwrappedOptional); return true; } // Everything from here on is some kind of pointer type. bool isConst; if (SD == ctx.getUnsafePointerDecl()) { isConst = true; } else if (SD == ctx.getAutoreleasingUnsafeMutablePointerDecl() || SD == ctx.getUnsafeMutablePointerDecl()) { isConst = false; } else { // Not a pointer. return false; } auto args = BGT->getGenericArgs(); assert(args.size() == 1); visitPart(args.front(), OTK_None); if (isConst) os << " const"; os << " *"; // FIXME: Pointer types should be nullable. printNullability(OptionalTypeKind::OTK_ImplicitlyUnwrappedOptional); return true; } void visitBoundGenericStructType(BoundGenericStructType *BGT, Optional optionalKind) { if (printIfKnownGenericStruct(BGT, optionalKind)) return; visitBoundGenericType(BGT, optionalKind); } void visitBoundGenericType(BoundGenericType *BGT, Optional optionalKind) { OptionalTypeKind innerOptionalKind; if (auto underlying = BGT->getAnyOptionalObjectType(innerOptionalKind)) { visitPart(underlying, innerOptionalKind); } else visitType(BGT, optionalKind); } void visitEnumType(EnumType *ET, Optional optionalKind) { const EnumDecl *ED = ET->getDecl(); maybePrintTagKeyword(ED); os << ED->getName(); } void visitClassType(ClassType *CT, Optional optionalKind) { const ClassDecl *CD = CT->getClassOrBoundGenericClass(); assert(CD->isObjC()); auto clangDecl = dyn_cast_or_null(CD->getClangDecl()); if (clangDecl) { if (isa(clangDecl)) { os << clangDecl->getName() << " *"; printNullability(optionalKind); } else { maybePrintTagKeyword(CD); os << clangDecl->getName(); printNullability(optionalKind); } } else { os << CD->getName() << " *"; printNullability(optionalKind); } } void visitProtocolType(ProtocolType *PT, Optional optionalKind, bool isMetatype = false) { os << (isMetatype ? "Class" : "id"); auto proto = PT->getDecl(); assert(proto->isObjC()); if (auto knownKind = proto->getKnownProtocolKind()) { if (*knownKind == KnownProtocolKind::AnyObject) { printNullability(optionalKind); return; } } printProtocols(proto); printNullability(optionalKind); } void visitProtocolCompositionType(ProtocolCompositionType *PCT, Optional optionalKind, bool isMetatype = false) { CanType canonicalComposition = PCT->getCanonicalType(); if (auto singleProto = dyn_cast(canonicalComposition)) return visitProtocolType(singleProto, optionalKind, isMetatype); PCT = cast(canonicalComposition); os << (isMetatype ? "Class" : "id"); SmallVector protos; std::transform(PCT->getProtocols().begin(), PCT->getProtocols().end(), std::back_inserter(protos), [] (Type ty) -> ProtocolDecl * { return ty->castTo()->getDecl(); }); printProtocols(protos); printNullability(optionalKind); } void visitExistentialMetatypeType(ExistentialMetatypeType *MT, Optional optionalKind) { Type instanceTy = MT->getInstanceType(); if (auto protoTy = instanceTy->getAs()) { visitProtocolType(protoTy, optionalKind, /*isMetatype=*/true); } else if (auto compTy = instanceTy->getAs()) { visitProtocolCompositionType(compTy, optionalKind, /*isMetatype=*/true); } else { visitType(MT, optionalKind); } } void visitMetatypeType(MetatypeType *MT, Optional optionalKind) { Type instanceTy = MT->getInstanceType(); if (auto classTy = instanceTy->getAs()) { const ClassDecl *CD = classTy->getDecl(); if (CD->isObjC()) os << "SWIFT_METATYPE(" << CD->getName() << ")"; else os << "Class"; printNullability(optionalKind); } else { visitType(MT, optionalKind); } } void printFunctionType(FunctionType *FT, char pointerSigil, Optional optionalKind) { visitPart(FT->getResult(), OTK_None); os << " (" << pointerSigil; printNullability(optionalKind); openFunctionTypes.push_back(FT); } void visitFunctionType(FunctionType *FT, Optional optionalKind) { switch (FT->getRepresentation()) { case AnyFunctionType::Representation::Thin: llvm_unreachable("can't represent thin functions in ObjC"); // Native Swift function types bridge to block types. case AnyFunctionType::Representation::Swift: case AnyFunctionType::Representation::Block: printFunctionType(FT, '^', optionalKind); break; case AnyFunctionType::Representation::CFunctionPointer: printFunctionType(FT, '*', optionalKind); } } /// Print the part of a function type that appears after where the variable /// name would go. /// /// This is necessary to handle C's awful declarator syntax. /// "(A) -> ((B) -> C)" becomes "C (^ (^)(A))(B)". void finishFunctionType(const FunctionType *FT) { os << ")("; Type paramsTy = FT->getInput(); if (auto tupleTy = dyn_cast(paramsTy.getPointer())) { if (tupleTy->getNumElements() == 0) { os << "void"; } else { interleave(tupleTy->getElementTypes(), [this](Type ty) { print(ty, OTK_None); }, [this] { os << ", "; }); } } else { print(paramsTy, OTK_None); } os << ")"; } void visitTupleType(TupleType *TT, Optional optionalKind) { assert(TT->getNumElements() == 0); os << "void"; } void visitParenType(ParenType *PT, Optional optionalKind) { visitPart(PT->getSinglyDesugaredType(), optionalKind); } void visitSubstitutedType(SubstitutedType *ST, Optional optionalKind) { visitPart(ST->getSinglyDesugaredType(), optionalKind); } void visitSyntaxSugarType(SyntaxSugarType *SST, Optional optionalKind) { visitPart(SST->getSinglyDesugaredType(), optionalKind); } void visitDictionaryType(DictionaryType *DT, Optional optionalKind) { visitPart(DT->getSinglyDesugaredType(), optionalKind); } void visitDynamicSelfType(DynamicSelfType *DST, Optional optionalKind) { printNullability(optionalKind, NullabilityPrintKind::ContextSensitive); os << "instancetype"; } void visitReferenceStorageType(ReferenceStorageType *RST, Optional optionalKind) { visitPart(RST->getReferentType(), optionalKind); } /// Print a full type, optionally declaring the given \p name. /// /// This will properly handle nested function types (see /// finishFunctionType()). If only a part of a type is being printed, use /// visitPart(). public: void print(Type ty, Optional optionalKind, StringRef name = "") { decltype(openFunctionTypes) savedFunctionTypes; savedFunctionTypes.swap(openFunctionTypes); visitPart(ty, optionalKind); if (!name.empty()) os << ' ' << name; while (!openFunctionTypes.empty()) { const FunctionType *openFunctionTy = openFunctionTypes.pop_back_val(); finishFunctionType(openFunctionTy); } openFunctionTypes = std::move(savedFunctionTypes); } }; class ReferencedTypeFinder : private TypeVisitor { friend TypeVisitor; llvm::function_ref Callback; ReferencedTypeFinder(decltype(Callback) callback) : Callback(callback) {} void visitType(TypeBase *base) { assert(base->getDesugaredType() == base && "unhandled sugared type"); return; } void visitNameAliasType(NameAliasType *aliasTy) { Callback(*this, aliasTy->getDecl()); } void visitParenType(ParenType *parenTy) { visit(parenTy->getSinglyDesugaredType()); } void visitTupleType(TupleType *tupleTy) { for (auto elemTy : tupleTy->getElementTypes()) visit(elemTy); } void visitNominalType(NominalType *nominal) { Callback(*this, nominal->getDecl()); } void visitMetatypeType(MetatypeType *metatype) { visit(metatype->getInstanceType()); } void visitSubstitutedType(SubstitutedType *sub) { visit(sub->getSinglyDesugaredType()); } void visitAnyFunctionType(AnyFunctionType *fnTy) { visit(fnTy->getInput()); visit(fnTy->getResult()); } void visitSyntaxSugarType(SyntaxSugarType *sugar) { visit(sugar->getSinglyDesugaredType()); } void visitDictionaryType(DictionaryType *DT) { visit(DT->getSinglyDesugaredType()); } void visitProtocolCompositionType(ProtocolCompositionType *composition) { for (auto proto : composition->getProtocols()) visit(proto); } void visitLValueType(LValueType *lvalue) { visit(lvalue->getObjectType()); } void visitInOutType(InOutType *inout) { visit(inout->getObjectType()); } void visitBoundGenericType(BoundGenericType *boundGeneric) { for (auto argTy : boundGeneric->getGenericArgs()) visit(argTy); // Ignore the base type; that can't be exposed to Objective-C. Every // bound generic type we care about gets mapped to a particular construct // in Objective-C we care about. (For example, Optional is mapped to // NSFoo *.) } public: using TypeVisitor::visit; static void walk(Type ty, decltype(Callback) callback) { ReferencedTypeFinder(callback).visit(ty); } }; /// A generalization of llvm::SmallSetVector that allows a custom comparator. template > using SmallSetVector = llvm::SetVector, llvm::SmallSet>; /// A comparator for types with PointerLikeTypeTraits that sorts by opaque /// void pointer representation. template struct PointerLikeComparator { using Traits = llvm::PointerLikeTypeTraits; bool operator()(T lhs, T rhs) { return std::less()(Traits::getAsVoidPointer(lhs), Traits::getAsVoidPointer(rhs)); } }; class ModuleWriter { enum class EmissionState { DefinitionRequested = 0, DefinitionInProgress, Defined }; llvm::DenseMap> seenTypes; std::vector declsToWrite; using ImportModuleTy = PointerUnion; SmallSetVector> imports; std::string bodyBuffer; llvm::raw_string_ostream os{bodyBuffer}; Module &M; StringRef bridgingHeader; ObjCPrinter printer; public: ModuleWriter(Module &mod, StringRef header, Accessibility access) : M(mod), bridgingHeader(header), printer(M.Ctx, os, access) {} /// 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) { Module *otherModule = D->getModuleContext(); if (otherModule == &M) return false; if (otherModule->isStdlibModule()) return true; // Don't need a module for SIMD types in C. if (otherModule->getName() == M.Ctx.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(M.Ctx.getClangModuleLoader()); if (const auto *clangModule = importer->getClangOwningModule(clangNode)) { while (clangModule && !clangModule->IsExplicit) clangModule = clangModule->Parent; if (clangModule) { imports.insert(clangModule); return true; } } } imports.insert(otherModule); return true; } bool require(const TypeDecl *D) { if (addImport(D)) { seenTypes[D] = { EmissionState::Defined, true }; return true; } auto &state = seenTypes[D]; switch (state.first) { case EmissionState::DefinitionRequested: declsToWrite.push_back(D); return false; case EmissionState::DefinitionInProgress: llvm_unreachable("circular requirements"); case EmissionState::Defined: return true; } } void forwardDeclare(const NominalTypeDecl *NTD, std::function Printer) { if (NTD->getModuleContext()->isStdlibModule()) return; auto &state = seenTypes[NTD]; if (state.second) return; Printer(); state.second = true; } bool forwardDeclare(const ClassDecl *CD) { if (!CD->isObjC() || CD->isForeign()) return false; forwardDeclare(CD, [&]{ os << "@class " << CD->getName() << ";\n"; }); return true; } void forwardDeclare(const ProtocolDecl *PD) { assert(PD->isObjC() || *PD->getKnownProtocolKind() == KnownProtocolKind::AnyObject); forwardDeclare(PD, [&]{ os << "@protocol " << PD->getName() << ";\n"; }); } void forwardDeclare(const EnumDecl *ED) { assert(ED->isObjC() || ED->hasClangNode()); forwardDeclare(ED, [&]{ os << "enum " << ED->getName() << " : "; printer.print(ED->getRawType(), OTK_None); os << ";\n"; }); } void forwardDeclareMemberTypes(DeclRange members) { SmallVector nestedTypes; for (auto member : members) { auto VD = dyn_cast(member); if (!VD || !printer.shouldInclude(VD)) continue; // Catch nested types and emit their definitions /after/ this class. if (isa(VD)) { // 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(attr) && !attr->isImplicit(); })) { nestedTypes.push_back(VD); } continue; } ReferencedTypeFinder::walk(VD->getType(), [this](ReferencedTypeFinder &finder, const TypeDecl *TD) { if (auto CD = dyn_cast(TD)) { if (!forwardDeclare(CD)) { (void)addImport(CD); } } else if (auto PD = dyn_cast(TD)) forwardDeclare(PD); else if (addImport(TD)) return; else if (auto ED = dyn_cast(TD)) forwardDeclare(ED); else if (auto TAD = dyn_cast(TD)) finder.visit(TAD->getUnderlyingType()); else if (isa(TD)) llvm_unreachable("should not see type params here"); else assert(false && "unknown local type decl"); }); } declsToWrite.insert(declsToWrite.end()-1, nestedTypes.rbegin(), nestedTypes.rend()); // Separate forward declarations from the class itself. os << '\n'; } 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 (Type superTy = CD->getSuperclass()) { superclass = superTy->getClassOrBoundGenericClass(); allRequirementsSatisfied &= require(superclass); } for (auto proto : CD->getLocalProtocols( ConformanceLookupKind::OnlyExplicit)) if (printer.shouldInclude(proto)) allRequirementsSatisfied &= require(proto); if (!allRequirementsSatisfied) return false; seenTypes[CD] = { EmissionState::Defined, true }; forwardDeclareMemberTypes(CD->getMembers()); printer.print(CD); return true; } bool writeProtocol(const ProtocolDecl *PD) { if (addImport(PD)) return true; auto knownProtocol = PD->getKnownProtocolKind(); if (knownProtocol && *knownProtocol == KnownProtocolKind::AnyObject) return true; if (seenTypes[PD].first == EmissionState::Defined) return true; bool allRequirementsSatisfied = true; for (auto proto : PD->getInheritedProtocols(nullptr)) { assert(proto->isObjC()); allRequirementsSatisfied &= require(proto); } if (!allRequirementsSatisfied) return false; seenTypes[PD] = { EmissionState::Defined, true }; forwardDeclareMemberTypes(PD->getMembers()); printer.print(PD); return true; } bool writeExtension(const ExtensionDecl *ED) { bool allRequirementsSatisfied = true; const ClassDecl *CD = ED->getExtendedType()->getClassOrBoundGenericClass(); allRequirementsSatisfied &= require(CD); for (auto proto : ED->getLocalProtocols()) if (printer.shouldInclude(proto)) allRequirementsSatisfied &= require(proto); if (!allRequirementsSatisfied) return false; forwardDeclareMemberTypes(ED->getMembers()); printer.print(ED); return true; } bool writeEnum(const EnumDecl *ED) { if (addImport(ED)) return true; if (seenTypes[ED].first == EmissionState::Defined) return true; seenTypes[ED] = {EmissionState::Defined, true}; printer.print(ED); return true; } void writePrologue(raw_ostream &out) { out << "// Generated by " << version::getSwiftFullVersion() << "\n" "#pragma clang diagnostic push\n" "\n" "#if defined(__has_include) && " "__has_include()\n" "# include \n" "#endif\n" "\n" "#pragma clang diagnostic ignored \"-Wauto-import\"\n" "#include \n" "#include \n" "#include \n" "#include \n" "\n" "#if defined(__has_include) && __has_include()\n" "# include \n" "#elif !defined(__cplusplus) || __cplusplus < 201103L\n" "typedef uint_least16_t char16_t;\n" "typedef uint_least32_t char32_t;\n" "#endif\n" "\n" "typedef struct _NSZone NSZone;\n" "\n" "#if !defined(SWIFT_PASTE)\n" "# define SWIFT_PASTE_HELPER(x, y) x##y\n" "# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y)\n" "#endif" "\n" "#if !defined(SWIFT_METATYPE)\n" "# define SWIFT_METATYPE(X) Class\n" "#endif\n" "\n" "#if defined(__has_attribute) && " "__has_attribute(objc_runtime_name)\n" "# define SWIFT_RUNTIME_NAME(X) " "__attribute__((objc_runtime_name(X)))\n" "#else\n" "# define SWIFT_RUNTIME_NAME(X)\n" "#endif\n" "#if !defined(SWIFT_CLASS_EXTRA)\n" "# define SWIFT_CLASS_EXTRA\n" "#endif\n" "#if !defined(SWIFT_PROTOCOL_EXTRA)\n" "# define SWIFT_PROTOCOL_EXTRA\n" "#endif\n" "#if !defined(SWIFT_ENUM_EXTRA)\n" "# define SWIFT_ENUM_EXTRA\n" "#endif\n" "#if !defined(SWIFT_CLASS)\n" "# if defined(__has_attribute) && " "__has_attribute(objc_subclassing_restricted) \n" "# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) " "__attribute__((objc_subclassing_restricted)) " "SWIFT_CLASS_EXTRA\n" "# else\n" "# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) " "SWIFT_CLASS_EXTRA\n" "# endif\n" "#endif\n" "\n" "#if !defined(SWIFT_PROTOCOL)\n" "# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) " "SWIFT_PROTOCOL_EXTRA\n" "#endif\n" "\n" "#if !defined(SWIFT_EXTENSION)\n" "# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__)\n" "#endif\n" "\n" "#if !defined(OBJC_DESIGNATED_INITIALIZER)\n" "# if defined(__has_attribute) && " "__has_attribute(objc_designated_initializer)\n" "# define OBJC_DESIGNATED_INITIALIZER " "__attribute__((objc_designated_initializer))\n" "# else\n" "# define OBJC_DESIGNATED_INITIALIZER\n" "# endif\n" "#endif\n" "#if !defined(SWIFT_ENUM)\n" "# define SWIFT_ENUM(_type, _name) " "enum _name : _type _name; " "enum SWIFT_ENUM_EXTRA _name : _type\n" "#endif\n" "#if __has_feature(nullability)\n" "# define SWIFT_NULLABILITY(X) X\n" "#else\n" "# if !defined(__nonnull)\n" "# define __nonnull\n" "# endif\n" "# if !defined(__nullable)\n" "# define __nullable\n" "# endif\n" "# if !defined(__null_unspecified)\n" "# define __null_unspecified\n" "# endif\n" "# define SWIFT_NULLABILITY(X)\n" "#endif\n" #define MAP_SIMD_TYPE(C_TYPE, _, SWIFT_NAME) \ "typedef " #C_TYPE " swift_" #SWIFT_NAME "_Vector2 __attribute__((__ext_vector_type__(2)));\n" \ "typedef " #C_TYPE " swift_" #SWIFT_NAME "_Vector3 __attribute__((__ext_vector_type__(3)));\n" \ "typedef " #C_TYPE " swift_" #SWIFT_NAME "_Vector4 __attribute__((__ext_vector_type__(4)));\n" #include "swift/ClangImporter/SIMDMappedTypes.def" ; static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4, "need to add SIMD typedefs here if max elements is increased"); } bool isUnderlyingModule(Module *import) { if (bridgingHeader.empty()) return import != &M && import->Name == M.Name; auto importer = static_cast(import->Ctx.getClangModuleLoader()); return import == importer->getImportedHeaderModule(); } void writeImports(raw_ostream &out) { out << "#if defined(__has_feature) && __has_feature(modules)\n"; // Track printed names to handle overlay modules. llvm::SmallPtrSet seenImports; bool includeUnderlying = false; for (auto import : imports) { if (auto *swiftModule = import.dyn_cast()) { auto Name = swiftModule->Name; if (isUnderlyingModule(swiftModule)) { includeUnderlying = true; continue; } if (seenImports.insert(Name).second) out << "@import " << Name.str() << ";\n"; } else { const auto *clangModule = import.get(); out << "@import "; // FIXME: This should be an API on clang::Module. SmallVector submoduleNames; do { submoduleNames.push_back(clangModule->Name); clangModule = clangModule->Parent; } while (clangModule); interleave(submoduleNames.rbegin(), submoduleNames.rend(), [&out](StringRef next) { out << next; }, [&out] { out << "."; }); out << ";\n"; } } out << "#endif\n\n"; if (includeUnderlying) { if (bridgingHeader.empty()) out << "#import <" << M.Name.str() << '/' << M.Name.str() << ".h>\n\n"; else out << "#import \"" << bridgingHeader << "\"\n\n"; } #ifndef SWIFT_DISABLE_OBJC_GENERICS // Once we've printed out the imports, deal with Foundations that // don't provide NS_ARRAY/NS_DICTIONARY/NS_SET. out << "#ifndef NS_ARRAY\n" "# pragma clang diagnostic push\n" "# pragma clang diagnostic ignored \"-Wvariadic-macros\"\n" "# define NS_ARRAY(...) NSArray *\n" "# pragma clang diagnostic pop\n" "#endif\n" "#ifndef NS_DICTIONARY\n" "# pragma clang diagnostic push\n" "# pragma clang diagnostic ignored \"-Wvariadic-macros\"\n" "# define NS_DICTIONARY(...) NSDictionary *\n" "# pragma clang diagnostic pop\n" "#endif\n" "#ifndef NS_SET\n" "# pragma clang diagnostic push\n" "# pragma clang diagnostic ignored \"-Wvariadic-macros\"\n" "# define NS_SET(...) NSSet *\n" "# pragma clang diagnostic pop\n" "#endif\n"; #endif } bool writeToStream(raw_ostream &out) { SmallVector decls; M.getTopLevelDecls(decls); auto newEnd = std::remove_if(decls.begin(), decls.end(), [this](const Decl *D) -> bool { if (auto VD = dyn_cast(D)) return !printer.shouldInclude(VD); if (auto ED = dyn_cast(D)) { auto baseClass = ED->getExtendedType()->getClassOrBoundGenericClass(); return !baseClass || !printer.shouldInclude(baseClass) || baseClass->isForeign(); } return true; }); decls.erase(newEnd, decls.end()); // REVERSE sort the decls, since we are going to copy them onto a stack. llvm::array_pod_sort(decls.begin(), decls.end(), [](Decl * const *lhs, Decl * const *rhs) -> int { enum : int { Ascending = -1, Equivalent = 0, Descending = 1, }; assert(*lhs != *rhs && "duplicate top-level decl"); auto getSortName = [](const Decl *D) -> StringRef { if (auto VD = dyn_cast(D)) return VD->getName().str(); if (auto ED = dyn_cast(D)) { auto baseClass = ED->getExtendedType()->getClassOrBoundGenericClass(); return baseClass->getName().str(); } llvm_unreachable("unknown top-level ObjC decl"); }; // Sort by names. int result = getSortName(*rhs).compare(getSortName(*lhs)); if (result != 0) return result; // Prefer value decls to extensions. assert(!(isa(*lhs) && isa(*rhs))); if (isa(*lhs) && !isa(*rhs)) return Descending; if (!isa(*lhs) && isa(*rhs)) return Ascending; // Break ties in extensions by putting smaller extensions last (in reverse // order). // FIXME: This will end up taking linear time. auto lhsMembers = cast(*lhs)->getMembers(); auto rhsMembers = cast(*rhs)->getMembers(); unsigned numLHSMembers = std::distance(lhsMembers.begin(), lhsMembers.end()); unsigned numRHSMembers = std::distance(rhsMembers.begin(), rhsMembers.end()); if (numLHSMembers != numRHSMembers) return numLHSMembers < numRHSMembers ? Descending : Ascending; // Or the extension with fewer protocols. auto lhsProtos = cast(*lhs)->getLocalProtocols(); auto rhsProtos = cast(*rhs)->getLocalProtocols(); if (lhsProtos.size() != rhsProtos.size()) return lhsProtos.size() < rhsProtos.size() ? Descending : Ascending; // If that fails, arbitrarily pick the extension whose protocols are // alphabetically first. auto mismatch = std::mismatch(lhsProtos.begin(), lhsProtos.end(), rhsProtos.begin(), [getSortName] (const ProtocolDecl *nextLHSProto, const ProtocolDecl *nextRHSProto) { return nextLHSProto->getName() != nextRHSProto->getName(); }); if (mismatch.first == lhsProtos.end()) return Equivalent; StringRef lhsProtoName = (*mismatch.first)->getName().str(); return lhsProtoName.compare((*mismatch.second)->getName().str()); }); assert(declsToWrite.empty()); declsToWrite.assign(decls.begin(), decls.end()); while (!declsToWrite.empty()) { const Decl *D = declsToWrite.back(); bool success = true; if (isa(D)) { if (auto CD = dyn_cast(D)) success = writeClass(CD); else if (auto PD = dyn_cast(D)) success = writeProtocol(PD); else if (auto ED = dyn_cast(D)) success = writeEnum(ED); else llvm_unreachable("unknown top-level ObjC value decl"); } else if (auto ED = dyn_cast(D)) { success = writeExtension(ED); } else { llvm_unreachable("unknown top-level ObjC decl"); } if (success) { assert(declsToWrite.back() == D); os << "\n"; declsToWrite.pop_back(); } } writePrologue(out); writeImports(out); out << "#pragma clang diagnostic ignored \"-Wproperty-attribute-mismatch\"\n" "#pragma clang diagnostic ignored \"-Wduplicate-method-arg\"\n" << os.str() << "#pragma clang diagnostic pop\n"; return false; } }; } bool swift::printAsObjC(llvm::raw_ostream &os, Module *M, StringRef bridgingHeader, Accessibility minRequiredAccess) { return ModuleWriter(*M, bridgingHeader, minRequiredAccess).writeToStream(os); }