Skip to content

Commit c1bf742

Browse files
committed
[Swiftify] Add support for free functions imported as instance methods
This adds support for attaching safe interop wrappers to functions explicitly imported as instance methods using swift_name. rdar://156288883
1 parent c82e85d commit c1bf742

File tree

4 files changed

+495
-10
lines changed

4 files changed

+495
-10
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ GROUPED_WARNING(clang_ignored_sendable_attr, ClangDeclarationImport, none,
105105
"cannot be added to it",
106106
(Type))
107107

108+
GROUPED_WARNING(warn_clang_ignored_bounds_on_self, ClangDeclarationImport, none,
109+
"bounds attribute '%0' ignored on parameter mapped to 'self'",
110+
(StringRef))
111+
NOTE(note_swift_name_instance_method, none,
112+
"swift_name maps free function to instance method here", ())
113+
108114
WARNING(implicit_bridging_header_imported_from_module,none,
109115
"implicit import of bridging header '%0' via module %1 "
110116
"is deprecated and will be removed in a later version of Swift",

lib/ClangImporter/ImportDecl.cpp

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9445,6 +9445,22 @@ void ClangImporter::Implementation::addOptionSetTypealiases(
94459445

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

9448+
// until CountAttributedType::getAttributeName lands in our LLVM branch
9449+
static StringRef getAttributeName(const clang::CountAttributedType *CAT) {
9450+
switch (CAT->getKind()) {
9451+
case clang::CountAttributedType::CountedBy:
9452+
return "__counted_by";
9453+
case clang::CountAttributedType::CountedByOrNull:
9454+
return "__counted_by_or_null";
9455+
case clang::CountAttributedType::SizedBy:
9456+
return "__sized_by";
9457+
case clang::CountAttributedType::SizedByOrNull:
9458+
return "__sized_by_or_null";
9459+
case clang::CountAttributedType::EndedBy:
9460+
llvm_unreachable("CountAttributedType cannot be ended_by");
9461+
}
9462+
}
9463+
94489464
void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
94499465
if (!SwiftContext.LangOpts.hasFeature(Feature::SafeInteropWrappers))
94509466
return;
@@ -9460,9 +9476,6 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
94609476
ClangDecl->getAccess() == clang::AS_private)
94619477
return;
94629478

9463-
if (ClangDecl->getNumParams() != MappedDecl->getParameters()->size())
9464-
return;
9465-
94669479
MacroDecl *SwiftifyImportDecl = dyn_cast_or_null<MacroDecl>(getKnownSingleDecl(SwiftContext, "_SwiftifyImport"));
94679480
if (!SwiftifyImportDecl)
94689481
return;
@@ -9519,14 +9532,42 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
95199532
true);
95209533
returnHasLifetimeInfo = true;
95219534
}
9535+
9536+
bool isClangInstanceMethod =
9537+
isa<clang::CXXMethodDecl>(ClangDecl) &&
9538+
!isa<clang::CXXConstructorDecl>(ClangDecl) &&
9539+
cast<clang::CXXMethodDecl>(ClangDecl)->isInstance();
9540+
ASSERT((MappedDecl->isImportAsInstanceMember() == isClangInstanceMethod) ==
9541+
(ClangDecl->getNumParams() == MappedDecl->getParameters()->size()));
9542+
9543+
size_t selfParamIndex = MappedDecl->isImportAsInstanceMember()
9544+
? MappedDecl->getSelfIndex()
9545+
: ClangDecl->getNumParams();
95229546
for (auto [index, clangParam] : llvm::enumerate(ClangDecl->parameters())) {
95239547
auto clangParamTy = clangParam->getType();
9524-
auto swiftParam = MappedDecl->getParameters()->get(index);
9548+
int mappedIndex = index < selfParamIndex ? index :
9549+
index > selfParamIndex ? index - 1 :
9550+
SwiftifyInfoPrinter::SELF_PARAM_INDEX;
9551+
ParamDecl *swiftParam = nullptr;
9552+
if (mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) {
9553+
swiftParam = MappedDecl->getImplicitSelfDecl(/*createIfNeeded*/true);
9554+
} else {
9555+
swiftParam = MappedDecl->getParameters()->get(mappedIndex);
9556+
}
9557+
ASSERT(swiftParam);
95259558
Type swiftParamTy = swiftParam->getInterfaceType();
95269559
bool paramHasBoundsInfo = false;
95279560
auto *CAT = clangParamTy->getAs<clang::CountAttributedType>();
9528-
if (SwiftifiableCAT(getClangASTContext(), CAT, swiftParamTy)) {
9529-
printer.printCountedBy(CAT, index);
9561+
if (CAT && mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX) {
9562+
diagnose(HeaderLoc(clangParam->getLocation()),
9563+
diag::warn_clang_ignored_bounds_on_self, getAttributeName(CAT));
9564+
auto swiftName = ClangDecl->getAttr<clang::SwiftNameAttr>();
9565+
ASSERT(swiftName &&
9566+
"free function mapped to instance method without swift_name??");
9567+
diagnose(HeaderLoc(swiftName->getLocation()),
9568+
diag::note_swift_name_instance_method);
9569+
} else if (SwiftifiableCAT(getClangASTContext(), CAT, swiftParamTy)) {
9570+
printer.printCountedBy(CAT, mappedIndex);
95309571
SIW_DBG(" Found bounds info '" << clangParamTy
95319572
<< "' on parameter '" << *clangParam << "'\n");
95329573
attachMacro = paramHasBoundsInfo = true;
@@ -9538,16 +9579,18 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
95389579
bool paramHasLifetimeInfo = false;
95399580
if (clangParam->hasAttr<clang::NoEscapeAttr>()) {
95409581
SIW_DBG(" Found noescape attribute on parameter '" << *clangParam << "'\n");
9541-
printer.printNonEscaping(index);
9582+
printer.printNonEscaping(mappedIndex);
95429583
paramHasLifetimeInfo = true;
95439584
}
95449585
if (clangParam->hasAttr<clang::LifetimeBoundAttr>()) {
95459586
SIW_DBG(" Found lifetimebound attribute on parameter '"
95469587
<< *clangParam << "'\n");
95479588
// If this parameter has bounds info we will tranform it into a Span,
95489589
// so then it will no longer be Escapable.
9549-
bool willBeEscapable = swiftParamTy->isEscapable() && !paramHasBoundsInfo;
9550-
printer.printLifetimeboundReturn(index, willBeEscapable);
9590+
bool willBeEscapable = swiftParamTy->isEscapable() &&
9591+
(!paramHasBoundsInfo ||
9592+
mappedIndex == SwiftifyInfoPrinter::SELF_PARAM_INDEX);
9593+
printer.printLifetimeboundReturn(mappedIndex, willBeEscapable);
95519594
paramHasLifetimeInfo = true;
95529595
returnHasLifetimeInfo = true;
95539596
}
@@ -9563,7 +9606,6 @@ void ClangImporter::Implementation::swiftify(AbstractFunctionDecl *MappedDecl) {
95639606
}
95649607
printer.printAvailability();
95659608
printer.printTypeMapping(typeMapping);
9566-
95679609
}
95689610

95699611
if (attachMacro) {
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
// REQUIRES: swift_feature_SafeInteropWrappers
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: split-file %s %t
5+
6+
// 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 -warnings-as-errors -Xcc -Werror %t/test.swift -dump-macro-expansions 2> %t/out.txt
7+
// RUN: diff %t/out.txt %t/out.expected
8+
// 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 -DVERIFY
9+
10+
//--- test.swift
11+
import Instance
12+
13+
@available(macOS 13.3.0, *)
14+
func foo(_ p: inout MutableSpan<CInt>, a: A, aa: inout A, c: C, b: B, bb: inout B) {
15+
aa.basic(&p)
16+
aa.bar(&p)
17+
a.constSelf(&p)
18+
a.valSelf(&p)
19+
let _: MutableSpan<CInt> = a.lifetimeBoundSelf(3)
20+
c.refSelf(&p)
21+
b.nonescaping(&p)
22+
let _: MutableSpan<CInt> = bb.nonescapingLifetimebound(73)
23+
24+
#if VERIFY
25+
aa.countedSelf(&p)
26+
a.basic(&p) // expected-error{{cannot use mutating member on immutable value: 'a' is a 'let' constant}}
27+
#endif
28+
}
29+
30+
//--- Inputs/instance.h
31+
#include <ptrcheck.h>
32+
#include <lifetimebound.h>
33+
34+
#define SWIFT_IMMORTAL_REFERENCE \
35+
__attribute__((swift_attr("import_reference"))) \
36+
__attribute__((swift_attr("retain:immortal"))) \
37+
__attribute__((swift_attr("release:immortal")))
38+
39+
#define SWIFT_NONESCAPABLE \
40+
__attribute__((swift_attr("~Escapable")))
41+
42+
struct A {};
43+
struct SWIFT_NONESCAPABLE B {};
44+
struct SWIFT_IMMORTAL_REFERENCE C {};
45+
46+
void basic(struct A *a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.basic(self:_:_:)")));
47+
48+
void renamed(struct A *a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.bar(self:_:_:)")));
49+
50+
void countedSelf(struct A * __counted_by(len)
51+
a, // expected-warning{{bounds attribute '__counted_by' ignored on parameter mapped to 'self'}}
52+
int * __counted_by(len) p __noescape, int len)
53+
__attribute__((
54+
swift_name // expected-note{{swift_name maps free function to instance method here}}
55+
("A.countedSelf(self:_:_:)")));
56+
57+
void constSelf(const struct A *a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.constSelf(self:_:_:)")));
58+
59+
void valSelf(struct A a, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("A.valSelf(self:_:_:)")));
60+
61+
void refSelf(struct C *c, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("C.refSelf(self:_:_:)")));
62+
63+
int * __counted_by(len) lifetimeBoundSelf(struct A a __lifetimebound, int len) __attribute__((swift_name("A.lifetimeBoundSelf(self:_:)")));
64+
65+
void nonescaping(const struct B *d, int * __counted_by(len) p __noescape, int len) __attribute__((swift_name("B.nonescaping(self:_:_:)")));
66+
67+
int * __counted_by(len) nonescapingLifetimebound(struct B *d __lifetimebound, int len) __attribute__((swift_name("B.nonescapingLifetimebound(self:_:)")));
68+
69+
//--- Inputs/module.modulemap
70+
module Instance {
71+
header "instance.h"
72+
}
73+
74+
//--- out.expected
75+
@__swiftmacro_So1AV5basic15_SwiftifyImportfMp_.swift
76+
------------------------------
77+
/// This is an auto-generated wrapper for safer interop
78+
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload
79+
public mutating func basic(_ p: inout MutableSpan<Int32>) {
80+
let len = Int32(exactly: p.count)!
81+
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
82+
return unsafe basic(_pPtr.baseAddress!, len)
83+
}
84+
}
85+
------------------------------
86+
@__swiftmacro_So5basic15_SwiftifyImportfMp_.swift
87+
------------------------------
88+
/// This is an auto-generated wrapper for safer interop
89+
@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
90+
public func basic(_ a: UnsafeMutablePointer<A>!, _ p: inout MutableSpan<Int32>) {
91+
let len = Int32(exactly: p.count)!
92+
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
93+
return unsafe basic(a, _pPtr.baseAddress!, len)
94+
}
95+
}
96+
------------------------------
97+
@__swiftmacro_So1AV3bar15_SwiftifyImportfMp_.swift
98+
------------------------------
99+
/// This is an auto-generated wrapper for safer interop
100+
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload
101+
public mutating func bar(_ p: inout MutableSpan<Int32>) {
102+
let len = Int32(exactly: p.count)!
103+
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
104+
return unsafe bar(_pPtr.baseAddress!, len)
105+
}
106+
}
107+
------------------------------
108+
@__swiftmacro_So7renamed15_SwiftifyImportfMp_.swift
109+
------------------------------
110+
/// This is an auto-generated wrapper for safer interop
111+
@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
112+
public func renamed(_ a: UnsafeMutablePointer<A>!, _ p: inout MutableSpan<Int32>) {
113+
let len = Int32(exactly: p.count)!
114+
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
115+
return unsafe renamed(a, _pPtr.baseAddress!, len)
116+
}
117+
}
118+
------------------------------
119+
@__swiftmacro_So1AV9constSelf15_SwiftifyImportfMp_.swift
120+
------------------------------
121+
/// This is an auto-generated wrapper for safer interop
122+
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload
123+
public func constSelf(_ p: inout MutableSpan<Int32>) {
124+
let len = Int32(exactly: p.count)!
125+
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
126+
return unsafe constSelf(_pPtr.baseAddress!, len)
127+
}
128+
}
129+
------------------------------
130+
@__swiftmacro_So9constSelf15_SwiftifyImportfMp_.swift
131+
------------------------------
132+
/// This is an auto-generated wrapper for safer interop
133+
@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
134+
public func constSelf(_ a: UnsafePointer<A>!, _ p: inout MutableSpan<Int32>) {
135+
let len = Int32(exactly: p.count)!
136+
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
137+
return unsafe constSelf(a, _pPtr.baseAddress!, len)
138+
}
139+
}
140+
------------------------------
141+
@__swiftmacro_So1AV7valSelf15_SwiftifyImportfMp_.swift
142+
------------------------------
143+
/// This is an auto-generated wrapper for safer interop
144+
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload
145+
public func valSelf(_ p: inout MutableSpan<Int32>) {
146+
let len = Int32(exactly: p.count)!
147+
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
148+
return unsafe valSelf(_pPtr.baseAddress!, len)
149+
}
150+
}
151+
------------------------------
152+
@__swiftmacro_So7valSelf15_SwiftifyImportfMp_.swift
153+
------------------------------
154+
/// This is an auto-generated wrapper for safer interop
155+
@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
156+
public func valSelf(_ a: A, _ p: inout MutableSpan<Int32>) {
157+
let len = Int32(exactly: p.count)!
158+
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
159+
return unsafe valSelf(a, _pPtr.baseAddress!, len)
160+
}
161+
}
162+
------------------------------
163+
@__swiftmacro_So1AV17lifetimeBoundSelf15_SwiftifyImportfMp_.swift
164+
------------------------------
165+
/// This is an auto-generated wrapper for safer interop
166+
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(borrow self) @_disfavoredOverload
167+
public func lifetimeBoundSelf(_ len: Int32) -> MutableSpan<Int32> {
168+
return unsafe _swiftifyOverrideLifetime(MutableSpan<Int32>(_unsafeStart: unsafe lifetimeBoundSelf(len), count: Int(len)), copying: ())
169+
}
170+
------------------------------
171+
@__swiftmacro_So17lifetimeBoundSelf15_SwiftifyImportfMp_.swift
172+
------------------------------
173+
/// This is an auto-generated wrapper for safer interop
174+
@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
175+
public func lifetimeBoundSelf(_ a: A, _ len: Int32) -> MutableSpan<Int32> {
176+
return unsafe _swiftifyOverrideLifetime(MutableSpan<Int32>(_unsafeStart: unsafe lifetimeBoundSelf(a, len), count: Int(len)), copying: ())
177+
}
178+
------------------------------
179+
@__swiftmacro_So1CV7refSelf15_SwiftifyImportfMp_.swift
180+
------------------------------
181+
/// This is an auto-generated wrapper for safer interop
182+
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload
183+
public func refSelf(_ p: inout MutableSpan<Int32>) {
184+
let len = Int32(exactly: p.count)!
185+
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
186+
return unsafe refSelf(_pPtr.baseAddress!, len)
187+
}
188+
}
189+
------------------------------
190+
@__swiftmacro_So7refSelf15_SwiftifyImportfMp_.swift
191+
------------------------------
192+
/// This is an auto-generated wrapper for safer interop
193+
@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
194+
public func refSelf(_ c: C!, _ p: inout MutableSpan<Int32>) {
195+
let len = Int32(exactly: p.count)!
196+
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
197+
return unsafe refSelf(c, _pPtr.baseAddress!, len)
198+
}
199+
}
200+
------------------------------
201+
@__swiftmacro_So1BV11nonescaping15_SwiftifyImportfMp_.swift
202+
------------------------------
203+
/// This is an auto-generated wrapper for safer interop
204+
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(p: copy p) @_disfavoredOverload
205+
public func nonescaping(_ p: inout MutableSpan<Int32>) {
206+
let len = Int32(exactly: p.count)!
207+
return unsafe p.withUnsafeMutableBufferPointer { _pPtr in
208+
return unsafe nonescaping(_pPtr.baseAddress!, len)
209+
}
210+
}
211+
------------------------------
212+
@__swiftmacro_So1BV24nonescapingLifetimebound15_SwiftifyImportfMp_.swift
213+
------------------------------
214+
/// This is an auto-generated wrapper for safer interop
215+
@_alwaysEmitIntoClient @available(visionOS 1.0, tvOS 12.2, watchOS 5.2, iOS 12.2, macOS 10.14.4, *) @_lifetime(copy self) @_disfavoredOverload
216+
public mutating func nonescapingLifetimebound(_ len: Int32) -> MutableSpan<Int32> {
217+
return unsafe _swiftifyOverrideLifetime(MutableSpan<Int32>(_unsafeStart: unsafe nonescapingLifetimebound(len), count: Int(len)), copying: ())
218+
}
219+
------------------------------

0 commit comments

Comments
 (0)