Skip to content

Commit 68e9e71

Browse files
committed
[transferring] Make transferring suppressible and validate that we do suppress transferring in swift interface files.
Importantly I added tests that validated this behavior. rdar://126780823 (cherry picked from commit dc25857)
1 parent 579beb4 commit 68e9e71

File tree

5 files changed

+144
-28
lines changed

5 files changed

+144
-28
lines changed

include/swift/AST/PrintOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,9 @@ struct PrintOptions {
382382
/// Suppress 'isolated' and '#isolation' on isolated parameters with optional type.
383383
bool SuppressOptionalIsolatedParams = false;
384384

385+
/// Suppress 'transferring' on arguments and results.
386+
bool SuppressTransferringArgsAndResults = false;
387+
385388
/// Suppress Noncopyable generics.
386389
bool SuppressNoncopyableGenerics = false;
387390

include/swift/Basic/Features.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ EXPERIMENTAL_FEATURE(FixedArrays, true)
349349
EXPERIMENTAL_FEATURE(GroupActorErrors, true)
350350

351351
// Allow for the 'transferring' keyword to be applied to arguments and results.
352-
EXPERIMENTAL_FEATURE(TransferringArgsAndResults, true)
352+
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(TransferringArgsAndResults, true)
353353

354354
// Allow for `switch` of noncopyable values to be borrowing or consuming.
355355
EXPERIMENTAL_FEATURE(BorrowingSwitch, true)

lib/AST/ASTPrinter.cpp

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3123,6 +3123,13 @@ static void suppressingFeatureOptionalIsolatedParameters(
31233123
action();
31243124
}
31253125

3126+
static void suppressingFeatureTransferringArgsAndResults(
3127+
PrintOptions &options, llvm::function_ref<void()> action) {
3128+
llvm::SaveAndRestore<bool> scope(options.SuppressTransferringArgsAndResults,
3129+
true);
3130+
action();
3131+
}
3132+
31263133
static void suppressingFeatureAssociatedTypeImplements(PrintOptions &options,
31273134
llvm::function_ref<void()> action) {
31283135
unsigned originalExcludeAttrCount = options.ExcludeAttrList.size();
@@ -3694,7 +3701,7 @@ static void printParameterFlags(ASTPrinter &printer,
36943701
if (!options.excludeAttrKind(TypeAttrKind::NoDerivative) &&
36953702
flags.isNoDerivative())
36963703
printer.printAttrName("@noDerivative ");
3697-
if (flags.isTransferring())
3704+
if (!options.SuppressTransferringArgsAndResults && flags.isTransferring())
36983705
printer.printAttrName("transferring ");
36993706

37003707
switch (flags.getOwnershipSpecifier()) {
@@ -4154,19 +4161,16 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
41544161
}
41554162
}
41564163
}
4157-
{
4158-
auto fnTy = decl->getInterfaceType();
4159-
bool hasTransferring = false;
4160-
if (auto *ft = llvm::dyn_cast_if_present<FunctionType>(fnTy)) {
4161-
if (ft->hasExtInfo())
4162-
hasTransferring = ft->hasTransferringResult();
4163-
} else if (auto *ft =
4164-
llvm::dyn_cast_if_present<GenericFunctionType>(fnTy)) {
4165-
if (ft->hasExtInfo())
4166-
hasTransferring = ft->hasTransferringResult();
4167-
}
4168-
if (hasTransferring)
4164+
4165+
if (!Options.SuppressTransferringArgsAndResults) {
4166+
if (decl->hasTransferringResult()) {
41694167
Printer << "transferring ";
4168+
} else if (auto *ft = llvm::dyn_cast_if_present<AnyFunctionType>(
4169+
decl->getInterfaceType())) {
4170+
if (ft->hasExtInfo() && ft->hasTransferringResult()) {
4171+
Printer << "transferring ";
4172+
}
4173+
}
41704174
}
41714175

41724176
// HACK: When printing result types for funcs with opaque result types,
@@ -6627,7 +6631,8 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
66276631

66286632
Printer << " -> ";
66296633

6630-
if (T->hasExtInfo() && T->hasTransferringResult()) {
6634+
if (!Options.SuppressTransferringArgsAndResults && T->hasExtInfo() &&
6635+
T->hasTransferringResult()) {
66316636
Printer.printKeyword("transferring ", Options);
66326637
}
66336638

lib/AST/FeatureSet.cpp

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -611,22 +611,38 @@ UNINTERESTING_FEATURE(FixedArrays)
611611
UNINTERESTING_FEATURE(GroupActorErrors)
612612

613613
static bool usesFeatureTransferringArgsAndResults(Decl *decl) {
614-
if (auto *pd = dyn_cast<ParamDecl>(decl))
615-
if (pd->isTransferring())
614+
auto functionTypeUsesTransferring = [](Decl *decl) {
615+
return usesTypeMatching(decl, [](Type type) {
616+
auto fnType = type->getAs<AnyFunctionType>();
617+
if (!fnType)
618+
return false;
619+
620+
if (fnType->hasExtInfo() && fnType->hasTransferringResult())
621+
return true;
622+
623+
return llvm::any_of(fnType->getParams(),
624+
[](AnyFunctionType::Param param) {
625+
return param.getParameterFlags().isTransferring();
626+
});
627+
});
628+
};
629+
630+
if (auto *pd = dyn_cast<ParamDecl>(decl)) {
631+
if (pd->isTransferring()) {
616632
return true;
633+
}
634+
635+
if (functionTypeUsesTransferring(pd))
636+
return true;
637+
}
617638

618639
if (auto *fDecl = dyn_cast<FuncDecl>(decl)) {
619-
auto fnTy = fDecl->getInterfaceType();
620-
bool hasTransferring = false;
621-
if (auto *ft = llvm::dyn_cast_if_present<FunctionType>(fnTy)) {
622-
if (ft->hasExtInfo())
623-
hasTransferring = ft->hasTransferringResult();
624-
} else if (auto *ft =
625-
llvm::dyn_cast_if_present<GenericFunctionType>(fnTy)) {
626-
if (ft->hasExtInfo())
627-
hasTransferring = ft->hasTransferringResult();
628-
}
629-
if (hasTransferring)
640+
// First check for param decl results.
641+
if (llvm::any_of(fDecl->getParameters()->getArray(), [](ParamDecl *pd) {
642+
return usesFeatureTransferringArgsAndResults(pd);
643+
}))
644+
return true;
645+
if (functionTypeUsesTransferring(decl))
630646
return true;
631647
}
632648

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -swift-version 5 -enable-library-evolution -module-name test -emit-module -o %t/test.swiftmodule -emit-module-interface-path - -enable-experimental-feature TransferringArgsAndResults %s | %FileCheck %s
3+
4+
public class NonSendableKlass {}
5+
6+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
7+
// CHECK-NEXT: public func transferArgTest(_ x: transferring test.NonSendableKlass)
8+
// CHECK-NEXT: #else
9+
// CHECK-NEXT: public func transferArgTest(_ x: test.NonSendableKlass)
10+
// CHECK-NEXT: #endif
11+
public func transferArgTest(_ x: transferring NonSendableKlass) {}
12+
13+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
14+
// CHECK-NEXT: public func transferResultTest() -> transferring test.NonSendableKlass
15+
// CHECK-NEXT: #else
16+
// CHECK-NEXT: public func transferResultTest() -> test.NonSendableKlass
17+
// CHECK-NEXT: #endif
18+
public func transferResultTest() -> transferring NonSendableKlass { fatalError() }
19+
20+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
21+
// CHECK-NEXT: public func transferArgAndResultTest(_ x: test.NonSendableKlass, _ y: transferring test.NonSendableKlass, _ z: test.NonSendableKlass) -> transferring test.NonSendableKlass
22+
// CHECK-NEXT: #else
23+
// CHECK-NEXT: public func transferArgAndResultTest(_ x: test.NonSendableKlass, _ y: test.NonSendableKlass, _ z: test.NonSendableKlass) -> test.NonSendableKlass
24+
// CHECK-NEXT: #endif
25+
public func transferArgAndResultTest(_ x: NonSendableKlass, _ y: transferring NonSendableKlass, _ z: NonSendableKlass) -> transferring NonSendableKlass { fatalError() }
26+
27+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
28+
// CHECK-NEXT: public func argEmbeddedInType(_ fn: (transferring test.NonSendableKlass) -> ())
29+
// CHECK-NEXT: #else
30+
// CHECK-NEXT: public func argEmbeddedInType(_ fn: (test.NonSendableKlass) -> ())
31+
// CHECK-NEXT: #endif
32+
public func argEmbeddedInType(_ fn: (transferring NonSendableKlass) -> ()) {}
33+
34+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
35+
// CHECK-NEXT: public func resultEmbeddedInType(_ fn: () -> transferring test.NonSendableKlass)
36+
// CHECK-NEXT: #else
37+
// CHECK-NEXT: public func resultEmbeddedInType(_ fn: () -> test.NonSendableKlass)
38+
// CHECK-NEXT: #endif
39+
public func resultEmbeddedInType(_ fn: () -> transferring NonSendableKlass) {}
40+
41+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
42+
// CHECK-NEXT: public func argAndResultEmbeddedInType(_ fn: (test.NonSendableKlass, transferring test.NonSendableKlass, test.NonSendableKlass) -> transferring test.NonSendableKlass)
43+
// CHECK-NEXT: #else
44+
// CHECK-NEXT: public func argAndResultEmbeddedInType(_ fn: (test.NonSendableKlass, test.NonSendableKlass, test.NonSendableKlass) -> test.NonSendableKlass)
45+
// CHECK-NEXT: #endif
46+
public func argAndResultEmbeddedInType(_ fn: (NonSendableKlass, transferring NonSendableKlass, NonSendableKlass) -> transferring NonSendableKlass) {}
47+
48+
public class TestInKlass {
49+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
50+
// CHECK-NEXT: public func testKlassArg(_ x: transferring test.NonSendableKlass)
51+
// CHECK-NEXT: #else
52+
// CHECK-NEXT: public func testKlassArg(_ x: test.NonSendableKlass)
53+
// CHECK-NEXT: #endif
54+
public func testKlassArg(_ x: transferring NonSendableKlass) { fatalError() }
55+
56+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
57+
// CHECK-NEXT: public func testKlassResult() -> transferring test.NonSendableKlass
58+
// CHECK-NEXT: #else
59+
// CHECK-NEXT: public func testKlassResult() -> test.NonSendableKlass
60+
// CHECK-NEXT: #endif
61+
public func testKlassResult() -> transferring NonSendableKlass { fatalError() }
62+
63+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
64+
// CHECK-NEXT: public func testKlassArgAndResult(_ x: test.NonSendableKlass, _ y: transferring test.NonSendableKlass, z: test.NonSendableKlass) -> transferring test.NonSendableKlass
65+
// CHECK-NEXT: #else
66+
// CHECK-NEXT: public func testKlassArgAndResult(_ x: test.NonSendableKlass, _ y: test.NonSendableKlass, z: test.NonSendableKlass) -> test.NonSendableKlass
67+
// CHECK-NEXT: #endif
68+
public func testKlassArgAndResult(_ x: NonSendableKlass, _ y: transferring NonSendableKlass, z: NonSendableKlass) -> transferring NonSendableKlass { fatalError() }
69+
}
70+
71+
public struct TestInStruct {
72+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
73+
// CHECK-NEXT: public func testKlassArg(_ x: transferring test.NonSendableKlass)
74+
// CHECK-NEXT: #else
75+
// CHECK-NEXT: public func testKlassArg(_ x: test.NonSendableKlass)
76+
// CHECK-NEXT: #endif
77+
public func testKlassArg(_ x: transferring NonSendableKlass) { fatalError() }
78+
79+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
80+
// CHECK-NEXT: public func testKlassResult() -> transferring test.NonSendableKlass
81+
// CHECK-NEXT: #else
82+
// CHECK-NEXT: public func testKlassResult() -> test.NonSendableKlass
83+
// CHECK-NEXT: #endif
84+
public func testKlassResult() -> transferring NonSendableKlass { fatalError() }
85+
86+
// CHECK-LABEL: #if compiler(>=5.3) && $TransferringArgsAndResults
87+
// CHECK-NEXT: public func testKlassArgAndResult(_ x: test.NonSendableKlass, _ y: transferring test.NonSendableKlass, z: test.NonSendableKlass) -> transferring test.NonSendableKlass
88+
// CHECK-NEXT: #else
89+
// CHECK-NEXT: public func testKlassArgAndResult(_ x: test.NonSendableKlass, _ y: test.NonSendableKlass, z: test.NonSendableKlass) -> test.NonSendableKlass
90+
// CHECK-NEXT: #endif
91+
public func testKlassArgAndResult(_ x: NonSendableKlass, _ y: transferring NonSendableKlass, z: NonSendableKlass) -> transferring NonSendableKlass { fatalError() }
92+
}

0 commit comments

Comments
 (0)