Skip to content

Commit dc25857

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
1 parent dbc8c4b commit dc25857

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
@@ -356,7 +356,7 @@ EXPERIMENTAL_FEATURE(FixedArrays, true)
356356
EXPERIMENTAL_FEATURE(GroupActorErrors, true)
357357

358358
// Allow for the 'transferring' keyword to be applied to arguments and results.
359-
EXPERIMENTAL_FEATURE(TransferringArgsAndResults, true)
359+
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(TransferringArgsAndResults, true)
360360

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

lib/AST/ASTPrinter.cpp

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3120,6 +3120,13 @@ static void suppressingFeatureOptionalIsolatedParameters(
31203120
action();
31213121
}
31223122

3123+
static void suppressingFeatureTransferringArgsAndResults(
3124+
PrintOptions &options, llvm::function_ref<void()> action) {
3125+
llvm::SaveAndRestore<bool> scope(options.SuppressTransferringArgsAndResults,
3126+
true);
3127+
action();
3128+
}
3129+
31233130
static void suppressingFeatureAssociatedTypeImplements(PrintOptions &options,
31243131
llvm::function_ref<void()> action) {
31253132
unsigned originalExcludeAttrCount = options.ExcludeAttrList.size();
@@ -3691,7 +3698,7 @@ static void printParameterFlags(ASTPrinter &printer,
36913698
if (!options.excludeAttrKind(TypeAttrKind::NoDerivative) &&
36923699
flags.isNoDerivative())
36933700
printer.printAttrName("@noDerivative ");
3694-
if (flags.isTransferring())
3701+
if (!options.SuppressTransferringArgsAndResults && flags.isTransferring())
36953702
printer.printAttrName("transferring ");
36963703

36973704
switch (flags.getOwnershipSpecifier()) {
@@ -4151,19 +4158,16 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
41514158
}
41524159
}
41534160
}
4154-
{
4155-
auto fnTy = decl->getInterfaceType();
4156-
bool hasTransferring = false;
4157-
if (auto *ft = llvm::dyn_cast_if_present<FunctionType>(fnTy)) {
4158-
if (ft->hasExtInfo())
4159-
hasTransferring = ft->hasTransferringResult();
4160-
} else if (auto *ft =
4161-
llvm::dyn_cast_if_present<GenericFunctionType>(fnTy)) {
4162-
if (ft->hasExtInfo())
4163-
hasTransferring = ft->hasTransferringResult();
4164-
}
4165-
if (hasTransferring)
4161+
4162+
if (!Options.SuppressTransferringArgsAndResults) {
4163+
if (decl->hasTransferringResult()) {
41664164
Printer << "transferring ";
4165+
} else if (auto *ft = llvm::dyn_cast_if_present<AnyFunctionType>(
4166+
decl->getInterfaceType())) {
4167+
if (ft->hasExtInfo() && ft->hasTransferringResult()) {
4168+
Printer << "transferring ";
4169+
}
4170+
}
41674171
}
41684172

41694173
// HACK: When printing result types for funcs with opaque result types,
@@ -6624,7 +6628,8 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
66246628

66256629
Printer << " -> ";
66266630

6627-
if (T->hasExtInfo() && T->hasTransferringResult()) {
6631+
if (!Options.SuppressTransferringArgsAndResults && T->hasExtInfo() &&
6632+
T->hasTransferringResult()) {
66286633
Printer.printKeyword("transferring ", Options);
66296634
}
66306635

lib/AST/FeatureSet.cpp

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

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

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

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)