Skip to content
6 changes: 6 additions & 0 deletions include/swift/AST/DiagnosticsClangImporter.def
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ GROUPED_WARNING(clang_ignored_sendable_attr, ClangDeclarationImport, none,
"cannot be added to it",
(Type))

GROUPED_WARNING(warn_clang_ignored_bounds_on_self, ClangDeclarationImport, none,
"bounds attribute '%0' ignored on parameter mapped to 'self'",
(StringRef))
NOTE(note_swift_name_instance_method, none,
"swift_name maps free function to instance method here", ())

WARNING(implicit_bridging_header_imported_from_module,none,
"implicit import of bridging header '%0' via module %1 "
"is deprecated and will be removed in a later version of Swift",
Expand Down
64 changes: 54 additions & 10 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9445,6 +9445,22 @@ void ClangImporter::Implementation::addOptionSetTypealiases(

#define SIW_DBG(x) DEBUG_WITH_TYPE("safe-interop-wrappers", llvm::dbgs() << x)

// until CountAttributedType::getAttributeName lands in our LLVM branch
static StringRef getAttributeName(const clang::CountAttributedType *CAT) {
switch (CAT->getKind()) {
case clang::CountAttributedType::CountedBy:
return "__counted_by";
case clang::CountAttributedType::CountedByOrNull:
return "__counted_by_or_null";
case clang::CountAttributedType::SizedBy:
return "__sized_by";
case clang::CountAttributedType::SizedByOrNull:
return "__sized_by_or_null";
case clang::CountAttributedType::EndedBy:
llvm_unreachable("CountAttributedType cannot be ended_by");
}
}

void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
return;
Expand All @@ -9460,9 +9476,6 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
ClangDecl->getAccess() == clang::AS_private)
return;

if (ClangDecl->getNumParams() != MappedDecl->getParameters()->size())
return;

MacroDecl *SwiftifyImportDecl = dyn_cast_or_null<MacroDecl>(getKnownSingleDecl(SwiftContext, "_SwiftifyImport"));
if (!SwiftifyImportDecl)
return;
Expand Down Expand Up @@ -9519,14 +9532,44 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
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) ==
(ClangDecl->getNumParams() == swiftNumParams));

size_t selfParamIndex = MappedDecl->isImportAsInstanceMember()
? MappedDecl->getSelfIndex()
: ClangDecl->getNumParams();
for (auto [index, clangParam] : llvm::enumerate(ClangDecl->parameters())) {
auto clangParamTy = clangParam->getType();
auto swiftParam = MappedDecl->getParameters()->get(index);
int mappedIndex = index < selfParamIndex ? index :
index > selfParamIndex ? index - 1 :
SwiftifyInfoPrinter::SELF_PARAM_INDEX;
ParamDecl *swiftParam = nullptr;
if (mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) {
swiftParam = MappedDecl->getImplicitSelfDecl(/*createIfNeeded*/true);
} else {
swiftParam = MappedDecl->getParameters()->get(mappedIndex);
}
ASSERT(swiftParam);
Type swiftParamTy = swiftParam->getInterfaceType();
bool paramHasBoundsInfo = false;
auto *CAT = clangParamTy->getAs<clang::CountAttributedType>();
if (SwiftifiableCAT(getClangASTContext(), CAT, swiftParamTy)) {
printer.printCountedBy(CAT, index);
if (CAT && mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) {
diagnose(HeaderLoc(clangParam->getLocation()),
diag::warn_clang_ignored_bounds_on_self, getAttributeName(CAT));
auto swiftName = ClangDecl->getAttr<clang::SwiftNameAttr>();
ASSERT(swiftName &&
"free function mapped to instance method without swift_name??");
diagnose(HeaderLoc(swiftName->getLocation()),
diag::note_swift_name_instance_method);
} else if (SwiftifiableCAT(getClangASTContext(), CAT, swiftParamTy)) {
printer.printCountedBy(CAT, mappedIndex);
SIW_DBG(" Found bounds info '" << clangParamTy
<< "' on parameter '" << *clangParam << "'\n");
attachMacro = paramHasBoundsInfo = true;
Expand All @@ -9538,16 +9581,18 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
bool paramHasLifetimeInfo = false;
if (clangParam->hasAttr<clang::NoEscapeAttr>()) {
SIW_DBG(" Found noescape attribute on parameter '" << *clangParam << "'\n");
printer.printNonEscaping(index);
printer.printNonEscaping(mappedIndex);
paramHasLifetimeInfo = true;
}
if (clangParam->hasAttr<clang::LifetimeBoundAttr>()) {
SIW_DBG(" Found lifetimebound attribute on parameter '"
<< *clangParam << "'\n");
// 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;
printer.printLifetimeboundReturn(index, willBeEscapable);
bool willBeEscapable = swiftParamTy->isEscapable() &&
(!paramHasBoundsInfo ||
mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX);
printer.printLifetimeboundReturn(mappedIndex, willBeEscapable);
paramHasLifetimeInfo = true;
returnHasLifetimeInfo = true;
}
Expand All @@ -9563,7 +9608,6 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
}
printer.printAvailability();
printer.printTypeMapping(typeMapping);

}

if (attachMacro) {
Expand Down
9 changes: 8 additions & 1 deletion lib/Macros/Sources/SwiftMacros/SwiftifyImportMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,14 @@ struct FunctionCallBuilder: BoundsCheckedThunkBuilder {
let functionRef = DeclReferenceExprSyntax(baseName: base.name)
let args: [ExprSyntax] = base.signature.parameterClause.parameters.enumerated()
.map { (i: Int, param: FunctionParameterSyntax) in
return pointerArgs[i] ?? ExprSyntax("\(param.name)")
if let overrideArg = pointerArgs[i] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this is a fix we want to backport to 6.2.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's probably a good idea

return overrideArg
}
if isInout(getParam(base.signature, i).type) {
return ExprSyntax("&\(param.name)")
} else {
return ExprSyntax("\(param.name)")
}
}
let labels: [TokenSyntax?] = base.signature.parameterClause.parameters.map { param in
let firstName = param.firstName.trimmed
Expand Down
212 changes: 212 additions & 0 deletions test/Interop/C/swiftify-import/import-as-instance-method.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
// 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/Test.swiftmodule -I %t/Inputs -enable-experimental-feature SafeInteropWrappers -strict-memory-safety -verify -verify-additional-file %t/Inputs/instance.h %t/test.swift -I %bridging-path -DVERIFY
// RUN: env SWIFT_BACKTRACE="" %target-swift-frontend -emit-module -plugin-path %swift-plugin-dir -o %t/Test.swiftmodule -I %t/Inputs -enable-experimental-feature SafeInteropWrappers -strict-memory-safety -warnings-as-errors -Xcc -Werror %t/test.swift -dump-macro-expansions -I %bridging-path 2> %t/out.txt
// RUN: diff --strip-trailing-cr %t/out.txt %t/out.expected

//--- test.swift
import Instance

@available(macOS 13.3.0, *)
func foo(_ p: inout MutableSpan<CInt>, a: A, aa: inout A, c: C, b: B, bb: inout B) {
aa.basic(&p)
aa.bar(&p)
a.constSelf(&p)
a.valSelf(&p)
let _: MutableSpan<CInt> = a.lifetimeBoundSelf(3)
c.refSelf(&p)
b.nonescaping(&p)
let _: MutableSpan<CInt> = bb.nonescapingLifetimebound(73)

#if VERIFY
aa.countedSelf(&p)
a.basic(&p) // expected-error{{cannot use mutating member on immutable value: 'a' is a 'let' constant}}
#endif
}

//--- Inputs/instance.h
#include <ptrcheck.h>
#include <lifetimebound.h>
#include <swift/bridging>

struct A {};
struct SWIFT_NONESCAPABLE B {};
struct SWIFT_IMMORTAL_REFERENCE C {};

void basic(struct A *a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.basic(self:_:_:)")));

void renamed(struct A *a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.bar(self:_:_:)")));

void countedSelf(struct A * __counted_by(len)
a, // expected-warning{{bounds attribute '__counted_by' ignored on parameter mapped to 'self'}}
int * __counted_by(len) p __noescape, int len)
__attribute__((
swift_name // expected-note{{swift_name maps free function to instance method here}}
("A.countedSelf(self:_:_:)")));

void constSelf(const struct A *a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.constSelf(self:_:_:)")));

void valSelf(struct A a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.valSelf(self:_:_:)")));

void refSelf(struct C *c, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("C.refSelf(self:_:_:)")));

int * __counted_by(len) lifetimeBoundSelf(struct A a __lifetimebound, int len) __attribute__((swift_name("A.lifetimeBoundSelf(self:_:)")));

void nonescaping(const struct B *d, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("B.nonescaping(self:_:_:)")));

int * __counted_by(len) nonescapingLifetimebound(struct B *d __lifetimebound, int len) __attribute__((swift_name("B.nonescapingLifetimebound(self:_:)")));

//--- Inputs/module.modulemap
module Instance {
header "instance.h"
}

//--- out.expected
@__swiftmacro_So1AV5basic15_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 mutating func basic(_ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe basic(_pPtr.baseAddress!, len)
}
}
------------------------------
@__swiftmacro_So5basic15_SwiftifyImportfMp_.swift
------------------------------
/// This is an auto-generated wrapper for safer interop
@available(swift, obsoleted: 3, renamed: "A.basic(self:_:_:)") @_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 basic(_ a: UnsafeMutablePointer<A>!, _ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe basic(a, _pPtr.baseAddress!, len)
}
}
------------------------------
@__swiftmacro_So1AV3bar15_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 mutating func bar(_ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe bar(_pPtr.baseAddress!, len)
}
}
------------------------------
@__swiftmacro_So7renamed15_SwiftifyImportfMp_.swift
------------------------------
/// This is an auto-generated wrapper for safer interop
@available(swift, obsoleted: 3, renamed: "A.bar(self:_:_:)") @_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 renamed(_ a: UnsafeMutablePointer<A>!, _ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe renamed(a, _pPtr.baseAddress!, len)
}
}
------------------------------
@__swiftmacro_So1AV9constSelf15_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 constSelf(_ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe constSelf(_pPtr.baseAddress!, len)
}
}
------------------------------
@__swiftmacro_So9constSelf15_SwiftifyImportfMp_.swift
------------------------------
/// This is an auto-generated wrapper for safer interop
@available(swift, obsoleted: 3, renamed: "A.constSelf(self:_:_:)") @_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 constSelf(_ a: UnsafePointer<A>!, _ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe constSelf(a, _pPtr.baseAddress!, len)
}
}
------------------------------
@__swiftmacro_So1AV7valSelf15_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 valSelf(_ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe valSelf(_pPtr.baseAddress!, len)
}
}
------------------------------
@__swiftmacro_So7valSelf15_SwiftifyImportfMp_.swift
------------------------------
/// This is an auto-generated wrapper for safer interop
@available(swift, obsoleted: 3, renamed: "A.valSelf(self:_:_:)") @_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 valSelf(_ a: A, _ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe valSelf(a, _pPtr.baseAddress!, len)
}
}
------------------------------
@__swiftmacro_So1AV17lifetimeBoundSelf15_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(borrow self) @_disfavoredOverload
public func lifetimeBoundSelf(_ len: Int32) -> MutableSpan<Int32> {
return unsafe _swiftifyOverrideLifetime(MutableSpan<Int32>(_unsafeStart: unsafe lifetimeBoundSelf(len), count: Int(len)), copying: ())
}
------------------------------
@__swiftmacro_So17lifetimeBoundSelf15_SwiftifyImportfMp_.swift
------------------------------
/// This is an auto-generated wrapper for safer interop
@available(swift, obsoleted: 3, renamed: "A.lifetimeBoundSelf(self:_:)") @_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(borrow a) @_disfavoredOverload
public func lifetimeBoundSelf(_ a: A, _ len: Int32) -> MutableSpan<Int32> {
return unsafe _swiftifyOverrideLifetime(MutableSpan<Int32>(_unsafeStart: unsafe lifetimeBoundSelf(a, len), count: Int(len)), copying: ())
}
------------------------------
@__swiftmacro_So1CV7refSelf15_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 refSelf(_ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe refSelf(_pPtr.baseAddress!, len)
}
}
------------------------------
@__swiftmacro_So7refSelf15_SwiftifyImportfMp_.swift
------------------------------
/// This is an auto-generated wrapper for safer interop
@available(swift, obsoleted: 3, renamed: "C.refSelf(self:_:_:)") @_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 refSelf(_ c: C!, _ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe refSelf(c, _pPtr.baseAddress!, len)
}
}
------------------------------
@__swiftmacro_So1BV11nonescaping15_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 nonescaping(_ p: inout MutableSpan<Int32>) {
let len = Int32(exactly: p.count)!
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
return unsafe nonescaping(_pPtr.baseAddress!, len)
}
}
------------------------------
@__swiftmacro_So1BV24nonescapingLifetimebound15_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(copy self) @_disfavoredOverload
public mutating func nonescapingLifetimebound(_ len: Int32) -> MutableSpan<Int32> {
return unsafe _swiftifyOverrideLifetime(MutableSpan<Int32>(_unsafeStart: unsafe nonescapingLifetimebound(len), count: Int(len)), copying: ())
}
------------------------------
Loading