//===--- 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/ASTPrinter.h" #include "swift/AST/Attr.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsClangImporter.h" #include "swift/AST/DiagnosticsSema.h" #include "swift/AST/Import.h" #include "swift/AST/MacroDefinition.h" #include "swift/AST/ParameterList.h" #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeWalker.h" #include "swift/Basic/Defer.h" #include "swift/ClangImporter/ClangImporterRequests.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/Type.h" #include "clang/Basic/Module.h" using namespace swift; using namespace importer; #define DEBUG_TYPE "safe-interop-wrappers" #define DLOG(x) LLVM_DEBUG(LogIndentTracker::indent(DBGS) << x) #ifndef NDEBUG #define DBGS llvm::dbgs() << "[swiftify:" << __LINE__ << "] " #define DUMP(x) DLOG(""); x->dump(llvm::errs()) #define DLOG_SCOPE(x) DLOG(x); LogIndentTracker Scope #else #define DLOG_SCOPE(x) do {} while(false); #endif namespace { #ifndef NDEBUG struct LogIndentTracker { static thread_local uint8_t LogIndent; static llvm::raw_ostream &indent(llvm::raw_ostream &out) { for (uint8_t i = 0; i < LogIndent; i++) out << "| "; return out; } LogIndentTracker() { LogIndent++; } ~LogIndentTracker() { LogIndent--; } }; thread_local uint8_t LogIndentTracker::LogIndent = 0; #endif 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]; } static bool isStdSpanType(clang::QualType clangType) { const auto *decl = clangType->getAsTagDecl(); return decl && decl->isInStdNamespace() && decl->getName() == "span"; } struct SwiftifyInfoPrinter { static const ssize_t SELF_PARAM_INDEX = -2; static const ssize_t RETURN_VALUE_INDEX = -1; clang::ASTContext &ctx; ASTContext &SwiftContext; llvm::raw_svector_ostream &out; MacroDecl &SwiftifyImportDecl; bool firstParam = true; llvm::StringMap &typeMapping; protected: SwiftifyInfoPrinter(clang::ASTContext &ctx, ASTContext &SwiftContext, llvm::raw_svector_ostream &out, MacroDecl &SwiftifyImportDecl, llvm::StringMap &typeMapping) : ctx(ctx), SwiftContext(SwiftContext), out(out), SwiftifyImportDecl(SwiftifyImportDecl), typeMapping(typeMapping) {} public: 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; ValueDecl *D = getKnownSingleDecl(SwiftContext, "Span"); const SemanticAvailableAttributes availabilityAttrs = D->getSemanticAvailableAttrs(/*includingInactive=*/true); if (availabilityAttrs.empty()) return; // don't print availability when targeting embedded printSeparator(); out << "spanAvailability: "; out << "\""; llvm::SaveAndRestore hasAvailbilitySeparatorRestore(firstParam, true); for (auto attr : availabilityAttrs) { auto introducedOpt = attr.getIntroduced(); if (!introducedOpt.has_value()) continue; printSeparator(); out << prettyPlatformString(attr.getPlatform()) << " " << introducedOpt.value(); } out << "\""; } private: bool hasMacroParameter(StringRef ParamName) const { for (auto *Param : *SwiftifyImportDecl.parameterList) if (Param->getArgumentName().str() == ParamName) return true; return false; } protected: void printSeparator() { if (!firstParam) { out << ", "; } else { firstParam = false; } } }; struct SwiftifyInfoFunctionPrinter : public SwiftifyInfoPrinter { SwiftifyInfoFunctionPrinter(clang::ASTContext &ctx, ASTContext &SwiftContext, llvm::raw_svector_ostream &out, MacroDecl &SwiftifyImportDecl, llvm::StringMap &typeMapping) : SwiftifyInfoPrinter(ctx, SwiftContext, out, SwiftifyImportDecl, typeMapping) {} 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) { if (isStdSpanType(clangType)) { typeMapping.try_emplace(swiftType->getString(), swiftType->getDesugaredType()->getString()); return true; } return false; } private: 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: DLOG("Ignoring count parameter with non-portable integer literal\n"); 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 *) { DLOG("Ignoring count parameter with unsupported expression\n"); return false; } }; static Type ConcretePointeeType(Type swiftType) { Type nonnullType = swiftType->lookThroughSingleOptionalType(); PointerTypeKind PTK; Type PointeeTy = nonnullType->getAnyPointerElementType(PTK); if (PointeeTy && (PTK == PTK_UnsafePointer || PTK == PTK_UnsafeMutablePointer)) return PointeeTy; return Type(); } // Don't try to transform any Swift types that _SwiftifyImport doesn't know how // to handle. 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)) { DLOG("Ignoring sized_by on non-pointer type\n"); return false; } if (PTK == PTK_UnsafeRawPointer || PTK == PTK_UnsafeMutableRawPointer) return true; if (PTK != PTK_UnsafePointer && PTK != PTK_UnsafeMutablePointer) { DLOG("Ignoring sized_by on Autoreleasing pointer\n"); CONDITIONAL_ASSERT(PTK == PTK_AutoreleasingUnsafeMutablePointer); 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(); bool isByteSized = ctx.getTypeSizeInChars(PointeeT).isOne(); if (!isByteSized) DLOG("Ignoring sized_by on non-byte-sized pointer\n"); return isByteSized; } 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) : !ConcretePointeeType(swiftType).isNull()); } // 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; DLOG("Signature contains raw template, skipping\n"); return false; } static bool checkTemplates(clang::QualType clangType, bool hasLifetime, bool isStdSpan) { if (hasLifetime && isStdSpan) { // std::span is transformed to Swift Span, so the std::span template // instantiation won't show up in the macro expansion's signature. The // element type still needs to be checked. const auto *TST = clangType->getAs(); ASSERT(TST && "std::span is not specialized?"); clangType = TST->template_arguments()[0].getAsType(); } UnaliasedInstantiationVisitor checker; checker.TraverseType(clangType); return checker.hasUnaliasedInstantiation; } }; static const clang::Decl *getTemplateInstantiation(const clang::Decl *D) { if (auto FuncD = dyn_cast(D)) { return FuncD->getTemplateInstantiationPattern(); } if (auto RecordD = dyn_cast(D)) { return RecordD->getTemplateInstantiationPattern(); } if (auto EnumD = dyn_cast(D)) { return EnumD->getTemplateInstantiationPattern(); } if (auto VarD = dyn_cast(D)) { return VarD->getTemplateInstantiationPattern(); } return nullptr; } static clang::Module *getOwningModule(const clang::Decl *ClangDecl) { std::optional M; if (const auto *Instance = getTemplateInstantiation(ClangDecl)) { M = importer::getClangSubmoduleForDecl(Instance, true); } else { M = importer::getClangSubmoduleForDecl(ClangDecl, true); } if (M) { // the inner value can be null, so flatten it return M.value(); } return nullptr; } struct ForwardDeclaredConcreteTypeVisitor : public TypeWalker { bool hasForwardDeclaredConcreteType = false; const clang::Module *Owner; explicit ForwardDeclaredConcreteTypeVisitor(const clang::Module *Owner) : Owner(Owner){}; Action walkToTypePre(Type ty) override { DLOG("Walking type:\n"); LLVM_DEBUG(DUMP(ty)); auto *Nom = ty->getAnyNominal(); if (!Nom) { return Action::Continue; } const clang::Decl *ClangDecl = Nom->getClangDecl(); if (!ClangDecl) { return Action::Continue; } auto TD = dyn_cast(ClangDecl); if (!TD) { return Action::Continue; } const clang::Module *M = getOwningModule(ClangDecl); if (!M) { DLOG("Concrete type is in bridging header, which is always imported\n"); return Action::Continue; } if (!Owner) { hasForwardDeclaredConcreteType = true; DLOG("Imported signature contains concrete type not available in bridging header, skipping\n"); if (const clang::TagDecl *Def = TD->getDefinition()) LLVM_DEBUG(DUMP(Def)); return Action::Stop; } if (!Owner->isModuleVisible(M)) { hasForwardDeclaredConcreteType = true; DLOG("Imported signature contains concrete type not available in clang module, skipping\n"); if (const clang::TagDecl *Def = TD->getDefinition()) LLVM_DEBUG(DUMP(Def)); return Action::Stop; } return Action::Continue; } bool IsIncompatibleImport(Type SwiftTy, clang::QualType ClangTy) { DLOG_SCOPE("Checking compatibility of type: " << ClangTy << "\n"); SwiftTy.walk(*this); return hasForwardDeclaredConcreteType; } }; // 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 wouldBeIllegalInitializer(const AbstractFunctionDecl *MappedDecl) { if (!isa(MappedDecl)) return false; const auto *Parent = MappedDecl->getParent(); if (const auto *Ext = dyn_cast(Parent)) { Parent = Ext->getExtendedNominal(); } const auto *ParentClass = dyn_cast(Parent); if (!ParentClass) return false; return ParentClass->getForeignClassKind() != ClassDecl::ForeignKind::Normal; } template static bool getImplicitObjectParamAnnotation(const clang::ObjCMethodDecl* D) { return false; // Only C++ methods have implicit params } static size_t getNumParams(const clang::FunctionDecl* D) { return D->getNumParams(); } static bool shouldSkipModule(ModuleDecl *M) { if (M->getName().str() == CLANG_HEADER_MODULE_NAME) { DLOG("is from bridging header (or C++ namespace)\n"); return false; } if (M->getImplicitImportInfo().StdlibKind != ImplicitStdlibKind::Stdlib) { DLOG("module " << M->getNameStr() << " does not import stdlib\n"); return true; } return false; } } // namespace template static bool swiftifyImpl(ClangImporter::Implementation &Self, SwiftifyInfoFunctionPrinter &printer, const AbstractFunctionDecl *MappedDecl, const T *ClangDecl) { DLOG_SCOPE("Checking '" << *ClangDecl << "' for bounds and lifetime info\n"); if (hasSwiftAttribute(ClangDecl, {"no_safe_wrapper"})) { DLOG("skipping function with no_safe_wrapper\n"); return false; } if (shouldSkipModule(MappedDecl->getParentModule())) return false; // 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; if (ClangDecl->isImplicit()) { DLOG("implicit functions lack lifetime and bounds info\n"); return false; } clang::ASTContext &clangASTContext = Self.getClangASTContext(); const clang::Module *OwningModule = getOwningModule(ClangDecl); bool IsInBridgingHeader = MappedDecl->getModuleContext()->getName().str() == CLANG_HEADER_MODULE_NAME; ASSERT(OwningModule || IsInBridgingHeader); ForwardDeclaredConcreteTypeVisitor CheckForwardDecls(OwningModule); if (wouldBeIllegalInitializer(MappedDecl)) { DLOG("illegal initializer\n"); return false; } // 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; { auto isNonEscapable = [&Self](clang::QualType ty) { // We only care whether it's _known_ ~Escapable, because it affects // lifetime info requirements. return evaluateOrDefault(Self.SwiftContext.evaluator, ClangTypeEscapability({ty.getTypePtr(), &Self}), CxxEscapability::Escapable) == CxxEscapability::NonEscapable; }; auto dependsOnClass = [](const ParamDecl *fromParam) { return fromParam->getInterfaceType()->isAnyClassReferenceType(); }; clang::QualType clangReturnTy = ClangDecl->getReturnType(); bool returnIsStdSpan = isStdSpanType(clangReturnTy); auto *CAT = clangReturnTy->getAs(); bool returnHasBoundsInfo = returnIsStdSpan || CAT != nullptr; bool returnValueIsNonEscapable = isNonEscapable(clangReturnTy); bool returnValueCanBeNonEscapable = returnValueIsNonEscapable || returnHasBoundsInfo; bool returnHasLifetimeInfo = false; if (getImplicitObjectParamAnnotation(ClangDecl)) { DLOG("Found lifetimebound attribute on implicit 'this'\n"); if (Self.SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers)) { if (!dependsOnClass( MappedDecl->getImplicitSelfDecl(/*createIfNeeded*/ true))) { if (returnValueCanBeNonEscapable) { printer.printLifetimeboundReturn( SwiftifyInfoPrinter::SELF_PARAM_INDEX, true); returnHasLifetimeInfo = true; } else { DLOG("lifetimebound ignored because return value is escapable"); } } else { DLOG("lifetimebound ignored because it depends on class with " "refcount\n"); } } else { DLOG("lifetimebound not yet supported by stable feature-set - " "skipping\n"); return false; } } bool isClangInstanceMethod = (isa(ClangDecl) && !isa(ClangDecl) && cast(ClangDecl)->isInstance()) || (isa(ClangDecl) && cast(ClangDecl)->isInstanceMethod()); size_t swiftNumParams = MappedDecl->getParameters()->size(); if (MappedDecl->isInstanceMember() && !isClangInstanceMethod) { ASSERT(MappedDecl->isImportAsInstanceMember()); swiftNumParams += 1; } if (getNumParams(ClangDecl) != swiftNumParams) { DLOG("mismatching parameter lists"); assert( ClangDecl->isVariadic() || MappedDecl->getForeignErrorConvention().has_value() || MappedDecl->getForeignAsyncConvention().has_value() || (swiftNumParams == 1 && MappedDecl->getParameters()->get(0)->getInterfaceType()->isVoid())); return false; } size_t selfParamIndex = MappedDecl->isImportAsInstanceMember() ? MappedDecl->getSelfIndex() : getNumParams(ClangDecl); for (auto [index, clangParam] : llvm::enumerate(ClangDecl->parameters())) { clang::QualType clangParamTy = clangParam->getType(); DLOG_SCOPE("Checking parameter '" << *clangParam << "' with type '" << clangParamTy << "'\n"); 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(); if (CheckForwardDecls.IsIncompatibleImport(swiftParamTy, clangParamTy)) return false; 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->template 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 << "'\n"); attachMacro = paramHasBoundsInfo = true; } bool paramIsStdSpan = printer.registerStdSpanTypeMapping(swiftParamTy, clangParamTy); paramHasBoundsInfo |= paramIsStdSpan; bool paramHasLifetimeInfo = false; if (clangParam->template hasAttr()) { DLOG("Found noescape attribute\n"); printer.printNonEscaping(mappedIndex); paramHasLifetimeInfo = true; } if (clangParam->template hasAttr()) { if (Self.SwiftContext.LangOpts.hasFeature( Feature::SafeInteropWrappers)) { DLOG("Found lifetimebound attribute\n"); if (!dependsOnClass(swiftParam)) { if (returnValueCanBeNonEscapable) { // If this parameter has bounds info we will tranform it into a // Span, so then it will no longer be Escapable. bool willBeEscapable = !isNonEscapable(clangParamTy) && (!paramHasBoundsInfo || mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX); printer.printLifetimeboundReturn(mappedIndex, willBeEscapable); paramHasLifetimeInfo = true; returnHasLifetimeInfo = true; } else { DLOG("lifetimebound ignored because return value is escapable\n"); } } else { DLOG("lifetimebound ignored because it depends on class with " "refcount\n"); } } else { DLOG("lifetimebound not yet supported by stable feature-set - skipping\n"); return false; } } if (UnaliasedInstantiationVisitor::checkTemplates( clangParamTy, paramHasLifetimeInfo, paramIsStdSpan)) { return false; } if (paramIsStdSpan && paramHasLifetimeInfo) { DLOG("Found both std::span and lifetime info\n"); attachMacro = true; } } if (!returnHasLifetimeInfo && returnValueIsNonEscapable) { DLOG("~Escapable return value without lifetime info\n"); return false; } if (UnaliasedInstantiationVisitor::checkTemplates( clangReturnTy, returnHasLifetimeInfo, returnIsStdSpan)) { return false; } if (returnIsStdSpan && returnHasLifetimeInfo) { DLOG("Found both std::span and lifetime info for return value\n"); attachMacro = true; } if (!attachMacro && CAT == nullptr) // The return type is not imported eagerly (unlike parameter types). Exit // early to avoid unnecessarily importing types we might not need. return 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."); if (CheckForwardDecls.IsIncompatibleImport(swiftReturnTy, clangReturnTy)) return false; (void)printer.registerStdSpanTypeMapping( swiftReturnTy, clangReturnTy); 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; } } return attachMacro; } static bool diagnoseMissingMacroPlugin(ASTContext &SwiftContext, StringRef MacroName, Decl *MappedDecl) { ExternalMacroDefinitionRequest request{ &SwiftContext, SwiftContext.getIdentifier("SwiftMacros"), SwiftContext.getIdentifier(MacroName)}; auto externalDef = evaluateOrDefault(SwiftContext.evaluator, request, ExternalMacroDefinition::error("failed request")); if (externalDef.isError()) { auto &diags = SwiftContext.Diags; auto didSuppressWarnings = diags.getSuppressWarnings(); // We are highly likely parsing a textual interface, where warnings are // silenced. Make sure this warning gets emitted anyways. diags.setSuppressWarnings(false); SWIFT_DEFER { diags.setSuppressWarnings(didSuppressWarnings); }; diags.diagnose(MappedDecl, diag::macro_on_import_not_loadable, MacroName); return true; } return false; } void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) { if (SwiftContext.LangOpts.DisableSafeInteropWrappers) return; auto ClangDecl = dyn_cast_or_null(MappedDecl->getClangDecl()); if (!ClangDecl) return; MacroDecl *SwiftifyImportDecl = dyn_cast_or_null(getKnownSingleDecl(SwiftContext, "_SwiftifyImport")); if (!SwiftifyImportDecl) { DLOG("_SwiftifyImport macro not found\n"); return; } llvm::SmallString<128> MacroString; { llvm::raw_svector_ostream out(MacroString); out << "@_SwiftifyImport("; llvm::StringMap typeMapping; SwiftifyInfoFunctionPrinter printer(getClangASTContext(), SwiftContext, out, *SwiftifyImportDecl, typeMapping); if (!swiftifyImpl(*this, printer, MappedDecl, ClangDecl)) { DLOG("No relevant bounds or lifetime info found\n"); return; } printer.printAvailability(); printer.printTypeMapping(); out << ")"; } if (diagnoseMissingMacroPlugin(SwiftContext, "_SwiftifyImport", MappedDecl)) return; 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); } }