blob: ddb84e062537c00504b18432042aa0790d979949 [file] [log] [blame]
//===--- ImportType.cpp - Import Clang Types ------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 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
//
//===----------------------------------------------------------------------===//
//
// This file implements support for importing Clang types as Swift types.
//
//===----------------------------------------------------------------------===//
#include "ImporterImpl.h"
#include "ClangDiagnosticConsumer.h"
#include "swift/Strings.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsClangImporter.h"
#include "swift/AST/Module.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/Types.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Parse/Token.h"
#include "swift/Basic/Fallthrough.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/TypeVisitor.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
using namespace swift;
/// Given that a type is the result of a special typedef import, was
/// it originally a CF pointer?
static bool isImportedCFPointer(clang::QualType clangType, Type type) {
return (clangType->isPointerType() &&
(type->is<ClassType>() || type->isClassExistentialType()));
}
namespace {
/// Various types that we want to do something interesting to after
/// importing them.
struct ImportHint {
enum ImportHintKind {
/// There is nothing special about the source type.
None,
/// The source type is 'void'.
Void,
/// The source type is 'BOOL'.
BOOL,
/// The source type is 'Boolean'.
Boolean,
/// The source type is 'NSString'.
NSString,
/// The source type is 'NSArray'.
NSArray,
/// The source type is 'NSDictionary'.
NSDictionary,
/// The source type is 'NSSet'.
NSSet,
/// The source type is 'NSUInteger'.
NSUInteger,
/// The source type is an Objective-C object pointer type.
ObjCPointer,
/// The source type is a CF object pointer type.
CFPointer,
/// The source type is a C++ reference type.
Reference,
/// The source type is a block pointer type.
Block,
/// The source type is a function pointer type.
CFunctionPointer,
/// The source type is a specially-handled pointer type (usually a mapped
/// typedef) that nonetheless needs to preserve nullability.
CustomNullablePointer,
};
ImportHintKind Kind;
// Type arguments, if provided.
Type TypeArgs[2];
/// Allow conversion from an import hint to an import hint kind,
/// which is useful for switches and comparisons.
operator ImportHintKind() const { return Kind; }
/// Determine the number of type arguments we expect.
static unsigned getNumTypeArgs(ImportHintKind kind) {
switch (kind) {
case None:
case Void:
case BOOL:
case Boolean:
case NSString:
case NSUInteger:
case ObjCPointer:
case CFPointer:
case Reference:
case Block:
case CFunctionPointer:
case CustomNullablePointer:
return 0;
case NSArray:
case NSSet:
return 1;
case NSDictionary:
return 2;
}
}
ImportHint(ImportHintKind kind) : Kind(kind) {
assert(getNumTypeArgs(kind) == 0 && "Wrong number of arguments");
}
ImportHint(ImportHintKind kind, Type typeArg1) : Kind(kind) {
assert(getNumTypeArgs(kind) == 1 && "Wrong number of arguments");
TypeArgs[0] = typeArg1;
}
ImportHint(ImportHintKind kind, Type typeArg1, Type typeArg2) : Kind(kind) {
assert(getNumTypeArgs(kind) == 2 && "Wrong number of arguments");
TypeArgs[0] = typeArg1;
TypeArgs[1] = typeArg2;
}
};
bool canImportAsOptional(ImportHint hint) {
// See also ClangImporter.cpp's canImportAsOptional.
switch (hint) {
case ImportHint::None:
case ImportHint::BOOL:
case ImportHint::Boolean:
case ImportHint::NSUInteger:
case ImportHint::Reference:
case ImportHint::Void:
return false;
case ImportHint::Block:
case ImportHint::CFPointer:
case ImportHint::NSArray:
case ImportHint::NSDictionary:
case ImportHint::NSSet:
case ImportHint::NSString:
case ImportHint::ObjCPointer:
case ImportHint::CFunctionPointer:
case ImportHint::CustomNullablePointer:
return true;
}
}
struct ImportResult {
Type AbstractType;
ImportHint Hint;
/*implicit*/ ImportResult(Type type = Type(),
ImportHint hint = ImportHint::None)
: AbstractType(type), Hint(hint) {}
/*implicit*/ ImportResult(TypeBase *type = nullptr,
ImportHint hint = ImportHint::None)
: AbstractType(type), Hint(hint) {}
explicit operator bool() const { return (bool) AbstractType; }
};
/// Wrap a type in the Optional type appropriate to the import kind.
static Type
getOptionalType(Type payloadType,
ImportTypeKind kind,
OptionalTypeKind OptKind = OTK_ImplicitlyUnwrappedOptional) {
// Import pointee types as true Optional.
if (kind == ImportTypeKind::Pointee)
return OptionalType::get(payloadType);
switch (OptKind) {
case OTK_ImplicitlyUnwrappedOptional:
return ImplicitlyUnwrappedOptionalType::get(payloadType);
case OTK_None:
return payloadType;
case OTK_Optional:
return OptionalType::get(payloadType);
}
}
class SwiftTypeConverter :
public clang::TypeVisitor<SwiftTypeConverter, ImportResult>
{
ClangImporter::Implementation &Impl;
bool AllowNSUIntegerAsInt;
bool CanFullyBridgeTypes;
public:
SwiftTypeConverter(ClangImporter::Implementation &impl,
bool allowNSUIntegerAsInt,
bool canFullyBridgeTypes)
: Impl(impl), AllowNSUIntegerAsInt(allowNSUIntegerAsInt),
CanFullyBridgeTypes(canFullyBridgeTypes) {}
using TypeVisitor::Visit;
ImportResult Visit(clang::QualType type) {
return Visit(type.getTypePtr());
}
#define DEPENDENT_TYPE(Class, Base) \
ImportResult Visit##Class##Type(const clang::Class##Type *) { \
llvm_unreachable("Dependent types cannot be converted"); \
}
#define TYPE(Class, Base)
#include "clang/AST/TypeNodes.def"
// Given a loaded type like CInt, look through the name alias sugar that the
// stdlib uses to show the underlying type. We want to import the signature
// of the exit(3) libc function as "func exit(Int32)", not as
// "func exit(CInt)".
static Type unwrapCType(Type T) {
if (auto *NAT = dyn_cast_or_null<NameAliasType>(T.getPointer()))
return NAT->getSinglyDesugaredType();
return T;
}
ImportResult VisitBuiltinType(const clang::BuiltinType *type) {
switch (type->getKind()) {
case clang::BuiltinType::Void:
return { Type(), ImportHint::Void };
#define MAP_BUILTIN_TYPE(CLANG_BUILTIN_KIND, SWIFT_TYPE_NAME) \
case clang::BuiltinType::CLANG_BUILTIN_KIND: \
return unwrapCType(Impl.getNamedSwiftType(Impl.getStdlibModule(), \
#SWIFT_TYPE_NAME));
#include "swift/ClangImporter/BuiltinMappedTypes.def"
// Types that cannot be mapped into Swift, and probably won't ever be.
case clang::BuiltinType::Dependent:
case clang::BuiltinType::ARCUnbridgedCast:
case clang::BuiltinType::BoundMember:
case clang::BuiltinType::BuiltinFn:
case clang::BuiltinType::Overload:
case clang::BuiltinType::PseudoObject:
case clang::BuiltinType::UnknownAny:
return Type();
// FIXME: Types that can be mapped, but aren't yet.
case clang::BuiltinType::Half:
case clang::BuiltinType::LongDouble:
case clang::BuiltinType::NullPtr:
return Type();
// Objective-C types that aren't mapped directly; rather, pointers to
// these types will be mapped.
case clang::BuiltinType::ObjCClass:
case clang::BuiltinType::ObjCId:
case clang::BuiltinType::ObjCSel:
return Type();
// OpenCL types that don't have Swift equivalents.
case clang::BuiltinType::OCLImage1d:
case clang::BuiltinType::OCLImage1dArray:
case clang::BuiltinType::OCLImage1dBuffer:
case clang::BuiltinType::OCLImage2d:
case clang::BuiltinType::OCLImage2dArray:
case clang::BuiltinType::OCLImage2dDepth:
case clang::BuiltinType::OCLImage2dArrayDepth:
case clang::BuiltinType::OCLImage2dMSAA:
case clang::BuiltinType::OCLImage2dArrayMSAA:
case clang::BuiltinType::OCLImage2dMSAADepth:
case clang::BuiltinType::OCLImage2dArrayMSAADepth:
case clang::BuiltinType::OCLImage3d:
case clang::BuiltinType::OCLSampler:
case clang::BuiltinType::OCLEvent:
case clang::BuiltinType::OCLClkEvent:
case clang::BuiltinType::OCLQueue:
case clang::BuiltinType::OCLNDRange:
case clang::BuiltinType::OCLReserveID:
return Type();
// OpenMP types that don't have Swift equivalents.
case clang::BuiltinType::OMPArraySection:
return Type();
}
}
ImportResult VisitComplexType(const clang::ComplexType *type) {
// FIXME: Implement once Complex is in the library.
return Type();
}
ImportResult VisitAtomicType(const clang::AtomicType *type) {
// FIXME: handle pointers and fields of atomic type
return Type();
}
ImportResult VisitMemberPointerType(const clang::MemberPointerType *type) {
return Type();
}
ImportResult VisitPointerType(const clang::PointerType *type) {
auto pointeeQualType = type->getPointeeType();
// Special case for NSZone*, which has its own Swift wrapper.
if (const clang::RecordType *pointee =
pointeeQualType->getAsStructureType()) {
if (pointee && !pointee->getDecl()->isCompleteDefinition() &&
pointee->getDecl()->getName() == "_NSZone") {
Identifier Id_ObjectiveC = Impl.SwiftContext.Id_ObjectiveC;
Module *objCModule = Impl.SwiftContext.getLoadedModule(Id_ObjectiveC);
Type wrapperTy = Impl.getNamedSwiftType(objCModule, "NSZone");
if (wrapperTy)
return wrapperTy;
}
}
// All other C pointers to concrete types map to
// UnsafeMutablePointer<T> or COpaquePointer (FIXME:, except in
// parameter position under the pre-
// intrinsic-pointer-conversion regime.)
// With pointer conversions enabled, map to the normal pointer types
// without special hints.
Type pointeeType;
if (pointeeQualType->isVoidType())
pointeeType = Impl.getNamedSwiftType(Impl.getStdlibModule(), "Void");
else
pointeeType = Impl.importType(pointeeQualType,
ImportTypeKind::Pointee,
AllowNSUIntegerAsInt,
/*can fully bridge*/false);
// If the pointed-to type is unrepresentable in Swift, import as
// COpaquePointer.
if (!pointeeType)
return getOpaquePointerType();
if (pointeeQualType->isFunctionType()) {
auto funcTy = pointeeType->castTo<FunctionType>();
return {
FunctionType::get(funcTy->getInput(), funcTy->getResult(),
funcTy->getExtInfo().withRepresentation(
AnyFunctionType::Representation::CFunctionPointer)),
ImportHint::CFunctionPointer
};
}
auto quals = pointeeQualType.getQualifiers();
if (quals.hasConst())
return {Impl.getNamedSwiftTypeSpecialization(Impl.getStdlibModule(),
"UnsafePointer",
pointeeType),
ImportHint::None};
// Mutable pointers with __autoreleasing or __unsafe_unretained
// ownership map to AutoreleasingUnsafeMutablePointer<T>.
else if (quals.getObjCLifetime() == clang::Qualifiers::OCL_Autoreleasing
|| quals.getObjCLifetime() == clang::Qualifiers::OCL_ExplicitNone)
return {
Impl.getNamedSwiftTypeSpecialization(
Impl.getStdlibModule(), "AutoreleasingUnsafeMutablePointer",
pointeeType),
ImportHint::None};
// All other mutable pointers map to UnsafeMutablePointer.
return {Impl.getNamedSwiftTypeSpecialization(Impl.getStdlibModule(),
"UnsafeMutablePointer",
pointeeType),
ImportHint::None};
}
Type getOpaquePointerType() {
return Impl.getNamedSwiftType(Impl.getStdlibModule(), "COpaquePointer");
}
ImportResult VisitBlockPointerType(const clang::BlockPointerType *type) {
// Block pointer types are mapped to function types.
Type pointeeType = Impl.importType(type->getPointeeType(),
ImportTypeKind::Abstract,
AllowNSUIntegerAsInt,
CanFullyBridgeTypes);
if (!pointeeType)
return Type();
FunctionType *fTy = pointeeType->castTo<FunctionType>();
auto rep = FunctionType::Representation::Block;
auto funcTy = FunctionType::get(fTy->getInput(), fTy->getResult(),
fTy->getExtInfo().withRepresentation(rep));
return { funcTy, ImportHint::Block };
}
ImportResult VisitReferenceType(const clang::ReferenceType *type) {
return { nullptr, ImportHint::Reference };
}
ImportResult VisitMemberPointer(const clang::MemberPointerType *type) {
// FIXME: Member function pointers can be mapped to curried functions,
// but only when we can express the notion of a function that does
// not capture anything from its enclosing context.
return Type();
}
ImportResult VisitArrayType(const clang::ArrayType *type) {
// FIXME: Array types will need to be mapped differently depending on
// context.
return Type();
}
ImportResult VisitConstantArrayType(const clang::ConstantArrayType *type) {
// FIXME: In a function argument context, arrays should import as
// pointers.
// FIXME: Map to a real fixed-size Swift array type when we have those.
// Importing as a tuple at least fills the right amount of space, and
// we can cheese static-offset "indexing" using .$n operations.
Type elementType = Impl.importType(type->getElementType(),
ImportTypeKind::Pointee,
AllowNSUIntegerAsInt,
/*can fully bridge*/false);
if (!elementType)
return Type();
TupleTypeElt elt(elementType);
SmallVector<TupleTypeElt, 8> elts;
for (size_t i = 0, size = type->getSize().getZExtValue(); i < size; ++i)
elts.push_back(elt);
return TupleType::get(elts, elementType->getASTContext());
}
ImportResult VisitVectorType(const clang::VectorType *type) {
auto *SIMD = Impl.tryLoadSIMDModule();
if (!SIMD)
return Type();
// Map the element type and count to a Swift name, such as
// float x 3 => Float3.
SmallString<16> name;
{
llvm::raw_svector_ostream names(name);
if (auto builtinTy
= dyn_cast<clang::BuiltinType>(type->getElementType())){
switch (builtinTy->getKind()) {
#define MAP_SIMD_TYPE(TYPE_NAME, BUILTIN_KIND) \
case clang::BuiltinType::BUILTIN_KIND: \
names << #TYPE_NAME; \
break;
#include "swift/ClangImporter/SIMDMappedTypes.def"
default:
// A vector type we don't know how to map.
return Type();
}
} else {
return Type();
}
names << type->getNumElements();
}
return Impl.getNamedSwiftType(SIMD, name);
}
ImportResult VisitFunctionProtoType(const clang::FunctionProtoType *type) {
// C-style variadic functions cannot be called from Swift.
if (type->isVariadic())
return Type();
// Import the result type. We currently provide no mechanism
// for this to be audited.
auto resultTy = Impl.importType(type->getReturnType(),
ImportTypeKind::Result,
AllowNSUIntegerAsInt,
CanFullyBridgeTypes);
if (!resultTy)
return Type();
SmallVector<TupleTypeElt, 4> params;
for (auto param = type->param_type_begin(),
paramEnd = type->param_type_end();
param != paramEnd; ++param) {
auto swiftParamTy = Impl.importType(*param, ImportTypeKind::Parameter,
AllowNSUIntegerAsInt,
CanFullyBridgeTypes);
if (!swiftParamTy)
return Type();
// FIXME: If we were walking TypeLocs, we could actually get parameter
// names. The probably doesn't matter outside of a FuncDecl, which
// we'll have to special-case, but it's an interesting bit of data loss.
params.push_back(swiftParamTy);
}
// Form the parameter tuple.
auto paramsTy = TupleType::get(params, Impl.SwiftContext);
// Form the function type.
return FunctionType::get(paramsTy, resultTy);
}
ImportResult
VisitFunctionNoProtoType(const clang::FunctionNoProtoType *type) {
// Import functions without prototypes as functions with no parameters.
auto resultTy = Impl.importType(type->getReturnType(),
ImportTypeKind::Result,
AllowNSUIntegerAsInt,
CanFullyBridgeTypes);
if (!resultTy)
return Type();
return FunctionType::get(TupleType::getEmpty(Impl.SwiftContext),resultTy);
}
ImportResult VisitParenType(const clang::ParenType *type) {
auto inner = Visit(type->getInnerType());
if (!inner)
return Type();
return { ParenType::get(Impl.SwiftContext, inner.AbstractType),
inner.Hint };
}
ImportResult VisitTypedefType(const clang::TypedefType *type) {
// If the underlying declaration is an Objective-C type parameter,
// import the underlying sugar instead.
if (isa<clang::ObjCTypeParamDecl>(type->getDecl()))
return Visit(type->desugar());
// Import the underlying declaration.
auto decl = dyn_cast_or_null<TypeDecl>(Impl.importDecl(type->getDecl()));
// If that fails, fall back on importing the underlying type.
if (!decl) return Visit(type->desugar());
Type mappedType = decl->getDeclaredType();
ImportHint hint = ImportHint::None;
// For certain special typedefs, we don't want to use the imported type.
if (auto specialKind = Impl.getSpecialTypedefKind(type->getDecl())) {
switch (specialKind.getValue()) {
case MappedTypeNameKind::DoNothing:
case MappedTypeNameKind::DefineAndUse:
break;
case MappedTypeNameKind::DefineOnly:
mappedType = cast<TypeAliasDecl>(decl)->getUnderlyingType();
break;
}
if (type->getDecl()->getName() == "BOOL") {
hint = ImportHint::BOOL;
} else if (type->getDecl()->getName() == "Boolean") {
// FIXME: Darwin only?
hint = ImportHint::Boolean;
} else if (type->getDecl()->getName() == "NSUInteger") {
hint = ImportHint::NSUInteger;
} else if (isImportedCFPointer(type->desugar(), mappedType)) {
hint = ImportHint::CFPointer;
} else if (mappedType->isAnyExistentialType()) { // id, Class
hint = ImportHint::ObjCPointer;
} else if (type->isBlockPointerType()) {
// FIXME: This should eventually be "isAnyPointerType", but right now
// non-object, non-block pointers are never Optional in Swift; they
// just can have a value of 'nil' themselves.
hint = ImportHint::CustomNullablePointer;
}
// Any other interesting mapped types should be hinted here.
// Otherwise, recurse on the underlying type in order to compute
// the hint correctly.
} else {
SwiftTypeConverter innerConverter(Impl, AllowNSUIntegerAsInt,
/*can fully bridge*/false);
auto underlyingResult = innerConverter.Visit(type->desugar());
#ifndef NDEBUG
switch (underlyingResult.Hint) {
case ImportHint::Block:
// Blocks change in all sorts of ways, due to bridging.
break;
case ImportHint::NSUInteger:
// NSUInteger might be imported as Int rather than UInt depending
// on where the import lives.
if (underlyingResult.AbstractType->getAnyNominal() ==
Impl.SwiftContext.getIntDecl())
break;
SWIFT_FALLTHROUGH;
default:
assert(underlyingResult.AbstractType->isEqual(mappedType) &&
"typedef without special typedef kind was mapped "
"differently from its underlying type?");
}
#endif
hint = underlyingResult.Hint;
}
return { mappedType, hint };
}
#define SUGAR_TYPE(KIND) \
ImportResult Visit##KIND##Type(const clang::KIND##Type *type) { \
return Visit(type->desugar()); \
}
SUGAR_TYPE(TypeOfExpr)
SUGAR_TYPE(TypeOf)
SUGAR_TYPE(Decltype)
SUGAR_TYPE(UnaryTransform)
SUGAR_TYPE(Elaborated)
SUGAR_TYPE(SubstTemplateTypeParm)
SUGAR_TYPE(TemplateSpecialization)
SUGAR_TYPE(Auto)
SUGAR_TYPE(Adjusted)
SUGAR_TYPE(PackExpansion)
ImportResult VisitAttributedType(const clang::AttributedType *type) {
return Visit(type->desugar());
}
ImportResult VisitDecayedType(const clang::DecayedType *type) {
clang::ASTContext &clangCtx = Impl.getClangASTContext();
if (clangCtx.hasSameType(type->getOriginalType(),
clangCtx.getBuiltinVaListType()))
return Impl.getNamedSwiftType(Impl.getStdlibModule(), "CVaListPointer");
return Visit(type->desugar());
}
ImportResult VisitRecordType(const clang::RecordType *type) {
auto decl = dyn_cast_or_null<TypeDecl>(Impl.importDecl(type->getDecl()));
if (!decl)
return nullptr;
return decl->getDeclaredType();
}
ImportResult VisitEnumType(const clang::EnumType *type) {
auto clangDecl = type->getDecl();
switch (Impl.classifyEnum(Impl.getClangPreprocessor(), clangDecl)) {
case ClangImporter::Implementation::EnumKind::Constants: {
auto clangDef = clangDecl->getDefinition();
// Map anonymous enums with no fixed underlying type to Int /if/
// they fit in an Int32. If not, this mapping isn't guaranteed to be
// consistent for all platforms we care about.
if (!clangDef->isFixed() &&
clangDef->getNumPositiveBits() < 32 &&
clangDef->getNumNegativeBits() <= 32)
return Impl.getNamedSwiftType(Impl.getStdlibModule(), "Int");
// Import the underlying integer type.
return Visit(clangDecl->getIntegerType());
}
case ClangImporter::Implementation::EnumKind::Enum:
case ClangImporter::Implementation::EnumKind::Unknown:
case ClangImporter::Implementation::EnumKind::Options: {
auto decl = dyn_cast_or_null<TypeDecl>(Impl.importDecl(clangDecl));
if (!decl)
return nullptr;
return decl->getDeclaredType();
}
}
}
ImportResult VisitObjCObjectType(const clang::ObjCObjectType *type) {
// We only handle pointers to objects.
return nullptr;
}
ImportResult
VisitObjCObjectPointerType(const clang::ObjCObjectPointerType *type) {
// If this object pointer refers to an Objective-C class (possibly
// qualified),
if (auto objcClass = type->getInterfaceDecl()) {
auto imported = cast_or_null<ClassDecl>(Impl.importDecl(objcClass));
if (!imported)
return nullptr;
Type importedType = imported->getDeclaredType();
if (!type->qual_empty()) {
// As a special case, turn 'NSObject <NSCopying>' into
// 'id <NSObject, NSCopying>', which can be imported more usefully.
Type nsObjectTy = Impl.getNSObjectType();
if (nsObjectTy && importedType->isEqual(nsObjectTy)) {
SmallVector<clang::ObjCProtocolDecl *, 4> protocols{
type->qual_begin(), type->qual_end()
};
auto *nsObjectProto =
Impl.getNSObjectProtocolType()->getAnyNominal();
auto *clangProto =
cast<clang::ObjCProtocolDecl>(nsObjectProto->getClangDecl());
protocols.push_back(
const_cast<clang::ObjCProtocolDecl *>(clangProto));
clang::ASTContext &clangCtx = Impl.getClangASTContext();
clang::QualType protosOnlyType =
clangCtx.getObjCObjectType(clangCtx.ObjCBuiltinIdTy,
/*type args*/{},
protocols,
/*kindof*/false);
return Visit(clangCtx.getObjCObjectPointerType(protosOnlyType));
}
}
if (imported->hasName() &&
imported->getName().str() == "NSString") {
return { importedType, ImportHint::NSString };
}
if (imported->hasName() && imported->getName().str() == "NSArray") {
// If we have type arguments, import them.
ArrayRef<clang::QualType> typeArgs = type->getTypeArgs();
if (typeArgs.size() == 1) {
Type elementType = Impl.importType(typeArgs[0],
ImportTypeKind::BridgedValue,
AllowNSUIntegerAsInt,
CanFullyBridgeTypes,
OTK_None);
return { importedType,
ImportHint(ImportHint::NSArray, elementType) };
}
return { importedType, ImportHint(ImportHint::NSArray, Type()) };
}
if (imported->hasName() && imported->getName().str() == "NSDictionary") {
// If we have type arguments, import them.
ArrayRef<clang::QualType> typeArgs = type->getTypeArgs();
if (typeArgs.size() == 2) {
Type keyType = Impl.importType(typeArgs[0],
ImportTypeKind::BridgedValue,
AllowNSUIntegerAsInt,
CanFullyBridgeTypes,
OTK_None);
Type objectType = Impl.importType(typeArgs[1],
ImportTypeKind::BridgedValue,
AllowNSUIntegerAsInt,
CanFullyBridgeTypes,
OTK_None);
if (keyType.isNull() != objectType.isNull()) {
keyType = nullptr;
objectType = nullptr;
}
return { importedType,
ImportHint(ImportHint::NSDictionary,
keyType, objectType) };
}
return { importedType,
ImportHint(ImportHint::NSDictionary, Type(), Type()) };
}
if (imported->hasName() && imported->getName().str() == "NSSet") {
// If we have type arguments, import them.
ArrayRef<clang::QualType> typeArgs = type->getTypeArgs();
if (typeArgs.size() == 1) {
Type elementType = Impl.importType(typeArgs[0],
ImportTypeKind::BridgedValue,
AllowNSUIntegerAsInt,
CanFullyBridgeTypes,
OTK_None);
return { importedType,
ImportHint(ImportHint::NSSet, elementType) };
}
return { importedType, ImportHint(ImportHint::NSSet, Type()) };
}
return { importedType, ImportHint::ObjCPointer };
}
// If this is id<P>, turn this into a protocol type.
// FIXME: What about Class<P>?
if (type->isObjCQualifiedIdType()) {
SmallVector<Type, 4> protocols;
for (auto cp = type->qual_begin(), cpEnd = type->qual_end();
cp != cpEnd; ++cp) {
auto proto = cast_or_null<ProtocolDecl>(Impl.importDecl(*cp));
if (!proto)
return Type();
protocols.push_back(proto->getDeclaredType());
}
return { ProtocolCompositionType::get(Impl.SwiftContext, protocols),
ImportHint::ObjCPointer };
}
// Beyond here, we're using AnyObject.
auto proto = Impl.SwiftContext.getProtocol(
KnownProtocolKind::AnyObject);
if (!proto)
return Type();
// id maps to AnyObject.
if (type->isObjCIdType()) {
return { proto->getDeclaredType(), ImportHint::ObjCPointer };
}
// Class maps to AnyObject.Type.
assert(type->isObjCClassType() || type->isObjCQualifiedClassType());
return { ExistentialMetatypeType::get(proto->getDeclaredType()),
ImportHint::ObjCPointer };
}
};
}
/// True if we're converting a function parameter, property type, or
/// function result type, and can thus safely apply representation
/// conversions for bridged types.
static bool canBridgeTypes(ImportTypeKind importKind) {
switch (importKind) {
case ImportTypeKind::Abstract:
case ImportTypeKind::Typedef:
case ImportTypeKind::Value:
case ImportTypeKind::Variable:
case ImportTypeKind::AuditedVariable:
case ImportTypeKind::Pointee:
case ImportTypeKind::Enum:
case ImportTypeKind::RecordField:
return false;
case ImportTypeKind::Result:
case ImportTypeKind::AuditedResult:
case ImportTypeKind::Parameter:
case ImportTypeKind::CFRetainedOutParameter:
case ImportTypeKind::CFUnretainedOutParameter:
case ImportTypeKind::Property:
case ImportTypeKind::PropertyAccessor:
case ImportTypeKind::BridgedValue:
return true;
}
}
/// True if the type has known CoreFoundation reference counting semantics.
static bool isCFAudited(ImportTypeKind importKind) {
switch (importKind) {
case ImportTypeKind::Abstract:
case ImportTypeKind::Typedef:
case ImportTypeKind::Value:
case ImportTypeKind::BridgedValue:
case ImportTypeKind::Variable:
case ImportTypeKind::Result:
case ImportTypeKind::Pointee:
case ImportTypeKind::Enum:
case ImportTypeKind::RecordField:
return false;
case ImportTypeKind::AuditedVariable:
case ImportTypeKind::AuditedResult:
case ImportTypeKind::Parameter:
case ImportTypeKind::CFRetainedOutParameter:
case ImportTypeKind::CFUnretainedOutParameter:
case ImportTypeKind::Property:
case ImportTypeKind::PropertyAccessor:
return true;
}
}
/// Turn T into Unmanaged<T>.
static Type getUnmanagedType(ClangImporter::Implementation &impl,
Type payloadType) {
NominalTypeDecl *unmanagedDecl = impl.SwiftContext.getUnmanagedDecl();
if (!unmanagedDecl || unmanagedDecl->getGenericParams()->size() != 1)
return payloadType;
Type unmanagedClassType = BoundGenericType::get(unmanagedDecl,
/*parent*/ Type(),
payloadType);
return unmanagedClassType;
}
static Type adjustTypeForConcreteImport(ClangImporter::Implementation &impl,
clang::QualType clangType,
Type importedType,
ImportTypeKind importKind,
ImportHint hint,
bool allowNSUIntegerAsInt,
bool canFullyBridgeTypes,
OptionalTypeKind optKind) {
if (importKind == ImportTypeKind::Abstract) {
return importedType;
}
// 'void' can only be imported as a function result type.
if (hint == ImportHint::Void &&
(importKind == ImportTypeKind::AuditedResult ||
importKind == ImportTypeKind::Result ||
importKind == ImportTypeKind::PropertyAccessor)) {
return impl.getNamedSwiftType(impl.getStdlibModule(), "Void");
}
// Import NSString * globals as String.
if (hint == ImportHint::NSString &&
(importKind == ImportTypeKind::Variable ||
importKind == ImportTypeKind::AuditedVariable)) {
return impl.getNamedSwiftType(impl.getStdlibModule(), "String");
}
// Reference types are only permitted as function parameter types.
if (hint == ImportHint::Reference &&
importKind == ImportTypeKind::Parameter) {
auto refType = clangType->castAs<clang::ReferenceType>();
// Import the underlying type.
auto objectType = impl.importType(refType->getPointeeType(),
ImportTypeKind::Pointee,
allowNSUIntegerAsInt,
canFullyBridgeTypes);
if (!objectType)
return nullptr;
return InOutType::get(objectType);
}
// For anything else, if we completely failed to import the type
// abstractly, give up now.
if (!importedType)
return Type();
// Special case AutoreleasingUnsafeMutablePointer<NSError?> parameters.
auto maybeImportNSErrorPointer = [&]() -> Type {
if (importKind != ImportTypeKind::Parameter)
return Type();
PointerTypeKind PTK;
auto elementType = importedType->getAnyPointerElementType(PTK);
if (!elementType || PTK != PTK_AutoreleasingUnsafeMutablePointer)
return Type();
auto elementObj = elementType->getAnyOptionalObjectType();
if (!elementObj)
return Type();
auto elementClass = elementObj->getClassOrBoundGenericClass();
if (!elementClass)
return Type();
// FIXME: Avoid string comparison by caching this identifier.
if (elementClass->getName().str() != "NSError")
return Type();
Module *foundationModule = impl.tryLoadFoundationModule();
if (!foundationModule ||
foundationModule->getName()
!= elementClass->getModuleContext()->getName())
return Type();
return impl.getNamedSwiftType(foundationModule, "NSErrorPointer");
};
if (Type result = maybeImportNSErrorPointer())
return result;
auto maybeImportCFOutParameter = [&]() -> Type {
if (importKind != ImportTypeKind::CFRetainedOutParameter &&
importKind != ImportTypeKind::CFUnretainedOutParameter) {
return Type();
}
PointerTypeKind PTK;
auto elementType = importedType->getAnyPointerElementType(PTK);
if (!elementType || PTK != PTK_UnsafeMutablePointer)
return Type();
OptionalTypeKind OTK;
auto insideOptionalType = elementType->getAnyOptionalObjectType(OTK);
if (!insideOptionalType)
insideOptionalType = elementType;
auto boundGenericTy = insideOptionalType->getAs<BoundGenericType>();
if (!boundGenericTy)
return Type();
auto boundGenericBase = boundGenericTy->getDecl();
if (boundGenericBase != impl.SwiftContext.getUnmanagedDecl())
return Type();
assert(boundGenericTy->getGenericArgs().size() == 1 &&
"signature of Unmanaged has changed");
auto resultTy = boundGenericTy->getGenericArgs().front();
if (OTK != OTK_None)
resultTy = OptionalType::get(OTK, resultTy);
StringRef pointerName;
if (importKind == ImportTypeKind::CFRetainedOutParameter)
pointerName = "UnsafeMutablePointer";
else
pointerName = "AutoreleasingUnsafeMutablePointer";
resultTy = impl.getNamedSwiftTypeSpecialization(impl.getStdlibModule(),
pointerName,
resultTy);
return resultTy;
};
if (Type outParamTy = maybeImportCFOutParameter()) {
importedType = outParamTy;
}
// Turn block pointer types back into normal function types in any
// context where bridging is possible, unless the block has a typedef.
if (hint == ImportHint::Block) {
if (!canFullyBridgeTypes) {
if (auto typedefType = clangType->getAs<clang::TypedefType>()) {
// In non-bridged contexts, drop the typealias sugar for blocks.
// FIXME: This will do the wrong thing if there's any adjustment to do
// besides optionality.
Type underlyingTy = impl.importType(typedefType->desugar(),
importKind,
allowNSUIntegerAsInt,
canFullyBridgeTypes,
OTK_None);
if (Type unwrappedTy = underlyingTy->getAnyOptionalObjectType())
underlyingTy = unwrappedTy;
if (!underlyingTy->isEqual(importedType))
importedType = underlyingTy;
}
}
if (canBridgeTypes(importKind) || importKind == ImportTypeKind::Typedef) {
auto fTy = importedType->castTo<FunctionType>();
FunctionType::ExtInfo einfo = fTy->getExtInfo();
if (einfo.getRepresentation() != FunctionTypeRepresentation::Swift) {
einfo = einfo.withRepresentation(FunctionTypeRepresentation::Swift);
importedType = fTy->withExtInfo(einfo);
}
}
}
// Turn BOOL and DarwinBoolean into Bool in contexts that can bridge types
// losslessly.
if ((hint == ImportHint::BOOL || hint == ImportHint::Boolean) &&
canFullyBridgeTypes && canBridgeTypes(importKind)) {
return impl.SwiftContext.getBoolDecl()->getDeclaredType();
}
// When NSUInteger is used as an enum's underlying type or if it does not come
// from a system module, make sure it stays unsigned.
if (hint == ImportHint::NSUInteger) {
if (importKind == ImportTypeKind::Enum || !allowNSUIntegerAsInt) {
return importedType = impl.SwiftContext.getUIntDecl()->getDeclaredType();
}
}
// Wrap CF pointers up as unmanaged types, unless this is an audited
// context.
if (hint == ImportHint::CFPointer && !isCFAudited(importKind)) {
importedType = getUnmanagedType(impl, importedType);
}
// When NSString* is the type of a function parameter or a function
// result type, map it to String.
// FIXME: It's not really safe to do this when Foundation is missing.
// We do it anyway for ImportForwardDeclarations mode so that generated
// interfaces are correct, but trying to use the resulting declarations
// may result in compiler crashes further down the line.
if (hint == ImportHint::NSString && canBridgeTypes(importKind) &&
(impl.tryLoadFoundationModule() || impl.ImportForwardDeclarations)) {
importedType = impl.SwiftContext.getStringDecl()->getDeclaredType();
}
// When NSArray* is the type of a function parameter or a function
// result type, map it to [AnyObject].
if (hint == ImportHint::NSArray && canBridgeTypes(importKind) &&
impl.tryLoadFoundationModule()) {
Type elementType = hint.TypeArgs[0];
if (elementType.isNull())
elementType = impl.getNamedSwiftType(impl.getStdlibModule(), "AnyObject");
importedType = ArraySliceType::get(elementType);
}
// When NSDictionary* is the type of a function parameter or a function
// result type, map it to [K : V].
if (hint == ImportHint::NSDictionary && canBridgeTypes(importKind) &&
impl.tryLoadFoundationModule()) {
Type keyType = hint.TypeArgs[0];
Type objectType = hint.TypeArgs[1];
// If no key type was provided, or the key doesn't match the 'NSObject'
// bound required by the Swift Dictionary key, substitute in 'NSObject'.
if (keyType.isNull() || !impl.matchesNSObjectBound(keyType)) {
keyType = impl.getNSObjectType();
}
if (objectType.isNull()) {
objectType = impl.getNamedSwiftType(impl.getStdlibModule(), "AnyObject");
}
importedType = DictionaryType::get(keyType, objectType);
}
// When NSSet* is the type of a function parameter or a function
// result type, map it to Set<T>.
if (hint == ImportHint::NSSet && canBridgeTypes(importKind) &&
impl.tryLoadFoundationModule()) {
Type elementType = hint.TypeArgs[0];
// If no element type was provided, or the element type doesn't match the
// 'NSObject' bound required by the Swift Set, substitute in 'NSObject'.
if (elementType.isNull() || !impl.matchesNSObjectBound(elementType))
elementType = impl.getNSObjectType();
importedType = impl.getNamedSwiftTypeSpecialization(impl.getStdlibModule(),
"Set",
elementType);
}
if (!importedType)
return importedType;
if (importKind == ImportTypeKind::RecordField &&
importedType->isAnyClassReferenceType()) {
// Wrap retainable struct fields in Unmanaged.
// FIXME: Eventually we might get C++-like support for strong pointers in
// structs, at which point we should really be checking the lifetime
// qualifiers.
// FIXME: This should apply to blocks as well, but Unmanaged is constrained
// to AnyObject.
importedType = getUnmanagedType(impl, importedType);
}
// Wrap class, class protocol, function, and metatype types in an
// optional type.
if (importKind != ImportTypeKind::Typedef && canImportAsOptional(hint)) {
importedType = getOptionalType(importedType, importKind, optKind);
}
return importedType;
}
Type ClangImporter::Implementation::importType(clang::QualType type,
ImportTypeKind importKind,
bool allowNSUIntegerAsInt,
bool canFullyBridgeTypes,
OptionalTypeKind optionality) {
if (type.isNull())
return Type();
// The "built-in" Objective-C types id, Class, and SEL can actually be (and
// are) defined within the library. Clang tracks the redefinition types
// separately, so it can provide fallbacks in certain cases. For Swift, we
// map the redefinition types back to the equivalent of the built-in types.
// This bans some trickery that the redefinition types enable, but is a more
// sane model overall.
auto &clangContext = getClangASTContext();
if (clangContext.getLangOpts().ObjC1) {
if (clangContext.hasSameUnqualifiedType(
type, clangContext.getObjCIdRedefinitionType()) &&
!clangContext.hasSameUnqualifiedType(
clangContext.getObjCIdType(),
clangContext.getObjCIdRedefinitionType()))
type = clangContext.getObjCIdType();
else if (clangContext.hasSameUnqualifiedType(
type, clangContext.getObjCClassRedefinitionType()) &&
!clangContext.hasSameUnqualifiedType(
clangContext.getObjCClassType(),
clangContext.getObjCClassRedefinitionType()))
type = clangContext.getObjCClassType();
else if (clangContext.hasSameUnqualifiedType(
type, clangContext.getObjCSelRedefinitionType()) &&
!clangContext.hasSameUnqualifiedType(
clangContext.getObjCSelType(),
clangContext.getObjCSelRedefinitionType()))
type = clangContext.getObjCSelType();
}
// If nullability is provided as part of the type, that overrides
// optionality provided externally.
if (auto nullability = type->getNullability(clangContext)) {
optionality = translateNullability(*nullability);
}
// Perform abstract conversion, ignoring how the type is actually used.
SwiftTypeConverter converter(*this, allowNSUIntegerAsInt,canFullyBridgeTypes);
auto importResult = converter.Visit(type);
// Now fix up the type based on we're concretely using it.
return adjustTypeForConcreteImport(*this, type, importResult.AbstractType,
importKind, importResult.Hint,
allowNSUIntegerAsInt,
canFullyBridgeTypes,
optionality);
}
bool ClangImporter::Implementation::shouldImportGlobalAsLet(
clang::QualType type)
{
// Const variables should be imported as 'let'.
if (type.isConstQualified()) {
return true;
}
// Globals of type NSString * should be imported as 'let'.
if (auto ptrType = type->getAs<clang::ObjCObjectPointerType>()) {
if (auto interfaceType = ptrType->getInterfaceType()) {
if (interfaceType->getDecl()->getName() == "NSString") {
return true;
}
}
}
return false;
}
/// Returns true if \p name contains the substring "Unsigned" or "unsigned".
static bool nameContainsUnsigned(StringRef name) {
size_t pos = name.find("nsigned");
if (pos == StringRef::npos || pos == 0)
return false;
--pos;
return (name[pos] == 'u') || (name[pos] == 'U');
}
Type ClangImporter::Implementation::importPropertyType(
const clang::ObjCPropertyDecl *decl,
bool isFromSystemModule) {
OptionalTypeKind optionality = OTK_ImplicitlyUnwrappedOptional;
if (auto info = getKnownObjCProperty(decl)) {
if (auto nullability = info->getNullability())
optionality = translateNullability(*nullability);
}
bool allowNSUIntegerAsInt = isFromSystemModule;
if (allowNSUIntegerAsInt)
allowNSUIntegerAsInt = !nameContainsUnsigned(decl->getName());
return importType(decl->getType(), ImportTypeKind::Property,
allowNSUIntegerAsInt, /*isFullyBridgeable*/true,
optionality);
}
/// Get a bit vector indicating which arguments are non-null for a
/// given function or method.
llvm::SmallBitVector ClangImporter::Implementation::getNonNullArgs(
const clang::Decl *decl,
ArrayRef<const clang::ParmVarDecl *> params) {
llvm::SmallBitVector result;
if (!decl)
return result;
for (const auto *nonnull : decl->specific_attrs<clang::NonNullAttr>()) {
if (!nonnull->args_size()) {
// Easy case: all pointer arguments are non-null.
if (result.empty())
result.resize(params.size(), true);
else
result.set(0, params.size());
return result;
}
// Mark each of the listed parameters as non-null.
if (result.empty())
result.resize(params.size(), false);
for (unsigned idx : nonnull->args()) {
if (idx < result.size())
result.set(idx);
}
}
return result;
}
/// Apply the @noescape attribute
static Type applyNoEscape(Type type) {
// Recurse into optional types.
OptionalTypeKind optKind;
if (Type objectType = type->getAnyOptionalObjectType(optKind)) {
return OptionalType::get(optKind, applyNoEscape(objectType));
}
// Apply @noescape to function types.
if (auto funcType = type->getAs<FunctionType>()) {
return FunctionType::get(funcType->getInput(), funcType->getResult(),
funcType->getExtInfo().withNoEscape());
}
return type;
}
/// Determine the optionality of the given Clang parameter.
///
/// \param param The Clang parameter.
///
/// \param knownNonNull Whether a function- or method-level "nonnull" attribute
/// applies to this parameter.
///
/// \param knownNullability When API notes describe the nullability of this
/// parameter, that nullability.
static OptionalTypeKind getParamOptionality(
const clang::ParmVarDecl *param,
bool knownNonNull,
Optional<clang::NullabilityKind> knownNullability) {
auto &clangCtx = param->getASTContext();
// If nullability is available on the type, use it.
if (auto nullability = param->getType()->getNullability(clangCtx)) {
return ClangImporter::Implementation::translateNullability(*nullability);
}
// If it's known non-null, use that.
if (knownNonNull || param->hasAttr<clang::NonNullAttr>())
return OTK_None;
// If API notes gives us nullability, use that.
if (knownNullability)
return ClangImporter::Implementation::translateNullability(
*knownNullability);
// Default to implicitly unwrapped optionals.
return OTK_ImplicitlyUnwrappedOptional;
}
Type ClangImporter::Implementation::
importFunctionType(const clang::FunctionDecl *clangDecl,
clang::QualType resultType,
ArrayRef<const clang::ParmVarDecl *> params,
bool isVariadic, bool isNoReturn,
bool isFromSystemModule, bool hasCustomName,
ParameterList **parameterList, DeclName &name) {
bool allowNSUIntegerAsInt = isFromSystemModule;
if (allowNSUIntegerAsInt) {
if (const clang::IdentifierInfo *clangNameID = clangDecl->getIdentifier()) {
allowNSUIntegerAsInt = !nameContainsUnsigned(clangNameID->getName());
}
}
// CF function results can be managed if they are audited or
// the ownership convention is explicitly declared.
bool isAuditedResult =
(clangDecl &&
(clangDecl->hasAttr<clang::CFAuditedTransferAttr>() ||
clangDecl->hasAttr<clang::CFReturnsRetainedAttr>() ||
clangDecl->hasAttr<clang::CFReturnsNotRetainedAttr>()));
// Check if we know more about the type from our whitelists.
Optional<api_notes::GlobalFunctionInfo> knownFn;
if (auto knownFnTmp = getKnownGlobalFunction(clangDecl))
if (knownFnTmp->NullabilityAudited)
knownFn = knownFnTmp;
OptionalTypeKind OptionalityOfReturn;
if (clangDecl->hasAttr<clang::ReturnsNonNullAttr>()) {
OptionalityOfReturn = OTK_None;
} else if (knownFn) {
OptionalityOfReturn = translateNullability(knownFn->getReturnTypeInfo());
} else {
OptionalityOfReturn = OTK_ImplicitlyUnwrappedOptional;
}
// Import the result type.
auto swiftResultTy = importType(resultType,
(isAuditedResult
? ImportTypeKind::AuditedResult
: ImportTypeKind::Result),
allowNSUIntegerAsInt,
/*isFullyBridgeable*/true,
OptionalityOfReturn);
if (!swiftResultTy)
return Type();
// Import the parameters.
SmallVector<ParamDecl*, 4> parameters;
unsigned index = 0;
llvm::SmallBitVector nonNullArgs = getNonNullArgs(clangDecl, params);
ArrayRef<Identifier> argNames = name.getArgumentNames();
for (auto param : params) {
auto paramTy = param->getType();
if (paramTy->isVoidType()) {
++index;
continue;
}
// Check nullability of the parameter.
OptionalTypeKind OptionalityOfParam
= getParamOptionality(param, !nonNullArgs.empty() && nonNullArgs[index],
knownFn
? Optional<clang::NullabilityKind>(
knownFn->getParamTypeInfo(index))
: None);
ImportTypeKind importKind = ImportTypeKind::Parameter;
if (param->hasAttr<clang::CFReturnsRetainedAttr>())
importKind = ImportTypeKind::CFRetainedOutParameter;
else if (param->hasAttr<clang::CFReturnsNotRetainedAttr>())
importKind = ImportTypeKind::CFUnretainedOutParameter;
// Import the parameter type into Swift.
Type swiftParamTy = importType(paramTy, importKind,
allowNSUIntegerAsInt,
/*isFullyBridgeable*/true,
OptionalityOfParam);
if (!swiftParamTy)
return Type();
// Map __attribute__((noescape)) to @noescape.
bool addNoEscapeAttr = false;
if (param->hasAttr<clang::NoEscapeAttr>()) {
Type newParamTy = applyNoEscape(swiftParamTy);
if (newParamTy.getPointer() != swiftParamTy.getPointer()) {
swiftParamTy = newParamTy;
addNoEscapeAttr = true;
}
}
// Figure out the name for this parameter.
Identifier bodyName = importFullName(param).Imported.getBaseName();
// Retrieve the argument name.
Identifier name;
if (index < argNames.size())
name = argNames[index];
// It doesn't actually matter which DeclContext we use, so just use the
// imported header unit.
auto bodyVar
= createDeclWithClangNode<ParamDecl>(param,
/*IsLet*/ true,
SourceLoc(), name,
importSourceLoc(param->getLocation()),
bodyName, swiftParamTy,
ImportedHeaderUnit);
if (addNoEscapeAttr)
bodyVar->getAttrs().add(
new (SwiftContext) NoEscapeAttr(/*IsImplicit=*/false));
parameters.push_back(bodyVar);
++index;
}
// Append an additional argument to represent varargs.
if (isVariadic) {
auto paramTy = BoundGenericType::get(SwiftContext.getArrayDecl(), Type(),
{SwiftContext.getAnyDecl()->getDeclaredType()});
auto name = SwiftContext.getIdentifier("varargs");
auto param = new (SwiftContext) ParamDecl(true, SourceLoc(),
Identifier(),
SourceLoc(), name, paramTy,
ImportedHeaderUnit);
param->setVariadic();
parameters.push_back(param);
}
// Form the parameter list.
*parameterList = ParameterList::create(SwiftContext, parameters);
FunctionType::ExtInfo extInfo;
extInfo = extInfo.withIsNoReturn(isNoReturn);
// Form the function type.
auto argTy = (*parameterList)->getType(SwiftContext);
return FunctionType::get(argTy, swiftResultTy, extInfo);
}
static bool isObjCMethodResultAudited(const clang::Decl *decl) {
if (!decl)
return false;
return (decl->hasAttr<clang::CFReturnsRetainedAttr>() ||
decl->hasAttr<clang::CFReturnsNotRetainedAttr>() ||
decl->hasAttr<clang::ObjCReturnsInnerPointerAttr>());
}
namespace {
struct ErrorImportInfo {
ForeignErrorConvention::Kind Kind;
ForeignErrorConvention::IsOwned_t IsOwned;
ForeignErrorConvention::IsReplaced_t ReplaceParamWithVoid;
unsigned ParamIndex;
CanType ParamType;
CanType OrigResultType;
ForeignErrorConvention asForeignErrorConvention() const {
assert(ParamType && "not fully initialized!");
using FEC = ForeignErrorConvention;
switch (Kind) {
case FEC::ZeroResult:
return FEC::getZeroResult(ParamIndex, IsOwned, ReplaceParamWithVoid,
ParamType, OrigResultType);
case FEC::NonZeroResult:
return FEC::getNonZeroResult(ParamIndex, IsOwned, ReplaceParamWithVoid,
ParamType, OrigResultType);
case FEC::ZeroPreservedResult:
return FEC::getZeroPreservedResult(ParamIndex, IsOwned,
ReplaceParamWithVoid, ParamType);
case FEC::NilResult:
return FEC::getNilResult(ParamIndex, IsOwned, ReplaceParamWithVoid,
ParamType);
case FEC::NonNilError:
return FEC::getNonNilError(ParamIndex, IsOwned, ReplaceParamWithVoid,
ParamType);
}
llvm_unreachable("bad error convention");
}
};
}
/// Determine whether this is the name of an Objective-C collection
/// with a single element type.
static bool isObjCCollectionName(StringRef typeName) {
auto lastWord = camel_case::getLastWord(typeName);
return lastWord == "Array" || lastWord == "Set";
}
/// Retrieve the name of the given Clang type for use when omitting
/// needless words.
OmissionTypeName ClangImporter::Implementation::getClangTypeNameForOmission(
clang::ASTContext &ctx, clang::QualType type) {
if (type.isNull())
return OmissionTypeName();
// Dig through the type, looking for a typedef-name and stripping
// references along the way.
StringRef lastTypedefName;
do {
// The name of a typedef-name.
auto typePtr = type.getTypePtr();
if (auto typedefType = dyn_cast<clang::TypedefType>(typePtr)) {
auto name = typedefType->getDecl()->getName();
// For Objective-C type parameters, drop the "Type" suffix if
// present.
if (isa<clang::ObjCTypeParamDecl>(typedefType->getDecl())) {
if (camel_case::getLastWord(name) == "Type") {
name = name.drop_back(4);
}
return name;
}
// Objective-C selector type.
if (ctx.hasSameUnqualifiedType(type, ctx.getObjCSelType()) &&
name == "SEL")
return "Selector";
// Objective-C "id" type.
if (type->isObjCIdType() && name == "id")
return "Object";
// Objective-C "Class" type.
if (type->isObjCClassType() && name == "Class")
return "Class";
// Objective-C "BOOL" type.
if (name == "BOOL")
return OmissionTypeName("Bool", OmissionTypeFlags::Boolean);
// If this is an imported CF type, use that name.
StringRef CFName = getCFTypeName(typedefType->getDecl());
if (!CFName.empty())
return CFName;
// If we have NS(U)Integer or CGFloat, return it.
if (name == "NSInteger" || name == "NSUInteger" || name == "CGFloat")
return name;
// Otherwise, desugar one level...
lastTypedefName = name;
type = typedefType->getDecl()->getUnderlyingType();
continue;
}
// Look through reference types.
if (auto refType = dyn_cast<clang::ReferenceType>(typePtr)) {
type = refType->getPointeeTypeAsWritten();
continue;
}
// Look through pointer types.
if (auto ptrType = dyn_cast<clang::PointerType>(typePtr)) {
type = ptrType->getPointeeType();
continue;
}
// Try to desugar one level...
clang::QualType desugared = type.getSingleStepDesugaredType(ctx);
if (desugared.getTypePtr() == type.getTypePtr())
break;
type = desugared;
} while (true);
// Objective-C object pointers.
if (auto objcObjectPtr = type->getAs<clang::ObjCObjectPointerType>()) {
auto objcClass = objcObjectPtr->getInterfaceDecl();
// For id<Proto> or NSObject<Proto>, retrieve the name of "Proto".
if (objcObjectPtr->getNumProtocols() == 1 &&
(!objcClass || objcClass->getName() == "NSObject"))
return (*objcObjectPtr->qual_begin())->getName();
// If there is a class, use it.
if (objcClass) {
// If this isn't the name of an Objective-C collection, we're done.
auto className = objcClass->getName();
if (!isObjCCollectionName(className))
return className;
// If we don't have type arguments, the collection element type
// is "Object".
auto typeArgs = objcObjectPtr->getTypeArgs();
if (typeArgs.empty())
return OmissionTypeName(className, None, "Object");
return OmissionTypeName(className, None,
getClangTypeNameForOmission(ctx,
typeArgs[0]).Name);
}
// Objective-C "id" type.
if (objcObjectPtr->isObjCIdType())
return "Object";
// Objective-C "Class" type.
if (objcObjectPtr->isObjCClassType())
return "Class";
return StringRef();
}
// Handle builtin types by importing them and getting the Swift name.
if (auto builtinTy = type->getAs<clang::BuiltinType>()) {
// Names of integer types.
static const char *intTypeNames[] = {
"UInt8",
"UInt16",
"UInt32",
"UInt64",
"UInt128"
};
/// Retrieve the name for an integer type based on its size.
auto getIntTypeName = [&](bool isSigned) -> StringRef {
switch (ctx.getTypeSize(builtinTy)) {
case 8: return StringRef(intTypeNames[0]).substr(isSigned ? 1 : 0);
case 16: return StringRef(intTypeNames[1]).substr(isSigned ? 1 : 0);
case 32: return StringRef(intTypeNames[2]).substr(isSigned ? 1 : 0);
case 64: return StringRef(intTypeNames[3]).substr(isSigned ? 1 : 0);
case 128: return StringRef(intTypeNames[4]).substr(isSigned ? 1 : 0);
default: llvm_unreachable("bad integer type size");
}
};
switch (builtinTy->getKind()) {
case clang::BuiltinType::Void:
return "Void";
case clang::BuiltinType::Bool:
return OmissionTypeName("Bool", OmissionTypeFlags::Boolean);
case clang::BuiltinType::Float:
return "Float";
case clang::BuiltinType::Double:
return "Double";
case clang::BuiltinType::Char16:
return "UInt16";
case clang::BuiltinType::Char32:
return "UnicodeScalar";
case clang::BuiltinType::Char_U:
case clang::BuiltinType::UChar:
case clang::BuiltinType::UShort:
case clang::BuiltinType::UInt:
case clang::BuiltinType::ULong:
case clang::BuiltinType::ULongLong:
case clang::BuiltinType::UInt128:
case clang::BuiltinType::WChar_U:
return getIntTypeName(false);
case clang::BuiltinType::Char_S:
case clang::BuiltinType::SChar:
case clang::BuiltinType::Short:
case clang::BuiltinType::Int:
case clang::BuiltinType::Long:
case clang::BuiltinType::LongLong:
case clang::BuiltinType::Int128:
case clang::BuiltinType::WChar_S:
return getIntTypeName(true);
// Types that cannot be mapped into Swift, and probably won't ever be.
case clang::BuiltinType::Dependent:
case clang::BuiltinType::ARCUnbridgedCast:
case clang::BuiltinType::BoundMember:
case clang::BuiltinType::BuiltinFn:
case clang::BuiltinType::Overload:
case clang::BuiltinType::PseudoObject:
case clang::BuiltinType::UnknownAny:
return OmissionTypeName();
// FIXME: Types that can be mapped, but aren't yet.
case clang::BuiltinType::Half:
case clang::BuiltinType::LongDouble:
case clang::BuiltinType::NullPtr:
return OmissionTypeName();
// Objective-C types that aren't mapped directly; rather, pointers to
// these types will be mapped.
case clang::BuiltinType::ObjCClass:
case clang::BuiltinType::ObjCId:
case clang::BuiltinType::ObjCSel:
return OmissionTypeName();
// OpenCL types that don't have Swift equivalents.
case clang::BuiltinType::OCLImage1d:
case clang::BuiltinType::OCLImage1dArray:
case clang::BuiltinType::OCLImage1dBuffer:
case clang::BuiltinType::OCLImage2d:
case clang::BuiltinType::OCLImage2dArray:
case clang::BuiltinType::OCLImage2dDepth:
case clang::BuiltinType::OCLImage2dArrayDepth:
case clang::BuiltinType::OCLImage2dMSAA:
case clang::BuiltinType::OCLImage2dArrayMSAA:
case clang::BuiltinType::OCLImage2dMSAADepth:
case clang::BuiltinType::OCLImage2dArrayMSAADepth:
case clang::BuiltinType::OCLImage3d:
case clang::BuiltinType::OCLSampler:
case clang::BuiltinType::OCLEvent:
case clang::BuiltinType::OCLClkEvent:
case clang::BuiltinType::OCLQueue:
case clang::BuiltinType::OCLNDRange:
case clang::BuiltinType::OCLReserveID:
return OmissionTypeName();
// OpenMP types that don't have Swift equivalents.
case clang::BuiltinType::OMPArraySection:
return OmissionTypeName();
}
}
// Tag types.
if (auto tagType = type->getAs<clang::TagType>()) {
if (tagType->getDecl()->getName().empty())
return lastTypedefName;
return tagType->getDecl()->getName();
}
// Block pointers.
if (type->getAs<clang::BlockPointerType>())
return "Block";
// Function pointers.
if (type->isFunctionType())
return "Function";
return StringRef();
}
/// Attempt to omit needless words from the given function name.
bool ClangImporter::Implementation::omitNeedlessWordsInFunctionName(
clang::Sema &clangSema,
StringRef &baseName,
SmallVectorImpl<StringRef> &argumentNames,
ArrayRef<const clang::ParmVarDecl *> params,
clang::QualType resultType,
const clang::DeclContext *dc,
const llvm::SmallBitVector &nonNullArgs,
const Optional<api_notes::ObjCMethodInfo> &knownMethod,
Optional<unsigned> errorParamIndex,
bool returnsSelf,
bool isInstanceMethod,
StringScratchSpace &scratch) {
clang::ASTContext &clangCtx = clangSema.Context;
// Collect the parameter type names.
StringRef firstParamName;
SmallVector<OmissionTypeName, 4> paramTypes;
for (unsigned i = 0, n = params.size(); i != n; ++i) {
auto param = params[i];
// Capture the first parameter name.
if (i == 0)
firstParamName = param->getName();
// Determine the number of parameters.
unsigned numParams = params.size();
if (errorParamIndex) --numParams;
bool isLastParameter
= (i == params.size() - 1) ||
(i == params.size() - 2 &&
errorParamIndex && *errorParamIndex == params.size() - 1);
// Figure out whether there will be a default argument for this
// parameter.
bool hasDefaultArg
= canInferDefaultArgument(
clangSema.PP,
param->getType(),
getParamOptionality(param,
!nonNullArgs.empty() && nonNullArgs[i],
knownMethod && knownMethod->NullabilityAudited
? Optional<clang::NullabilityKind>(
knownMethod->getParamTypeInfo(i))
: None),
SwiftContext.getIdentifier(baseName), numParams,
isLastParameter);
paramTypes.push_back(getClangTypeNameForOmission(clangCtx, param->getType())
.withDefaultArgument(hasDefaultArg));
}
// Find the property names.
const InheritedNameSet *allPropertyNames = nullptr;
auto contextType = getClangDeclContextType(dc);
if (!contextType.isNull()) {
if (auto objcPtrType = contextType->getAsObjCInterfacePointerType())
if (auto objcClassDecl = objcPtrType->getInterfaceDecl())
allPropertyNames = SwiftContext.getAllPropertyNames(objcClassDecl,
isInstanceMethod);
}
// Omit needless words.
return omitNeedlessWords(baseName, argumentNames, firstParamName,
getClangTypeNameForOmission(clangCtx, resultType),
getClangTypeNameForOmission(clangCtx, contextType),
paramTypes, returnsSelf, /*isProperty=*/false,
allPropertyNames, scratch);
}
/// Retrieve the instance type of the given Clang declaration context.
clang::QualType ClangImporter::Implementation::getClangDeclContextType(
const clang::DeclContext *dc) {
auto &ctx = getClangASTContext();
if (auto objcClass = dyn_cast<clang::ObjCInterfaceDecl>(dc))
return ctx.getObjCObjectPointerType(ctx.getObjCInterfaceType(objcClass));
if (auto objcCategory = dyn_cast<clang::ObjCCategoryDecl>(dc)) {
return ctx.getObjCObjectPointerType(
ctx.getObjCInterfaceType(
objcCategory->getClassInterface()));
}
if (auto tag = dyn_cast<clang::TagDecl>(dc)) {
return ctx.getTagDeclType(tag);
}
return clang::QualType();
}
bool ClangImporter::Implementation::canInferDefaultArgument(
clang::Preprocessor &pp, clang::QualType type,
OptionalTypeKind clangOptionality, Identifier baseName,
unsigned numParams, bool isLastParameter) {
// Don't introduce a default argument for setters with only a single
// parameter.
if (numParams == 1 && camel_case::getFirstWord(baseName.str()) == "set")
return false;
// Some nullable parameters default to 'nil'.
if (clangOptionality == OTK_Optional) {
// Nullable trailing closure parameters default to 'nil'.
if (isLastParameter &&
(type->isFunctionPointerType() || type->isBlockPointerType()))
return true;
// NSZone parameters default to 'nil'.
if (auto ptrType = type->getAs<clang::PointerType>()) {
if (auto recType
= ptrType->getPointeeType()->getAs<clang::RecordType>()) {
if (recType->isStructureOrClassType() &&
recType->getDecl()->getName() == "_NSZone")
return true;
}
}
}
// Option sets default to "[]" if they have "Options" in their name.
if (const clang::EnumType *enumTy = type->getAs<clang::EnumType>())
if (classifyEnum(pp, enumTy->getDecl()) == EnumKind::Options) {
auto enumName = enumTy->getDecl()->getName();
for (auto word : reversed(camel_case::getWords(enumName))) {
if (camel_case::sameWordIgnoreFirstCase(word, "options"))
return true;
}
}
return false;
}
/// Adjust the result type of a throwing function based on the
/// imported error information.
static Type adjustResultTypeForThrowingFunction(
const ClangImporter::Implementation::ImportedErrorInfo &errorInfo,
Type resultTy) {
switch (errorInfo.Kind) {
case ForeignErrorConvention::ZeroResult:
case ForeignErrorConvention::NonZeroResult:
return TupleType::getEmpty(resultTy->getASTContext());
case ForeignErrorConvention::NilResult:
resultTy = resultTy->getAnyOptionalObjectType();
assert(resultTy &&
"result type of NilResult convention was not imported as optional");
return resultTy;
case ForeignErrorConvention::ZeroPreservedResult:
case ForeignErrorConvention::NonNilError:
return resultTy;
}
}
/// Produce the foreign error convention from the imported error info,
/// error parameter type, and original result type.
static ForeignErrorConvention
getForeignErrorInfo(
const ClangImporter::Implementation::ImportedErrorInfo &errorInfo,
CanType errorParamTy, CanType origResultTy) {
assert(errorParamTy && "not fully initialized!");
using FEC = ForeignErrorConvention;
auto ReplaceParamWithVoid = errorInfo.ReplaceParamWithVoid
? FEC::IsReplaced
: FEC::IsNotReplaced;
switch (errorInfo.Kind) {
case FEC::ZeroResult:
return FEC::getZeroResult(errorInfo.ParamIndex, errorInfo.IsOwned,
ReplaceParamWithVoid, errorParamTy, origResultTy);
case FEC::NonZeroResult:
return FEC::getNonZeroResult(errorInfo.ParamIndex, errorInfo.IsOwned,
ReplaceParamWithVoid, errorParamTy,
origResultTy);
case FEC::ZeroPreservedResult:
return FEC::getZeroPreservedResult(errorInfo.ParamIndex, errorInfo.IsOwned,
ReplaceParamWithVoid, errorParamTy);
case FEC::NilResult:
return FEC::getNilResult(errorInfo.ParamIndex, errorInfo.IsOwned,
ReplaceParamWithVoid, errorParamTy);
case FEC::NonNilError:
return FEC::getNonNilError(errorInfo.ParamIndex, errorInfo.IsOwned,
ReplaceParamWithVoid, errorParamTy);
}
llvm_unreachable("bad error convention");
}
Type ClangImporter::Implementation::importMethodType(
const clang::ObjCMethodDecl *clangDecl,
clang::QualType resultType,
ArrayRef<const clang::ParmVarDecl *> params,
bool isVariadic, bool isNoReturn,
bool isFromSystemModule,
ParameterList **bodyParams,
ImportedName importedName,
DeclName &methodName,
Optional<ForeignErrorConvention> &foreignErrorInfo,
SpecialMethodKind kind) {
// Cannot import variadic types unless specially handled before calling this
// function.
if (isVariadic || clangDecl->sel_param_end() != clangDecl->param_end())
return Type();
// Clang doesn't provide pragmas for auditing the CF behavior of
// ObjC methods, but it does have attributes for declaring
// return-type management:
// - cf_returns_retained and cf_returns_not_retained are obvious
// - objc_returns_inner_pointer is sometimes used on methods
// returning CF types as a workaround for ARC not managing CF
// objects
ImportTypeKind resultKind;
if (kind == SpecialMethodKind::PropertyAccessor)
resultKind = ImportTypeKind::PropertyAccessor;
else if (isObjCMethodResultAudited(clangDecl))
resultKind = ImportTypeKind::AuditedResult;
else
resultKind = ImportTypeKind::Result;
// Check if we know more about the type from our whitelists.
Optional<api_notes::ObjCMethodInfo> knownMethod;
if (auto knownMethodTmp = getKnownObjCMethod(clangDecl)) {
if (knownMethodTmp->NullabilityAudited)
knownMethod = knownMethodTmp;
}
// Determine if the method is a property getter/setter.
const clang::ObjCPropertyDecl *property = nullptr;
bool isPropertyGetter = false;
bool isPropertySetter = false;
if (clangDecl->isPropertyAccessor()) {
property = clangDecl->findPropertyDecl();
if (property) {
if (property->getGetterMethodDecl() == clangDecl) {
isPropertyGetter = true;
} else if (property->getSetterMethodDecl() == clangDecl) {
isPropertySetter = true;
}
}
}
// Import the result type.
CanType origSwiftResultTy;
Type swiftResultTy;
auto errorInfo = importedName.ErrorInfo;
if (isPropertyGetter) {
swiftResultTy = importPropertyType(property, isFromSystemModule);
} else {
OptionalTypeKind OptionalityOfReturn;
if (clangDecl->hasAttr<clang::ReturnsNonNullAttr>()) {
OptionalityOfReturn = OTK_None;
} else if (knownMethod) {
OptionalityOfReturn = translateNullability(
knownMethod->getReturnTypeInfo());
} else {
OptionalityOfReturn = OTK_ImplicitlyUnwrappedOptional;
}
bool allowNSUIntegerAsIntInResult = isFromSystemModule;
if (allowNSUIntegerAsIntInResult) {
Identifier name = methodName.getBaseName();
if (!name.empty()) {
allowNSUIntegerAsIntInResult = !nameContainsUnsigned(name.str());
}
}
swiftResultTy = importType(resultType, resultKind,
allowNSUIntegerAsIntInResult,
/*isFullyBridgeable*/true,
OptionalityOfReturn);
// Adjust the result type for a throwing function.
if (swiftResultTy && errorInfo) {
origSwiftResultTy = swiftResultTy->getCanonicalType();
swiftResultTy = adjustResultTypeForThrowingFunction(*errorInfo,
swiftResultTy);
}
if (swiftResultTy &&
clangDecl->getMethodFamily() == clang::OMF_performSelector) {
// performSelector methods that return 'id' should be imported into Swift
// as returning Unmanaged<AnyObject>.
Type nonOptionalTy =
swiftResultTy->getAnyOptionalObjectType(OptionalityOfReturn);
if (!nonOptionalTy)
nonOptionalTy = swiftResultTy;
if (nonOptionalTy->isAnyClassReferenceType()) {
swiftResultTy = getUnmanagedType(*this, nonOptionalTy);
if (OptionalityOfReturn != OTK_None)
swiftResultTy = OptionalType::get(OptionalityOfReturn, swiftResultTy);
}
}
}
if (!swiftResultTy)
return Type();
CanType errorParamType;
llvm::SmallBitVector nonNullArgs = getNonNullArgs(clangDecl, params);
// Import the parameters.
SmallVector<ParamDecl*, 4> swiftParams;
auto addEmptyTupleParameter = [&](Identifier argName) {
// It doesn't actually matter which DeclContext we use, so just
// use the imported header unit.
auto type = TupleType::getEmpty(SwiftContext);
auto var = new (SwiftContext) ParamDecl(/*IsLet*/ true,
SourceLoc(), argName,
SourceLoc(), argName, type,
ImportedHeaderUnit);
swiftParams.push_back(var);
};
// Determine the number of parameters.
unsigned numEffectiveParams = params.size();
if (errorInfo) --numEffectiveParams;
auto argNames = methodName.getArgumentNames();
unsigned nameIndex = 0;
for (size_t paramIndex = 0; paramIndex != params.size(); paramIndex++) {
auto param = params[paramIndex];
auto paramTy = param->getType();
if (paramTy->isVoidType()) {
assert(!errorInfo || paramIndex != errorInfo->ParamIndex);
++nameIndex;
continue;
}
if (kind == SpecialMethodKind::NSDictionarySubscriptGetter)
nonNullArgs.empty();
// Import the parameter type into Swift.
// Check nullability of the parameter.
OptionalTypeKind optionalityOfParam
= getParamOptionality(param,
!nonNullArgs.empty() && nonNullArgs[paramIndex],
knownMethod
? Optional<clang::NullabilityKind>(
knownMethod->getParamTypeInfo(paramIndex))
: None);
bool allowNSUIntegerAsIntInParam = isFromSystemModule;
if (allowNSUIntegerAsIntInParam) {
Identifier name;
if (nameIndex < argNames.size())
name = argNames[nameIndex];
if (name.empty() && nameIndex == 0)
name = methodName.getBaseName();
if (!name.empty())
allowNSUIntegerAsIntInParam = !nameContainsUnsigned(name.str());
}
Type swiftParamTy;
if (paramIndex == 0 && isPropertySetter) {
swiftParamTy = importPropertyType(property, isFromSystemModule);
} else if (kind == SpecialMethodKind::NSDictionarySubscriptGetter &&
paramTy->isObjCIdType()) {
swiftParamTy = getOptionalType(getNSCopyingType(),
ImportTypeKind::Parameter,
optionalityOfParam);
} else if (kind == SpecialMethodKind::PropertyAccessor) {
swiftParamTy = importType(paramTy,
ImportTypeKind::PropertyAccessor,
allowNSUIntegerAsIntInParam,
/*isFullyBridgeable*/true,
optionalityOfParam);
} else {
ImportTypeKind importKind = ImportTypeKind::Parameter;
if (param->hasAttr<clang::CFReturnsRetainedAttr>())
importKind = ImportTypeKind::CFRetainedOutParameter;
else if (param->hasAttr<clang::CFReturnsNotRetainedAttr>())
importKind = ImportTypeKind::CFUnretainedOutParameter;
swiftParamTy = importType(paramTy, importKind,
allowNSUIntegerAsIntInParam,
/*isFullyBridgeable*/true,
optionalityOfParam);
}
if (!swiftParamTy)
return Type();
// If this is the error parameter, remember it, but don't build it
// into the parameter type.
if (errorInfo && paramIndex == errorInfo->ParamIndex) {
errorParamType = swiftParamTy->getCanonicalType();
// ...unless we're supposed to replace it with ().
if (errorInfo->ReplaceParamWithVoid) {
addEmptyTupleParameter(argNames[nameIndex]);
++nameIndex;
}
continue;
}
// Map __attribute__((noescape)) to @noescape.
bool addNoEscapeAttr = false;
if (param->hasAttr<clang::NoEscapeAttr>()) {
Type newParamTy = applyNoEscape(swiftParamTy);
if (newParamTy.getPointer() != swiftParamTy.getPointer()) {
swiftParamTy = newParamTy;
addNoEscapeAttr = true;
}
}
// Figure out the name for this parameter.
Identifier bodyName = importFullName(param).Imported.getBaseName();
// Figure out the name for this argument, which comes from the method name.
Identifier name;
if (nameIndex < argNames.size()) {
name = argNames[nameIndex];
}
++nameIndex;
// It doesn't actually matter which DeclContext we use, so just use the
// imported header unit.
auto bodyVar
= createDeclWithClangNode<ParamDecl>(param,
/*IsLet*/ true, SourceLoc(), name,
importSourceLoc(param->getLocation()),
bodyName, swiftParamTy,
ImportedHeaderUnit);
if (addNoEscapeAttr) {
bodyVar->getAttrs().add(
new (SwiftContext) NoEscapeAttr(/*IsImplicit=*/false));
}
// Set up the parameter info.
auto paramInfo = bodyVar;
// Determine whether we have a default argument.
if (InferDefaultArguments &&
(kind == SpecialMethodKind::Regular ||
kind == SpecialMethodKind::Constructor)) {
bool isLastParameter = (paramIndex == params.size() - 1) ||
(paramIndex == params.size() - 2 &&
errorInfo && errorInfo->ParamIndex == params.size() - 1);
if (canInferDefaultArgument(getClangPreprocessor(),
param->getType(), optionalityOfParam,
methodName.getBaseName(), numEffectiveParams,
isLastParameter))
paramInfo->setDefaultArgumentKind(DefaultArgumentKind::Normal);
}
swiftParams.push_back(paramInfo);
}
// If we have a constructor with no parameters and a name with an
// argument name, synthesize a Void parameter with that name.
if (kind == SpecialMethodKind::Constructor && params.empty() &&
argNames.size() == 1) {
addEmptyTupleParameter(argNames[0]);
}
if (importedName.HasCustomName && argNames.size() != swiftParams.size()) {
// Note carefully: we're emitting a warning in the /Clang/ buffer.
auto &srcMgr = getClangASTContext().getSourceManager();
auto &rawDiagClient = Instance->getDiagnosticClient();
auto &diagClient = static_cast<ClangDiagnosticConsumer &>(rawDiagClient);
SourceLoc methodLoc =
diagClient.resolveSourceLocation(srcMgr, clangDecl->getLocation());
if (methodLoc.isValid()) {
SwiftContext.Diags.diagnose(methodLoc, diag::invalid_swift_name_method,
swiftParams.size() < argNames.size(),
swiftParams.size(), argNames.size());
}
return Type();
}
// Form the parameter list.
*bodyParams = ParameterList::create(SwiftContext, swiftParams);
FunctionType::ExtInfo extInfo;
extInfo = extInfo.withIsNoReturn(isNoReturn);
if (errorInfo) {
foreignErrorInfo = getForeignErrorInfo(*errorInfo, errorParamType,
origSwiftResultTy);
// Mark that the function type throws.
extInfo = extInfo.withThrows(true);
}
// Form the function type.
return FunctionType::get((*bodyParams)->getType(SwiftContext),
swiftResultTy, extInfo);
}
Module *ClangImporter::Implementation::getStdlibModule() {
return SwiftContext.getStdlibModule(true);
}
Module *ClangImporter::Implementation::getNamedModule(StringRef name) {
return SwiftContext.getLoadedModule(SwiftContext.getIdentifier(name));
}
static Module *tryLoadModule(ASTContext &C,
Identifier name,
bool importForwardDeclarations,
Optional<Module *> &cache) {
if (!cache.hasValue()) {
// If we're synthesizing forward declarations, we don't want to pull in
// the module too eagerly.
if (importForwardDeclarations)
cache = C.getLoadedModule(name);
else
cache = C.getModule({ {name, SourceLoc()} });
}
return cache.getValue();
}
Module *ClangImporter::Implementation::tryLoadFoundationModule() {
return tryLoadModule(SwiftContext, SwiftContext.Id_Foundation,
ImportForwardDeclarations, checkedFoundationModule);
}
Module *ClangImporter::Implementation::tryLoadSIMDModule() {
return tryLoadModule(SwiftContext, SwiftContext.Id_simd,
ImportForwardDeclarations, checkedSIMDModule);
}
Type ClangImporter::Implementation::getNamedSwiftType(Module *module,
StringRef name) {
if (!module)
return Type();
// Look for the type.
Identifier identifier = SwiftContext.getIdentifier(name);
SmallVector<ValueDecl *, 2> results;
// Check if the lookup we're about to perform a lookup within is
// a Clang module.
for (auto *file : module->getFiles()) {
if (auto clangUnit = dyn_cast<ClangModuleUnit>(file)) {
// If we have an overlay, look in the overlay. Otherwise, skip
// the lookup to avoid infinite recursion.
if (auto module = clangUnit->getAdapterModule())
module->lookupValue({ }, identifier,
NLKind::UnqualifiedLookup, results);
} else {
file->lookupValue({ }, identifier,
NLKind::UnqualifiedLookup, results);
}
}
if (results.size() != 1)
return Type();
auto type = dyn_cast<TypeDecl>(results.front());
if (!type)
return Type();
assert(!type->hasClangNode() && "picked up the original type?");
if (auto *typeResolver = getTypeResolver())
typeResolver->resolveDeclSignature(type);
return type->getDeclaredType();
}
Type
ClangImporter::Implementation::
getNamedSwiftTypeSpecialization(Module *module, StringRef name,
ArrayRef<Type> args) {
if (!module)
return Type();
// Look for the type.
SmallVector<ValueDecl *, 2> results;
module->lookupValue({ }, SwiftContext.getIdentifier(name),
NLKind::UnqualifiedLookup, results);
if (results.size() == 1) {
if (auto nominalDecl = dyn_cast<NominalTypeDecl>(results.front())) {
if (auto *typeResolver = getTypeResolver())
typeResolver->resolveDeclSignature(nominalDecl);
if (auto params = nominalDecl->getGenericParams()) {
if (params->size() == args.size()) {
// When we form the bound generic type, make sure we get the
// substitutions.
auto *BGT = BoundGenericType::get(nominalDecl, Type(), args);
return BGT;
}
}
}
}
return Type();
}
Decl *ClangImporter::Implementation::importDeclByName(StringRef name) {
auto &sema = Instance->getSema();
// Map the name. If we can't represent the Swift name in Clang, bail out now.
auto clangName = &getClangASTContext().Idents.get(name);
// Perform name lookup into the global scope.
// FIXME: Map source locations over.
clang::LookupResult lookupResult(sema, clangName, clang::SourceLocation(),
clang::Sema::LookupOrdinaryName);
if (!sema.LookupName(lookupResult, /*Scope=*/0)) {
return nullptr;
}
for (auto decl : lookupResult) {
if (auto swiftDecl = importDecl(decl->getUnderlyingDecl())) {
return swiftDecl;
}
}
return nullptr;
}
Type ClangImporter::Implementation::getNSObjectType() {
if (NSObjectTy)
return NSObjectTy;
if (auto decl = dyn_cast_or_null<ClassDecl>(importDeclByName("NSObject"))) {
NSObjectTy = decl->getDeclaredType();
return NSObjectTy;
}
return Type();
}
bool ClangImporter::Implementation::matchesNSObjectBound(Type type) {
Type NSObjectType = getNSObjectType();
if (!NSObjectType)
return false;
// Class type or existential that inherits from NSObject.
if (NSObjectType->isSuperclassOf(type, getTypeResolver()))
return true;
// Struct or enum type must have been bridged.
if (type->getStructOrBoundGenericStruct() ||
type->getEnumOrBoundGenericEnum())
return true;
return false;
}
static Type getNamedProtocolType(ClangImporter::Implementation &impl,
StringRef name) {
auto &sema = impl.getClangSema();
auto clangName = &sema.getASTContext().Idents.get(name);
assert(clangName);
// Perform name lookup into the global scope.
clang::LookupResult lookupResult(sema, clangName, clang::SourceLocation(),
clang::Sema::LookupObjCProtocolName);
if (!sema.LookupName(lookupResult, /*Scope=*/0))
return Type();
for (auto decl : lookupResult) {
if (auto swiftDecl = impl.importDecl(decl->getUnderlyingDecl())) {
if (auto protoDecl = dyn_cast<ProtocolDecl>(swiftDecl)) {
return protoDecl->getDeclaredType();
}
}
}
return Type();
}
Type ClangImporter::Implementation::getNSCopyingType() {
return getNamedProtocolType(*this, "NSCopying");
}
Type ClangImporter::Implementation::getNSObjectProtocolType() {
return getNamedProtocolType(*this, "NSObject");
}
Type ClangImporter::Implementation::getCFStringRefType() {
if (auto decl = dyn_cast_or_null<TypeDecl>(importDeclByName("CFStringRef")))
return decl->getDeclaredType();
return Type();
}