diff --git a/lib/ClangImporter/CMakeLists.txt b/lib/ClangImporter/CMakeLists.txt index 6c5f8f92c0dd8..055cf8942a4a8 100644 --- a/lib/ClangImporter/CMakeLists.txt +++ b/lib/ClangImporter/CMakeLists.txt @@ -24,6 +24,7 @@ add_swift_host_library(swiftClangImporter STATIC ImportName.cpp ImportType.cpp Serializability.cpp + SwiftifyDecl.cpp SwiftLookupTable.cpp ) target_link_libraries(swiftClangImporter PRIVATE diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 044ab04cc65a2..17dc9aa1a7194 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -3784,22 +3784,6 @@ namespace { return true; } - template - static const T * - getImplicitObjectParamAnnotation(const clang::FunctionDecl *FD) { - const clang::TypeSourceInfo *TSI = FD->getTypeSourceInfo(); - if (!TSI) - return nullptr; - clang::AttributedTypeLoc ATL; - for (clang::TypeLoc TL = TSI->getTypeLoc(); - (ATL = TL.getAsAdjusted()); - TL = ATL.getModifiedLoc()) { - if (auto attr = ATL.getAttrAs()) - return attr; - } - return nullptr; - } - Decl *importFunctionDecl( const clang::FunctionDecl *decl, ImportedName importedName, std::optional correctSwiftName, @@ -9151,259 +9135,6 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) { } } -namespace { -ValueDecl *getKnownSingleDecl(ASTContext &SwiftContext, StringRef DeclName) { - SmallVector decls; - SwiftContext.lookupInSwiftModule(DeclName, decls); - assert(decls.size() < 2); - if (decls.size() != 1) return nullptr; - return decls[0]; -} - -class SwiftifyInfoPrinter { -public: - static const ssize_t SELF_PARAM_INDEX = -2; - static const ssize_t RETURN_VALUE_INDEX = -1; - clang::ASTContext &ctx; - ASTContext &SwiftContext; - llvm::raw_ostream &out; - MacroDecl &SwiftifyImportDecl; - bool firstParam = true; - llvm::StringMap typeMapping; - SwiftifyInfoPrinter(clang::ASTContext &ctx, ASTContext &SwiftContext, llvm::raw_ostream &out, MacroDecl &SwiftifyImportDecl) - : ctx(ctx), SwiftContext(SwiftContext), out(out), SwiftifyImportDecl(SwiftifyImportDecl) { - out << "@_SwiftifyImport("; - } - ~SwiftifyInfoPrinter() { out << ")"; } - - void printCountedBy(const clang::CountAttributedType *CAT, - ssize_t pointerIndex) { - printSeparator(); - clang::Expr *countExpr = CAT->getCountExpr(); - bool isSizedBy = CAT->isCountInBytes(); - out << "."; - if (isSizedBy) - out << "sizedBy"; - else - out << "countedBy"; - out << "(pointer: "; - printParamOrReturn(pointerIndex); - out << ", "; - if (isSizedBy) - out << "size"; - else - out << "count"; - out << ": \""; - countExpr->printPretty( - out, {}, {ctx.getLangOpts()}); // TODO: map clang::Expr to Swift Expr - out << "\")"; - } - - void printNonEscaping(int idx) { - printSeparator(); - out << ".nonescaping(pointer: "; - printParamOrReturn(idx); - out << ")"; - } - - void printLifetimeboundReturn(int idx, bool borrow) { - printSeparator(); - out << ".lifetimeDependence(dependsOn: "; - printParamOrReturn(idx); - out << ", pointer: .return, type: "; - out << (borrow ? ".borrow" : ".copy"); - out << ")"; - } - - bool registerStdSpanTypeMapping(Type swiftType, const clang::QualType clangType) { - const auto *decl = clangType->getAsTagDecl(); - if (decl && decl->isInStdNamespace() && decl->getName() == "span") { - typeMapping.insert(std::make_pair( - swiftType->getString(), swiftType->getDesugaredType()->getString())); - return true; - } - return false; - } - - void printTypeMapping() { - printSeparator(); - out << "typeMappings: ["; - if (typeMapping.empty()) { - out << ":]"; - return; - } - llvm::interleaveComma(typeMapping, out, [&](const auto &entry) { - out << '"' << entry.getKey() << "\" : \"" << entry.getValue() << '"'; - }); - out << "]"; - } - - void printAvailability() { - if (!hasMacroParameter("spanAvailability")) - return; - printSeparator(); - out << "spanAvailability: "; - printAvailabilityOfType("Span"); - } - -private: - bool hasMacroParameter(StringRef ParamName) { - for (auto *Param : *SwiftifyImportDecl.parameterList) - if (Param->getArgumentName().str() == ParamName) - return true; - return false; - } - - void printAvailabilityOfType(StringRef Name) { - ValueDecl *D = getKnownSingleDecl(SwiftContext, Name); - out << "\""; - llvm::SaveAndRestore hasAvailbilitySeparatorRestore(firstParam, true); - for (auto attr : D->getSemanticAvailableAttrs(/*includingInactive=*/true)) { - auto introducedOpt = attr.getIntroduced(); - if (!introducedOpt.has_value()) continue; - printSeparator(); - out << prettyPlatformString(attr.getPlatform()) << " " << introducedOpt.value(); - } - out << "\""; - } - - void printSeparator() { - if (!firstParam) { - out << ", "; - } else { - firstParam = false; - } - } - - void printParamOrReturn(ssize_t pointerIndex) { - if (pointerIndex == SELF_PARAM_INDEX) - out << ".self"; - else if (pointerIndex == RETURN_VALUE_INDEX) - out << ".return"; - else - out << ".param(" << pointerIndex + 1 << ")"; - } -}; -} // namespace - -namespace { -struct CountedByExpressionValidator - : clang::ConstStmtVisitor { - bool VisitDeclRefExpr(const clang::DeclRefExpr *e) { return true; } - - bool VisitIntegerLiteral(const clang::IntegerLiteral *IL) { - switch (IL->getType()->castAs()->getKind()) { - case clang::BuiltinType::Char_S: - case clang::BuiltinType::Char_U: - case clang::BuiltinType::UChar: - case clang::BuiltinType::SChar: - case clang::BuiltinType::Short: - case clang::BuiltinType::UShort: - case clang::BuiltinType::UInt: - case clang::BuiltinType::Long: - case clang::BuiltinType::ULong: - case clang::BuiltinType::LongLong: - case clang::BuiltinType::ULongLong: - // These integer literals are printed with a suffix that isn't valid Swift - // syntax - return false; - default: - return true; - } - } - - bool VisitImplicitCastExpr(const clang::ImplicitCastExpr *c) { - return this->Visit(c->getSubExpr()); - } - bool VisitParenExpr(const clang::ParenExpr *p) { - return this->Visit(p->getSubExpr()); - } - -#define SUPPORTED_UNOP(UNOP) \ - bool VisitUnary ## UNOP(const clang::UnaryOperator *unop) { \ - return this->Visit(unop->getSubExpr()); \ - } - SUPPORTED_UNOP(Plus) - SUPPORTED_UNOP(Minus) - SUPPORTED_UNOP(Not) -#undef SUPPORTED_UNOP - -#define SUPPORTED_BINOP(BINOP) \ - bool VisitBin ## BINOP(const clang::BinaryOperator *binop) { \ - return this->Visit(binop->getLHS()) && this->Visit(binop->getRHS()); \ - } - SUPPORTED_BINOP(Add) - SUPPORTED_BINOP(Sub) - SUPPORTED_BINOP(Div) - SUPPORTED_BINOP(Mul) - SUPPORTED_BINOP(Rem) - SUPPORTED_BINOP(Shl) - SUPPORTED_BINOP(Shr) - SUPPORTED_BINOP(And) - SUPPORTED_BINOP(Xor) - SUPPORTED_BINOP(Or) -#undef SUPPORTED_BINOP - - bool VisitStmt(const clang::Stmt *) { return false; } -}; -} // namespace - -// Don't try to transform any Swift types that _SwiftifyImport doesn't know how -// to handle. -static bool SwiftifiableCountedByPointerType(Type swiftType) { - Type nonnullType = swiftType->lookThroughSingleOptionalType(); - PointerTypeKind PTK; - return nonnullType->getAnyPointerElementType(PTK) && - (PTK == PTK_UnsafePointer || PTK == PTK_UnsafeMutablePointer); -} -static bool SwiftifiableSizedByPointerType(const clang::ASTContext &ctx, - Type swiftType, - const clang::CountAttributedType *CAT) { - Type nonnullType = swiftType->lookThroughSingleOptionalType(); - if (nonnullType->isOpaquePointer()) - return true; - PointerTypeKind PTK; - if (!nonnullType->getAnyPointerElementType(PTK)) - return false; - if (PTK == PTK_UnsafeRawPointer || PTK == PTK_UnsafeMutableRawPointer) - return true; - if (PTK != PTK_UnsafePointer && PTK != PTK_UnsafeMutablePointer) - return false; - // We have a pointer to a type with a size. Verify that it is char-sized. - auto PtrT = CAT->getAs(); - auto PointeeT = PtrT->getPointeeType(); - return ctx.getTypeSizeInChars(PointeeT).isOne(); -} -static bool SwiftifiableCAT(const clang::ASTContext &ctx, - const clang::CountAttributedType *CAT, - Type swiftType) { - return CAT && CountedByExpressionValidator().Visit(CAT->getCountExpr()) && - (CAT->isCountInBytes() ? - SwiftifiableSizedByPointerType(ctx, swiftType, CAT) - : SwiftifiableCountedByPointerType(swiftType)); -} - -namespace { - -// Searches for template instantiations that are not behind type aliases. -// FIXME: make sure the generated code compiles for template -// instantiations that are not behind type aliases. -struct UnaliasedInstantiationVisitor - : clang::RecursiveASTVisitor { - bool hasUnaliasedInstantiation = false; - - bool TraverseTypedefType(const clang::TypedefType *) { return true; } - - bool - VisitTemplateSpecializationType(const clang::TemplateSpecializationType *) { - hasUnaliasedInstantiation = true; - return false; - } -}; -} // namespace - - - void ClangImporter::Implementation::addExplicitProtocolConformance( NominalTypeDecl *decl, clang::SwiftAttrAttr *conformsToAttr, @@ -9498,194 +9229,6 @@ void ClangImporter::Implementation::addOptionSetTypealiases( selfType); } -#define SIW_DBG(x) DEBUG_WITH_TYPE("safe-interop-wrappers", llvm::dbgs() << x) - -// until CountAttributedType::getAttributeName lands in our LLVM branch -static StringRef getAttributeName(const clang::CountAttributedType *CAT) { - switch (CAT->getKind()) { - case clang::CountAttributedType::CountedBy: - return "__counted_by"; - case clang::CountAttributedType::CountedByOrNull: - return "__counted_by_or_null"; - case clang::CountAttributedType::SizedBy: - return "__sized_by"; - case clang::CountAttributedType::SizedByOrNull: - return "__sized_by_or_null"; - case clang::CountAttributedType::EndedBy: - llvm_unreachable("CountAttributedType cannot be ended_by"); - } -} - -void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { - if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers) || - SwiftContext.ClangImporterOpts.DisableSafeInteropWrappers) - return; - auto ClangDecl = - dyn_cast_or_null(MappedDecl->getClangDecl()); - if (!ClangDecl) - return; - SIW_DBG("Checking " << *ClangDecl << " for bounds and lifetime info\n"); - - // FIXME: for private macro generated functions we do not serialize the - // SILFunction's body anywhere triggering assertions. - if (ClangDecl->getAccess() == clang::AS_protected || - ClangDecl->getAccess() == clang::AS_private) - return; - - MacroDecl *SwiftifyImportDecl = dyn_cast_or_null(getKnownSingleDecl(SwiftContext, "_SwiftifyImport")); - if (!SwiftifyImportDecl) - return; - - { - UnaliasedInstantiationVisitor visitor; - visitor.TraverseType(ClangDecl->getType()); - if (visitor.hasUnaliasedInstantiation) - return; - } - - llvm::SmallString<128> MacroString; - // We only attach the macro if it will produce an overload. Any __counted_by - // will produce an overload, since UnsafeBufferPointer is still an improvement - // over UnsafePointer, but std::span will only produce an overload if it also - // has lifetime information, since std::span already contains bounds info. - bool attachMacro = false; - { - llvm::raw_svector_ostream out(MacroString); - - SwiftifyInfoPrinter printer(getClangASTContext(), SwiftContext, out, *SwiftifyImportDecl); - Type swiftReturnTy; - if (const auto *funcDecl = dyn_cast(MappedDecl)) - swiftReturnTy = funcDecl->getResultInterfaceType(); - else if (const auto *ctorDecl = dyn_cast(MappedDecl)) - swiftReturnTy = ctorDecl->getResultInterfaceType(); - else - ABORT("Unexpected AbstractFunctionDecl subclass."); - bool returnIsStdSpan = printer.registerStdSpanTypeMapping( - swiftReturnTy, ClangDecl->getReturnType()); - auto *CAT = ClangDecl->getReturnType()->getAs(); - if (SwiftifiableCAT(getClangASTContext(), CAT, swiftReturnTy)) { - printer.printCountedBy(CAT, SwiftifyInfoPrinter::RETURN_VALUE_INDEX); - SIW_DBG(" Found bounds info '" << clang::QualType(CAT, 0) << "' on return value\n"); - attachMacro = true; - } - auto dependsOnClass = [](ParamDecl *fromParam) { - return fromParam->getInterfaceType()->isAnyClassReferenceType(); - }; - bool returnHasLifetimeInfo = false; - if (SwiftDeclConverter::getImplicitObjectParamAnnotation< - clang::LifetimeBoundAttr>(ClangDecl)) { - SIW_DBG(" Found lifetimebound attribute on implicit 'this'\n"); - if (!dependsOnClass( - MappedDecl->getImplicitSelfDecl(/*createIfNeeded*/ true))) { - printer.printLifetimeboundReturn(SwiftifyInfoPrinter::SELF_PARAM_INDEX, - true); - returnHasLifetimeInfo = true; - } - } - - bool isClangInstanceMethod = - isa(ClangDecl) && - !isa(ClangDecl) && - cast(ClangDecl)->isInstance(); - size_t swiftNumParams = MappedDecl->getParameters()->size() - - (ClangDecl->isVariadic() ? 1 : 0); - ASSERT((MappedDecl->isImportAsInstanceMember() == isClangInstanceMethod) == - (ClangDecl->getNumParams() == swiftNumParams)); - - size_t selfParamIndex = MappedDecl->isImportAsInstanceMember() - ? MappedDecl->getSelfIndex() - : ClangDecl->getNumParams(); - for (auto [index, clangParam] : llvm::enumerate(ClangDecl->parameters())) { - auto clangParamTy = clangParam->getType(); - int mappedIndex = index < selfParamIndex ? index : - index > selfParamIndex ? index - 1 : - SwiftifyInfoPrinter::SELF_PARAM_INDEX; - ParamDecl *swiftParam = nullptr; - if (mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) { - swiftParam = MappedDecl->getImplicitSelfDecl(/*createIfNeeded*/true); - } else { - swiftParam = MappedDecl->getParameters()->get(mappedIndex); - } - ASSERT(swiftParam); - Type swiftParamTy = swiftParam->getInterfaceType(); - bool paramHasBoundsInfo = false; - auto *CAT = clangParamTy->getAs(); - if (CAT && mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) { - diagnose(HeaderLoc(clangParam->getLocation()), - diag::warn_clang_ignored_bounds_on_self, getAttributeName(CAT)); - auto swiftName = ClangDecl->getAttr(); - ASSERT(swiftName && - "free function mapped to instance method without swift_name??"); - diagnose(HeaderLoc(swiftName->getLocation()), - diag::note_swift_name_instance_method); - } else if (SwiftifiableCAT(getClangASTContext(), CAT, swiftParamTy)) { - printer.printCountedBy(CAT, mappedIndex); - SIW_DBG(" Found bounds info '" << clangParamTy - << "' on parameter '" << *clangParam << "'\n"); - attachMacro = paramHasBoundsInfo = true; - } - bool paramIsStdSpan = - printer.registerStdSpanTypeMapping(swiftParamTy, clangParamTy); - paramHasBoundsInfo |= paramIsStdSpan; - - bool paramHasLifetimeInfo = false; - if (clangParam->hasAttr()) { - SIW_DBG(" Found noescape attribute on parameter '" << *clangParam << "'\n"); - printer.printNonEscaping(mappedIndex); - paramHasLifetimeInfo = true; - } - if (clangParam->hasAttr()) { - SIW_DBG(" Found lifetimebound attribute on parameter '" - << *clangParam << "'\n"); - if (!dependsOnClass(swiftParam)) { - // If this parameter has bounds info we will tranform it into a Span, - // so then it will no longer be Escapable. - bool willBeEscapable = - swiftParamTy->isEscapable() && - (!paramHasBoundsInfo || - mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX); - printer.printLifetimeboundReturn(mappedIndex, willBeEscapable); - paramHasLifetimeInfo = true; - returnHasLifetimeInfo = true; - } - } - if (paramIsStdSpan && paramHasLifetimeInfo) { - SIW_DBG(" Found both std::span and lifetime info " - "for parameter '" << *clangParam << "'\n"); - attachMacro = true; - } - } - if (returnIsStdSpan && returnHasLifetimeInfo) { - SIW_DBG(" Found both std::span and lifetime info for return value\n"); - attachMacro = true; - } - printer.printAvailability(); - printer.printTypeMapping(); - } - - if (attachMacro) { - SIW_DBG("Attaching safe interop macro: " << MacroString << "\n"); - if (clang::RawComment *raw = - getClangASTContext().getRawCommentForDeclNoCache(ClangDecl)) { - // swift::RawDocCommentAttr doesn't contain its text directly, but instead - // references the source range of the parsed comment. Instead of creating - // a new source file just to parse the doc comment, we can add the - // comment to the macro invocation attribute, which the macro has access - // to. Waiting until we know that the macro will be attached before - // emitting the comment to the string, despite the comment occurring - // first, avoids copying a bunch of potentially long comments for nodes - // that don't end up with wrappers. - auto commentString = - raw->getRawText(getClangASTContext().getSourceManager()); - importNontrivialAttribute(MappedDecl, - (commentString + "\n" + MacroString).str()); - } else { - importNontrivialAttribute(MappedDecl, MacroString); - } - } -} -#undef SIW_DBG - static bool isUsingMacroName(clang::SourceManager &SM, clang::SourceLocation loc, StringRef MacroName) { diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index ed4c18dd1ca7a..e17f4d8d20d6e 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -2205,6 +2205,22 @@ ImportedType findOptionSetEnum(clang::QualType type, llvm::SmallVector getValueDeclsForName(NominalTypeDecl* decl, StringRef name); +template +const T * +getImplicitObjectParamAnnotation(const clang::FunctionDecl *FD) { + const clang::TypeSourceInfo *TSI = FD->getTypeSourceInfo(); + if (!TSI) + return nullptr; + clang::AttributedTypeLoc ATL; + for (clang::TypeLoc TL = TSI->getTypeLoc(); + (ATL = TL.getAsAdjusted()); + TL = ATL.getModifiedLoc()) { + if (auto attr = ATL.getAttrAs()) + return attr; + } + return nullptr; +} + } // end namespace importer } // end namespace swift diff --git a/lib/ClangImporter/SwiftifyDecl.cpp b/lib/ClangImporter/SwiftifyDecl.cpp new file mode 100644 index 0000000000000..7be08be0032a9 --- /dev/null +++ b/lib/ClangImporter/SwiftifyDecl.cpp @@ -0,0 +1,481 @@ +//===--- ImportDecl.cpp - Import Clang Declarations -----------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// This file infers and attaches macros to imported decls based on their attributes. +/// +//===----------------------------------------------------------------------===// + +#include "ImporterImpl.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticsClangImporter.h" +#include "swift/AST/ParameterList.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/Type.h" + +using namespace swift; +using namespace importer; + +#define DEBUG_TYPE "safe-interop-wrappers" +#define DLOG(x) LLVM_DEBUG(llvm::dbgs() << x) + +namespace { +ValueDecl *getKnownSingleDecl(ASTContext &SwiftContext, StringRef DeclName) { + SmallVector decls; + SwiftContext.lookupInSwiftModule(DeclName, decls); + assert(decls.size() < 2); + if (decls.size() != 1) return nullptr; + return decls[0]; +} + +class SwiftifyInfoPrinter { +public: + static const ssize_t SELF_PARAM_INDEX = -2; + static const ssize_t RETURN_VALUE_INDEX = -1; + clang::ASTContext &ctx; + ASTContext &SwiftContext; + llvm::raw_ostream &out; + MacroDecl &SwiftifyImportDecl; + bool firstParam = true; + llvm::StringMap typeMapping; + + SwiftifyInfoPrinter(clang::ASTContext &ctx, ASTContext &SwiftContext, + llvm::raw_ostream &out, MacroDecl &SwiftifyImportDecl) + : ctx(ctx), SwiftContext(SwiftContext), out(out), + SwiftifyImportDecl(SwiftifyImportDecl) { + out << "("; + } + ~SwiftifyInfoPrinter() { out << ")"; } + + void printCountedBy(const clang::CountAttributedType *CAT, + ssize_t pointerIndex) { + printSeparator(); + clang::Expr *countExpr = CAT->getCountExpr(); + bool isSizedBy = CAT->isCountInBytes(); + out << "."; + if (isSizedBy) + out << "sizedBy"; + else + out << "countedBy"; + out << "(pointer: "; + printParamOrReturn(pointerIndex); + out << ", "; + if (isSizedBy) + out << "size"; + else + out << "count"; + out << ": \""; + countExpr->printPretty( + out, {}, {ctx.getLangOpts()}); // TODO: map clang::Expr to Swift Expr + out << "\")"; + } + + void printNonEscaping(int idx) { + printSeparator(); + out << ".nonescaping(pointer: "; + printParamOrReturn(idx); + out << ")"; + } + + void printLifetimeboundReturn(int idx, bool borrow) { + printSeparator(); + out << ".lifetimeDependence(dependsOn: "; + printParamOrReturn(idx); + out << ", pointer: .return, type: "; + out << (borrow ? ".borrow" : ".copy"); + out << ")"; + } + + bool registerStdSpanTypeMapping(Type swiftType, const clang::QualType clangType) { + const auto *decl = clangType->getAsTagDecl(); + if (decl && decl->isInStdNamespace() && decl->getName() == "span") { + typeMapping.insert(std::make_pair( + swiftType->getString(), swiftType->getDesugaredType()->getString())); + return true; + } + return false; + } + + void printTypeMapping() { + printSeparator(); + out << "typeMappings: ["; + if (typeMapping.empty()) { + out << ":]"; + return; + } + llvm::interleaveComma(typeMapping, out, [&](const auto &entry) { + out << '"' << entry.getKey() << "\" : \"" << entry.getValue() << '"'; + }); + out << "]"; + } + + void printAvailability() { + if (!hasMacroParameter("spanAvailability")) + return; + printSeparator(); + out << "spanAvailability: "; + printAvailabilityOfType("Span"); + } + +private: + bool hasMacroParameter(StringRef ParamName) { + for (auto *Param : *SwiftifyImportDecl.parameterList) + if (Param->getArgumentName().str() == ParamName) + return true; + return false; + } + + void printAvailabilityOfType(StringRef Name) { + ValueDecl *D = getKnownSingleDecl(SwiftContext, Name); + out << "\""; + llvm::SaveAndRestore hasAvailbilitySeparatorRestore(firstParam, true); + for (auto attr : D->getSemanticAvailableAttrs(/*includingInactive=*/true)) { + auto introducedOpt = attr.getIntroduced(); + if (!introducedOpt.has_value()) continue; + printSeparator(); + out << prettyPlatformString(attr.getPlatform()) << " " << introducedOpt.value(); + } + out << "\""; + } + + void printSeparator() { + if (!firstParam) { + out << ", "; + } else { + firstParam = false; + } + } + + void printParamOrReturn(ssize_t pointerIndex) { + if (pointerIndex == SELF_PARAM_INDEX) + out << ".self"; + else if (pointerIndex == RETURN_VALUE_INDEX) + out << ".return"; + else + out << ".param(" << pointerIndex + 1 << ")"; + } +}; + +struct CountedByExpressionValidator + : clang::ConstStmtVisitor { + bool VisitDeclRefExpr(const clang::DeclRefExpr *e) { return true; } + + bool VisitIntegerLiteral(const clang::IntegerLiteral *IL) { + switch (IL->getType()->castAs()->getKind()) { + case clang::BuiltinType::Char_S: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::SChar: + case clang::BuiltinType::Short: + case clang::BuiltinType::UShort: + case clang::BuiltinType::UInt: + case clang::BuiltinType::Long: + case clang::BuiltinType::ULong: + case clang::BuiltinType::LongLong: + case clang::BuiltinType::ULongLong: + // These integer literals are printed with a suffix that isn't valid Swift + // syntax + return false; + default: + return true; + } + } + + bool VisitImplicitCastExpr(const clang::ImplicitCastExpr *c) { + return this->Visit(c->getSubExpr()); + } + bool VisitParenExpr(const clang::ParenExpr *p) { + return this->Visit(p->getSubExpr()); + } + +#define SUPPORTED_UNOP(UNOP) \ + bool VisitUnary ## UNOP(const clang::UnaryOperator *unop) { \ + return this->Visit(unop->getSubExpr()); \ + } + SUPPORTED_UNOP(Plus) + SUPPORTED_UNOP(Minus) + SUPPORTED_UNOP(Not) +#undef SUPPORTED_UNOP + +#define SUPPORTED_BINOP(BINOP) \ + bool VisitBin ## BINOP(const clang::BinaryOperator *binop) { \ + return this->Visit(binop->getLHS()) && this->Visit(binop->getRHS()); \ + } + SUPPORTED_BINOP(Add) + SUPPORTED_BINOP(Sub) + SUPPORTED_BINOP(Div) + SUPPORTED_BINOP(Mul) + SUPPORTED_BINOP(Rem) + SUPPORTED_BINOP(Shl) + SUPPORTED_BINOP(Shr) + SUPPORTED_BINOP(And) + SUPPORTED_BINOP(Xor) + SUPPORTED_BINOP(Or) +#undef SUPPORTED_BINOP + + bool VisitStmt(const clang::Stmt *) { return false; } +}; + + +// Don't try to transform any Swift types that _SwiftifyImport doesn't know how +// to handle. +static bool SwiftifiableCountedByPointerType(Type swiftType) { + Type nonnullType = swiftType->lookThroughSingleOptionalType(); + PointerTypeKind PTK; + return nonnullType->getAnyPointerElementType(PTK) && + (PTK == PTK_UnsafePointer || PTK == PTK_UnsafeMutablePointer); +} +static bool SwiftifiableSizedByPointerType(const clang::ASTContext &ctx, + Type swiftType, + const clang::CountAttributedType *CAT) { + Type nonnullType = swiftType->lookThroughSingleOptionalType(); + if (nonnullType->isOpaquePointer()) + return true; + PointerTypeKind PTK; + if (!nonnullType->getAnyPointerElementType(PTK)) + return false; + if (PTK == PTK_UnsafeRawPointer || PTK == PTK_UnsafeMutableRawPointer) + return true; + if (PTK != PTK_UnsafePointer && PTK != PTK_UnsafeMutablePointer) + return false; + // We have a pointer to a type with a size. Verify that it is char-sized. + auto PtrT = CAT->getAs(); + auto PointeeT = PtrT->getPointeeType(); + return ctx.getTypeSizeInChars(PointeeT).isOne(); +} +static bool SwiftifiableCAT(const clang::ASTContext &ctx, + const clang::CountAttributedType *CAT, + Type swiftType) { + return CAT && CountedByExpressionValidator().Visit(CAT->getCountExpr()) && + (CAT->isCountInBytes() ? + SwiftifiableSizedByPointerType(ctx, swiftType, CAT) + : SwiftifiableCountedByPointerType(swiftType)); +} + +// Searches for template instantiations that are not behind type aliases. +// FIXME: make sure the generated code compiles for template +// instantiations that are not behind type aliases. +struct UnaliasedInstantiationVisitor + : clang::RecursiveASTVisitor { + bool hasUnaliasedInstantiation = false; + + bool TraverseTypedefType(const clang::TypedefType *) { return true; } + + bool + VisitTemplateSpecializationType(const clang::TemplateSpecializationType *) { + hasUnaliasedInstantiation = true; + return false; + } +}; + +// until CountAttributedType::getAttributeName lands in our LLVM branch +static StringRef getAttributeName(const clang::CountAttributedType *CAT) { + switch (CAT->getKind()) { + case clang::CountAttributedType::CountedBy: + return "__counted_by"; + case clang::CountAttributedType::CountedByOrNull: + return "__counted_by_or_null"; + case clang::CountAttributedType::SizedBy: + return "__sized_by"; + case clang::CountAttributedType::SizedByOrNull: + return "__sized_by_or_null"; + case clang::CountAttributedType::EndedBy: + llvm_unreachable("CountAttributedType cannot be ended_by"); + } +} +} // namespace + +static bool swiftifyImpl(ClangImporter::Implementation &Self, + SwiftifyInfoPrinter &printer, + const AbstractFunctionDecl *MappedDecl, + const clang::FunctionDecl *ClangDecl) { + DLOG("Checking " << *ClangDecl << " for bounds and lifetime info\n"); + + // FIXME: for private macro generated functions we do not serialize the + // SILFunction's body anywhere triggering assertions. + if (ClangDecl->getAccess() == clang::AS_protected || + ClangDecl->getAccess() == clang::AS_private) + return false; + + { + UnaliasedInstantiationVisitor visitor; + visitor.TraverseType(ClangDecl->getType()); + if (visitor.hasUnaliasedInstantiation) + return false; + } + + clang::ASTContext &clangASTContext = Self.getClangASTContext(); + + // We only attach the macro if it will produce an overload. Any __counted_by + // will produce an overload, since UnsafeBufferPointer is still an improvement + // over UnsafePointer, but std::span will only produce an overload if it also + // has lifetime information, since std::span already contains bounds info. + bool attachMacro = false; + { + Type swiftReturnTy; + if (const auto *funcDecl = dyn_cast(MappedDecl)) + swiftReturnTy = funcDecl->getResultInterfaceType(); + else if (const auto *ctorDecl = dyn_cast(MappedDecl)) + swiftReturnTy = ctorDecl->getResultInterfaceType(); + else + ABORT("Unexpected AbstractFunctionDecl subclass."); + bool returnIsStdSpan = printer.registerStdSpanTypeMapping( + swiftReturnTy, ClangDecl->getReturnType()); + auto *CAT = ClangDecl->getReturnType()->getAs(); + if (SwiftifiableCAT(clangASTContext, CAT, swiftReturnTy)) { + printer.printCountedBy(CAT, SwiftifyInfoPrinter::RETURN_VALUE_INDEX); + DLOG(" Found bounds info '" << clang::QualType(CAT, 0) << "' on return value\n"); + attachMacro = true; + } + auto dependsOnClass = [](const ParamDecl *fromParam) { + return fromParam->getInterfaceType()->isAnyClassReferenceType(); + }; + bool returnHasLifetimeInfo = false; + if (getImplicitObjectParamAnnotation(ClangDecl)) { + DLOG(" Found lifetimebound attribute on implicit 'this'\n"); + if (!dependsOnClass( + MappedDecl->getImplicitSelfDecl(/*createIfNeeded*/ true))) { + printer.printLifetimeboundReturn(SwiftifyInfoPrinter::SELF_PARAM_INDEX, + true); + returnHasLifetimeInfo = true; + } + } + + bool isClangInstanceMethod = + isa(ClangDecl) && + !isa(ClangDecl) && + cast(ClangDecl)->isInstance(); + size_t swiftNumParams = MappedDecl->getParameters()->size() - + (ClangDecl->isVariadic() ? 1 : 0); + ASSERT((MappedDecl->isImportAsInstanceMember() == isClangInstanceMethod) == + (ClangDecl->getNumParams() == swiftNumParams)); + + size_t selfParamIndex = MappedDecl->isImportAsInstanceMember() + ? MappedDecl->getSelfIndex() + : ClangDecl->getNumParams(); + for (auto [index, clangParam] : llvm::enumerate(ClangDecl->parameters())) { + auto clangParamTy = clangParam->getType(); + int mappedIndex = index < selfParamIndex ? index : + index > selfParamIndex ? index - 1 : + SwiftifyInfoPrinter::SELF_PARAM_INDEX; + const ParamDecl *swiftParam = nullptr; + if (mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) { + swiftParam = MappedDecl->getImplicitSelfDecl(/*createIfNeeded*/true); + } else { + swiftParam = MappedDecl->getParameters()->get(mappedIndex); + } + ASSERT(swiftParam); + Type swiftParamTy = swiftParam->getInterfaceType(); + bool paramHasBoundsInfo = false; + auto *CAT = clangParamTy->getAs(); + if (CAT && mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) { + Self.diagnose(HeaderLoc(clangParam->getLocation()), + diag::warn_clang_ignored_bounds_on_self, + getAttributeName(CAT)); + auto swiftName = ClangDecl->getAttr(); + ASSERT(swiftName && + "free function mapped to instance method without swift_name??"); + Self.diagnose(HeaderLoc(swiftName->getLocation()), + diag::note_swift_name_instance_method); + } else if (SwiftifiableCAT(clangASTContext, CAT, swiftParamTy)) { + printer.printCountedBy(CAT, mappedIndex); + DLOG(" Found bounds info '" << clangParamTy << "' on parameter '" + << *clangParam << "'\n"); + attachMacro = paramHasBoundsInfo = true; + } + bool paramIsStdSpan = + printer.registerStdSpanTypeMapping(swiftParamTy, clangParamTy); + paramHasBoundsInfo |= paramIsStdSpan; + + bool paramHasLifetimeInfo = false; + if (clangParam->hasAttr()) { + DLOG(" Found noescape attribute on parameter '" << *clangParam << "'\n"); + printer.printNonEscaping(mappedIndex); + paramHasLifetimeInfo = true; + } + if (clangParam->template hasAttr()) { + DLOG(" Found lifetimebound attribute on parameter '" << *clangParam + << "'\n"); + if (!dependsOnClass(swiftParam)) { + // If this parameter has bounds info we will tranform it into a Span, + // so then it will no longer be Escapable. + bool willBeEscapable = + swiftParamTy->isEscapable() && + (!paramHasBoundsInfo || + mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX); + printer.printLifetimeboundReturn(mappedIndex, willBeEscapable); + paramHasLifetimeInfo = true; + returnHasLifetimeInfo = true; + } + } + if (paramIsStdSpan && paramHasLifetimeInfo) { + DLOG(" Found both std::span and lifetime info for parameter '" + << *clangParam << "'\n"); + attachMacro = true; + } + } + if (returnIsStdSpan && returnHasLifetimeInfo) { + DLOG(" Found both std::span and lifetime info for return value\n"); + attachMacro = true; + } + } + return attachMacro; +} + +void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { + if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers) || + SwiftContext.ClangImporterOpts.DisableSafeInteropWrappers) + return; + auto ClangDecl = dyn_cast_or_null(MappedDecl->getClangDecl()); + if (!ClangDecl) + return; + + MacroDecl *SwiftifyImportDecl = dyn_cast_or_null(getKnownSingleDecl(SwiftContext, "_SwiftifyImport")); + if (!SwiftifyImportDecl) + return; + + llvm::SmallString<128> MacroString; + { + llvm::raw_svector_ostream out(MacroString); + out << "@_SwiftifyImport"; + + SwiftifyInfoPrinter printer(getClangASTContext(), SwiftContext, out, *SwiftifyImportDecl); + if (!swiftifyImpl(*this, printer, MappedDecl, ClangDecl)) + return; + printer.printAvailability(); + printer.printTypeMapping(); + } + + DLOG("Attaching safe interop macro: " << MacroString << "\n"); + if (clang::RawComment *raw = + getClangASTContext().getRawCommentForDeclNoCache(ClangDecl)) { + // swift::RawDocCommentAttr doesn't contain its text directly, but instead + // references the source range of the parsed comment. Instead of creating + // a new source file just to parse the doc comment, we can add the + // comment to the macro invocation attribute, which the macro has access + // to. Waiting until we know that the macro will be attached before + // emitting the comment to the string, despite the comment occurring + // first, avoids copying a bunch of potentially long comments for nodes + // that don't end up with wrappers. + auto commentString = + raw->getRawText(getClangASTContext().getSourceManager()); + importNontrivialAttribute(MappedDecl, + (commentString + "\n" + MacroString).str()); + } else { + importNontrivialAttribute(MappedDecl, MacroString); + } +} +