Skip to content

Commit 363c563

Browse files
committed
[transferring] Implement swift_attr support for transferring results and parameters.
rdar://125211624
1 parent bf685b4 commit 363c563

File tree

6 files changed

+167
-1
lines changed

6 files changed

+167
-1
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ void importer::getNormalInvocationArguments(
610610
// '@_nonSendable' on Clang declarations is fully supported, including the
611611
// 'attribute push' pragma.
612612
if (clangSupportsPragmaAttributeWithSwiftAttr())
613-
invocationArgStrs.push_back( "-D__SWIFT_ATTR_SUPPORTS_SENDABLE_DECLS=1");
613+
invocationArgStrs.push_back("-D__SWIFT_ATTR_SUPPORTS_SENDABLE_DECLS=1");
614614

615615
// Get the version of this compiler and pass it to C/Objective-C
616616
// declarations.
@@ -661,6 +661,11 @@ void importer::getNormalInvocationArguments(
661661
}
662662
}
663663

664+
// If we support TransferringArgsAndResults, set the -D flag to signal that it
665+
// is supported.
666+
if (LangOpts.hasFeature(Feature::TransferringArgsAndResults))
667+
invocationArgStrs.push_back("-D__SWIFT_ATTR_SUPPORTS_TRANSFERRING=1");
668+
664669
if (searchPathOpts.getSDKPath().empty()) {
665670
invocationArgStrs.push_back("-Xclang");
666671
invocationArgStrs.push_back("-nostdsysteminc");

lib/ClangImporter/ImportDecl.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8063,6 +8063,18 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
80638063
nominal->registerProtocolConformance(conformance, /*synthesized=*/true);
80648064
}
80658065

8066+
if (swiftAttr->getAttribute() == "transferring") {
8067+
// Swallow this if the feature is not enabled.
8068+
if (!SwiftContext.LangOpts.hasFeature(
8069+
Feature::TransferringArgsAndResults))
8070+
continue;
8071+
auto *funcDecl = dyn_cast<FuncDecl>(MappedDecl);
8072+
if (!funcDecl)
8073+
continue;
8074+
funcDecl->setTransferringResult();
8075+
continue;
8076+
}
8077+
80668078
// Dig out a buffer with the attribute text.
80678079
unsigned bufferID = getClangSwiftAttrSourceBuffer(
80688080
swiftAttr->getAttribute());

lib/ClangImporter/ImportType.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2577,6 +2577,18 @@ static ParamDecl *getParameterInfo(ClangImporter::Implementation *impl,
25772577
param, AccessLevel::Private, SourceLoc(), SourceLoc(), name,
25782578
impl->importSourceLoc(param->getLocation()), bodyName,
25792579
impl->ImportedHeaderUnit);
2580+
2581+
// If TransferringArgsAndResults are enabled and we have a transferring
2582+
// argument, set that the param was transferring.
2583+
if (paramInfo->getASTContext().LangOpts.hasFeature(
2584+
Feature::TransferringArgsAndResults)) {
2585+
if (auto *attr = param->getAttr<clang::SwiftAttrAttr>()) {
2586+
if (attr->getAttribute() == "transferring") {
2587+
paramInfo->setTransferring();
2588+
}
2589+
}
2590+
}
2591+
25802592
// Foreign references are already references so they don't need to be passed
25812593
// as inout.
25822594
paramInfo->setSpecifier(isInOut && !swiftParamTy->isForeignReferenceType()
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
// Make sure that we have set the -D flag appropriately.
3+
#ifdef __SWIFT_ATTR_SUPPORTS_TRANSFERRING
4+
#if !__SWIFT_ATTR_SUPPORTS_TRANSFERRING
5+
#error "Compiler should have set __SWIFT_ATTR_SUPPORTS_TRANSFERRING to 1"
6+
#endif
7+
#else
8+
#error "Compiler should have defined __SWIFT_ATTR_SUPPORTS_TRANSFERRING"
9+
#endif
10+
11+
#define SWIFT_TRANSFERRING __attribute__((swift_attr("transferring")))
12+
13+
#pragma clang assume_nonnull begin
14+
15+
#ifdef __OBJC__
16+
17+
@import Foundation;
18+
19+
@interface MyType : NSObject
20+
- (NSObject *)getTransferringResult SWIFT_TRANSFERRING;
21+
- (NSObject *)getTransferringResultWithArgument:(NSObject *)arg SWIFT_TRANSFERRING;
22+
- (NSObject *)getResultWithTransferringArgument:(NSObject *) SWIFT_TRANSFERRING arg;
23+
@end
24+
25+
SWIFT_TRANSFERRING
26+
@interface DoesntMakeSense : NSObject
27+
@end
28+
29+
NSObject *returnNSObjectFromGlobalFunction(NSObject *other);
30+
NSObject *transferNSObjectFromGlobalFunction(NSObject *other) SWIFT_TRANSFERRING;
31+
void transferNSObjectToGlobalFunction(NSObject * arg SWIFT_TRANSFERRING);
32+
33+
#endif
34+
35+
typedef struct {
36+
int state;
37+
} NonSendableCStruct;
38+
39+
NonSendableCStruct returnUserDefinedFromGlobalFunction(NonSendableCStruct other);
40+
NonSendableCStruct transferUserDefinedFromGlobalFunction(NonSendableCStruct other) SWIFT_TRANSFERRING;
41+
void transferUserDefinedIntoGlobalFunction(NonSendableCStruct arg SWIFT_TRANSFERRING);
42+
43+
#pragma clang assume_nonnull end

test/ClangImporter/transferring.swift

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %target-swift-frontend -swift-version 6 -disable-availability-checking -emit-sil -o /dev/null %s -parse-as-library -enable-experimental-feature TransferringArgsAndResults -verify -import-objc-header %S/Inputs/transferring.h
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: asserts
5+
6+
////////////////////////
7+
// MARK: Declarations //
8+
////////////////////////
9+
10+
// Make our non sendable c struct non-Sendable
11+
@available(*, unavailable)
12+
extension NonSendableCStruct: Sendable {}
13+
14+
@MainActor func transferToMain<T>(_ t: T) async {}
15+
func useValue<T>(_ t: T) {}
16+
17+
/////////////////
18+
// MARK: Tests //
19+
/////////////////
20+
21+
func funcTestTransferringResult() async {
22+
let x = NonSendableCStruct()
23+
let y = transferUserDefinedFromGlobalFunction(x)
24+
await transferToMain(x)
25+
useValue(y)
26+
27+
// Just to show that without the transferring param, we generate diagnostics.
28+
let x2 = NonSendableCStruct()
29+
let y2 = returnUserDefinedFromGlobalFunction(x2)
30+
await transferToMain(x2) // expected-error {{transferring value of non-Sendable type 'NonSendableCStruct' from nonisolated context to main actor-isolated context}}
31+
useValue(y2) // expected-note {{access here could race}}
32+
}
33+
34+
func funcTestTransferringArg() async {
35+
let x = NonSendableCStruct()
36+
transferUserDefinedIntoGlobalFunction(x) // expected-error {{binding of non-Sendable type 'NonSendableCStruct' accessed after being transferred}}
37+
useValue(x) // expected-note {{access here could race}}
38+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %target-swift-frontend -swift-version 6 -disable-availability-checking -emit-sil -o /dev/null %s -parse-as-library -enable-experimental-feature TransferringArgsAndResults -verify -import-objc-header %S/Inputs/transferring.h
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: asserts
5+
// REQUIRES: objc_interop
6+
7+
////////////////////////
8+
// MARK: Declarations //
9+
////////////////////////
10+
11+
@MainActor func transferToMain<T>(_ t: T) async {}
12+
func useValue<T>(_ t: T) {}
13+
14+
/////////////////
15+
// MARK: Tests //
16+
/////////////////
17+
18+
func methodTestTransferringResult() async {
19+
let x = MyType()
20+
let y = x.getTransferringResult()
21+
await transferToMain(x)
22+
useValue(y)
23+
}
24+
25+
func methodTestTransferringArg() async {
26+
let x = MyType()
27+
let s = NSObject()
28+
let _ = x.getResultWithTransferringArgument(s) // expected-error {{binding of non-Sendable type 'NSObject' accessed after being transferred}}
29+
useValue(s) // expected-note {{access here could race}}
30+
}
31+
32+
// Make sure we just ignore the swift_attr if it is applied to something like a
33+
// class.
34+
func testDoesntMakeSense() {
35+
let _ = DoesntMakeSense()
36+
}
37+
38+
func funcTestTransferringResult() async {
39+
let x = NSObject()
40+
let y = transferNSObjectFromGlobalFunction(x)
41+
await transferToMain(x)
42+
useValue(y)
43+
44+
// Just to show that without the transferring param, we generate diagnostics.
45+
let x2 = NSObject()
46+
let y2 = returnNSObjectFromGlobalFunction(x2)
47+
await transferToMain(x2) // expected-error {{transferring 'x2' may cause a race}}
48+
// expected-note @-1 {{transferring disconnected 'x2' to main actor-isolated callee could cause races in between callee main actor-isolated and local nonisolated uses}}
49+
useValue(y2) // expected-note {{access here could race}}
50+
}
51+
52+
func funcTestTransferringArg() async {
53+
let x = NSObject()
54+
transferNSObjectToGlobalFunction(x) // expected-error {{binding of non-Sendable type 'NSObject' accessed after being transferred}}
55+
useValue(x) // expected-note {{access here could race}}
56+
}

0 commit comments

Comments
 (0)