Skip to content

Commit d6e8a9e

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 f576462 commit d6e8a9e

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
@@ -350,7 +350,7 @@ EXPERIMENTAL_FEATURE(FixedArrays, true)
350350
EXPERIMENTAL_FEATURE(GroupActorErrors, true)
351351

352352
// Allow for the 'transferring' keyword to be applied to arguments and results.
353-
EXPERIMENTAL_FEATURE(TransferringArgsAndResults, true)
353+
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(TransferringArgsAndResults, true)
354354

355355
// Allow for `switch` of noncopyable values to be borrowing or consuming.
356356
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();
@@ -3721,7 +3728,7 @@ static void printParameterFlags(ASTPrinter &printer,
37213728
if (!options.excludeAttrKind(TypeAttrKind::NoDerivative) &&
37223729
flags.isNoDerivative())
37233730
printer.printAttrName("@noDerivative ");
3724-
if (flags.isTransferring())
3731+
if (!options.SuppressTransferringArgsAndResults && flags.isTransferring())
37253732
printer.printAttrName("transferring ");
37263733

37273734
switch (flags.getOwnershipSpecifier()) {
@@ -4181,19 +4188,16 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
41814188
}
41824189
}
41834190
}
4184-
{
4185-
auto fnTy = decl->getInterfaceType();
4186-
bool hasTransferring = false;
4187-
if (auto *ft = llvm::dyn_cast_if_present<FunctionType>(fnTy)) {
4188-
if (ft->hasExtInfo())
4189-
hasTransferring = ft->hasTransferringResult();
4190-
} else if (auto *ft =
4191-
llvm::dyn_cast_if_present<GenericFunctionType>(fnTy)) {
4192-
if (ft->hasExtInfo())
4193-
hasTransferring = ft->hasTransferringResult();
4194-
}
4195-
if (hasTransferring)
4191+
4192+
if (!Options.SuppressTransferringArgsAndResults) {
4193+
if (decl->hasTransferringResult()) {
41964194
Printer << "transferring ";
4195+
} else if (auto *ft = llvm::dyn_cast_if_present<AnyFunctionType>(
4196+
decl->getInterfaceType())) {
4197+
if (ft->hasExtInfo() && ft->hasTransferringResult()) {
4198+
Printer << "transferring ";
4199+
}
4200+
}
41974201
}
41984202

41994203
// HACK: When printing result types for funcs with opaque result types,
@@ -6654,7 +6658,8 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
66546658

66556659
Printer << " -> ";
66566660

6657-
if (T->hasExtInfo() && T->hasTransferringResult()) {
6661+
if (!Options.SuppressTransferringArgsAndResults && T->hasExtInfo() &&
6662+
T->hasTransferringResult()) {
66586663
Printer.printKeyword("transferring ", Options);
66596664
}
66606665

lib/AST/FeatureSet.cpp

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

642642
static bool usesFeatureTransferringArgsAndResults(Decl *decl) {
643-
if (auto *pd = dyn_cast<ParamDecl>(decl))
644-
if (pd->isTransferring())
643+
auto functionTypeUsesTransferring = [](Decl *decl) {
644+
return usesTypeMatching(decl, [](Type type) {
645+
auto fnType = type->getAs<AnyFunctionType>();
646+
if (!fnType)
647+
return false;
648+
649+
if (fnType->hasExtInfo() && fnType->hasTransferringResult())
650+
return true;
651+
652+
return llvm::any_of(fnType->getParams(),
653+
[](AnyFunctionType::Param param) {
654+
return param.getParameterFlags().isTransferring();
655+
});
656+
});
657+
};
658+
659+
if (auto *pd = dyn_cast<ParamDecl>(decl)) {
660+
if (pd->isTransferring()) {
645661
return true;
662+
}
663+
664+
if (functionTypeUsesTransferring(pd))
665+
return true;
666+
}
646667

647668
if (auto *fDecl = dyn_cast<FuncDecl>(decl)) {
648-
auto fnTy = fDecl->getInterfaceType();
649-
bool hasTransferring = false;
650-
if (auto *ft = llvm::dyn_cast_if_present<FunctionType>(fnTy)) {
651-
if (ft->hasExtInfo())
652-
hasTransferring = ft->hasTransferringResult();
653-
} else if (auto *ft =
654-
llvm::dyn_cast_if_present<GenericFunctionType>(fnTy)) {
655-
if (ft->hasExtInfo())
656-
hasTransferring = ft->hasTransferringResult();
657-
}
658-
if (hasTransferring)
669+
// First check for param decl results.
670+
if (llvm::any_of(fDecl->getParameters()->getArray(), [](ParamDecl *pd) {
671+
return usesFeatureTransferringArgsAndResults(pd);
672+
}))
673+
return true;
674+
if (functionTypeUsesTransferring(decl))
659675
return true;
660676
}
661677

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)