From 4ab1d5db5e672f1f9826cce4a215d707e2390238 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Tue, 21 Oct 2025 22:55:01 -0700 Subject: [PATCH 1/5] [ClangImporter] refactor swiftify before swiftifyProtocol (NFC) This is a pure refactor to make it easier to follow the upcoming addition of swiftifyProtocol. --- lib/ClangImporter/ImportDecl.cpp | 89 ++++++++++++++++++-------------- lib/ClangImporter/ImporterImpl.h | 7 +++ 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 044ab04cc65a2..832b473582c24 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -9172,7 +9172,7 @@ class SwiftifyInfoPrinter { 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("; + out << "("; } ~SwiftifyInfoPrinter() { out << ")"; } @@ -9516,43 +9516,30 @@ static StringRef getAttributeName(const clang::CountAttributedType *CAT) { } } -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; +bool ClangImporter::Implementation::swiftifyImpl( + SwiftifyInfoPrinter &printer, const AbstractFunctionDecl *MappedDecl, + const clang::FunctionDecl *ClangDecl) { 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; + return false; { UnaliasedInstantiationVisitor visitor; visitor.TraverseType(ClangDecl->getType()); if (visitor.hasUnaliasedInstantiation) - return; + return false; } - 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(); @@ -9568,7 +9555,7 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { SIW_DBG(" Found bounds info '" << clang::QualType(CAT, 0) << "' on return value\n"); attachMacro = true; } - auto dependsOnClass = [](ParamDecl *fromParam) { + auto dependsOnClass = [](const ParamDecl *fromParam) { return fromParam->getInterfaceType()->isAnyClassReferenceType(); }; bool returnHasLifetimeInfo = false; @@ -9600,7 +9587,7 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { int mappedIndex = index < selfParamIndex ? index : index > selfParamIndex ? index - 1 : SwiftifyInfoPrinter::SELF_PARAM_INDEX; - ParamDecl *swiftParam = nullptr; + const ParamDecl *swiftParam = nullptr; if (mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) { swiftParam = MappedDecl->getImplicitSelfDecl(/*createIfNeeded*/true); } else { @@ -9659,29 +9646,51 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { SIW_DBG(" 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(printer, MappedDecl, ClangDecl)) + return; 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); - } + 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 diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index ed4c18dd1ca7a..c9f787fd11818 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -80,6 +80,10 @@ class QualType; class TypedefNameDecl; } +namespace { + class SwiftifyInfoPrinter; +} + namespace swift { class ASTContext; @@ -1827,6 +1831,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation void addOptionSetTypealiases(NominalTypeDecl *nominal); void swiftify(AbstractFunctionDecl *MappedDecl); + bool swiftifyImpl(SwiftifyInfoPrinter &printer, + const AbstractFunctionDecl *MappedDecl, + const clang::FunctionDecl *ClangDecl); /// Find the lookup table that corresponds to the given Clang module. /// From 0a8314b9c4ade92445c958b82380e46312b1292e Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Thu, 23 Oct 2025 19:17:34 -0700 Subject: [PATCH 2/5] convert swiftifyImpl to a free function This will make it more straightforward to later templatize it. --- lib/ClangImporter/ImportDecl.cpp | 24 ++++++++++++++---------- lib/ClangImporter/ImporterImpl.h | 7 ------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 832b473582c24..9efe96c41409b 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -9516,9 +9516,10 @@ static StringRef getAttributeName(const clang::CountAttributedType *CAT) { } } -bool ClangImporter::Implementation::swiftifyImpl( - SwiftifyInfoPrinter &printer, const AbstractFunctionDecl *MappedDecl, - const clang::FunctionDecl *ClangDecl) { +static bool swiftifyImpl(ClangImporter::Implementation &Self, + SwiftifyInfoPrinter &printer, + const AbstractFunctionDecl *MappedDecl, + const clang::FunctionDecl *ClangDecl) { SIW_DBG("Checking " << *ClangDecl << " for bounds and lifetime info\n"); // FIXME: for private macro generated functions we do not serialize the @@ -9534,6 +9535,8 @@ bool ClangImporter::Implementation::swiftifyImpl( 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 @@ -9550,7 +9553,7 @@ bool ClangImporter::Implementation::swiftifyImpl( bool returnIsStdSpan = printer.registerStdSpanTypeMapping( swiftReturnTy, ClangDecl->getReturnType()); auto *CAT = ClangDecl->getReturnType()->getAs(); - if (SwiftifiableCAT(getClangASTContext(), CAT, swiftReturnTy)) { + if (SwiftifiableCAT(clangASTContext, CAT, swiftReturnTy)) { printer.printCountedBy(CAT, SwiftifyInfoPrinter::RETURN_VALUE_INDEX); SIW_DBG(" Found bounds info '" << clang::QualType(CAT, 0) << "' on return value\n"); attachMacro = true; @@ -9598,14 +9601,15 @@ bool ClangImporter::Implementation::swiftifyImpl( 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)); + 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??"); - diagnose(HeaderLoc(swiftName->getLocation()), - diag::note_swift_name_instance_method); - } else if (SwiftifiableCAT(getClangASTContext(), CAT, swiftParamTy)) { + Self.diagnose(HeaderLoc(swiftName->getLocation()), + diag::note_swift_name_instance_method); + } else if (SwiftifiableCAT(clangASTContext, CAT, swiftParamTy)) { printer.printCountedBy(CAT, mappedIndex); SIW_DBG(" Found bounds info '" << clangParamTy << "' on parameter '" << *clangParam << "'\n"); @@ -9668,7 +9672,7 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { out << "@_SwiftifyImport"; SwiftifyInfoPrinter printer(getClangASTContext(), SwiftContext, out, *SwiftifyImportDecl); - if (!swiftifyImpl(printer, MappedDecl, ClangDecl)) + if (!swiftifyImpl(*this, printer, MappedDecl, ClangDecl)) return; printer.printAvailability(); printer.printTypeMapping(); diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index c9f787fd11818..ed4c18dd1ca7a 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -80,10 +80,6 @@ class QualType; class TypedefNameDecl; } -namespace { - class SwiftifyInfoPrinter; -} - namespace swift { class ASTContext; @@ -1831,9 +1827,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation void addOptionSetTypealiases(NominalTypeDecl *nominal); void swiftify(AbstractFunctionDecl *MappedDecl); - bool swiftifyImpl(SwiftifyInfoPrinter &printer, - const AbstractFunctionDecl *MappedDecl, - const clang::FunctionDecl *ClangDecl); /// Find the lookup table that corresponds to the given Clang module. /// From 6d8d09ee03f71dd8a233d1beaed7010da902dc13 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Tue, 21 Oct 2025 23:23:12 -0700 Subject: [PATCH 3/5] [ClangImporter] move swiftify to SwiftifyDecl.cpp (NFC) This code doesn't interact that much with the rest of ClangImporter, and will continue to grow. Keeping it in a separate file makes it easier to navigate. --- lib/ClangImporter/CMakeLists.txt | 1 + lib/ClangImporter/ImportDecl.cpp | 470 ---------------------------- lib/ClangImporter/ImporterImpl.h | 16 + lib/ClangImporter/SwiftifyDecl.cpp | 485 +++++++++++++++++++++++++++++ 4 files changed, 502 insertions(+), 470 deletions(-) create mode 100644 lib/ClangImporter/SwiftifyDecl.cpp 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 9efe96c41409b..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 << "("; - } - ~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,207 +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"); - } -} - -static bool swiftifyImpl(ClangImporter::Implementation &Self, - SwiftifyInfoPrinter &printer, - const AbstractFunctionDecl *MappedDecl, - const clang::FunctionDecl *ClangDecl) { - 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 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); - SIW_DBG(" 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 (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; - 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); - 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; - } - } - 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(); - } - - 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..84be6df1ce4a3 --- /dev/null +++ b/lib/ClangImporter/SwiftifyDecl.cpp @@ -0,0 +1,485 @@ +//===--- 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 SIW_DBG(x) DEBUG_WITH_TYPE("safe-interop-wrappers", 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 << ")"; + } +}; +} // 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 + +// 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"); + } +} + +static bool swiftifyImpl(ClangImporter::Implementation &Self, + SwiftifyInfoPrinter &printer, + const AbstractFunctionDecl *MappedDecl, + const clang::FunctionDecl *ClangDecl) { + 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 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); + SIW_DBG(" 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)) { + 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; + 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); + 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->template 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; + } + } + 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(); + } + + 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 From a2384b32c66f6005d846645ad0bacbd8cb425920 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Tue, 21 Oct 2025 23:32:13 -0700 Subject: [PATCH 4/5] merge anonymous namespaces (NFC) --- lib/ClangImporter/SwiftifyDecl.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/ClangImporter/SwiftifyDecl.cpp b/lib/ClangImporter/SwiftifyDecl.cpp index 84be6df1ce4a3..953137a199469 100644 --- a/lib/ClangImporter/SwiftifyDecl.cpp +++ b/lib/ClangImporter/SwiftifyDecl.cpp @@ -167,9 +167,7 @@ class SwiftifyInfoPrinter { out << ".param(" << pointerIndex + 1 << ")"; } }; -} // namespace -namespace { struct CountedByExpressionValidator : clang::ConstStmtVisitor { bool VisitDeclRefExpr(const clang::DeclRefExpr *e) { return true; } @@ -229,7 +227,6 @@ struct CountedByExpressionValidator bool VisitStmt(const clang::Stmt *) { return false; } }; -} // namespace // Don't try to transform any Swift types that _SwiftifyImport doesn't know how @@ -267,8 +264,6 @@ static bool SwiftifiableCAT(const clang::ASTContext &ctx, : 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. @@ -284,7 +279,6 @@ struct UnaliasedInstantiationVisitor return false; } }; -} // namespace // until CountAttributedType::getAttributeName lands in our LLVM branch static StringRef getAttributeName(const clang::CountAttributedType *CAT) { @@ -301,6 +295,7 @@ static StringRef getAttributeName(const clang::CountAttributedType *CAT) { llvm_unreachable("CountAttributedType cannot be ended_by"); } } +} // namespace static bool swiftifyImpl(ClangImporter::Implementation &Self, SwiftifyInfoPrinter &printer, From 722afefff845b7f56396bf1f9843dfda4316b9b6 Mon Sep 17 00:00:00 2001 From: "Henrik G. Olsson" Date: Tue, 21 Oct 2025 23:37:51 -0700 Subject: [PATCH 5/5] replace SIW_DBG with DLOG (NFC) Now that swiftify related functions have their own file there's no need to use DEBUG_WITH_TYPE explicitly anymore. --- lib/ClangImporter/SwiftifyDecl.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/ClangImporter/SwiftifyDecl.cpp b/lib/ClangImporter/SwiftifyDecl.cpp index 953137a199469..7be08be0032a9 100644 --- a/lib/ClangImporter/SwiftifyDecl.cpp +++ b/lib/ClangImporter/SwiftifyDecl.cpp @@ -29,7 +29,8 @@ using namespace swift; using namespace importer; -#define SIW_DBG(x) DEBUG_WITH_TYPE("safe-interop-wrappers", llvm::dbgs() << x) +#define DEBUG_TYPE "safe-interop-wrappers" +#define DLOG(x) LLVM_DEBUG(llvm::dbgs() << x) namespace { ValueDecl *getKnownSingleDecl(ASTContext &SwiftContext, StringRef DeclName) { @@ -301,7 +302,7 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, SwiftifyInfoPrinter &printer, const AbstractFunctionDecl *MappedDecl, const clang::FunctionDecl *ClangDecl) { - SIW_DBG("Checking " << *ClangDecl << " for bounds and lifetime info\n"); + 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. @@ -336,7 +337,7 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, auto *CAT = ClangDecl->getReturnType()->getAs(); if (SwiftifiableCAT(clangASTContext, CAT, swiftReturnTy)) { printer.printCountedBy(CAT, SwiftifyInfoPrinter::RETURN_VALUE_INDEX); - SIW_DBG(" Found bounds info '" << clang::QualType(CAT, 0) << "' on return value\n"); + DLOG(" Found bounds info '" << clang::QualType(CAT, 0) << "' on return value\n"); attachMacro = true; } auto dependsOnClass = [](const ParamDecl *fromParam) { @@ -344,7 +345,7 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, }; bool returnHasLifetimeInfo = false; if (getImplicitObjectParamAnnotation(ClangDecl)) { - SIW_DBG(" Found lifetimebound attribute on implicit 'this'\n"); + DLOG(" Found lifetimebound attribute on implicit 'this'\n"); if (!dependsOnClass( MappedDecl->getImplicitSelfDecl(/*createIfNeeded*/ true))) { printer.printLifetimeboundReturn(SwiftifyInfoPrinter::SELF_PARAM_INDEX, @@ -391,8 +392,8 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, diag::note_swift_name_instance_method); } else if (SwiftifiableCAT(clangASTContext, CAT, swiftParamTy)) { printer.printCountedBy(CAT, mappedIndex); - SIW_DBG(" Found bounds info '" << clangParamTy - << "' on parameter '" << *clangParam << "'\n"); + DLOG(" Found bounds info '" << clangParamTy << "' on parameter '" + << *clangParam << "'\n"); attachMacro = paramHasBoundsInfo = true; } bool paramIsStdSpan = @@ -401,12 +402,12 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, bool paramHasLifetimeInfo = false; if (clangParam->hasAttr()) { - SIW_DBG(" Found noescape attribute on parameter '" << *clangParam << "'\n"); + DLOG(" Found noescape attribute on parameter '" << *clangParam << "'\n"); printer.printNonEscaping(mappedIndex); paramHasLifetimeInfo = true; } if (clangParam->template hasAttr()) { - SIW_DBG(" Found lifetimebound attribute on parameter '" << *clangParam + DLOG(" Found lifetimebound attribute on parameter '" << *clangParam << "'\n"); if (!dependsOnClass(swiftParam)) { // If this parameter has bounds info we will tranform it into a Span, @@ -421,13 +422,13 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self, } } if (paramIsStdSpan && paramHasLifetimeInfo) { - SIW_DBG(" Found both std::span and lifetime info " - "for parameter '" << *clangParam << "'\n"); + DLOG(" 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"); + DLOG(" Found both std::span and lifetime info for return value\n"); attachMacro = true; } } @@ -458,7 +459,7 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { printer.printTypeMapping(); } - SIW_DBG("Attaching safe interop macro: " << MacroString << "\n"); + DLOG("Attaching safe interop macro: " << MacroString << "\n"); if (clang::RawComment *raw = getClangASTContext().getRawCommentForDeclNoCache(ClangDecl)) { // swift::RawDocCommentAttr doesn't contain its text directly, but instead @@ -477,4 +478,4 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { importNontrivialAttribute(MappedDecl, MacroString); } } -#undef SIW_DBG +