Merge pull request #85123 from hnrklssn/swiftify-obj-c2

[ClangImporter] Attach _SwiftifyImportProtocol to imported protocols with bounds attributes
This commit is contained in:
Henrik G. Olsson
2025-11-13 12:02:17 -08:00
committed by GitHub
9 changed files with 627 additions and 89 deletions

View File

@@ -5847,6 +5847,8 @@ namespace {
result->setMemberLoader(&Impl, 0);
Impl.swiftifyProtocol(result);
return result;
}

View File

@@ -1828,6 +1828,7 @@ public:
void addOptionSetTypealiases(NominalTypeDecl *nominal);
void swiftify(AbstractFunctionDecl *MappedDecl);
void swiftifyProtocol(NominalTypeDecl *MappedDecl);
/// Find the lookup table that corresponds to the given Clang module.
///

View File

@@ -16,6 +16,7 @@
#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"
@@ -36,29 +37,87 @@ namespace {
ValueDecl *getKnownSingleDecl(ASTContext &SwiftContext, StringRef DeclName) {
SmallVector<ValueDecl *, 1> decls;
SwiftContext.lookupInSwiftModule(DeclName, decls);
assert(decls.size() < 2);
ASSERT(decls.size() < 2);
if (decls.size() != 1) return nullptr;
return decls[0];
}
class SwiftifyInfoPrinter {
public:
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_ostream &out;
llvm::raw_svector_ostream &out;
MacroDecl &SwiftifyImportDecl;
bool firstParam = true;
llvm::StringMap<std::string> typeMapping;
llvm::StringMap<std::string> &typeMapping;
protected:
SwiftifyInfoPrinter(clang::ASTContext &ctx, ASTContext &SwiftContext,
llvm::raw_ostream &out, MacroDecl &SwiftifyImportDecl)
llvm::raw_svector_ostream &out,
MacroDecl &SwiftifyImportDecl,
llvm::StringMap<std::string> &typeMapping)
: ctx(ctx), SwiftContext(SwiftContext), out(out),
SwiftifyImportDecl(SwiftifyImportDecl) {
out << "(";
SwiftifyImportDecl(SwiftifyImportDecl), typeMapping(typeMapping) {}
public:
void printTypeMapping() {
printSeparator();
out << "typeMappings: [";
if (typeMapping.empty()) {
out << ":]";
return;
}
~SwiftifyInfoPrinter() { out << ")"; }
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) {
@@ -109,56 +168,7 @@ public:
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<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 << "\"";
}
void printSeparator() {
if (!firstParam) {
out << ", ";
} else {
firstParam = false;
}
}
void printParamOrReturn(ssize_t pointerIndex) {
if (pointerIndex == SELF_PARAM_INDEX)
out << ".self";
@@ -296,12 +306,25 @@ static StringRef getAttributeName(const clang::CountAttributedType *CAT) {
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,
SwiftifyInfoPrinter &printer,
SwiftifyInfoFunctionPrinter &printer,
const AbstractFunctionDecl *MappedDecl,
const clang::FunctionDecl *ClangDecl) {
const T *ClangDecl) {
DLOG("Checking " << *ClangDecl << " for bounds and lifetime info\n");
// FIXME: for private macro generated functions we do not serialize the
@@ -310,13 +333,6 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
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
@@ -332,9 +348,10 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
swiftReturnTy = ctorDecl->getResultInterfaceType();
else
ABORT("Unexpected AbstractFunctionDecl subclass.");
clang::QualType clangReturnTy = ClangDecl->getReturnType();
bool returnIsStdSpan = printer.registerStdSpanTypeMapping(
swiftReturnTy, ClangDecl->getReturnType());
auto *CAT = ClangDecl->getReturnType()->getAs<clang::CountAttributedType>();
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");
@@ -361,13 +378,13 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
size_t swiftNumParams = MappedDecl->getParameters()->size() -
(ClangDecl->isVariadic() ? 1 : 0);
ASSERT((MappedDecl->isImportAsInstanceMember() == isClangInstanceMethod) ==
(ClangDecl->getNumParams() == swiftNumParams));
(getNumParams(ClangDecl) == swiftNumParams));
size_t selfParamIndex = MappedDecl->isImportAsInstanceMember()
? MappedDecl->getSelfIndex()
: ClangDecl->getNumParams();
: getNumParams(ClangDecl);
for (auto [index, clangParam] : llvm::enumerate(ClangDecl->parameters())) {
auto clangParamTy = clangParam->getType();
clang::QualType clangParamTy = clangParam->getType();
int mappedIndex = index < selfParamIndex ? index :
index > selfParamIndex ? index - 1 :
SwiftifyInfoPrinter::SELF_PARAM_INDEX;
@@ -383,9 +400,8 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
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->getAttr<clang::SwiftNameAttr>();
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()),
@@ -401,7 +417,7 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
paramHasBoundsInfo |= paramIsStdSpan;
bool paramHasLifetimeInfo = false;
if (clangParam->hasAttr<clang::NoEscapeAttr>()) {
if (clangParam->template hasAttr<clang::NoEscapeAttr>()) {
DLOG(" Found noescape attribute on parameter '" << *clangParam << "'\n");
printer.printNonEscaping(mappedIndex);
paramHasLifetimeInfo = true;
@@ -435,6 +451,48 @@ static bool swiftifyImpl(ClangImporter::Implementation &Self,
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)
@@ -443,6 +501,13 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
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;
@@ -450,13 +515,16 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
llvm::SmallString<128> MacroString;
{
llvm::raw_svector_ostream out(MacroString);
out << "@_SwiftifyImport";
out << "@_SwiftifyImport(";
SwiftifyInfoPrinter printer(getClangASTContext(), SwiftContext, out, *SwiftifyImportDecl);
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");
@@ -479,3 +547,46 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
}
}
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);
}

View File

@@ -1808,11 +1808,22 @@ public struct SwiftifyImportProtocolMacro: ExtensionMacro {
}
let overloads = try arguments.map {
let (method, args) = try parseProtocolMacroParam($0, methods: methods)
let function = try constructOverloadFunction(
let hasVisibilityModifier = method.modifiers.contains {
return switch $0.name.trimmed.text {
case "open", "public", "package", "internal", "fileprivate", "private": true
default: false
}
}
let result = try constructOverloadFunction(
forDecl: method, leadingTrivia: Trivia(), args: args,
spanAvailability: spanAvailability,
typeMappings: typeMappings)
return MemberBlockItemSyntax(decl: function)
guard let newMethod = result.as(FunctionDeclSyntax.self)?
.with(\.modifiers, method.modifiers
+ (hasVisibilityModifier ? [] : [DeclModifierSyntax(name: .identifier("public"))])) else {
throw RuntimeError("expected FunctionDeclSyntax but got \(result.kind) for \(method.description)")
}
return MemberBlockItemSyntax(decl: newMethod)
}
return [ExtensionDeclSyntax(extensionKeyword: .identifier("extension"), extendedType: type,

View File

@@ -1379,6 +1379,7 @@ swift::expandFreestandingMacro(MacroExpansionDecl *med) {
return macroSourceFile->getBufferID();
}
static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo,
CustomAttr *attr,
bool passParentContext, MacroRole role,
@@ -1389,7 +1390,11 @@ static SourceFile *evaluateAttachedMacro(MacroDecl *macro, Decl *attachedTo,
dc = attachedTo->getDeclContext();
} else if (role == MacroRole::Conformance || role == MacroRole::Extension) {
// Conformance macros always expand to extensions at file-scope.
dc = attachedTo->getDeclContext()->getParentSourceFile();
dc = attachedTo->getDeclContext();
if (!isa<ClangModuleUnit>(dc->getModuleScopeContext()))
dc = dc->getParentSourceFile();
else
ASSERT(isa<FileUnit>(dc) && !isa<SourceFile>(dc) && "decls imported from Clang should not have a SourceFile");
} else {
dc = attachedTo->getInnermostDeclContext();
}

View File

@@ -0,0 +1,178 @@
// REQUIRES: swift_feature_SafeInteropWrappers
// REQUIRES: swift_feature_LifetimeDependence
// REQUIRES: foundation
// RUN: %empty-directory(%t)
// RUN: split-file %s %t
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -I %t -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence %t/test.swift -verify -Xcc -Wno-nullability-completeness
// RUN: env SWIFT_BACKTRACE="" %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -I %t -enable-experimental-feature SafeInteropWrappers -enable-experimental-feature LifetimeDependence %t/test.swift -dump-macro-expansions 2> %t/expansions.out
// RUN: %diff %t/expansions.out %t/expansions.expected
//--- test.h
#pragma once
#include <Foundation/Foundation.h>
// __counted_by definition inherited from Foundation.h
#define __noescape __attribute__((noescape))
#define __lifetimebound __attribute__((lifetimebound))
void foo(int len, int * __counted_by(len) p __noescape);
@protocol TestProtocol
- (void) bar:(int * __noescape)p _Nullable:(int)asdf;
- (void) simple:(int)len :(int * __counted_by(len) __noescape)p;
- (void) shared:(int)len :(int * __counted_by(len) __noescape)p1 :(int * __counted_by(len) __noescape)p2;
- (void) complexExpr:(int)len :(int) offset :(int * __counted_by(len - offset) __noescape)p;
- (void) nullUnspecified:(int)len :(int * __counted_by(len) _Null_unspecified __noescape)p;
- (void) nonnull:(int)len :(int * __counted_by(len) _Nonnull __noescape)p;
- (void) nullable:(int)len :(int * __counted_by(len) _Nullable __noescape)p;
- (int * __counted_by(len)) returnPointer:(int)len : (int * __counted_by(len) _Nullable) __lifetimebound p;
- (void) mixedEscapability:(int)len :(int * __counted_by(len) __noescape)p1 :(int * __counted_by(len))p2;
@end
@protocol StaticProtocol
+ (void) staticMethod:(int)len :(int * __counted_by(len) __noescape)p;
@end
@interface StaticInterface : NSObject <StaticProtocol> {}
@end
//--- expansions.expected
@__swiftmacro_So12TestProtocol015_SwiftifyImportB0fMe_.swift
------------------------------
extension TestProtocol {
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public
func simple(_ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe simple(len, _pPtr.baseAddress!)
}
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p1: copy p1) @_lifetime(p2: copy p2) @_disfavoredOverload public
func shared(_ p1: inout MutableSpan<Int32>, _ p2: inout MutableSpan<Int32>) {
let len = Int32(exactly: p1.count)!
if p2.count != len {
fatalError("bounds check failure in shared: expected \(len) but got \(p2.count)")
}
return unsafe p2.withUnsafeMutableBufferPointer { _p2Ptr in
return unsafe p1.withUnsafeMutableBufferPointer { _p1Ptr in
return unsafe shared(len, _p1Ptr.baseAddress!, _p2Ptr.baseAddress!)
}
}
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public
func complexExpr(_ len: Int32, _ offset: Int32, _ p: inout MutableSpan<Int32>) {
let _pCount = p.count
if _pCount != len - offset {
fatalError("bounds check failure in complexExpr: expected \(len - offset) but got \(_pCount)")
}
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe complexExpr(len, offset, _pPtr.baseAddress!)
}
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public
func nullUnspecified(_ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe nullUnspecified(len, _pPtr.baseAddress!)
}
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public
func nonnull(_ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe nonnull(len, _pPtr.baseAddress!)
}
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public
func nullable(_ p: inout MutableSpan<Int32>?) {
let len = Int32(exactly: p?.count ?? 0)!
return { () in
return if p == nil {
unsafe nullable(len, nil)
} else {
unsafe p!.withUnsafeMutableBufferPointer { _pPtr in
return unsafe nullable(len, _pPtr.baseAddress)
}
}
}()
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(copy p) @_lifetime(p: copy p) @_disfavoredOverload public
func returnPointer(_ p: inout MutableSpan<Int32>?) -> MutableSpan<Int32> {
let len = Int32(exactly: p?.count ?? 0)!
return unsafe _swiftifyOverrideLifetime(MutableSpan<Int32>(_unsafeStart: { () in
return if p == nil {
unsafe returnPointer(len, nil)
} else {
unsafe p!.withUnsafeMutableBufferPointer { _pPtr in
return unsafe returnPointer(len, _pPtr.baseAddress)
}
}
}(), count: Int(len)), copying: ())
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p1: copy p1) @_disfavoredOverload public
func mixedEscapability(_ p1: inout MutableSpan<Int32>, _ p2: UnsafeMutableBufferPointer<Int32>) {
let len = Int32(exactly: p1.count)!
if p2.count != len {
fatalError("bounds check failure in mixedEscapability: expected \(len) but got \(p2.count)")
}
return unsafe p1.withUnsafeMutableBufferPointer { _p1Ptr in
return unsafe mixedEscapability(len, _p1Ptr.baseAddress!, p2.baseAddress!)
}
}
}
------------------------------
@__swiftmacro_So14StaticProtocol015_SwiftifyImportB0fMe_.swift
------------------------------
extension StaticProtocol {
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload
static public func staticMethod(_ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe staticMethod(len, _pPtr.baseAddress!)
}
}
}
------------------------------
@__swiftmacro_So3foo15_SwiftifyImportfMp_.swift
------------------------------
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload public func foo(_ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe foo(len, _pPtr.baseAddress!)
}
}
------------------------------
//--- test.swift
import TestClang
@inlinable
public func call(p: inout MutableSpan<CInt>, p2: inout MutableSpan<CInt>, p3: inout MutableSpan<CInt>?, a: TestProtocol) {
a.simple(&p)
a.shared(&p, &p2)
a.complexExpr(2, 3, &p)
a.nullUnspecified(&p)
a.nonnull(&p)
a.nullable(&p3)
let _: MutableSpan<CInt> = a.returnPointer(&p3)
StaticInterface.staticMethod(&p2)
foo(&p)
}
//--- module.modulemap
module TestClang {
header "test.h"
export *
}

View File

@@ -0,0 +1,133 @@
// REQUIRES: swift_feature_SafeInteropWrappers
// RUN: %empty-directory(%t)
// RUN: split-file %s %t
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/CountedByProtocol.swiftmodule -I %t/Inputs -enable-experimental-feature SafeInteropWrappers %t/counted-by-protocol.swift -verify -Xcc -Wno-nullability-completeness
// RUN: env SWIFT_BACKTRACE="" %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/CountedByProtocol.swiftmodule -I %t/Inputs -enable-experimental-feature SafeInteropWrappers %t/counted-by-protocol.swift -dump-macro-expansions 2> %t/expansions.out
// RUN: %diff %t/expansions.out %t/expansions.expected
//--- Inputs/module.modulemap
module CountedByProtocolClang {
header "counted-by-protocol.h"
export *
}
//--- Inputs/counted-by-protocol.h
#pragma once
#define __counted_by(x) __attribute__((__counted_by__(x)))
int foo(int len, int * __counted_by(len) p);
@protocol CountedByProtocol
- (void) simple:(int)len :(int * __counted_by(len))p;
- (void) shared:(int)len :(int * __counted_by(len))p1 :(int * __counted_by(len))p2;
- (void) complexExpr:(int)len :(int) offset :(int * __counted_by(len - offset))p;
- (void) nullUnspecified:(int)len :(int * __counted_by(len) _Null_unspecified)p;
- (void) nonnull:(int)len :(int * __counted_by(len) _Nonnull)p;
- (void) nullable:(int)len :(int * __counted_by(len) _Nullable)p;
- (int * __counted_by(len)) returnPointer:(int)len;
+ (void) staticMethod:(int)len :(int * __counted_by(len))p;
@end
__attribute__((swift_attr("@_SwiftifyImportProtocol(.method(signature: \"func swiftAttr(_ len: Int32, _ p: UnsafeMutablePointer<Int32>!)\", paramInfo: [.countedBy(pointer: .param(2), count: \"len\")]))")))
@protocol SwiftAttrProtocol
- (void)swiftAttr:(int)len :(int *)p;
@end
//--- expansions.expected
@__swiftmacro_So17CountedByProtocol015_SwiftifyImportC0fMe_.swift
------------------------------
extension CountedByProtocol {
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func simple(_ p: UnsafeMutableBufferPointer<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe simple(len, p.baseAddress!)
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func shared(_ p1: UnsafeMutableBufferPointer<Int32>, _ p2: UnsafeMutableBufferPointer<Int32>) {
let len = Int32(exactly: p1.count)!
if p2.count != len {
fatalError("bounds check failure in shared: expected \(len) but got \(p2.count)")
}
return unsafe shared(len, p1.baseAddress!, p2.baseAddress!)
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func complexExpr(_ len: Int32, _ offset: Int32, _ p: UnsafeMutableBufferPointer<Int32>) {
let _pCount = p.count
if _pCount != len - offset {
fatalError("bounds check failure in complexExpr: expected \(len - offset) but got \(_pCount)")
}
return unsafe complexExpr(len, offset, p.baseAddress!)
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func nullUnspecified(_ p: UnsafeMutableBufferPointer<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe nullUnspecified(len, p.baseAddress!)
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func nonnull(_ p: UnsafeMutableBufferPointer<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe nonnull(len, p.baseAddress!)
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func nullable(_ p: UnsafeMutableBufferPointer<Int32>?) {
let len = Int32(exactly: unsafe p?.count ?? 0)!
return unsafe nullable(len, p?.baseAddress)
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func returnPointer(_ len: Int32) -> UnsafeMutableBufferPointer<Int32> {
return unsafe UnsafeMutableBufferPointer<Int32>(start: unsafe returnPointer(len), count: Int(len))
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload
static public func staticMethod(_ p: UnsafeMutableBufferPointer<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe staticMethod(len, p.baseAddress!)
}
}
------------------------------
@__swiftmacro_So17SwiftAttrProtocol015_SwiftifyImportC0fMe_.swift
------------------------------
extension SwiftAttrProtocol {
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func swiftAttr(_ p: UnsafeMutableBufferPointer<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe swiftAttr(len, p.baseAddress!)
}
}
------------------------------
@__swiftmacro_So3foo15_SwiftifyImportfMp_.swift
------------------------------
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public func foo(_ p: UnsafeMutableBufferPointer<Int32>) -> Int32 {
let len = Int32(exactly: p.count)!
return unsafe foo(len, p.baseAddress!)
}
------------------------------
//--- counted-by-protocol.swift
import CountedByProtocolClang
@inlinable
public func call(p: UnsafeMutableBufferPointer<CInt>, x: CInt, y: CInt, a: CountedByProtocol, b: SwiftAttrProtocol) {
a.simple(p)
a.shared(p, p)
a.complexExpr(x, y, p)
a.nullUnspecified(p)
a.nonnull(p)
a.nullable(p)
let _: UnsafeMutableBufferPointer<CInt> = a.returnPointer(x)
let r2 = a.returnPointer(x)
let _: UnsafeMutablePointer<CInt>? = r2 // make sure the original is the favored overload
b.swiftAttr(p)
let _ = foo(p)
}

View File

@@ -0,0 +1,97 @@
// REQUIRES: swift_feature_SafeInteropWrappers
// RUN: %empty-directory(%t)
// RUN: split-file %s %t
// RUN: %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -I %t -enable-experimental-feature SafeInteropWrappers %t/test.swift -verify -Xcc -Wno-nullability-completeness
// RUN: env SWIFT_BACKTRACE="" %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -I %t -enable-experimental-feature SafeInteropWrappers %t/test.swift -dump-macro-expansions 2> %t/expansions.out
// RUN: %diff %t/expansions.out %t/expansions.expected
//--- test.h
#pragma once
#define __counted_by(x) __attribute__((__counted_by__(x)))
@protocol NoBounds
- (void) ignore;
@end
@protocol NoYesNo
- (void) ignore;
- (void) simple:(int)len :(int * __counted_by(len))p;
- (void) ignore2;
@end
@protocol YesNo
- (void) simple:(int)len :(int * __counted_by(len))p;
- (void) ignore;
@end
@protocol YesNoYes
- (void) simple:(int)len :(int * __counted_by(len))p;
- (void) ignore;
- (void) simple2:(int)len :(int * __counted_by(len))p;
@end
//--- expansions.expected
@__swiftmacro_So05NoYesA023_SwiftifyImportProtocolfMe_.swift
------------------------------
extension NoYesNo {
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func simple(_ p: UnsafeMutableBufferPointer<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe simple(len, p.baseAddress!)
}
}
------------------------------
@__swiftmacro_So5YesNo23_SwiftifyImportProtocolfMe_.swift
------------------------------
extension YesNo {
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func simple(_ p: UnsafeMutableBufferPointer<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe simple(len, p.baseAddress!)
}
}
------------------------------
@__swiftmacro_So05YesNoA023_SwiftifyImportProtocolfMe_.swift
------------------------------
extension YesNoYes {
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func simple(_ p: UnsafeMutableBufferPointer<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe simple(len, p.baseAddress!)
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload public
func simple2(_ p: UnsafeMutableBufferPointer<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe simple2(len, p.baseAddress!)
}
}
------------------------------
//--- test.swift
import TestClang
@inlinable
public func call(p: UnsafeMutableBufferPointer<CInt>, x: NoBounds, y: NoYesNo, z: YesNo, å: YesNoYes) {
x.ignore()
y.ignore()
y.simple(p)
z.ignore()
z.simple(p)
å.ignore()
å.simple(p)
å.simple2(p)
}
//--- module.modulemap
module TestClang {
header "test.h"
export *
}

View File

@@ -42,7 +42,7 @@ protocol OverloadedProtocol {
------------------------------
extension SimpleProtocol {
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload
@_alwaysEmitIntoClient @_disfavoredOverload public
func myFunc(_ ptr: UnsafeBufferPointer<CInt>) {
let len = CInt(exactly: ptr.count)!
return unsafe myFunc(ptr.baseAddress!, len)
@@ -53,7 +53,7 @@ extension SimpleProtocol {
------------------------------
extension SpanProtocol {
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload
@_alwaysEmitIntoClient @_disfavoredOverload public
func foo(_ ptr: Span<CInt>) {
let len = CInt(exactly: ptr.count)!
return unsafe ptr.withUnsafeBufferPointer { _ptrPtr in
@@ -61,7 +61,7 @@ extension SpanProtocol {
}
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_lifetime(borrow self) @_disfavoredOverload
@_alwaysEmitIntoClient @_lifetime(borrow self) @_disfavoredOverload public
func bar(_ len: CInt) -> Span<CInt> {
return unsafe _swiftifyOverrideLifetime(Span<CInt>(_unsafeStart: unsafe bar(len), count: Int(len)), copying: ())
}
@@ -71,7 +71,7 @@ extension SpanProtocol {
------------------------------
extension MixedProtocol {
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload
@_alwaysEmitIntoClient @_disfavoredOverload public
/// Some doc comment
func foo(_ ptr: Span<CInt>) {
let len = CInt(exactly: ptr.count)!
@@ -80,7 +80,7 @@ extension MixedProtocol {
}
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload
@_alwaysEmitIntoClient @_disfavoredOverload public
func bar(_ ptr: UnsafeBufferPointer<CInt>) {
let len = CInt(exactly: ptr.count)!
return unsafe bar(ptr.baseAddress!, len)
@@ -91,13 +91,13 @@ extension MixedProtocol {
------------------------------
extension OverloadedProtocol {
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload
@_alwaysEmitIntoClient @_disfavoredOverload public
func foo(_ ptr: UnsafeBufferPointer<CInt>) {
let len1 = CInt(exactly: ptr.count)!
return unsafe foo(ptr.baseAddress!, len1)
}
/// This is an auto-generated wrapper for safer interop
@_alwaysEmitIntoClient @_disfavoredOverload
@_alwaysEmitIntoClient @_disfavoredOverload public
func foo(bar: UnsafeBufferPointer<CInt>) {
let len2 = CInt(exactly: bar.count)!
return unsafe foo(bar: bar.baseAddress!, len2)