Skip to content

Commit 3bdeb57

Browse files
authored
Merge pull request #71940 from gottesmm/layer-of-onion
[region-isolation] Transferring results shouldn't have the following error emitted: "non-sendable type 'NonSendableKlass' returned by implicitly asynchronous call to main actor-isolated function cannot cross actor boundary"
2 parents 244717b + bd6a7bf commit 3bdeb57

10 files changed

+111
-18
lines changed

lib/AST/ASTDumper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4309,6 +4309,7 @@ namespace {
43094309
printFlag(T->isSendable(), "Sendable");
43104310
printFlag(T->isAsync(), "async");
43114311
printFlag(T->isThrowing(), "throws");
4312+
printFlag(T->hasTransferringResult(), "transferring_result");
43124313
}
43134314
if (Type globalActor = T->getGlobalActor()) {
43144315
printFieldQuoted(globalActor.getString(), "global_actor");

lib/AST/ASTPrinter.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3809,7 +3809,20 @@ static bool usesFeatureTransferringArgsAndResults(Decl *decl) {
38093809
if (pd->isTransferring())
38103810
return true;
38113811

3812-
// TODO: Results.
3812+
if (auto *fDecl = dyn_cast<FuncDecl>(decl)) {
3813+
auto fnTy = fDecl->getInterfaceType();
3814+
bool hasTransferring = false;
3815+
if (auto *ft = llvm::dyn_cast_if_present<FunctionType>(fnTy)) {
3816+
if (ft->hasExtInfo())
3817+
hasTransferring = ft->hasTransferringResult();
3818+
} else if (auto *ft =
3819+
llvm::dyn_cast_if_present<GenericFunctionType>(fnTy)) {
3820+
if (ft->hasExtInfo())
3821+
hasTransferring = ft->hasTransferringResult();
3822+
}
3823+
if (hasTransferring)
3824+
return true;
3825+
}
38133826

38143827
return false;
38153828
}
@@ -7432,7 +7445,7 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
74327445
Printer << " -> ";
74337446

74347447
if (T->hasExtInfo() && T->hasTransferringResult()) {
7435-
Printer.printKeyword("transferring", Options);
7448+
Printer.printKeyword("transferring ", Options);
74367449
}
74377450

74387451
if (T->hasLifetimeDependenceInfo()) {

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -417,13 +417,19 @@ static bool isProjectedFromAggregate(SILValue value) {
417417
return visitor.isMerge;
418418
}
419419

420-
static PartialApplyInst *findAsyncLetPartialApplyFromStart(BuiltinInst *bi) {
420+
namespace {
421+
using AsyncLetSourceValue =
422+
llvm::PointerUnion<PartialApplyInst *, ThinToThickFunctionInst *>;
423+
} // namespace
424+
425+
static std::optional<AsyncLetSourceValue>
426+
findAsyncLetPartialApplyFromStart(BuiltinInst *bi) {
421427
// If our operand is Sendable then we want to return nullptr. We only want to
422428
// return a value if we are not
423429
SILValue value = bi->getOperand(1);
424430
auto fType = value->getType().castTo<SILFunctionType>();
425431
if (fType->isSendable())
426-
return nullptr;
432+
return {};
427433

428434
SILValue temp = value;
429435
while (true) {
@@ -436,10 +442,15 @@ static PartialApplyInst *findAsyncLetPartialApplyFromStart(BuiltinInst *bi) {
436442
value = temp;
437443
}
438444

439-
return cast<PartialApplyInst>(value);
445+
// We can also get a thin_to_thick_function here if we do not capture
446+
// anything. In such a case, we just do not process the partial apply get
447+
if (auto *pai = dyn_cast<PartialApplyInst>(value))
448+
return {{pai}};
449+
return {{cast<ThinToThickFunctionInst>(value)}};
440450
}
441451

442-
static PartialApplyInst *findAsyncLetPartialApplyFromGet(ApplyInst *ai) {
452+
static std::optional<AsyncLetSourceValue>
453+
findAsyncLetPartialApplyFromGet(ApplyInst *ai) {
443454
auto *bi = cast<BuiltinInst>(FullApplySite(ai).getArgument(0));
444455
assert(*bi->getBuiltinKind() ==
445456
BuiltinValueKind::StartAsyncLetWithLocalBuffer);
@@ -1552,11 +1563,18 @@ class PartitionOpTranslator {
15521563
}
15531564

15541565
void translateAsyncLetGet(ApplyInst *ai) {
1555-
auto *pai = findAsyncLetPartialApplyFromGet(ai);
1566+
auto source = findAsyncLetPartialApplyFromGet(ai);
1567+
assert(source.has_value());
1568+
1569+
// If we didn't find a partial_apply, then we must have had a
1570+
// thin_to_thick_function meaning we did not capture anything.
1571+
if (source->is<ThinToThickFunctionInst *>())
1572+
return;
15561573

15571574
// We should always be able to derive a partial_apply since we pattern
15581575
// matched against the actual function call to swift_asyncLet_get in our
15591576
// caller.
1577+
auto *pai = source->get<PartialApplyInst *>();
15601578
assert(pai && "AsyncLet Get should always have a derivable partial_apply");
15611579

15621580
ApplySite applySite(pai);
@@ -2965,6 +2983,11 @@ static bool canComputeRegionsForFunction(SILFunction *fn) {
29652983
if (!fn->getASTContext().LangOpts.hasFeature(Feature::RegionBasedIsolation))
29662984
return false;
29672985

2986+
assert(fn->getASTContext().LangOpts.StrictConcurrencyLevel ==
2987+
StrictConcurrency::Complete &&
2988+
"Need strict concurrency to be enabled for RegionBasedIsolation to be "
2989+
"enabled as well");
2990+
29682991
// If this function does not correspond to a syntactic declContext and it
29692992
// doesn't have a parent module, don't check it since we cannot check if a
29702993
// type is sendable.

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3522,8 +3522,11 @@ namespace {
35223522
diagnoseApplyArgSendability(apply, getDeclContext());
35233523
}
35243524

3525-
// Check for sendability of the result type.
3526-
if (diagnoseNonSendableTypes(
3525+
// Check for sendability of the result type if we do not have a
3526+
// transferring result.
3527+
if ((!ctx.LangOpts.hasFeature(Feature::TransferringArgsAndResults) ||
3528+
!fnType->hasTransferringResult()) &&
3529+
diagnoseNonSendableTypes(
35273530
fnType->getResult(), getDeclContext(),
35283531
/*inDerivedConformance*/Type(),
35293532
apply->getLoc(),

test/Concurrency/transfernonsendable.sil

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ case none
7474
case some(T)
7575
}
7676

77+
sil @swift_asyncLet_get : $@convention(thin) @async (Builtin.RawPointer, Builtin.RawPointer) -> ()
78+
sil @swift_asyncLet_finish : $@convention(thin) @async (Builtin.RawPointer, Builtin.RawPointer) -> ()
79+
7780
/////////////////
7881
// MARK: Tests //
7982
/////////////////
@@ -144,4 +147,31 @@ bb0:
144147

145148
%9999 = tuple ()
146149
return %9999 : $()
150+
}
151+
152+
sil @implicitClosure : $@convention(thin) @Sendable @async @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass>
153+
154+
sil [ossa] @asyncLetWithThinToThickFunction : $@convention(thin) @async () -> () {
155+
bb0:
156+
%0 = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
157+
hop_to_executor %0 : $Optional<Builtin.Executor>
158+
%2 = alloc_stack $NonSendableKlass
159+
%3 = address_to_pointer %2 : $*NonSendableKlass to $Builtin.RawPointer
160+
%4 = enum $Optional<Builtin.RawPointer>, #Optional.none!enumelt
161+
%5 = function_ref @implicitClosure : $@convention(thin) @Sendable @async @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass>
162+
%6 = convert_function %5 : $@convention(thin) @Sendable @async @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass> to $@convention(thin) @async @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass>
163+
%7 = thin_to_thick_function %6 : $@convention(thin) @async @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass> to $@noescape @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass>
164+
%8 = builtin "startAsyncLetWithLocalBuffer"<NonSendableKlass>(%4 : $Optional<Builtin.RawPointer>, %7 : $@noescape @async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <NonSendableKlass>, %3 : $Builtin.RawPointer) : $Builtin.RawPointer
165+
%9 = function_ref @swift_asyncLet_get : $@convention(thin) @async (Builtin.RawPointer, Builtin.RawPointer) -> ()
166+
%10 = apply %9(%8, %3) : $@convention(thin) @async (Builtin.RawPointer, Builtin.RawPointer) -> ()
167+
hop_to_executor %0 : $Optional<Builtin.Executor>
168+
%12 = load [copy] %2 : $*NonSendableKlass
169+
destroy_value %12 : $NonSendableKlass
170+
%14 = function_ref @swift_asyncLet_finish : $@convention(thin) @async (Builtin.RawPointer, Builtin.RawPointer) -> ()
171+
%15 = apply %14(%8, %3) : $@convention(thin) @async (Builtin.RawPointer, Builtin.RawPointer) -> ()
172+
hop_to_executor %0 : $Optional<Builtin.Executor>
173+
%17 = builtin "endAsyncLetLifetime"(%8 : $Builtin.RawPointer) : $()
174+
dealloc_stack %2 : $*NonSendableKlass
175+
%19 = tuple ()
176+
return %19 : $()
147177
}

test/Concurrency/transfernonsendable_asynclet.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
////////////////////////
1010

1111
/// Classes are always non-sendable, so this is non-sendable
12-
class NonSendableKlass { // expected-complete-note 92{{}}
12+
class NonSendableKlass { // expected-complete-note 94{{}}
13+
// expected-tns-note @-1 {{}}
1314
var field: NonSendableKlass? = nil
1415
var field2: NonSendableKlass? = nil
1516

@@ -38,6 +39,7 @@ func useInOut<T>(_ x: inout T) {}
3839
func useValue<T>(_ x: T) -> T { x }
3940
func useValueWrapInOptional<T>(_ x: T) -> T? { x }
4041

42+
@MainActor func returnValueFromMain<T>() async -> T { fatalError() }
4143
@MainActor func transferToMain<T>(_ t: T) async {}
4244
@MainActor func transferToMainInt<T>(_ t: T) async -> Int { 5 }
4345
@MainActor func transferToMainIntOpt<T>(_ t: T) async -> Int? { 5 }
@@ -735,3 +737,15 @@ func asyncLet_Let_NormalUse_Simple2() async {
735737
let _ = await y
736738
useValue(x)
737739
}
740+
741+
func asyncLetWithoutCapture() async {
742+
// Make sure that we setup y correctly as fresh.
743+
//
744+
// NOTE: Error below will go away in next commit.
745+
async let x: NonSendableKlass = await returnValueFromMain()
746+
// expected-warning @-1 {{non-sendable type 'NonSendableKlass' returned by implicitly asynchronous call to main actor-isolated function cannot cross actor boundary}}
747+
let y = await x
748+
await transferToMain(y) // expected-tns-warning {{transferring value of non-Sendable type 'NonSendableKlass' from nonisolated context to main actor-isolated context}}
749+
// expected-complete-warning @-1 {{passing argument of non-sendable type 'NonSendableKlass' into main actor-isolated context may introduce data races}}
750+
useValue(y) // expected-tns-note {{access here could race}}
751+
}

test/Concurrency/transfernonsendable_strong_transferring_params.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -emit-sil -disable-availability-checking -enable-experimental-feature TransferringArgsAndResults -verify -enable-experimental-feature RegionBasedIsolation %s -o /dev/null
1+
// RUN: %target-swift-frontend -emit-sil -parse-as-library -disable-availability-checking -strict-concurrency=complete -enable-experimental-feature TransferringArgsAndResults -verify -enable-experimental-feature RegionBasedIsolation %s -o /dev/null
22

33
// REQUIRES: asserts
44

@@ -174,8 +174,7 @@ actor MyActor {
174174
}
175175

176176
@MainActor func canAssignTransferringIntoGlobalActor3(_ x: transferring Klass) async {
177-
await transferToCustom(globalKlass) // expected-warning {{transferring 'globalKlass' may cause a race}}
178-
// expected-note @-1 {{transferring main actor-isolated 'globalKlass' to global actor 'CustomActor'-isolated callee could cause races between global actor 'CustomActor'-isolated and main actor-isolated uses}}
177+
await transferToCustom(globalKlass) // expected-warning {{task isolated value of type 'Klass' transferred to global actor 'CustomActor'-isolated context}}
179178
}
180179

181180
func canTransferAssigningIntoLocal(_ x: transferring Klass) async {

test/Concurrency/transfernonsendable_strong_transferring_results.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -emit-sil -disable-experimental-parser-round-trip -disable-availability-checking -enable-experimental-feature TransferringArgsAndResults -verify -enable-experimental-feature RegionBasedIsolation %s -o /dev/null
1+
// RUN: %target-swift-frontend -emit-sil -parse-as-library -strict-concurrency=complete -disable-experimental-parser-round-trip -disable-availability-checking -enable-experimental-feature TransferringArgsAndResults -verify -enable-experimental-feature RegionBasedIsolation %s -o /dev/null
22

33
// REQUIRES: concurrency
44
// REQUIRES: asserts
@@ -49,6 +49,8 @@ func transferAsyncResultWithTransferringArg(_ x: transferring NonSendableKlass)
4949
func transferAsyncResultWithTransferringArg2(_ x: transferring NonSendableKlass, _ y: NonSendableKlass) async -> transferring NonSendableKlass { NonSendableKlass() }
5050
func transferAsyncResultWithTransferringArg2Throwing(_ x: transferring NonSendableKlass, _ y: NonSendableKlass) async throws -> transferring NonSendableKlass { NonSendableKlass() }
5151

52+
@MainActor func transferAsyncResultMainActor() async -> transferring NonSendableKlass { NonSendableKlass() }
53+
5254
@MainActor var globalNonSendableKlass = NonSendableKlass()
5355

5456
/////////////////
@@ -106,3 +108,11 @@ func transferReturnArg(_ x: NonSendableKlass) -> transferring NonSendableKlass {
106108
func transferReturnArgTuple(_ x: transferring NonSendableKlass) -> transferring (NonSendableKlass, NonSendableKlass) {
107109
return (x, x)
108110
}
111+
112+
func useTransferredResultMainActor() async {
113+
let _ = await transferAsyncResultMainActor()
114+
}
115+
116+
func useTransferredResult() async {
117+
let _ = await transferAsyncResult()
118+
}

test/Parse/transferring.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-typecheck-verify-swift -disable-experimental-parser-round-trip -disable-availability-checking -enable-experimental-feature TransferringArgsAndResults
1+
// RUN: %target-typecheck-verify-swift -disable-experimental-parser-round-trip -disable-availability-checking -enable-experimental-feature TransferringArgsAndResults -strict-concurrency=complete -enable-experimental-feature RegionBasedIsolation
22

33
// REQUIRES: asserts
44

test/Serialization/transferring.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-frontend -enable-experimental-feature RegionBasedIsolation -enable-experimental-feature TransferringArgsAndResults -disable-experimental-parser-round-trip -module-name transferring_test -emit-module -o %t/transferring_test.swiftmodule %S/Inputs/transferring.swift
3-
// RUN: %target-swift-frontend -enable-experimental-feature RegionBasedIsolation -enable-experimental-feature TransferringArgsAndResults -disable-experimental-parser-round-trip -emit-sil -I %t %s | %FileCheck %s
4-
// RUN: %target-sil-opt -enable-experimental-feature RegionBasedIsolation -enable-experimental-feature TransferringArgsAndResults %t/transferring_test.swiftmodule | %FileCheck -check-prefix=AST %s
2+
// RUN: %target-swift-frontend -strict-concurrency=complete -enable-experimental-feature RegionBasedIsolation -enable-experimental-feature TransferringArgsAndResults -disable-experimental-parser-round-trip -module-name transferring_test -emit-module -o %t/transferring_test.swiftmodule %S/Inputs/transferring.swift
3+
// RUN: %target-swift-frontend -strict-concurrency=complete -enable-experimental-feature RegionBasedIsolation -enable-experimental-feature TransferringArgsAndResults -disable-experimental-parser-round-trip -emit-sil -I %t %s | %FileCheck %s
4+
// RUN: %target-sil-opt -strict-concurrency=complete -enable-experimental-feature RegionBasedIsolation -enable-experimental-feature TransferringArgsAndResults %t/transferring_test.swiftmodule | %FileCheck -check-prefix=AST %s
55

66
// REQUIRES: concurrency
77
// REQUIRES: asserts

0 commit comments

Comments
 (0)