//===--- SwiftNameTranslation.cpp - Swift to ObjC Name Translation APIs ---===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file contains utilities for translating Swift names to ObjC. // //===----------------------------------------------------------------------===// #include "swift/AST/SwiftNameTranslation.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Attr.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/LazyResolver.h" #include "swift/AST/Module.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/StringExtras.h" #include "clang/AST/DeclObjC.h" #include "clang/Basic/Module.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include using namespace swift; StringRef swift::objc_translation:: getNameForObjC(const ValueDecl *VD, CustomNamesOnly_t customNamesOnly) { assert(isa(VD) || isa(VD) || isa(VD) || isa(VD) || isa(VD) || isa(VD)); auto abiRole = ABIRoleInfo(VD); if (!abiRole.providesAPI() && abiRole.getCounterpart()) return getNameForObjC(abiRole.getCounterpart(), customNamesOnly); if (auto objc = VD->getAttrs().getAttribute()) { if (auto name = objc->getName()) { assert(name->getNumSelectorPieces() == 1); return name->getSelectorPieces().front().str(); } } if (auto cdeclAttr = VD->getAttrs().getAttribute()) if (!customNamesOnly || !cdeclAttr->Name.empty()) return VD->getCDeclName(); if (customNamesOnly) return StringRef(); if (auto clangDecl = dyn_cast_or_null(VD->getClangDecl())) { if (const clang::IdentifierInfo *II = clangDecl->getIdentifier()) return II->getName(); if (auto *anonDecl = dyn_cast(clangDecl)) { if (auto *anonTypedef = anonDecl->getTypedefNameForAnonDecl()) return anonTypedef->getIdentifier()->getName(); if (auto *cfOptionsTy = VD->getASTContext() .getClangModuleLoader() ->getTypeDefForCXXCFOptionsDefinition(anonDecl)) return cfOptionsTy->getDecl()->getName(); } } return VD->getBaseIdentifier().str(); } std::string swift::objc_translation:: getErrorDomainStringForObjC(const EnumDecl *ED) { // Should have already been diagnosed as diag::objc_enum_generic. assert(!ED->isGenericContext() && "Trying to bridge generic enum error to Obj-C"); auto abiRole = ABIRoleInfo(ED); if (!abiRole.providesAPI() && abiRole.getCounterpart()) return getErrorDomainStringForObjC(abiRole.getCounterpart()); SmallVector outerTypes; for (const NominalTypeDecl * D = ED; D != nullptr; D = D->getDeclContext()->getSelfNominalTypeDecl()) { // We don't currently PrintAsClang any types whose parents are private or // fileprivate. assert(D->getFormalAccess() >= AccessLevel::Internal && "We don't currently append private discriminators"); outerTypes.push_back(D); } std::string buffer = ED->getParentModule()->getNameStr().str(); for (auto D : llvm::reverse(outerTypes)) { buffer += "."; buffer += D->getNameStr(); } return buffer; } bool swift::objc_translation:: printSwiftEnumElemNameInObjC(const EnumElementDecl *EL, llvm::raw_ostream &OS, Identifier PreferredName) { auto abiRole = ABIRoleInfo(EL); if (!abiRole.providesAPI() && abiRole.getCounterpart()) return printSwiftEnumElemNameInObjC(abiRole.getCounterpart(), OS, PreferredName); StringRef ElemName = getNameForObjC(EL, CustomNamesOnly); if (!ElemName.empty()) { OS << ElemName; return true; } OS << getNameForObjC(EL->getDeclContext()->getSelfEnumDecl()); if (PreferredName.empty()) ElemName = EL->getBaseIdentifier().str(); else ElemName = PreferredName.str(); SmallString<64> Scratch; OS << camel_case::toSentencecase(ElemName, Scratch); return false; } std::pair swift::objc_translation:: getObjCNameForSwiftDecl(const ValueDecl *VD, DeclName PreferredName){ auto abiRole = ABIRoleInfo(VD); if (!abiRole.providesAPI() && abiRole.getCounterpart()) return getObjCNameForSwiftDecl(abiRole.getCounterpart(), PreferredName); ASTContext &Ctx = VD->getASTContext(); Identifier BaseName; if (PreferredName) { auto BaseNameStr = PreferredName.getBaseName().userFacingName(); BaseName = Ctx.getIdentifier(BaseNameStr); } if (auto *FD = dyn_cast(VD)) { return {Identifier(), FD->getObjCSelector(PreferredName)}; } else if (auto *VAD = dyn_cast(VD)) { if (PreferredName) return {BaseName, ObjCSelector()}; return {VAD->getObjCPropertyName(), ObjCSelector()}; } else if (auto *SD = dyn_cast(VD)) { return getObjCNameForSwiftDecl(SD->getParsedAccessor(AccessorKind::Get), PreferredName); } else if (auto *EL = dyn_cast(VD)) { SmallString<64> Buffer; { llvm::raw_svector_ostream OS(Buffer); printSwiftEnumElemNameInObjC(EL, OS, BaseName); } return {Ctx.getIdentifier(Buffer.str()), ObjCSelector()}; } else { // @objc(ExplicitName) > PreferredName > Swift name. StringRef Name = getNameForObjC(VD, CustomNamesOnly); if (!Name.empty()) return {Ctx.getIdentifier(Name), ObjCSelector()}; if (PreferredName) return {BaseName, ObjCSelector()}; return {Ctx.getIdentifier(getNameForObjC(VD)), ObjCSelector()}; } } bool swift::objc_translation:: isVisibleToObjC(const ValueDecl *VD, AccessLevel minRequiredAccess, bool checkParent) { if (!(VD->isObjC() || !VD->getCDeclName().empty())) return false; if (VD->getFormalAccess() >= minRequiredAccess) { return true; } else if (checkParent) { if (auto ctor = dyn_cast(VD)) { // Check if we're overriding an initializer that is visible to obj-c if (auto parent = ctor->getOverriddenDecl()) return isVisibleToObjC(parent, minRequiredAccess, false); } } return false; } StringRef swift::cxx_translation::getNameForCxx(const ValueDecl *VD, CustomNamesOnly_t customNamesOnly) { auto abiRole = ABIRoleInfo(VD); if (!abiRole.providesAPI() && abiRole.getCounterpart()) return getNameForCxx(abiRole.getCounterpart(), customNamesOnly); ASTContext& ctx = VD->getASTContext(); for (auto *EA : VD->getAttrs().getAttributes()) { if (EA->getExposureKind() == ExposureKind::Cxx && !EA->Name.empty()) return EA->Name; } if (customNamesOnly) return StringRef(); if (isa(VD)) return "init"; if (VD->isOperator()) { std::string name = ("operator" + VD->getBaseIdentifier().str()).str(); return ctx.getIdentifier(name).str(); } if (auto *mod = dyn_cast(VD)) { if (mod->isStdlibModule()) return "swift"; } if (VD->getModuleContext()->isStdlibModule()) { // Incorporate argument labels into Stdlib API names. // FIXME: This should be done more broadly. if (auto *AFD = dyn_cast(VD)) { std::string result; llvm::raw_string_ostream os(result); os << VD->getBaseIdentifier().str(); if (!AFD->getParameters()) return os.str(); for (const auto *param : *AFD->getParameters()) { auto paramName = param->getArgumentName(); if (paramName.empty()) continue; auto paramNameStr = paramName.str(); os << char(std::toupper(paramNameStr[0])); os << paramNameStr.drop_front(1); } auto r = ctx.getIdentifier(os.str()); return r.str(); } } return VD->getBaseIdentifier().str(); } namespace { struct ObjCTypeWalker : TypeWalker { bool hadObjCType = false; const ASTContext &ctx; ObjCTypeWalker(const ASTContext &ctx) : ctx(ctx) {} Action walkToTypePre(Type ty) override { if (auto *nominal = ty->getNominalOrBoundGenericNominal()) { if (auto clangDecl = nominal->getClangDecl()) { if (cxx_translation::isObjCxxOnly(clangDecl, ctx)) { hadObjCType = true; return Action::Stop; } } } return Action::Continue; } }; } // namespace bool swift::cxx_translation::isObjCxxOnly(const ValueDecl *VD) { ObjCTypeWalker walker{VD->getASTContext()}; VD->getInterfaceType().walk(walker); return walker.hadObjCType; } bool swift::cxx_translation::isObjCxxOnly(const clang::Decl *D, const ASTContext &ctx) { // By default, we import all modules in Obj-C++ mode, so there is no robust // way to tell if something is coming from an Obj-C module. Use the // requirements and the language options to check if we should actually // consider the module to have ObjC constructs. const auto &langOpts = D->getASTContext().getLangOpts(); // TODO: have a reasonable guess for headers specified via // `-import-objc-header`. if (!D->hasOwningModule()) return false; auto clangModule = D->getOwningModule()->getTopLevelModule(); bool requiresObjC = false; for (auto req : clangModule->Requirements) if (req.RequiredState && req.FeatureName == "objc") requiresObjC = true; return langOpts.ObjC && (requiresObjC || llvm::any_of(ctx.LangOpts.ModulesRequiringObjC, [clangModule](StringRef moduleName) { return clangModule->getFullModuleName() == moduleName; })); } swift::cxx_translation::DeclRepresentation swift::cxx_translation::getDeclRepresentation( const ValueDecl *VD, std::optional> isZeroSized) { auto abiRole = ABIRoleInfo(VD); if (!abiRole.providesAPI() && abiRole.getCounterpart()) return getDeclRepresentation(abiRole.getCounterpart(), isZeroSized); if (getActorIsolation(const_cast(VD)).isActorIsolated()) return {Unsupported, UnrepresentableIsolatedInActor}; if (isa(VD)) return {Unsupported, UnrepresentableMacro}; GenericSignature genericSignature; // Don't expose decls with definitions that are emitted into the client. if (VD->isAlwaysEmittedIntoClient()) return {Unsupported, UnrepresentableRequiresClientEmission}; if (auto *AFD = dyn_cast(VD)) { if (AFD->hasAsync()) return {Unsupported, UnrepresentableAsync}; if (AFD->hasThrows() && !AFD->getASTContext().LangOpts.hasFeature( Feature::GenerateBindingsForThrowingFunctionsInCXX)) return {Unsupported, UnrepresentableThrows}; if (AFD->hasGenericParamList()) genericSignature = AFD->getGenericSignature(); } if (const auto *typeDecl = dyn_cast(VD)) { if (isa(typeDecl)) { if (typeDecl->hasClangNode()) return {ObjCxxOnly, std::nullopt}; return {Unsupported, UnrepresentableProtocol}; } // Swift's consume semantics are not yet supported in C++. if (!typeDecl->canBeCopyable()) return {Unsupported, UnrepresentableMoveOnly}; if (isa(VD) && VD->isObjC()) return {Unsupported, UnrepresentableObjC}; if (typeDecl->hasGenericParamList()) { if (isa(VD)) return {Unsupported, UnrepresentableGeneric}; genericSignature = typeDecl->getGenericSignature(); } if (!isa(typeDecl) && isZeroSized && (*isZeroSized)(typeDecl)) return {Unsupported, UnrepresentableZeroSizedValueType}; } if (const auto *varDecl = dyn_cast(VD)) { // Check if any property accessor throws, do not expose it in that case. for (const auto *accessor : varDecl->getAllAccessors()) { if (accessor->hasThrows()) return {Unsupported, UnrepresentableThrows}; } } if (const auto *enumDecl = dyn_cast(VD)) { if (enumDecl->isIndirect()) return {Unsupported, UnrepresentableIndirectEnum}; for (const auto *enumCase : enumDecl->getAllCases()) { for (const auto *elementDecl : enumCase->getElements()) { if (!elementDecl->hasAssociatedValues()) continue; if (elementDecl->isIndirect()) return {Unsupported, UnrepresentableIndirectEnum}; // Do not expose any enums with > 1 // enum parameter, or any enum parameter // whose type we do not yet support. if (auto *params = elementDecl->getParameterList()) { if (params->size() > 1) return {Unsupported, UnrepresentableEnumCaseTuple}; for (const auto *param : *params) { auto paramType = param->getInterfaceType(); if (!paramType->is()) { auto *nominal = paramType->getNominalOrBoundGenericNominal(); if (!nominal || isa(nominal)) return {Unsupported, UnrepresentableEnumCaseType}; } } } } } } // Generic requirements are not yet supported in C++. if (!isExposableToCxx(genericSignature)) { return {Unsupported, UnrepresentableGenericRequirements}; } if (isObjCxxOnly(VD)) return {ObjCxxOnly, std::nullopt}; return {Representable, std::nullopt}; } bool swift::cxx_translation::isVisibleToCxx(const ValueDecl *VD, AccessLevel minRequiredAccess, bool checkParent) { // Do not expose anything from _Concurrency module yet. if (VD->getModuleContext()->ValueDecl::getName().getBaseIdentifier() == VD->getASTContext().Id_Concurrency) return false; if (VD->getFormalAccess() >= minRequiredAccess) { return true; } else if (checkParent) { if (auto ctor = dyn_cast(VD)) { // Check if we're overriding an initializer that is visible to obj-c if (auto parent = ctor->getOverriddenDecl()) return isVisibleToCxx(parent, minRequiredAccess, false); } } return false; } bool swift::cxx_translation::isExposableToCxx(GenericSignature genericSig) { // If there's no generic signature, it's fine. if (!genericSig) return true; // FIXME: This should use getRequirements() and actually // support arbitrary requirements. We don't really want // to use getRequirementsWithInverses() here. // // For now, we use the inverse transform as a quick way to // check for the "default" generic signature where each // generic parameter is Copyable and Escapable, but not // subject to any other requirements; that's exactly the // generic signature that C++ interop supports today. SmallVector reqs; SmallVector inverseReqs; genericSig->getRequirementsWithInverses(reqs, inverseReqs); if (!reqs.empty()) { // Conformance requirements to marker protocols are okay. for (const auto &req: reqs) { if (req.getKind() != RequirementKind::Conformance) return false; auto proto = req.getProtocolDecl(); if (!proto->isMarkerProtocol() && !proto->hasClangNode()) return false; } } // Allow Copyable and Escapable. for (const auto &req: inverseReqs) { switch (req.getKind()) { case InvertibleProtocolKind::Copyable: continue; case InvertibleProtocolKind::Escapable: continue; } return false; } return true; } Diagnostic swift::cxx_translation::diagnoseRepresenationError(RepresentationError error, ValueDecl *vd) { switch (error) { case UnrepresentableObjC: return Diagnostic(diag::expose_unsupported_objc_decl_to_cxx, vd); case UnrepresentableAsync: return Diagnostic(diag::expose_unsupported_async_decl_to_cxx, vd); case UnrepresentableIsolatedInActor: return Diagnostic(diag::expose_unsupported_actor_isolated_to_cxx, vd); case UnrepresentableRequiresClientEmission: return Diagnostic(diag::expose_unsupported_client_emission_to_cxx, vd); case UnrepresentableGeneric: return Diagnostic(diag::expose_generic_decl_to_cxx, vd); case UnrepresentableGenericRequirements: return Diagnostic(diag::expose_generic_requirement_to_cxx, vd); case UnrepresentableThrows: return Diagnostic(diag::expose_throwing_to_cxx, vd); case UnrepresentableIndirectEnum: return Diagnostic(diag::expose_indirect_enum_cxx, vd); case UnrepresentableEnumCaseType: return Diagnostic(diag::expose_enum_case_type_to_cxx, vd); case UnrepresentableEnumCaseTuple: return Diagnostic(diag::expose_enum_case_tuple_to_cxx, vd); case UnrepresentableProtocol: return Diagnostic(diag::expose_protocol_to_cxx_unsupported, vd); case UnrepresentableMoveOnly: return Diagnostic(diag::expose_move_only_to_cxx, vd); case UnrepresentableMacro: return Diagnostic(diag::expose_macro_to_cxx, vd); case UnrepresentableZeroSizedValueType: return Diagnostic(diag::expose_zero_size_to_cxx, vd); } }