diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 4d32b65aadf12..c9f3fbe06e187 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -23,11 +23,12 @@ #include "swift/AST/Import.h" #include "swift/AST/SearchPathOptions.h" #include "swift/AST/Type.h" -#include "swift/AST/Types.h" #include "swift/AST/TypeAlignments.h" +#include "swift/AST/Types.h" #include "swift/Basic/LangOptions.h" #include "swift/Basic/Located.h" #include "swift/Basic/Malloc.h" +#include "clang/AST/DeclTemplate.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" @@ -628,6 +629,13 @@ class ASTContext final { ArrayRef params, Optional result, SILFunctionType::Representation trueRep); + /// Instantiates "Impl.Converter" if needed, then calls + /// ClangTypeConverter::getClangTemplateArguments. + std::unique_ptr getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs); + /// Get the Swift declaration that a Clang declaration was exported from, /// if applicable. const Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl); diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index 06c41a3d8e61e..b02bac076b25a 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -14,7 +14,9 @@ #define SWIFT_AST_CLANG_MODULE_LOADER_H #include "swift/AST/ModuleLoader.h" +#include "swift/AST/SubstitutionMap.h" #include "swift/Basic/TaggedUnion.h" +#include "clang/AST/DeclTemplate.h" namespace clang { class ASTContext; @@ -219,6 +221,18 @@ class ClangModuleLoader : public ModuleLoader { /// based on subtleties like the target module interface format. virtual bool isSerializable(const clang::Type *type, bool checkCanonical) const = 0; + + virtual clang::FunctionDecl * + instantiateCXXFunctionTemplate(ASTContext &ctx, + clang::FunctionTemplateDecl *func, + SubstitutionMap subst) = 0; +}; + +/// Used to describe a template instantiation error. +struct TemplateInstantiationError { + /// Generic types that could not be converted to QualTypes using the + /// ClangTypeConverter. + SmallVector failedTypes; }; } // namespace swift diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index c36f461da6c92..a23065ac95b25 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5960,9 +5960,10 @@ class FuncDecl : public AbstractFunctionDecl { DeclContext *Parent); static FuncDecl *createImported(ASTContext &Context, SourceLoc FuncLoc, - DeclName Name, SourceLoc NameLoc, - bool Async, bool Throws, - ParameterList *BodyParams, Type FnRetType, + DeclName Name, SourceLoc NameLoc, bool Async, + bool Throws, ParameterList *BodyParams, + Type FnRetType, + GenericParamList *GenericParams, DeclContext *Parent, ClangNode ClangN); bool isStatic() const; diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 328a7fc06ba72..23e8487d8f994 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1674,6 +1674,11 @@ ERROR(where_nongeneric_ctx,none, ERROR(where_nongeneric_toplevel,none, "'where' clause cannot be applied to a non-generic top-level " "declaration", ()) +ERROR(unable_to_convert_generic_swift_types,none, + "could not generate C++ types from the generic Swift types provided. " + "The following Swift type(s) provided to '%0' were unable to be " + "converted: %1.", + (StringRef, StringRef)) // Type aliases ERROR(type_alias_underlying_type_access,none, diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 578287637964b..2a30888e5cba3 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -472,6 +472,11 @@ class ClangImporter final : public ClangModuleLoader { bool isSerializable(const clang::Type *type, bool checkCanonical) const override; + + clang::FunctionDecl * + instantiateCXXFunctionTemplate(ASTContext &ctx, + clang::FunctionTemplateDecl *func, + SubstitutionMap subst) override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index facc679307835..7cf42afa2d904 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -18,13 +18,13 @@ #include "ClangTypeConverter.h" #include "ForeignRepresentationInfo.h" #include "SubstitutionMapStorage.h" -#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/FileUnit.h" +#include "swift/AST/ForeignAsyncConvention.h" #include "swift/AST/ForeignErrorConvention.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" @@ -41,19 +41,19 @@ #include "swift/AST/PropertyWrappers.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/RawComment.h" +#include "swift/AST/SILLayout.h" #include "swift/AST/SemanticAttrs.h" #include "swift/AST/SourceFile.h" #include "swift/AST/SubstitutionMap.h" -#include "swift/AST/SILLayout.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/StringExtras.h" -#include "swift/Syntax/References.h" -#include "swift/Syntax/SyntaxArena.h" #include "swift/Strings.h" #include "swift/Subsystems.h" +#include "swift/Syntax/References.h" +#include "swift/Syntax/SyntaxArena.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringMap.h" @@ -4563,6 +4563,21 @@ ASTContext::getCanonicalClangFunctionType( return ty ? ty->getCanonicalTypeInternal().getTypePtr() : nullptr; } +std::unique_ptr +ASTContext::getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs) { + auto &impl = getImpl(); + if (!impl.Converter) { + auto *cml = getClangModuleLoader(); + impl.Converter.emplace(*this, cml->getClangASTContext(), LangOpts.Target); + } + + return impl.Converter->getClangTemplateArguments(templateParams, genericArgs, + templateArgs); +} + const Decl * ASTContext::getSwiftDeclForExportedClangDecl(const clang::Decl *decl) { auto &impl = getImpl(); diff --git a/lib/AST/ClangTypeConverter.cpp b/lib/AST/ClangTypeConverter.cpp index 365d9b72e3d03..9da86112ebae6 100644 --- a/lib/AST/ClangTypeConverter.cpp +++ b/lib/AST/ClangTypeConverter.cpp @@ -807,3 +807,33 @@ Decl *ClangTypeConverter::getSwiftDeclForExportedClangDecl( auto it = ReversedExportMap.find(decl); return (it != ReversedExportMap.end() ? it->second : nullptr); } + +std::unique_ptr +ClangTypeConverter::getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs) { + // Keep track of the types we failed to convert so we can return a useful + // error. + SmallVector 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(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(); + llvm::for_each(failedTypes, [&errorInfo](auto type) { + errorInfo->failedTypes.push_back(type); + }); + return errorInfo; +} diff --git a/lib/AST/ClangTypeConverter.h b/lib/AST/ClangTypeConverter.h index e41a824163312..b590b3ef0c0ec 100644 --- a/lib/AST/ClangTypeConverter.h +++ b/lib/AST/ClangTypeConverter.h @@ -84,8 +84,18 @@ class ClangTypeConverter : /// Swift declaration. Decl *getSwiftDeclForExportedClangDecl(const clang::Decl *decl) const; + /// Translate Swift generic arguments to template arguments. + /// + /// \returns nullptr if successful. If an error occors, returns a unique_ptr + /// to a `TemplateInstantiationError` with a list of the failed types. + std::unique_ptr getClangTemplateArguments( + const clang::TemplateParameterList *templateParams, + ArrayRef genericArgs, + SmallVectorImpl &templateArgs); + private: clang::QualType convert(Type type); + clang::QualType convertMemberType(NominalTypeDecl *DC, StringRef memberName); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 29079e477b1ef..78fc055fd0e0c 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7227,16 +7227,15 @@ FuncDecl *FuncDecl::createImplicit(ASTContext &Context, } FuncDecl *FuncDecl::createImported(ASTContext &Context, SourceLoc FuncLoc, - DeclName Name, SourceLoc NameLoc, - bool Async, bool Throws, - ParameterList *BodyParams, - Type FnRetType, DeclContext *Parent, - ClangNode ClangN) { + DeclName Name, SourceLoc NameLoc, bool Async, + bool Throws, ParameterList *BodyParams, + Type FnRetType, + GenericParamList *GenericParams, + DeclContext *Parent, ClangNode ClangN) { assert(ClangN && FnRetType); auto *const FD = FuncDecl::createImpl( Context, SourceLoc(), StaticSpellingKind::None, FuncLoc, Name, NameLoc, - Async, SourceLoc(), Throws, SourceLoc(), - /*GenericParams=*/nullptr, Parent, ClangN); + Async, SourceLoc(), Throws, SourceLoc(), GenericParams, Parent, ClangN); FD->setParameters(BodyParams); FD->setResultInterfaceType(FnRetType); return FD; diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 35719f241d73b..eaf38cbaf7b0e 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -21,8 +21,9 @@ #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsClangImporter.h" -#include "swift/AST/ImportCache.h" +#include "swift/AST/DiagnosticsSema.h" #include "swift/AST/IRGenOptions.h" +#include "swift/AST/ImportCache.h" #include "swift/AST/LinkLibrary.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" @@ -35,8 +36,6 @@ #include "swift/ClangImporter/ClangModule.h" #include "swift/Config.h" #include "swift/Demangling/Demangle.h" -#include "swift/ClangImporter/ClangModule.h" -#include "swift/Config.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/Parser.h" #include "swift/Strings.h" @@ -4082,3 +4081,32 @@ swift::getModuleCachePathFromClang(const clang::CompilerInstance &Clang) { return llvm::sys::path::parent_path(SpecificModuleCachePath).str(); } +clang::FunctionDecl *ClangImporter::instantiateCXXFunctionTemplate( + ASTContext &ctx, clang::FunctionTemplateDecl *func, SubstitutionMap subst) { + SmallVector templateSubst; + std::unique_ptr error = + ctx.getClangTemplateArguments(func->getTemplateParameters(), + subst.getReplacementTypes(), templateSubst); + if (error) { + std::string failedTypesStr; + llvm::raw_string_ostream failedTypesStrStream(failedTypesStr); + llvm::interleaveComma(error->failedTypes, failedTypesStrStream); + // TODO: Use the location of the apply here. + // TODO: This error message should not reference implementation details. + // See: https://github.com/apple/swift/pull/33053#discussion_r477003350 + ctx.Diags.diagnose(SourceLoc(), + diag::unable_to_convert_generic_swift_types.ID, + {func->getName(), StringRef(failedTypesStr)}); + // Return a valid FunctionDecl but, we'll never use it. + return func->getAsFunction(); + } + + // Instanciate a specialization of this template using the substitution map. + auto *templateArgList = clang::TemplateArgumentList::CreateCopy( + func->getASTContext(), templateSubst); + auto &sema = getClangInstance().getSema(); + auto *spec = sema.InstantiateFunctionDeclaration(func, templateArgList, + clang::SourceLocation()); + sema.InstantiateFunctionDefinition(clang::SourceLocation(), spec); + return spec; +} diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index b2128bea8b732..2a058ac9ed739 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -17,6 +17,7 @@ #include "CFTypeInfo.h" #include "ImporterImpl.h" #include "swift/AST/ASTContext.h" +#include "swift/AST/ASTDemangler.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/Attr.h" #include "swift/AST/Builtins.h" @@ -51,7 +52,6 @@ #include "clang/AST/DeclObjCCommon.h" #include "clang/AST/DeclCXX.h" #include "clang/Basic/CharInfo.h" -#include "swift/Basic/Statistic.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" @@ -161,27 +161,22 @@ createVarWithPattern(ASTContext &ctx, DeclContext *dc, Identifier name, Type ty, static FuncDecl *createFuncOrAccessor(ASTContext &ctx, SourceLoc funcLoc, Optional accessorInfo, DeclName name, SourceLoc nameLoc, - ParameterList *bodyParams, - Type resultTy, - bool async, - bool throws, - DeclContext *dc, + GenericParamList *genericParams, + ParameterList *bodyParams, Type resultTy, + bool async, bool throws, DeclContext *dc, ClangNode clangNode) { if (accessorInfo) { return AccessorDecl::create(ctx, funcLoc, /*accessorKeywordLoc*/ SourceLoc(), - accessorInfo->Kind, - accessorInfo->Storage, - /*StaticLoc*/SourceLoc(), - StaticSpellingKind::None, - throws, - /*ThrowsLoc=*/SourceLoc(), - /*GenericParams=*/nullptr, - bodyParams, - resultTy, dc, clangNode); + accessorInfo->Kind, accessorInfo->Storage, + /*StaticLoc*/ SourceLoc(), + StaticSpellingKind::None, throws, + /*ThrowsLoc=*/SourceLoc(), genericParams, + bodyParams, resultTy, dc, clangNode); } else { return FuncDecl::createImported(ctx, funcLoc, name, nameLoc, async, throws, - bodyParams, resultTy, dc, clangNode); + bodyParams, resultTy, genericParams, dc, + clangNode); } } @@ -3736,9 +3731,9 @@ namespace { continue; nonSelfParams.push_back(decl->getParamDecl(i)); } - return Impl.importFunctionParameterList(dc, decl, nonSelfParams, - decl->isVariadic(), - allowNSUIntegerAsInt, argNames); + return Impl.importFunctionParameterList( + dc, decl, nonSelfParams, decl->isVariadic(), allowNSUIntegerAsInt, + argNames, /*genericParams=*/{}); } Decl *importGlobalAsInitializer(const clang::FunctionDecl *decl, @@ -3783,10 +3778,11 @@ namespace { return importFunctionDecl(decl, importedName, correctSwiftName, None); } - Decl *importFunctionDecl(const clang::FunctionDecl *decl, - ImportedName importedName, - Optional correctSwiftName, - Optional accessorInfo) { + Decl *importFunctionDecl( + const clang::FunctionDecl *decl, ImportedName importedName, + Optional correctSwiftName, + Optional accessorInfo, + const clang::FunctionTemplateDecl *funcTemplate = nullptr) { if (decl->isDeleted()) return nullptr; @@ -3801,6 +3797,22 @@ namespace { ImportedType importedType; bool selfIsInOut = false; ParameterList *bodyParams = nullptr; + GenericParamList *genericParams = nullptr; + SmallVector templateParams; + if (funcTemplate) { + unsigned i = 0; + for (auto param : *funcTemplate->getTemplateParameters()) { + auto *typeParam = Impl.createDeclWithClangNode( + param, AccessLevel::Public, dc, + Impl.SwiftContext.getIdentifier(param->getName()), SourceLoc(), 0, + i); + templateParams.push_back(typeParam); + (void)++i; + } + genericParams = GenericParamList::create(Impl.SwiftContext, SourceLoc(), + templateParams, SourceLoc()); + } + if (!dc->isModuleScopeContext() && !isa(decl)) { // Handle initializers. if (name.getBaseName() == DeclBaseName::createConstructor()) { @@ -3868,7 +3880,8 @@ namespace { // names get into the resulting function type. importedType = Impl.importFunctionParamsAndReturnType( dc, decl, {decl->param_begin(), decl->param_size()}, - decl->isVariadic(), isInSystemModule(dc), name, bodyParams); + decl->isVariadic(), isInSystemModule(dc), name, bodyParams, + templateParams); if (auto *mdecl = dyn_cast(decl)) { if (mdecl->isStatic() || @@ -3901,6 +3914,10 @@ namespace { auto loc = Impl.importSourceLoc(decl->getLocation()); + ClangNode clangNode = decl; + if (funcTemplate) + clangNode = funcTemplate; + // FIXME: Poor location info. auto nameLoc = Impl.importSourceLoc(decl->getLocation()); @@ -3914,17 +3931,17 @@ namespace { DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(), bodyParams); result = Impl.createDeclWithClangNode( - decl, AccessLevel::Public, ctorName, loc, /*failable=*/false, + clangNode, AccessLevel::Public, ctorName, loc, /*failable=*/false, /*FailabilityLoc=*/SourceLoc(), /*Throws=*/false, - /*ThrowsLoc=*/SourceLoc(), bodyParams, /*GenericParams=*/nullptr, - dc); + /*ThrowsLoc=*/SourceLoc(), bodyParams, genericParams, dc); } else { auto resultTy = importedType.getType(); FuncDecl *func = createFuncOrAccessor(Impl.SwiftContext, loc, accessorInfo, name, - nameLoc, bodyParams, resultTy, - /*async=*/false, /*throws=*/false, dc, decl); + nameLoc, genericParams, bodyParams, resultTy, + /*async=*/false, /*throws=*/false, dc, + clangNode); result = func; if (!dc->isModuleScopeContext()) { @@ -4158,6 +4175,21 @@ namespace { return nullptr; } + Decl *VisitFunctionTemplateDecl(const clang::FunctionTemplateDecl *decl) { + Optional correctSwiftName; + auto importedName = + importFullName(decl->getAsFunction(), correctSwiftName); + if (!importedName) + return nullptr; + // All template parameters must be template type parameters. + if (!llvm::all_of(*decl->getTemplateParameters(), [](auto param) { + return isa(param); + })) + return nullptr; + return importFunctionDecl(decl->getAsFunction(), importedName, + correctSwiftName, None, decl); + } + Decl *VisitUsingDecl(const clang::UsingDecl *decl) { // Using declarations are not imported. return nullptr; @@ -4530,12 +4562,11 @@ namespace { } auto result = createFuncOrAccessor(Impl.SwiftContext, - /*funcLoc*/SourceLoc(), - accessorInfo, + /*funcLoc*/ SourceLoc(), accessorInfo, importedName.getDeclName(), - /*nameLoc*/SourceLoc(), - bodyParams, resultTy, - async, throws, dc, decl); + /*nameLoc*/ SourceLoc(), + /*genericParams=*/nullptr, bodyParams, + resultTy, async, throws, dc, decl); result->setAccess(getOverridableAccessLevel(dc)); @@ -6045,7 +6076,7 @@ Decl *SwiftDeclConverter::importGlobalAsInitializer( } else { parameterList = Impl.importFunctionParameterList( dc, decl, {decl->param_begin(), decl->param_end()}, decl->isVariadic(), - allowNSUIntegerAsInt, argNames); + allowNSUIntegerAsInt, argNames, /*genericParams=*/{}); } if (!parameterList) return nullptr; diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 70bd70dff9ac6..7156b8cde5353 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -24,6 +24,7 @@ #include "swift/AST/DiagnosticsClangImporter.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/GenericEnvironment.h" +#include "swift/AST/GenericSignatureBuilder.h" #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" @@ -210,10 +211,10 @@ namespace { ImportResult VisitType(const Type*) = delete; -#define DEPENDENT_TYPE(Class, Base) \ - ImportResult Visit##Class##Type(const clang::Class##Type *) { \ - llvm_unreachable("Dependent types cannot be converted"); \ - } +#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.inc" @@ -1696,22 +1697,51 @@ ImportedType ClangImporter::Implementation::importFunctionReturnType( OptionalityOfReturn); } +static Type +findGenericTypeInGenericDecls(const clang::TemplateTypeParmType *templateParam, + ArrayRef genericParams) { + StringRef name = templateParam->getIdentifier()->getName(); + auto genericParamIter = + llvm::find_if(genericParams, [name](GenericTypeParamDecl *generic) { + return generic->getName().str() == name; + }); + // TODO: once we support generics in class types, replace this with + // "return nullptr". Once support for template classes, this will need to + // be updated, though. I'm leaving the assert here to make it easier to + // find. + assert(genericParamIter != genericParams.end() && + "Could not find generic param type in generic params."); + auto *genericParamDecl = *genericParamIter; + auto metatype = + cast(genericParamDecl->getInterfaceType().getPointer()); + return metatype->getMetatypeInstanceType(); +} + ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( DeclContext *dc, const clang::FunctionDecl *clangDecl, ArrayRef params, bool isVariadic, - bool isFromSystemModule, DeclName name, ParameterList *¶meterList) { + bool isFromSystemModule, DeclName name, ParameterList *¶meterList, + ArrayRef genericParams) { bool allowNSUIntegerAsInt = shouldAllowNSUIntegerAsInt(isFromSystemModule, clangDecl); - auto importedType = - importFunctionReturnType(dc, clangDecl, allowNSUIntegerAsInt); - if (!importedType) - return {Type(), false}; + ImportedType importedType; + if (auto templateType = + dyn_cast(clangDecl->getReturnType())) { + importedType = {findGenericTypeInGenericDecls(templateType, genericParams), + false}; + } else { + importedType = + importFunctionReturnType(dc, clangDecl, allowNSUIntegerAsInt); + if (!importedType) + return {Type(), false}; + } ArrayRef argNames = name.getArgumentNames(); parameterList = importFunctionParameterList(dc, clangDecl, params, isVariadic, - allowNSUIntegerAsInt, argNames); + allowNSUIntegerAsInt, argNames, + genericParams); if (!parameterList) return {Type(), false}; @@ -1725,7 +1755,8 @@ ImportedType ClangImporter::Implementation::importFunctionParamsAndReturnType( ParameterList *ClangImporter::Implementation::importFunctionParameterList( DeclContext *dc, const clang::FunctionDecl *clangDecl, ArrayRef params, bool isVariadic, - bool allowNSUIntegerAsInt, ArrayRef argNames) { + bool allowNSUIntegerAsInt, ArrayRef argNames, + ArrayRef genericParams) { // Import the parameters. SmallVector parameters; unsigned index = 0; @@ -1773,12 +1804,21 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( importKind = ImportTypeKind::CFUnretainedOutParameter; // Import the parameter type into Swift. - auto importedType = importType(paramTy, importKind, allowNSUIntegerAsInt, - Bridgeability::Full, OptionalityOfParam); - if (!importedType) - return nullptr; + Type swiftParamTy; + bool isParamTypeImplicitlyUnwrapped = false; + if (auto *templateParamType = + dyn_cast(paramTy)) { + swiftParamTy = + findGenericTypeInGenericDecls(templateParamType, genericParams); + } else { + auto importedType = importType(paramTy, importKind, allowNSUIntegerAsInt, + Bridgeability::Full, OptionalityOfParam); + if (!importedType) + return nullptr; - auto swiftParamTy = importedType.getType(); + isParamTypeImplicitlyUnwrapped = importedType.isImplicitlyUnwrapped(); + swiftParamTy = importedType.getType(); + } // Map __attribute__((noescape)) to @noescape. if (param->hasAttr()) { @@ -1806,8 +1846,7 @@ ParameterList *ClangImporter::Implementation::importFunctionParameterList( ImportedHeaderUnit); paramInfo->setSpecifier(ParamSpecifier::Default); paramInfo->setInterfaceType(swiftParamTy); - recordImplicitUnwrapForDecl(paramInfo, - importedType.isImplicitlyUnwrapped()); + recordImplicitUnwrapForDecl(paramInfo, isParamTypeImplicitlyUnwrapped); parameters.push_back(paramInfo); ++index; } diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 5b0876cb98e45..45f537f3d33fb 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1077,13 +1077,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// to system APIs. /// \param name The name of the function. /// \param[out] parameterList The parameters visible inside the function body. - ImportedType - importFunctionParamsAndReturnType(DeclContext *dc, - const clang::FunctionDecl *clangDecl, - ArrayRef params, - bool isVariadic, bool isFromSystemModule, - DeclName name, - ParameterList *¶meterList); + ImportedType importFunctionParamsAndReturnType( + DeclContext *dc, const clang::FunctionDecl *clangDecl, + ArrayRef params, bool isVariadic, + bool isFromSystemModule, DeclName name, ParameterList *¶meterList, + ArrayRef genericParams); /// Import the given function return type. /// @@ -1110,12 +1108,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// \param argNames The argument names /// /// \returns The imported parameter list on success, or null on failure - ParameterList * - importFunctionParameterList(DeclContext *dc, - const clang::FunctionDecl *clangDecl, - ArrayRef params, - bool isVariadic, bool allowNSUIntegerAsInt, - ArrayRef argNames); + ParameterList *importFunctionParameterList( + DeclContext *dc, const clang::FunctionDecl *clangDecl, + ArrayRef params, bool isVariadic, + bool allowNSUIntegerAsInt, ArrayRef argNames, + ArrayRef genericParams); ImportedType importPropertyType(const clang::ObjCPropertyDecl *clangDecl, bool isFromSystemModule); diff --git a/lib/ClangImporter/SwiftLookupTable.h b/lib/ClangImporter/SwiftLookupTable.h index 0a5afe26e0d4d..db60c67a90807 100644 --- a/lib/ClangImporter/SwiftLookupTable.h +++ b/lib/ClangImporter/SwiftLookupTable.h @@ -201,6 +201,7 @@ class EffectiveClangContext { DC = nsDecl->getCanonicalDecl(); } else { assert(isa(dc) || + isa(dc) || isa(dc) && "No other kinds of effective Clang contexts"); DC = dc; diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 571965a2f387d..2b08dda1cd2bb 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -16,17 +16,18 @@ // //===----------------------------------------------------------------------===// -#include "CodeSynthesis.h" #include "CSDiagnostics.h" +#include "CodeSynthesis.h" #include "MiscDiagnostics.h" #include "TypeCheckProtocol.h" #include "TypeCheckType.h" #include "swift/AST/ASTVisitor.h" #include "swift/AST/ASTWalker.h" +#include "swift/AST/ClangModuleLoader.h" #include "swift/AST/ExistentialLayout.h" -#include "swift/AST/Initializer.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" +#include "swift/AST/Initializer.h" #include "swift/AST/OperatorNameLookup.h" #include "swift/AST/ParameterList.h" #include "swift/AST/ProtocolConformance.h" @@ -34,6 +35,12 @@ #include "swift/Basic/StringExtras.h" #include "swift/Sema/ConstraintSystem.h" #include "swift/Sema/SolutionResult.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Mangle.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/Template.h" +#include "clang/Sema/TemplateDeduction.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallString.h" @@ -99,6 +106,45 @@ Solution::computeSubstitutions(GenericSignature sig, lookupConformanceFn); } +static ConcreteDeclRef generateDeclRefForSpecializedCXXFunctionTemplate( + ASTContext &ctx, FuncDecl *oldDecl, SubstitutionMap subst, + clang::FunctionDecl *specialized) { + // Create a new ParameterList with the substituted type. + auto oldFnType = + cast(oldDecl->getInterfaceType().getPointer()); + auto newFnType = oldFnType->substGenericArgs(subst); + SmallVector newParams; + unsigned i = 0; + for (auto paramTy : newFnType->getParams()) { + auto *oldParamDecl = oldDecl->getParameters()->get(i); + auto *newParamDecl = + ParamDecl::cloneWithoutType(oldDecl->getASTContext(), oldParamDecl); + newParamDecl->setInterfaceType(paramTy.getParameterType()); + newParams.push_back(newParamDecl); + (void)++i; + } + auto *newParamList = + ParameterList::create(ctx, SourceLoc(), newParams, SourceLoc()); + + // Generate a name for the specialized function. + std::string newNameStr; + llvm::raw_string_ostream buffer(newNameStr); + clang::MangleContext *mangler = + specialized->getASTContext().createMangleContext(); + mangler->mangleName(specialized, buffer); + buffer.flush(); + // Add all parameters as empty parameters. + auto newName = DeclName( + ctx, DeclName(ctx.getIdentifier(newNameStr)).getBaseName(), newParamList); + + auto newFnDecl = FuncDecl::createImported( + ctx, oldDecl->getLoc(), newName, oldDecl->getNameLoc(), + /*Async*/ false, oldDecl->hasThrows(), newParamList, + newFnType->getResult(), /*GenericParams*/ nullptr, + oldDecl->getDeclContext(), specialized); + return ConcreteDeclRef(newFnDecl); +} + ConcreteDeclRef Solution::resolveConcreteDeclRef(ValueDecl *decl, ConstraintLocator *locator) const { @@ -107,7 +153,25 @@ Solution::resolveConcreteDeclRef(ValueDecl *decl, // Get the generic signatue of the decl and compute the substitutions. auto sig = decl->getInnermostDeclContext()->getGenericSignatureOfContext(); - return ConcreteDeclRef(decl, computeSubstitutions(sig, locator)); + auto subst = computeSubstitutions(sig, locator); + + // If this is a C++ function template, get it's specialization for the given + // substitution map and update the decl accordingly. + if (decl->getClangDecl() && + isa(decl->getClangDecl())) { + auto *newFn = + decl->getASTContext() + .getClangModuleLoader() + ->instantiateCXXFunctionTemplate( + decl->getASTContext(), + const_cast( + cast(decl->getClangDecl())), + subst); + return generateDeclRefForSpecializedCXXFunctionTemplate( + decl->getASTContext(), cast(decl), subst, newFn); + } + + return ConcreteDeclRef(decl, subst); } ConstraintLocator *Solution::getCalleeLocator(ConstraintLocator *locator, @@ -634,7 +698,6 @@ namespace { if (!ref) ref = solution.resolveConcreteDeclRef(decl, loc); - assert(ref.getDecl() == decl); return ref; } diff --git a/test/Interop/Cxx/templates/Inputs/function-templates.h b/test/Interop/Cxx/templates/Inputs/function-templates.h new file mode 100644 index 0000000000000..31a0567e8a4d3 --- /dev/null +++ b/test/Interop/Cxx/templates/Inputs/function-templates.h @@ -0,0 +1,22 @@ +template T add(T a, T b) { return a + b; } + +template A addTwoTemplates(A a, B b) { return a + b; } + +template T passThrough(T value) { return value; } + +template const T passThroughConst(const T value) { return value; } + +void takesString(const char *) {} +template void expectsString(T str) { takesString(str); } + +template void integerTemplate() {} +template void defaultIntegerTemplate() {} + +// We cannot yet use this in swift but, make sure we don't crash when parsing +// it. +template R returns_template(T a, U b) { + return a + b; +} + +// Same here: +template void cannot_infer_template() {} diff --git a/test/Interop/Cxx/templates/Inputs/module.modulemap b/test/Interop/Cxx/templates/Inputs/module.modulemap index ca82db18fc25f..0b52858a95826 100644 --- a/test/Interop/Cxx/templates/Inputs/module.modulemap +++ b/test/Interop/Cxx/templates/Inputs/module.modulemap @@ -22,6 +22,10 @@ module ExplicitClassSpecialization { header "explicit-class-specialization.h" } +module FunctionTemplates { + header "function-templates.h" +} + module UsingDirective { header "using-directive.h" } diff --git a/test/Interop/Cxx/templates/function-template-errors.swift b/test/Interop/Cxx/templates/function-template-errors.swift new file mode 100644 index 0000000000000..28d163218d19e --- /dev/null +++ b/test/Interop/Cxx/templates/function-template-errors.swift @@ -0,0 +1,39 @@ +// RUN: not %target-swift-emit-sil %s -I %S/Inputs -enable-cxx-interop 2>&1 | %FileCheck %s + +// README: If you just added support for protocol composition to the +// ClangTypeConverter, please update this test to use a different type that we +// don't support so the error messages here are still tested. + + +import FunctionTemplates + +// Use protocol composition to create a type that we cannot (yet) turn into a clang::QualType. +protocol A { } +protocol B { } +protocol C { } + +// CHECK: error: could not generate C++ types from the generic Swift types provided. The following Swift type(s) provided to 'passThrough' were unable to be converted: A & B. +public func caller1(x: A & B) -> A & B { + return passThrough(x) +} + +// CHECK: error: could not generate C++ types from the generic Swift types provided. The following Swift type(s) provided to 'addTwoTemplates' were unable to be converted: A & B, A & C. +public func caller2(x: A & B, y: A & C) -> A & B { + return addTwoTemplates(x, y) +} + +// Make sure we emit an error and don't crash when failing to instantiate a function. +// CHECK: error: no matching function for call to 'takesString' +// CHECK: note: in instantiation of function template specialization 'expectsString' requested here +// CHECK: note: candidate function not viable: no known conversion from 'int' to 'const char *' for 1st argument +public func callExpectsString() { + expectsString(0 as Int32) +} + +// Make sure we don't import non-type template parameters. +// CHECK: error: cannot find 'integerTemplate' in scope +// CHECK: error: cannot find 'defaultIntegerTemplate' in scope +public func callIntegerTemplates() { + integerTemplate() + defaultIntegerTemplate() +} diff --git a/test/Interop/Cxx/templates/function-template-irgen-objc.swift b/test/Interop/Cxx/templates/function-template-irgen-objc.swift new file mode 100644 index 0000000000000..8999172836226 --- /dev/null +++ b/test/Interop/Cxx/templates/function-template-irgen-objc.swift @@ -0,0 +1,17 @@ +// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s + +// TODO: This needs to be fixed in the SwiftTypeConverter. +// Currently "Any" is imported as an Objective-C "id". +// That doesn't work unless we have Objective-C interop. +// Once that's done, this test can be merged with "template-irgen". +// REQUIRES: objc_interop + +import FunctionTemplates + +// CHECK-LABEL: define {{.*}}void @"$s4main18testPassThroughAny1xypyp_tF"(%Any* noalias nocapture sret %0, %Any* noalias nocapture dereferenceable({{32|16}}) %1) +// CHECK: call i8* @_Z11passThroughIP11objc_objectET_S2_(i8* +// CHECK: ret void +public func testPassThroughAny(x: Any) -> Any { + return passThrough(x) +} + diff --git a/test/Interop/Cxx/templates/function-template-irgen.swift b/test/Interop/Cxx/templates/function-template-irgen.swift new file mode 100644 index 0000000000000..b3ce1ef6b0801 --- /dev/null +++ b/test/Interop/Cxx/templates/function-template-irgen.swift @@ -0,0 +1,39 @@ +// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s + +import FunctionTemplates + +// CHECK-LABEL: define {{.*}}i32 @"$s4main20testPassThroughConst1xs5Int32VAE_tF"(i32 %0) +// CHECK: [[RET_VAL:%.*]] = call i32 @{{_Z16passThroughConstIiEKT_S0_|"\?\?\$passThroughConst@H@@YA\?BHH@Z"}}(i32 %0) +// CHECK: ret i32 [[RET_VAL]] + +// CHECK-LABEL: define linkonce_odr {{.*}}i32 @{{_Z16passThroughConstIiEKT_S0_|"\?\?\$passThroughConst@H@@YA\?BHH@Z"}}(i32 %value) +public func testPassThroughConst(x: Int32) -> Int32 { + return passThroughConst(x) +} + +// CHECK-LABEL: define {{.*}}i32 @"$s4main15testPassThrough1xs5Int32VAE_tF"(i32 %0) +// CHECK: [[RET_VAL:%.*]] = call i32 @{{_Z11passThroughIiET_S0_|"\?\?\$passThrough@H@@YAHH@Z"}}(i32 %0) +// CHECK: ret i32 [[RET_VAL]] + +// CHECK-LABEL: define linkonce_odr {{.*}}i32 @{{_Z11passThroughIiET_S0_|"\?\?\$passThrough@H@@YAHH@Z"}}(i32 %value) +public func testPassThrough(x: Int32) -> Int32 { + return passThrough(x) +} + +// CHECK-LABEL: define {{.*}}i32 @"$s4main19testAddTwoTemplates1xs5Int32VAE_tF"(i32 %0) +// CHECK: [[OUT_VAL:%.*]] = call i32 @{{_Z15addTwoTemplatesIiiET_S0_T0_|"\?\?\$addTwoTemplates@HH@@YAHHH@Z"}}(i32 %0, i32 %0) +// CHECK: ret i32 [[OUT_VAL]] + +// CHECK-LABEL: define linkonce_odr {{.*}}i32 @{{_Z15addTwoTemplatesIiiET_S0_T0_|"\?\?\$addTwoTemplates@HH@@YAHHH@Z"}}(i32 %a, i32 %b) +public func testAddTwoTemplates(x: Int32) -> Int32 { + return addTwoTemplates(x, x) +} + +// CHECK-LABEL: define {{.*}}i32 @"$s4main7testAdd1xs5Int32VAE_tF"(i32 %0) +// CHECK: [[OUT_VAL:%.*]] = call i32 @{{_Z3addIiET_S0_S0_|"\?\?\$add@H@@YAHHH@Z"}}(i32 %0, i32 %0) +// CHECK: ret i32 [[OUT_VAL]] + +// CHECK-LABEL: define linkonce_odr {{.*}}i32 @{{_Z3addIiET_S0_S0_|"\?\?\$add@H@@YAHHH@Z"}}(i32 %a, i32 %b) +public func testAdd(x: Int32) -> Int32 { + return add(x, x) +} diff --git a/test/Interop/Cxx/templates/function-template-module-interface.swift b/test/Interop/Cxx/templates/function-template-module-interface.swift new file mode 100644 index 0000000000000..b4239dbeb690c --- /dev/null +++ b/test/Interop/Cxx/templates/function-template-module-interface.swift @@ -0,0 +1,8 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=FunctionTemplates -I %S/Inputs -source-filename=x -enable-cxx-interop | %FileCheck %s + +// CHECK: func add(_ a: T, _ b: T) -> T +// CHECK: func addTwoTemplates(_ a: A, _ b: B) -> A +// CHECK: func passThrough(_ value: T) -> T +// CHECK: func passThroughConst(_ value: T) -> T +// CHECK: func returns_template(_ a: T, _ b: U) -> R +// CHECK: func cannot_infer_template() diff --git a/test/Interop/Cxx/templates/function-template-silgen.swift b/test/Interop/Cxx/templates/function-template-silgen.swift new file mode 100644 index 0000000000000..bfc5256fe2baf --- /dev/null +++ b/test/Interop/Cxx/templates/function-template-silgen.swift @@ -0,0 +1,31 @@ +// RUN: %target-swift-emit-sil %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s + +import FunctionTemplates + +// CHECK-LABEL: sil @$s4main4test1xs5Int32VAE_tF + +// CHECK: bb0(%0 : $Int32): +// CHECK: [[IL_ZERO:%.*]] = integer_literal $Builtin.Int32, 0 +// CHECK: [[ZERO:%.*]] = struct $Int32 ([[IL_ZERO]] : $Builtin.Int32) +// CHECK: [[PASS_THROUGH_CONST_FN:%.*]] = function_ref @{{_Z16passThroughConstIiEKT_S0_|\?\?\$passThroughConst@H@@YA\?BHH@Z}} : $@convention(c) (Int32) -> Int32 +// CHECK: [[A:%.*]] = apply [[PASS_THROUGH_CONST_FN]]([[ZERO]]) : $@convention(c) (Int32) -> Int32 + +// CHECK: [[PASS_THROUGH_FN:%.*]] = function_ref @{{_Z11passThroughIiET_S0_|\?\?\$passThrough@H@@YAHH@Z}} : $@convention(c) (Int32) -> Int32 +// CHECK: [[B:%.*]] = apply [[PASS_THROUGH_FN]](%0) : $@convention(c) (Int32) -> Int32 + +// CHECK: [[ADD_TWO_FN:%.*]] = function_ref @{{_Z15addTwoTemplatesIiiET_S0_T0_|\?\?\$addTwoTemplates@HH@@YAHHH@Z}} : $@convention(c) (Int32, Int32) -> Int32 +// CHECK: [[C:%.*]] = apply [[ADD_TWO_FN]]([[A]], [[B]]) : $@convention(c) (Int32, Int32) -> Int32 + +// CHECK: [[C_32_ADDR:%.*]] = alloc_stack $Int32 +// CHECK: [[C_32:%.*]] = load [[C_32_ADDR]] : $*Int32 +// CHECK: [[ADD_FN:%.*]] = function_ref @{{_Z3addIiET_S0_S0_|\?\?\$add@H@@YAHHH@Z}} : $@convention(c) (Int32, Int32) -> Int32 +// CHECK: [[OUT:%.*]] = apply [[ADD_FN]]([[B]], [[C_32]]) : $@convention(c) (Int32, Int32) -> Int32 +// CHECK: return [[OUT]] : $Int32 + +// CHECK-LABEL: end sil function '$s4main4test1xs5Int32VAE_tF' +public func test(x: Int32) -> Int32 { + let a = passThroughConst(Int32(0)) + let b = passThrough(x) + let c = addTwoTemplates(a, b) + return add(b, Int32(c)) +} diff --git a/test/Interop/Cxx/templates/function-template.swift b/test/Interop/Cxx/templates/function-template.swift new file mode 100644 index 0000000000000..cb3b6310e235a --- /dev/null +++ b/test/Interop/Cxx/templates/function-template.swift @@ -0,0 +1,37 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop) +// +// REQUIRES: executable_test + +import FunctionTemplates +import StdlibUnittest + +var FunctionTemplateTestSuite = TestSuite("Function Templates") + +FunctionTemplateTestSuite.test("passThrough where T == Int") { + let result = passThrough(42) + expectEqual(42, result) +} + +FunctionTemplateTestSuite.test("add where T == Int") { + let result = add(42, 23) + expectEqual(65, result) +} + +FunctionTemplateTestSuite.test("add where T, U == Int") { + let result = addTwoTemplates(42, 23) + expectEqual(65, result) +} + +// TODO: currently "Any" is imported as an Objective-C "id". +// This doesn't work without the Objective-C runtime. +#if _runtime(_ObjC) +FunctionTemplateTestSuite.test("passThrough where T == Any") { + let result = passThrough(42 as Any) + expectEqual(42, result as! Int) +} +#endif + +// TODO: Generics, Any, and Protocols should be tested here but need to be +// better supported in ClangTypeConverter first. + +runAllTests()