| //===--- ClangTypeConverter.cpp - Convert Swift types to C types ----------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements generation of Clang AST types from Swift AST types for |
| // types that are representable in Objective-C interfaces. |
| // |
| // The usage of ClangTypeConverter at the AST level means that we may |
| // encounter ill-formed types and/or sugared types. To avoid crashing and |
| // keeping sugar as much as possible (in case the generated Clang type needs |
| // to be surfaced to the user): |
| // |
| // 1. We fail gracefully instead of asserting/UB. |
| // 2. We try to keep clang sugar instead of discarding it. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ClangTypeConverter.h" |
| |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/ClangModuleLoader.h" |
| #include "swift/AST/ClangSwiftTypeCorrespondence.h" |
| #include "swift/AST/ExistentialLayout.h" |
| #include "swift/AST/Module.h" |
| #include "swift/AST/Type.h" |
| #include "swift/AST/TypeVisitor.h" |
| #include "swift/Basic/LLVM.h" |
| |
| #include "clang/AST/ASTContext.h" |
| #include "clang/Basic/TargetInfo.h" |
| #include "clang/Sema/Sema.h" |
| |
| using namespace swift; |
| |
| namespace { |
| |
| static Type getNamedSwiftType(ModuleDecl *stdlib, StringRef name) { |
| auto &ctx = stdlib->getASTContext(); |
| SmallVector<ValueDecl*, 1> results; |
| stdlib->lookupValue(ctx.getIdentifier(name), NLKind::QualifiedLookup, |
| results); |
| |
| // If we have one single type decl, and that decl has been |
| // type-checked, return its declared type. |
| // |
| // ...non-type-checked types should only ever show up here because |
| // of test cases using -enable-source-import, but unfortunately |
| // that's a real thing. |
| if (results.size() == 1) { |
| if (auto typeDecl = dyn_cast<TypeDecl>(results[0])) |
| return typeDecl->getDeclaredInterfaceType(); |
| } |
| return Type(); |
| } |
| |
| static clang::QualType |
| getClangBuiltinTypeFromKind(const clang::ASTContext &context, |
| clang::BuiltinType::Kind kind) { |
| switch (kind) { |
| #define BUILTIN_TYPE(Id, SingletonId) \ |
| case clang::BuiltinType::Id: \ |
| return context.SingletonId; |
| #include "clang/AST/BuiltinTypes.def" |
| #define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ |
| case clang::BuiltinType::Id: \ |
| return context.SingletonId; |
| #include "clang/Basic/OpenCLImageTypes.def" |
| #define EXT_OPAQUE_TYPE(ExtType, Id, Ext) \ |
| case clang::BuiltinType::Id: \ |
| return context.Id##Ty; |
| #include "clang/Basic/OpenCLExtensionTypes.def" |
| #define SVE_TYPE(Name, Id, SingletonId) \ |
| case clang::BuiltinType::Id: \ |
| return context.SingletonId; |
| #include "clang/Basic/AArch64SVEACLETypes.def" |
| } |
| |
| // Not a valid BuiltinType. |
| return clang::QualType(); |
| } |
| |
| static clang::QualType getClangSelectorType( |
| const clang::ASTContext &clangCtx) { |
| return clangCtx.getPointerType(clangCtx.ObjCBuiltinSelTy); |
| } |
| |
| static clang::QualType getClangMetatypeType( |
| const clang::ASTContext &clangCtx) { |
| clang::QualType clangType = |
| clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinClassTy, nullptr, 0); |
| return clangCtx.getObjCObjectPointerType(clangType); |
| } |
| |
| static clang::QualType getClangIdType( |
| const clang::ASTContext &clangCtx) { |
| clang::QualType clangType = |
| clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, nullptr, 0); |
| return clangCtx.getObjCObjectPointerType(clangType); |
| } |
| |
| static clang::QualType getClangDecayedVaListType( |
| const clang::ASTContext &clangCtx) { |
| clang::QualType clangType = clangCtx.getBuiltinVaListType(); |
| if (clangType->isConstantArrayType()) |
| clangType = clangCtx.getDecayedType(clangType); |
| return clangType; |
| } |
| |
| } // end anonymous namespace |
| |
| const clang::Type *ClangTypeConverter::getFunctionType( |
| ArrayRef<AnyFunctionType::Param> params, Type resultTy, |
| AnyFunctionType::Representation repr) { |
| |
| #if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB |
| return nullptr; |
| #endif |
| |
| auto resultClangTy = convert(resultTy); |
| if (resultClangTy.isNull()) |
| return nullptr; |
| |
| SmallVector<clang::FunctionProtoType::ExtParameterInfo, 4> extParamInfos; |
| SmallVector<clang::QualType, 4> paramsClangTy; |
| bool someParamIsConsumed = false; |
| for (auto p : params) { |
| auto pc = convert(p.getPlainType()); |
| if (pc.isNull()) |
| return nullptr; |
| clang::FunctionProtoType::ExtParameterInfo extParamInfo; |
| if (p.getParameterFlags().isOwned()) { |
| someParamIsConsumed = true; |
| extParamInfo = extParamInfo.withIsConsumed(true); |
| } |
| extParamInfos.push_back(extParamInfo); |
| paramsClangTy.push_back(pc); |
| } |
| |
| clang::FunctionProtoType::ExtProtoInfo info(clang::CallingConv::CC_C); |
| if (someParamIsConsumed) |
| info.ExtParameterInfos = extParamInfos.begin(); |
| auto fn = ClangASTContext.getFunctionType(resultClangTy, paramsClangTy, info); |
| if (fn.isNull()) |
| return nullptr; |
| |
| switch (repr) { |
| case AnyFunctionType::Representation::CFunctionPointer: |
| return ClangASTContext.getPointerType(fn).getTypePtr(); |
| case AnyFunctionType::Representation::Block: |
| return ClangASTContext.getBlockPointerType(fn).getTypePtr(); |
| case AnyFunctionType::Representation::Swift: |
| case AnyFunctionType::Representation::Thin: |
| llvm_unreachable("Expected a C-compatible representation."); |
| } |
| llvm_unreachable("invalid representation"); |
| } |
| |
| const clang::Type *ClangTypeConverter::getFunctionType( |
| ArrayRef<SILParameterInfo> params, Optional<SILResultInfo> result, |
| SILFunctionType::Representation repr) { |
| |
| #if SWIFT_BUILD_ONLY_SYNTAXPARSERLIB |
| return nullptr; |
| #endif |
| |
| // Using the interface type is sufficient as type parameters get mapped to |
| // `id`, since ObjC lightweight generics use type erasure. (See also: SE-0057) |
| auto resultClangTy = result.hasValue() |
| ? convert(result.getValue().getInterfaceType()) |
| : ClangASTContext.VoidTy; |
| |
| if (resultClangTy.isNull()) |
| return nullptr; |
| |
| SmallVector<clang::FunctionProtoType::ExtParameterInfo, 4> extParamInfos; |
| SmallVector<clang::QualType, 4> paramsClangTy; |
| bool someParamIsConsumed = false; |
| for (auto &p : params) { |
| auto pc = convert(p.getInterfaceType()); |
| if (pc.isNull()) |
| return nullptr; |
| clang::FunctionProtoType::ExtParameterInfo extParamInfo; |
| if (p.isConsumed()) { |
| someParamIsConsumed = true; |
| extParamInfo = extParamInfo.withIsConsumed(true); |
| } |
| extParamInfos.push_back(extParamInfo); |
| paramsClangTy.push_back(pc); |
| } |
| |
| clang::FunctionProtoType::ExtProtoInfo info(clang::CallingConv::CC_C); |
| if (someParamIsConsumed) |
| info.ExtParameterInfos = extParamInfos.begin(); |
| auto fn = ClangASTContext.getFunctionType(resultClangTy, paramsClangTy, info); |
| if (fn.isNull()) |
| return nullptr; |
| |
| switch (repr) { |
| case SILFunctionType::Representation::CFunctionPointer: |
| return ClangASTContext.getPointerType(fn).getTypePtr(); |
| case SILFunctionType::Representation::Block: |
| return ClangASTContext.getBlockPointerType(fn).getTypePtr(); |
| case SILFunctionType::Representation::Thick: |
| case SILFunctionType::Representation::Thin: |
| case SILFunctionType::Representation::Method: |
| case SILFunctionType::Representation::ObjCMethod: |
| case SILFunctionType::Representation::WitnessMethod: |
| case SILFunctionType::Representation::Closure: |
| llvm_unreachable("Expected a C-compatible representation."); |
| } |
| llvm_unreachable("unhandled representation!"); |
| } |
| |
| clang::QualType ClangTypeConverter::convertMemberType(NominalTypeDecl *DC, |
| StringRef memberName) { |
| auto memberTypeDecl = cast<TypeDecl>( |
| DC->lookupDirect(Context.getIdentifier(memberName))[0]); |
| auto memberType = memberTypeDecl->getDeclaredInterfaceType(); |
| return convert(memberType); |
| } |
| |
| // TODO: It is unfortunate that we parse the name of a public library type |
| // in order to break it down into a vector component and length that in theory |
| // we could recover in some other way. |
| static clang::QualType getClangVectorType(const clang::ASTContext &ctx, |
| clang::BuiltinType::Kind eltKind, |
| clang::VectorType::VectorKind vecKind, |
| StringRef numEltsString) { |
| unsigned numElts; |
| bool failedParse = numEltsString.getAsInteger<unsigned>(10, numElts); |
| if (failedParse) |
| return clang::QualType(); |
| auto eltTy = getClangBuiltinTypeFromKind(ctx, eltKind); |
| if (eltTy.isNull()) |
| return clang::QualType(); |
| return ctx.getVectorType(eltTy, numElts, vecKind); |
| } |
| |
| clang::QualType ClangTypeConverter::visitStructType(StructType *type) { |
| auto &ctx = ClangASTContext; |
| |
| auto swiftDecl = type->getDecl(); |
| StringRef name = swiftDecl->getName().str(); |
| |
| // We assume that the importer translates all of the following types |
| // directly to structs in the standard library. |
| |
| // We want to recognize most of these types by name. |
| #define CHECK_NAMED_TYPE(NAME, CLANG_TYPE) do { \ |
| if (name == (NAME)) return CLANG_TYPE; \ |
| } while (false) |
| |
| CHECK_NAMED_TYPE("CGFloat", convertMemberType(swiftDecl, "NativeType")); |
| CHECK_NAMED_TYPE("OpaquePointer", ctx.VoidPtrTy); |
| CHECK_NAMED_TYPE("CVaListPointer", getClangDecayedVaListType(ctx)); |
| CHECK_NAMED_TYPE("DarwinBoolean", ctx.UnsignedCharTy); |
| CHECK_NAMED_TYPE(swiftDecl->getASTContext().getSwiftName( |
| KnownFoundationEntity::NSZone), |
| ctx.VoidPtrTy); |
| CHECK_NAMED_TYPE("WindowsBool", ctx.IntTy); |
| CHECK_NAMED_TYPE("ObjCBool", ctx.ObjCBuiltinBoolTy); |
| CHECK_NAMED_TYPE("Selector", getClangSelectorType(ctx)); |
| CHECK_NAMED_TYPE("UnsafeRawPointer", ctx.VoidPtrTy); |
| CHECK_NAMED_TYPE("UnsafeMutableRawPointer", ctx.VoidPtrTy); |
| #undef CHECK_NAMED_TYPE |
| |
| // Map vector types to the corresponding C vectors. |
| #define MAP_SIMD_TYPE(TYPE_NAME, _, BUILTIN_KIND) \ |
| if (name.startswith(#TYPE_NAME)) { \ |
| return getClangVectorType(ctx, clang::BuiltinType::BUILTIN_KIND, \ |
| clang::VectorType::GenericVector, \ |
| name.drop_front(sizeof(#TYPE_NAME)-1)); \ |
| } |
| #include "swift/ClangImporter/SIMDMappedTypes.def" |
| |
| // We might be looking at a builtin |
| auto ret = reverseBuiltinTypeMapping(type); |
| if (!ret.isNull()) |
| return ret; |
| |
| if (type->isPotentiallyBridgedValueType()) { |
| if (auto t = Context.getBridgedToObjC(type->getDecl(), type)) |
| return convert(t); |
| } |
| |
| // Out of ideas, there must've been some error. :( |
| return clang::QualType(); |
| } |
| |
| static clang::QualType |
| getClangBuiltinTypeFromTypedef(clang::Sema &sema, StringRef typedefName) { |
| auto &context = sema.getASTContext(); |
| auto identifier = &context.Idents.get(typedefName); |
| auto found = sema.LookupSingleName(sema.TUScope, identifier, |
| clang::SourceLocation(), |
| clang::Sema::LookupOrdinaryName); |
| auto typedefDecl = dyn_cast_or_null<clang::TypedefDecl>(found); |
| if (!typedefDecl) |
| return clang::QualType(); |
| |
| auto underlyingTy = |
| context.getCanonicalType(typedefDecl->getUnderlyingType()); |
| |
| if (underlyingTy->getAs<clang::BuiltinType>()) |
| return underlyingTy; |
| return clang::QualType(); |
| } |
| |
| clang::QualType |
| ClangTypeConverter::reverseBuiltinTypeMapping(StructType *type) { |
| // Handle builtin types by adding entries to the cache that reverse |
| // the mapping done by the importer. We could try to look at the |
| // members of the struct instead, but even if that's ABI-equivalent |
| // (which it had better be!), it might erase interesting semantic |
| // differences like integers vs. characters. This is important |
| // because CC lowering isn't the only purpose of this conversion. |
| // |
| // The importer maps builtin types like 'int' to named types like |
| // 'CInt', which are generally typealiases. So what we do here is |
| // map the underlying types of those typealiases back to the builtin |
| // type. These typealiases frequently create a many-to-one mapping, |
| // so just use the first type that mapped to a particular underlying |
| // type. |
| // |
| // This is the last thing that happens before asserting that the |
| // struct type doesn't have a mapping. Furthermore, all of the |
| // builtin types are pre-built in the clang ASTContext. So it's not |
| // really a significant performance problem to just cache all them |
| // right here; it makes making a few more entries in the cache than |
| // we really need, but it also means we won't end up repeating these |
| // stdlib lookups multiple times, and we have to perform multiple |
| // lookups anyway because the MAP_BUILTIN_TYPE database uses |
| // typealias names (like 'CInt') that aren't obviously associated |
| // with the underlying C library type. |
| |
| auto stdlib = Context.getStdlibModule(); |
| assert(stdlib && "translating stdlib type to C without stdlib module?"); |
| auto &ctx = ClangASTContext; |
| |
| if (!StdlibTypesAreCached) { |
| auto cacheStdlibType = [&](StringRef swiftName, |
| clang::BuiltinType::Kind builtinKind) { |
| Type swiftType = getNamedSwiftType(stdlib, swiftName); |
| if (!swiftType) return; |
| |
| auto &sema = Context.getClangModuleLoader()->getClangSema(); |
| |
| if (Context.LangOpts.EnableObjCInterop) { |
| // Handle Int and UInt specially. On Apple platforms, these map to |
| // the NSInteger and NSUInteger typedefs. So try that if the typedefs |
| // are available, to ensure we get consistent ObjC @encode strings. |
| if (swiftType->getAnyNominal() == Context.getIntDecl()) { |
| auto NSIntegerTy = getClangBuiltinTypeFromTypedef(sema, "NSInteger"); |
| if (!NSIntegerTy.isNull()) { |
| Cache.insert({swiftType->getCanonicalType(), NSIntegerTy}); |
| return; |
| } |
| } else if (swiftType->getAnyNominal() == Context.getUIntDecl()) { |
| auto NSUIntegerTy = getClangBuiltinTypeFromTypedef(sema, "NSUInteger"); |
| if (!NSUIntegerTy.isNull()) { |
| Cache.insert({swiftType->getCanonicalType(), NSUIntegerTy}); |
| return; |
| } |
| } |
| } |
| |
| // For something like `typealias CInt = Int32`, reverseBuiltinTypeMapping |
| // will get Int32 as the input, so we need to record the desugared type. |
| Cache.insert({swiftType->getCanonicalType(), |
| getClangBuiltinTypeFromKind(ctx, builtinKind)}); |
| }; |
| |
| #define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \ |
| cacheStdlibType(#SWIFT_TYPE_NAME, clang::BuiltinType::CLANG_BUILTIN_KIND); |
| #include "swift/ClangImporter/BuiltinMappedTypes.def" |
| |
| // On 64-bit Windows, no C type is imported as an Int or UInt; CLong is |
| // imported as an Int32 and CLongLong as an Int64. Therefore, manually |
| // add mappings to C for Int and UInt. |
| // On 64-bit Cygwin, no manual mapping is required. |
| if (Triple.isOSWindows() && Triple.isArch64Bit() |
| && !Triple.isWindowsCygwinEnvironment()) { |
| // Map UInt to uintptr_t |
| auto swiftUIntType = getNamedSwiftType(stdlib, "UInt"); |
| auto clangUIntPtrType = ctx.getCanonicalType(ctx.getUIntPtrType()); |
| Cache.insert({swiftUIntType, clangUIntPtrType}); |
| |
| // Map Int to intptr_t |
| auto swiftIntType = getNamedSwiftType(stdlib, "Int"); |
| auto clangIntPtrType = ctx.getCanonicalType(ctx.getIntPtrType()); |
| Cache.insert({swiftIntType, clangIntPtrType}); |
| } |
| StdlibTypesAreCached = true; |
| } |
| |
| auto it = Cache.find(Type(type)); |
| if (it != Cache.end()) |
| return it->second; |
| |
| it = Cache.find(type->getCanonicalType()); |
| if (it != Cache.end()) { |
| Cache.insert({Type(type), it->second}); |
| return it->second; |
| } |
| |
| return clang::QualType(); |
| } |
| |
| clang::QualType ClangTypeConverter::visitTupleType(TupleType *type) { |
| unsigned tupleNumElements = type->getNumElements(); |
| if (tupleNumElements == 0) |
| return ClangASTContext.VoidTy; |
| |
| Type eltTy = type->getElementType(0); |
| for (unsigned i = 1; i < tupleNumElements; ++i) { |
| if (!eltTy->isEqual(type->getElementType(i))) |
| // Only tuples where all element types are equal map to fixed-size |
| // arrays. |
| return clang::QualType(); |
| } |
| |
| auto clangEltTy = convert(eltTy); |
| if (clangEltTy.isNull()) |
| return clang::QualType(); |
| |
| APInt size(32, tupleNumElements); |
| return ClangASTContext.getConstantArrayType(clangEltTy, size, nullptr, |
| clang::ArrayType::Normal, 0); |
| } |
| |
| clang::QualType ClangTypeConverter::visitProtocolType(ProtocolType *type) { |
| auto proto = type->getDecl(); |
| auto &clangCtx = ClangASTContext; |
| |
| if (!proto->isObjC()) |
| return clang::QualType(); |
| |
| assert(!cast_or_null<clang::ObjCProtocolDecl>(proto->getClangDecl()) |
| && "We shouldn't be creating duplicate decls; see `convert`"); |
| |
| // Single protocol -> id<Proto> |
| clang::IdentifierInfo *name = &clangCtx.Idents.get(proto->getName().get()); |
| auto *PDecl = clang::ObjCProtocolDecl::Create( |
| const_cast<clang::ASTContext &>(clangCtx), |
| clangCtx.getTranslationUnitDecl(), name, |
| clang::SourceLocation(), clang::SourceLocation(), nullptr); |
| |
| // Attach an objc_runtime_name attribute with the Objective-C name to use |
| // for this protocol. |
| SmallString<64> runtimeNameBuffer; |
| PDecl->addAttr(clang::ObjCRuntimeNameAttr::CreateImplicit( |
| PDecl->getASTContext(), |
| proto->getObjCRuntimeName(runtimeNameBuffer))); |
| |
| registerExportedClangDecl(proto, PDecl); |
| |
| auto clangType = clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy, |
| &PDecl, 1); |
| return clangCtx.getObjCObjectPointerType(clangType); |
| } |
| |
| // TODO: [stronger-checking-in-clang-type-conversion] |
| // Metatypes can be converted to Class when they are metatypes for concrete |
| // classes. https://github.com/apple/swift/pull/27479#discussion_r344418131 |
| clang::QualType ClangTypeConverter::visitMetatypeType(MetatypeType *type) { |
| return getClangMetatypeType(ClangASTContext); |
| } |
| |
| // TODO: [stronger-checking-in-clang-type-conversion] |
| // Existential metatypes where the base is a non-metatype existential can be |
| // converted to Class<P, Q, ...> when the protocols are all ObjC. |
| // https://github.com/apple/swift/pull/27479#discussion_r344418131 |
| clang::QualType |
| ClangTypeConverter::visitExistentialMetatypeType(ExistentialMetatypeType *type) { |
| return getClangMetatypeType(ClangASTContext); |
| } |
| |
| clang::QualType ClangTypeConverter::visitClassType(ClassType *type) { |
| auto &clangCtx = ClangASTContext; |
| auto swiftDecl = type->getDecl(); |
| |
| // TODO: [non-objc-class-clang-type-conversion] |
| // See the corresponding note in GenClangType.cpp |
| if (!swiftDecl->isObjC()) |
| return getClangIdType(clangCtx); |
| |
| assert(!cast_or_null<clang::ObjCInterfaceDecl>(swiftDecl->getClangDecl()) |
| && "We shouldn't be creating duplicate decls; see `convert`"); |
| |
| // produce the clang type INTF * if it is imported ObjC object. |
| clang::IdentifierInfo *ForwardClassId = |
| &clangCtx.Idents.get(swiftDecl->getName().get()); |
| auto *CDecl = clang::ObjCInterfaceDecl::Create( |
| clangCtx, clangCtx.getTranslationUnitDecl(), |
| clang::SourceLocation(), ForwardClassId, |
| /*typeParamList*/nullptr, /*PrevDecl=*/nullptr, |
| clang::SourceLocation()); |
| |
| // Attach an objc_runtime_name attribute with the Objective-C name to use |
| // for this class. |
| SmallString<64> runtimeNameBuffer; |
| CDecl->addAttr(clang::ObjCRuntimeNameAttr::CreateImplicit( |
| CDecl->getASTContext(), |
| swiftDecl->getObjCRuntimeName(runtimeNameBuffer))); |
| |
| registerExportedClangDecl(swiftDecl, CDecl); |
| |
| auto clangType = clangCtx.getObjCInterfaceType(CDecl); |
| return clangCtx.getObjCObjectPointerType(clangType); |
| } |
| |
| // TODO: We should try to preserve type arguments on imported ObjC generic |
| // classes, instead of relying on our knowledge of clang's encoding. |
| // This would entail extracting the type arguments, calling `convert` to |
| // create clang types, extracting the ObjCProtocolDecls and then using |
| // getObjCObjectType with `id` as the base. |
| clang::QualType |
| ClangTypeConverter::visitBoundGenericClassType(BoundGenericClassType *type) { |
| // Any @objc class type in Swift that shows up in an @objc method maps 1-1 to |
| // "id <SomeProto>"; with clang's encoding ignoring the protocol list. |
| return getClangIdType(ClangASTContext); |
| } |
| |
| clang::QualType |
| ClangTypeConverter::visitBoundGenericType(BoundGenericType *type) { |
| // The only possibilities are *Pointer<T>, SIMD*<T> and Optional<T>. |
| |
| if (type->getDecl()->isOptionalDecl()) { |
| auto args = type->getGenericArgs(); |
| assert((args.size() == 1) && "Optional should have 1 generic argument."); |
| clang::QualType innerTy = convert(args[0]); |
| if (swift::canImportAsOptional(innerTy.getTypePtrOrNull())) |
| return innerTy; |
| return clang::QualType(); |
| } |
| |
| auto swiftStructDecl = type->getDecl(); |
| |
| enum class StructKind { |
| Invalid, |
| UnsafeMutablePointer, |
| UnsafePointer, |
| AutoreleasingUnsafeMutablePointer, |
| Unmanaged, |
| CFunctionPointer, |
| SIMD, |
| } kind = llvm::StringSwitch<StructKind>(swiftStructDecl->getName().str()) |
| .Case("UnsafeMutablePointer", StructKind::UnsafeMutablePointer) |
| .Case("UnsafePointer", StructKind::UnsafePointer) |
| .Case("AutoreleasingUnsafeMutablePointer", |
| StructKind::AutoreleasingUnsafeMutablePointer) |
| .Case("Unmanaged", StructKind::Unmanaged) |
| .Case("CFunctionPointer", StructKind::CFunctionPointer) |
| .StartsWith("SIMD", StructKind::SIMD) |
| .Default(StructKind::Invalid); |
| |
| auto args = type->getGenericArgs(); |
| if (args.size() != 1) |
| // Must've got something other than *Pointer or SIMD* |
| return clang::QualType(); |
| auto argCanonicalTy = args[0]->getCanonicalType(); |
| |
| switch (kind) { |
| case StructKind::Invalid: |
| return clang::QualType(); |
| |
| case StructKind::Unmanaged: |
| return convert(argCanonicalTy); |
| |
| case StructKind::UnsafeMutablePointer: |
| case StructKind::AutoreleasingUnsafeMutablePointer: { |
| auto clangTy = convert(argCanonicalTy); |
| if (clangTy.isNull()) |
| return clang::QualType(); |
| return ClangASTContext.getPointerType(clangTy); |
| } |
| case StructKind::UnsafePointer: { |
| auto clangTy = convert(argCanonicalTy); |
| if (clangTy.isNull()) |
| return clang::QualType(); |
| return ClangASTContext.getPointerType(clangTy.withConst()); |
| } |
| |
| case StructKind::CFunctionPointer: { |
| auto &clangCtx = ClangASTContext; |
| |
| clang::QualType functionTy; |
| if (isa<SILFunctionType>(argCanonicalTy->getCanonicalType())) { |
| functionTy = convert(argCanonicalTy); |
| if (functionTy.isNull()) |
| return clang::QualType(); |
| } else { |
| // Fall back to void(). |
| functionTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy); |
| } |
| return clangCtx.getPointerType(functionTy); |
| } |
| |
| case StructKind::SIMD: { |
| clang::QualType scalarTy = convert(argCanonicalTy); |
| if (scalarTy.isNull()) |
| return clang::QualType(); |
| auto numEltsString = swiftStructDecl->getName().str(); |
| numEltsString.consume_front("SIMD"); |
| unsigned numElts; |
| bool failedParse = numEltsString.getAsInteger<unsigned>(10, numElts); |
| if (failedParse) |
| return clang::QualType(); |
| (void) failedParse; |
| auto vectorTy = ClangASTContext.getVectorType(scalarTy, numElts, |
| clang::VectorType::VectorKind::GenericVector); |
| return vectorTy; |
| } |
| } |
| |
| llvm_unreachable("Not a valid StructKind."); |
| } |
| |
| clang::QualType ClangTypeConverter::visitEnumType(EnumType *type) { |
| // Special case: Uninhabited enums are not @objc, so we don't |
| // know what to do below, but we can just convert to 'void'. |
| if (type->isUninhabited()) |
| return convert(Context.TheEmptyTupleType); |
| |
| if (!type->getDecl()->isObjC()) |
| // Can't translate something not marked with @objc |
| return clang::QualType(); |
| |
| // @objc enums lower to their raw types. |
| return convert(type->getDecl()->getRawType()); |
| } |
| |
| clang::QualType ClangTypeConverter::visitFunctionType(FunctionType *type) { |
| const clang::Type *clangTy = nullptr; |
| auto repr = type->getRepresentation(); |
| bool useClangTypes = type->getASTContext().LangOpts.UseClangFunctionTypes; |
| if (useClangTypes && (getSILFunctionLanguage(convertRepresentation(repr)) == |
| SILFunctionLanguage::C)) { |
| clangTy = type->getClangTypeInfo().getType(); |
| } else if (!useClangTypes || repr == FunctionTypeRepresentation::Swift) { |
| // C function pointer types themselves are not bridged but their components |
| // can be. If a component is an @convention(block) function, it may be |
| // bridged to a Swift function type. |
| auto newRepr = (repr == FunctionTypeRepresentation::Swift |
| ? FunctionTypeRepresentation::Block |
| : repr); |
| clangTy = getFunctionType(type->getParams(), type->getResult(), newRepr); |
| } |
| return clang::QualType(clangTy, 0); |
| } |
| |
| clang::QualType ClangTypeConverter::visitSILFunctionType(SILFunctionType *type) { |
| const clang::Type *clangTy = nullptr; |
| auto repr = type->getRepresentation(); |
| bool useClangTypes = type->getASTContext().LangOpts.UseClangFunctionTypes; |
| if (useClangTypes && |
| (getSILFunctionLanguage(repr) == SILFunctionLanguage::C)) { |
| clangTy = type->getClangTypeInfo().getType(); |
| } else if (!useClangTypes || repr == SILFunctionTypeRepresentation::Thick) { |
| // C function pointer types themselves are not bridged but their components |
| // can be. If a component is an @convention(block) function, it may be |
| // bridged to a Swift function type. |
| auto newRepr = (repr == SILFunctionTypeRepresentation::Thick |
| ? SILFunctionTypeRepresentation::Block |
| : repr); |
| auto results = type->getResults(); |
| auto optionalResult = |
| results.empty() ? None : llvm::Optional<SILResultInfo>(results[0]); |
| clangTy = getFunctionType(type->getParameters(), optionalResult, newRepr); |
| } |
| return clang::QualType(clangTy, 0); |
| } |
| |
| clang::QualType |
| ClangTypeConverter::visitSILBlockStorageType(SILBlockStorageType *type) { |
| // We'll select (void)(^)(). This isn't correct for all blocks, but block |
| // storage type should only be converted for function signature lowering, |
| // where the parameter types do not matter. |
| auto &clangCtx = ClangASTContext; |
| auto fnTy = clangCtx.getFunctionNoProtoType(clangCtx.VoidTy); |
| auto blockTy = clangCtx.getBlockPointerType(fnTy); |
| return clangCtx.getCanonicalType(blockTy); |
| } |
| |
| clang::QualType |
| ClangTypeConverter::visitProtocolCompositionType(ProtocolCompositionType *type) { |
| // Any will be lowered to AnyObject, so we return the same result. |
| if (type->isAny()) |
| return getClangIdType(ClangASTContext); |
| |
| auto &clangCtx = ClangASTContext; |
| |
| // FIXME. Eventually, this will have its own helper routine. |
| SmallVector<const clang::ObjCProtocolDecl *, 4> Protocols; |
| auto layout = type->getExistentialLayout(); |
| if (!layout.isObjC()) |
| // Cannot represent opaque existential in Clang |
| return clang::QualType(); |
| |
| // AnyObject -> id. |
| if (layout.isAnyObject()) |
| return getClangIdType(ClangASTContext); |
| |
| auto superclassTy = clangCtx.ObjCBuiltinIdTy; |
| if (auto layoutSuperclassTy = layout.getSuperclass()) { |
| auto clangTy = convert(layoutSuperclassTy); |
| if (clangTy.isNull()) |
| return clang::QualType(); |
| superclassTy = clangCtx.getCanonicalType( |
| clangTy->getAs<clang::ObjCObjectPointerType>()->getPointeeType()); |
| } |
| |
| for (Type t : layout.getProtocols()) { |
| auto clangTy = convert(t); |
| if (clangTy.isNull()) |
| return clang::QualType(); |
| for (auto p : clangTy->getAs<clang::ObjCObjectPointerType>()->quals()) |
| Protocols.push_back(p); |
| } |
| |
| if (Protocols.empty()) |
| return superclassTy; |
| |
| // id<protocol-list> |
| clang::ObjCProtocolDecl **ProtoQuals = |
| new(clangCtx) clang::ObjCProtocolDecl*[Protocols.size()]; |
| memcpy(ProtoQuals, Protocols.data(), |
| sizeof(clang::ObjCProtocolDecl*)*Protocols.size()); |
| auto clangType = clangCtx.getObjCObjectType(superclassTy, |
| ProtoQuals, |
| Protocols.size()); |
| return clangCtx.getObjCObjectPointerType(clangType); |
| } |
| |
| clang::QualType |
| ClangTypeConverter::visitBuiltinRawPointerType(BuiltinRawPointerType *type) { |
| return ClangASTContext.VoidPtrTy; |
| } |
| |
| clang::QualType |
| ClangTypeConverter::visitBuiltinIntegerType(BuiltinIntegerType *type) { |
| auto &clangCtx = ClangASTContext; |
| if (type->getWidth().isPointerWidth()) { |
| return clangCtx.getUIntPtrType(); |
| } |
| assert(type->getWidth().isFixedWidth()); |
| auto width = type->getWidth().getFixedWidth(); |
| if (width == 1) |
| return clangCtx.BoolTy; |
| return clangCtx.getIntTypeForBitwidth(width, /*signed*/ 0); |
| } |
| |
| clang::QualType |
| ClangTypeConverter::visitBuiltinFloatType(BuiltinFloatType *type) { |
| auto &clangCtx = ClangASTContext; |
| auto &clangTargetInfo = clangCtx.getTargetInfo(); |
| const llvm::fltSemantics *format = &type->getAPFloatSemantics(); |
| if (format == &clangTargetInfo.getHalfFormat()) |
| return clangCtx.HalfTy; |
| if (format == &clangTargetInfo.getFloatFormat()) |
| return clangCtx.FloatTy; |
| if (format == &clangTargetInfo.getDoubleFormat()) |
| return clangCtx.DoubleTy; |
| if (format == &clangTargetInfo.getLongDoubleFormat()) |
| return clangCtx.LongDoubleTy; |
| llvm_unreachable("cannot translate floating-point format to C"); |
| } |
| |
| clang::QualType ClangTypeConverter::visitArchetypeType(ArchetypeType *type) { |
| // We see these in the case where we invoke an @objc function |
| // through a protocol. |
| return getClangIdType(ClangASTContext); |
| } |
| |
| clang::QualType ClangTypeConverter::visitDynamicSelfType(DynamicSelfType *type) { |
| // Dynamic Self is equivalent to 'instancetype', which is treated as |
| // 'id' within the Objective-C type system. |
| return getClangIdType(ClangASTContext); |
| } |
| |
| clang::QualType |
| ClangTypeConverter::visitGenericTypeParamType(GenericTypeParamType *type) { |
| // We see these in the case where we invoke an @objc function |
| // through a protocol argument that is a generic type. |
| return getClangIdType(ClangASTContext); |
| } |
| |
| clang::QualType |
| ClangTypeConverter::visitSugarType(SugarType *type) { |
| return convert(Type(type->getDesugaredType())); |
| } |
| |
| clang::QualType |
| ClangTypeConverter::visitType(TypeBase *type) { |
| // We only convert specific types. |
| return clang::QualType(); |
| } |
| |
| clang::QualType ClangTypeConverter::visit(Type type) { |
| return static_cast<super *>(this)->visit(type); |
| } |
| |
| clang::QualType ClangTypeConverter::convert(Type type) { |
| auto it = Cache.find(type); |
| if (it != Cache.end()) |
| return it->second; |
| |
| // Try to do this without making cache entries for obvious cases. |
| if (auto nominal = type->getAs<NominalType>()) { |
| auto decl = nominal->getDecl(); |
| if (auto clangDecl = decl->getClangDecl()) { |
| auto &ctx = ClangASTContext; |
| if (auto clangTypeDecl = dyn_cast<clang::TypeDecl>(clangDecl)) { |
| return ctx.getTypeDeclType(clangTypeDecl).getUnqualifiedType(); |
| } else if (auto ifaceDecl = dyn_cast<clang::ObjCInterfaceDecl>(clangDecl)) { |
| auto clangType = ctx.getObjCInterfaceType(ifaceDecl); |
| return ctx.getObjCObjectPointerType(clangType); |
| } else if (auto protoDecl = dyn_cast<clang::ObjCProtocolDecl>(clangDecl)){ |
| auto clangType = ctx.getObjCObjectType( |
| ctx.ObjCBuiltinIdTy, |
| const_cast<clang::ObjCProtocolDecl **>(&protoDecl), |
| 1); |
| return ctx.getObjCObjectPointerType(clangType); |
| } |
| } |
| } |
| |
| // If that failed, convert the type, cache, and return. |
| clang::QualType result = visit(type); |
| Cache.insert({type, result}); |
| return result; |
| } |
| |
| void ClangTypeConverter::registerExportedClangDecl(Decl *swiftDecl, |
| const clang::Decl *clangDecl) { |
| assert(clangDecl->isCanonicalDecl() && |
| "generated Clang declaration for Swift declaration should not " |
| "have multiple declarations"); |
| ReversedExportMap.insert({clangDecl, swiftDecl}); |
| } |
| |
| Decl *ClangTypeConverter::getSwiftDeclForExportedClangDecl( |
| const clang::Decl *decl) const { |
| // We don't need to canonicalize the declaration because these exported |
| // declarations are never redeclarations. |
| auto it = ReversedExportMap.find(decl); |
| return (it != ReversedExportMap.end() ? it->second : nullptr); |
| } |
| |
| std::unique_ptr<TemplateInstantiationError> |
| ClangTypeConverter::getClangTemplateArguments( |
| const clang::TemplateParameterList *templateParams, |
| ArrayRef<Type> genericArgs, |
| SmallVectorImpl<clang::TemplateArgument> &templateArgs) { |
| // Keep track of the types we failed to convert so we can return a useful |
| // error. |
| SmallVector<Type, 2> failedTypes; |
| for (clang::NamedDecl *param : *templateParams) { |
| // Note: all template parameters must be template type parameters. This is |
| // verified when we import the clang decl. |
| auto templateParam = cast<clang::TemplateTypeParmDecl>(param); |
| auto replacement = genericArgs[templateParam->getIndex()]; |
| auto qualType = convert(replacement); |
| if (qualType.isNull()) { |
| failedTypes.push_back(replacement); |
| // Find all the types we can't convert. |
| continue; |
| } |
| templateArgs.push_back(clang::TemplateArgument(qualType)); |
| } |
| if (failedTypes.empty()) |
| return nullptr; |
| auto errorInfo = std::make_unique<TemplateInstantiationError>(); |
| llvm::for_each(failedTypes, [&errorInfo](auto type) { |
| errorInfo->failedTypes.push_back(type); |
| }); |
| return errorInfo; |
| } |