mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This updates SwiftifyImportProtocolPrinter such that it no longer emits
anything for methods without bounds or lifetime info. Previously we
would not attach the macro if no methods in the protocol contained
bounds or lifetime info. However if one of the methods did, we would
still emit `.method(signature: "func foo()", paramInfo: [])` for the
methods without bounds of lifetime info. This would result in overloads
being generated, like so:
```
@_alwaysEmitIntoClient @_disfavoredOverload public
func foo() {
return unsafe foo()
}
```
As part of this change, SwiftifyImportPrinter is now an abstract parent
type for SwiftifyImportProtocolPrinter, and the new
SwiftifyImportFunctionPrinter. Instead of SwiftifyImportProtocolPrinter
inheriting the function printing, `printMethod` instead creates a new
SwiftifyImportFunctionPrinter each time, with a new output string. If it
output anything interesting the result is forwarded, otherwise it is
discarded.
593 lines
22 KiB
C++
593 lines
22 KiB
C++
//===--- 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/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<ValueDecl *, 1> decls;
|
|
SwiftContext.lookupInSwiftModule(DeclName, decls);
|
|
ASSERT(decls.size() < 2);
|
|
if (decls.size() != 1) return nullptr;
|
|
return decls[0];
|
|
}
|
|
|
|
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<std::string> &typeMapping;
|
|
|
|
protected:
|
|
SwiftifyInfoPrinter(clang::ASTContext &ctx, ASTContext &SwiftContext,
|
|
llvm::raw_svector_ostream &out,
|
|
MacroDecl &SwiftifyImportDecl,
|
|
llvm::StringMap<std::string> &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;
|
|
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<bool> 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 << "\"";
|
|
}
|
|
|
|
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<std::string> &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) {
|
|
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;
|
|
}
|
|
|
|
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<CountedByExpressionValidator, bool> {
|
|
bool VisitDeclRefExpr(const clang::DeclRefExpr *e) { return true; }
|
|
|
|
bool VisitIntegerLiteral(const clang::IntegerLiteral *IL) {
|
|
switch (IL->getType()->castAs<clang::BuiltinType>()->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<clang::PointerType>();
|
|
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<UnaliasedInstantiationVisitor> {
|
|
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");
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
static bool getImplicitObjectParamAnnotation(const clang::ObjCMethodDecl* D) {
|
|
return false; // Only C++ methods have implicit params
|
|
}
|
|
|
|
static size_t getNumParams(const clang::ObjCMethodDecl* D) {
|
|
return D->param_size();
|
|
}
|
|
static size_t getNumParams(const clang::FunctionDecl* D) {
|
|
return D->getNumParams();
|
|
}
|
|
} // namespace
|
|
|
|
template<typename T>
|
|
static bool swiftifyImpl(ClangImporter::Implementation &Self,
|
|
SwiftifyInfoFunctionPrinter &printer,
|
|
const AbstractFunctionDecl *MappedDecl,
|
|
const T *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;
|
|
|
|
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<FuncDecl>(MappedDecl))
|
|
swiftReturnTy = funcDecl->getResultInterfaceType();
|
|
else if (const auto *ctorDecl = dyn_cast<ConstructorDecl>(MappedDecl))
|
|
swiftReturnTy = ctorDecl->getResultInterfaceType();
|
|
else
|
|
ABORT("Unexpected AbstractFunctionDecl subclass.");
|
|
clang::QualType clangReturnTy = ClangDecl->getReturnType();
|
|
bool returnIsStdSpan = printer.registerStdSpanTypeMapping(
|
|
swiftReturnTy, clangReturnTy);
|
|
auto *CAT = clangReturnTy->getAs<clang::CountAttributedType>();
|
|
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<clang::LifetimeBoundAttr>(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<clang::CXXMethodDecl>(ClangDecl) &&
|
|
!isa<clang::CXXConstructorDecl>(ClangDecl) &&
|
|
cast<clang::CXXMethodDecl>(ClangDecl)->isInstance();
|
|
size_t swiftNumParams = MappedDecl->getParameters()->size() -
|
|
(ClangDecl->isVariadic() ? 1 : 0);
|
|
ASSERT((MappedDecl->isImportAsInstanceMember() == isClangInstanceMethod) ==
|
|
(getNumParams(ClangDecl) == swiftNumParams));
|
|
|
|
size_t selfParamIndex = MappedDecl->isImportAsInstanceMember()
|
|
? MappedDecl->getSelfIndex()
|
|
: getNumParams(ClangDecl);
|
|
for (auto [index, clangParam] : llvm::enumerate(ClangDecl->parameters())) {
|
|
clang::QualType 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<clang::CountAttributedType>();
|
|
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<clang::SwiftNameAttr>();
|
|
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->template hasAttr<clang::NoEscapeAttr>()) {
|
|
DLOG(" Found noescape attribute on parameter '" << *clangParam << "'\n");
|
|
printer.printNonEscaping(mappedIndex);
|
|
paramHasLifetimeInfo = true;
|
|
}
|
|
if (clangParam->template hasAttr<clang::LifetimeBoundAttr>()) {
|
|
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;
|
|
}
|
|
|
|
class SwiftifyProtocolInfoPrinter : public SwiftifyInfoPrinter {
|
|
private:
|
|
ClangImporter::Implementation &ImporterImpl;
|
|
|
|
public:
|
|
SwiftifyProtocolInfoPrinter(ClangImporter::Implementation &ImporterImpl,
|
|
clang::ASTContext &ctx, ASTContext &SwiftContext,
|
|
llvm::raw_svector_ostream &out,
|
|
MacroDecl &SwiftifyImportDecl,
|
|
llvm::StringMap<std::string> &typeMapping)
|
|
: SwiftifyInfoPrinter(ctx, SwiftContext, out, SwiftifyImportDecl, typeMapping),
|
|
ImporterImpl(ImporterImpl) {}
|
|
|
|
bool printMethod(const FuncDecl *Method) {
|
|
auto ClangDecl = dyn_cast_or_null<clang::ObjCMethodDecl>(Method->getClangDecl());
|
|
if (!ClangDecl)
|
|
return false;
|
|
|
|
llvm::SmallString<128> paramInfoString;
|
|
llvm::raw_svector_ostream tmpOut(paramInfoString);
|
|
|
|
SwiftifyInfoFunctionPrinter methodPrinter(ctx, SwiftContext, tmpOut,
|
|
SwiftifyImportDecl, typeMapping);
|
|
bool hadAttributes = swiftifyImpl(ImporterImpl, methodPrinter, Method, ClangDecl);
|
|
if (hadAttributes) {
|
|
printSeparator();
|
|
out << ".method(signature: \"";
|
|
printMethodSignature(Method);
|
|
out << "\", paramInfo: [" << paramInfoString << "])";
|
|
}
|
|
return hadAttributes;
|
|
}
|
|
|
|
private:
|
|
void printMethodSignature(const FuncDecl *Method) {
|
|
auto options =
|
|
PrintOptions::printForDiagnostics(AccessLevel::Private, true);
|
|
StreamPrinter printer(out);
|
|
Method->print(printer, options);
|
|
}
|
|
};
|
|
|
|
void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
|
|
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers) ||
|
|
SwiftContext.ClangImporterOpts.DisableSafeInteropWrappers)
|
|
return;
|
|
auto ClangDecl = dyn_cast_or_null<clang::FunctionDecl>(MappedDecl->getClangDecl());
|
|
if (!ClangDecl)
|
|
return;
|
|
|
|
{
|
|
UnaliasedInstantiationVisitor visitor;
|
|
visitor.TraverseType(ClangDecl->getType());
|
|
if (visitor.hasUnaliasedInstantiation)
|
|
return;
|
|
}
|
|
|
|
MacroDecl *SwiftifyImportDecl = dyn_cast_or_null<MacroDecl>(getKnownSingleDecl(SwiftContext, "_SwiftifyImport"));
|
|
if (!SwiftifyImportDecl)
|
|
return;
|
|
|
|
llvm::SmallString<128> MacroString;
|
|
{
|
|
llvm::raw_svector_ostream out(MacroString);
|
|
out << "@_SwiftifyImport(";
|
|
|
|
llvm::StringMap<std::string> typeMapping;
|
|
SwiftifyInfoFunctionPrinter printer(getClangASTContext(), SwiftContext, out,
|
|
*SwiftifyImportDecl, typeMapping);
|
|
if (!swiftifyImpl(*this, printer, MappedDecl, ClangDecl))
|
|
return;
|
|
printer.printAvailability();
|
|
printer.printTypeMapping();
|
|
out << ")";
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void ClangImporter::Implementation::swiftifyProtocol(
|
|
NominalTypeDecl *MappedDecl) {
|
|
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers) ||
|
|
SwiftContext.ClangImporterOpts.DisableSafeInteropWrappers)
|
|
return;
|
|
if (!isa<ProtocolDecl, ClassDecl>(MappedDecl))
|
|
return;
|
|
|
|
MacroDecl *SwiftifyImportDecl = dyn_cast_or_null<MacroDecl>(
|
|
getKnownSingleDecl(SwiftContext, "_SwiftifyImportProtocol"));
|
|
if (!SwiftifyImportDecl)
|
|
return;
|
|
|
|
DLOG("Checking " << MappedDecl->getName() << " protocol for methods with bounds and lifetime info\n");
|
|
llvm::SmallString<128> MacroString;
|
|
{
|
|
llvm::raw_svector_ostream out(MacroString);
|
|
out << "@_SwiftifyImportProtocol(";
|
|
|
|
bool hasBoundsAttributes = false;
|
|
llvm::StringMap<std::string> typeMapping;
|
|
SwiftifyProtocolInfoPrinter printer(*this, getClangASTContext(),
|
|
SwiftContext, out, *SwiftifyImportDecl,
|
|
typeMapping);
|
|
for (Decl *SwiftMember :
|
|
cast<IterableDeclContext>(MappedDecl)->getAllMembers()) {
|
|
FuncDecl *SwiftDecl = dyn_cast<FuncDecl>(SwiftMember);
|
|
if (!SwiftDecl)
|
|
continue;
|
|
hasBoundsAttributes |= printer.printMethod(SwiftDecl);
|
|
}
|
|
|
|
if (!hasBoundsAttributes)
|
|
return;
|
|
printer.printAvailability();
|
|
printer.printTypeMapping();
|
|
out << ")";
|
|
}
|
|
|
|
DLOG("Attaching safe interop macro: " << MacroString << "\n");
|
|
importNontrivialAttribute(MappedDecl, MacroString);
|
|
}
|
|
|