Skip to content

Commit 7d78fcc

Browse files
authored
Merge pull request #81378 from gottesmm/release/6.2-rdar131422332
[6.2][silgen] When emitting a foreign async completion handler for a method, use merge isolation region to tie self and the block storage into the same region.
2 parents c8c035e + 714cc00 commit 7d78fcc

File tree

5 files changed

+171
-13
lines changed

5 files changed

+171
-13
lines changed

lib/SILGen/ResultPlan.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -792,10 +792,9 @@ class ForeignAsyncInitializationPlan final : public ResultPlan {
792792
return std::make_tuple(blockStorage, blockStorageTy, continuationTy);
793793
}
794794

795-
ManagedValue
796-
emitForeignAsyncCompletionHandler(SILGenFunction &SGF,
797-
AbstractionPattern origFormalType,
798-
SILLocation loc) override {
795+
ManagedValue emitForeignAsyncCompletionHandler(
796+
SILGenFunction &SGF, AbstractionPattern origFormalType, ManagedValue self,
797+
SILLocation loc) override {
799798
// Get the current continuation for the task.
800799
bool throws =
801800
calleeTypeInfo.foreign.async->completionHandlerErrorParamIndex()
@@ -845,7 +844,14 @@ class ForeignAsyncInitializationPlan final : public ResultPlan {
845844
SILValue block = SGF.B.createInitBlockStorageHeader(loc, blockStorage,
846845
impRef, SILType::getPrimitiveObjectType(impFnTy),
847846
SGF.getForwardingSubstitutionMap());
848-
847+
848+
// If our block is Sendable, we have lost the connection in between self and
849+
// blockStorage. We need to restore that connection by using a merge
850+
// isolation region.
851+
if (self && block->getType().isSendable(&SGF.F)) {
852+
SGF.B.createMergeIsolationRegion(loc, {self.getValue(), blockStorage});
853+
}
854+
849855
// Wrap it in optional if the callee expects it.
850856
if (handlerIsOptional) {
851857
block = SGF.B.createOptionalSome(loc, block, impTy);
@@ -1091,11 +1097,11 @@ class ForeignErrorInitializationPlan final : public ResultPlan {
10911097
subPlan->gatherIndirectResultAddrs(SGF, loc, outList);
10921098
}
10931099

1094-
ManagedValue
1095-
emitForeignAsyncCompletionHandler(SILGenFunction &SGF,
1096-
AbstractionPattern origFormalType,
1097-
SILLocation loc) override {
1098-
return subPlan->emitForeignAsyncCompletionHandler(SGF, origFormalType, loc);
1100+
ManagedValue emitForeignAsyncCompletionHandler(
1101+
SILGenFunction &SGF, AbstractionPattern origFormalType, ManagedValue self,
1102+
SILLocation loc) override {
1103+
return subPlan->emitForeignAsyncCompletionHandler(SGF, origFormalType, self,
1104+
loc);
10991105
}
11001106

11011107
std::optional<std::pair<ManagedValue, ManagedValue>>

lib/SILGen/ResultPlan.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ class ResultPlan {
6565
return std::nullopt;
6666
}
6767

68-
virtual ManagedValue emitForeignAsyncCompletionHandler(
69-
SILGenFunction &SGF, AbstractionPattern origFormalType, SILLocation loc) {
68+
virtual ManagedValue
69+
emitForeignAsyncCompletionHandler(SILGenFunction &SGF,
70+
AbstractionPattern origFormalType,
71+
ManagedValue self, SILLocation loc) {
7072
return {};
7173
}
7274
};

lib/SILGen/SILGenApply.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5859,9 +5859,16 @@ RValue SILGenFunction::emitApply(
58595859
// we left during the first pass.
58605860
auto &completionArgSlot = const_cast<ManagedValue &>(args[completionIndex]);
58615861

5862+
// We have already lowered foreign self/moved it into position at this
5863+
// point, so we know that self will be back.
5864+
ManagedValue self;
5865+
if (substFnType->hasSelfParam()) {
5866+
self = args.back();
5867+
}
5868+
58625869
auto origFormalType = *calleeTypeInfo.origFormalType;
58635870
completionArgSlot = resultPlan->emitForeignAsyncCompletionHandler(
5864-
*this, origFormalType, loc);
5871+
*this, origFormalType, self, loc);
58655872
}
58665873
if (auto foreignError = calleeTypeInfo.foreign.error) {
58675874
unsigned errorParamIndex =
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
#import <Foundation/Foundation.h>
3+
4+
NS_ASSUME_NONNULL_BEGIN
5+
6+
@interface ObjCObject : NSObject
7+
8+
- (void)loadObjectsWithCompletionHandler:
9+
(void (^NS_SWIFT_SENDABLE)(NSArray<NSObject *> *_Nullable,
10+
NSError *_Nullable))completionHandler;
11+
12+
- (void)loadObjects2WithCompletionHandler:
13+
(void (^)(NSArray<NSObject *> *_Nullable,
14+
NSError *_Nullable))completionHandler;
15+
16+
@end
17+
18+
NS_ASSUME_NONNULL_END
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// RUN: %target-swift-frontend %s -import-objc-header %S/Inputs/regionbasedisolation.h -verify -c -swift-version 6
2+
// RUN: %target-swift-frontend %s -import-objc-header %S/Inputs/regionbasedisolation.h -emit-silgen -swift-version 6 | %FileCheck %s
3+
4+
// REQUIRES: objc_interop
5+
6+
extension ObjCObject {
7+
// CHECK-LABEL: sil hidden [ossa] @$sSo10ObjCObjectC20regionbasedisolationE11sendObjectsSaySo8NSObjectCGyYaKF : $@convention(method) @async (@guaranteed ObjCObject) -> (@sil_sending @owned Array<NSObject>, @error any Error) {
8+
// CHECK: bb0([[SELF:%.*]] : @guaranteed $ObjCObject):
9+
10+
// Our result.
11+
// CHECK: [[RESULT:%.*]] = alloc_stack $Array<NSObject>
12+
13+
// Our method.
14+
// CHECK: [[METHOD:%.*]] = objc_method [[SELF]], #ObjCObject.loadObjects!foreign : (ObjCObject) -> () async throws -> [NSObject], $@convention(objc_method) (@convention(block) @Sendable (Optional<NSArray>, Optional<NSError>) -> (), ObjCObject) -> ()
15+
16+
// Begin setting up the unsafe continuation for our method. Importantly note
17+
// that [[UNSAFE_CONT]] is Sendable, so we lose any connection from the
18+
// continuation addr to any uses of the UnsafeContinuation.
19+
//
20+
// CHECK: [[CONT:%.*]] = get_async_continuation_addr [throws] Array<NSObject>, [[RESULT]]
21+
// CHECK: [[UNSAFE_CONT:%.*]] = struct $UnsafeContinuation<Array<NSObject>, any Error> ([[CONT]])
22+
23+
// Then prepare the block storage.
24+
// CHECK: [[BLOCK_STORAGE:%.*]] = alloc_stack $@block_storage Any
25+
// CHECK: [[PROJECT_BLOCK_STORAGE:%.*]] = project_block_storage [[BLOCK_STORAGE]]
26+
// CHECK: [[EXISTENTIAL_BLOCK_STORAGE:%.*]] = init_existential_addr [[PROJECT_BLOCK_STORAGE]]
27+
28+
// Then create a checked continuation from the unsafe continuation.
29+
//
30+
// CHECK: [[CREATE_CHECKED_CONT:%.*]] = function_ref @$ss34_createCheckedThrowingContinuationyScCyxs5Error_pGSccyxsAB_pGnlF : $@convention(thin) <τ_0_0> (UnsafeContinuation<τ_0_0, any Error>) -> @out CheckedContinuation<τ_0_0, any Error>
31+
// CHECK: [[CHECKED_CONT:%.*]] = alloc_stack $CheckedContinuation<Array<NSObject>, any Error>
32+
// CHECK: apply [[CREATE_CHECKED_CONT]]<Array<NSObject>>([[CHECKED_CONT]], [[UNSAFE_CONT]])
33+
34+
// Then place the checked continuation into the block storage and perform a
35+
// merge_isolation_region in between the block storage and the result to
36+
// propagate that the result and the block storage are apart of the same
37+
// region despite the UnsafeContinuation blocking the relation in between
38+
// them.
39+
//
40+
// CHECK: copy_addr [take] [[CHECKED_CONT]] to [init] [[EXISTENTIAL_BLOCK_STORAGE]]
41+
// CHECK: merge_isolation_region [[BLOCK_STORAGE]], [[RESULT]]
42+
43+
// Then create the actual block. NOTE: Since the block is @Sendable, the block
44+
// does not propagate regions.
45+
//
46+
// CHECK: [[COMPLETION_HANDLER_BLOCK:%.*]] = function_ref @$sSo7NSArrayCSgSo7NSErrorCSgIeyBhyy_SaySo8NSObjectCGTz_ : $@convention(c) @Sendable (@inout_aliasable @block_storage Any, Optional<NSArray>, Optional<NSError>) -> ()
47+
// CHECK: [[COMPLETION_BLOCK:%.*]] = init_block_storage_header [[BLOCK_STORAGE]], invoke [[COMPLETION_HANDLER_BLOCK]]
48+
//
49+
// Since the block is @Sendable, it does not propagate the connection in
50+
// between self and the block storage when we just call the method. Thus we
51+
// need to perform a merge_isolation_region to communicate that the block
52+
// storage and self are part of the same region.
53+
//
54+
// CHECK: merge_isolation_region [[SELF]], [[BLOCK_STORAGE]]
55+
//
56+
// Then call the method.
57+
// CHECK: apply [[METHOD]]([[COMPLETION_BLOCK]], [[SELF]])
58+
// CHECK: } // end sil function '$sSo10ObjCObjectC20regionbasedisolationE11sendObjectsSaySo8NSObjectCGyYaKF'
59+
60+
func sendObjects() async throws -> sending [NSObject] {
61+
// We emit an error since loadObjects just returns an [NSObject], not a
62+
// sending [NSObject].
63+
try await loadObjects()
64+
} // expected-error {{task or actor isolated value cannot be sent}}
65+
66+
// Check if we do not mark the block as NS_SWIFT_SENDABLE
67+
//
68+
// CHECK-LABEL: sil hidden [ossa] @$sSo10ObjCObjectC20regionbasedisolationE12sendObjects2SaySo8NSObjectCGyYaKF : $@convention(method) @async (@guaranteed ObjCObject) -> (@sil_sending @owned Array<NSObject>, @error any Error) {
69+
// CHECK: bb0([[SELF:%.*]] : @guaranteed $ObjCObject):
70+
71+
// Our result.
72+
// CHECK: [[RESULT:%.*]] = alloc_stack $Array<NSObject>
73+
74+
// Our method.
75+
// CHECK: [[METHOD:%.*]] = objc_method [[SELF]], #ObjCObject.loadObjects2!foreign : (ObjCObject) -> () async throws -> [NSObject], $@convention(objc_method) (@convention(block) @Sendable (Optional<NSArray>, Optional<NSError>) -> (), ObjCObject) -> ()
76+
77+
// Begin setting up the unsafe continuation for our method. Importantly note
78+
// that [[UNSAFE_CONT]] is Sendable, so we lose any connection from the
79+
// continuation addr to any uses of the UnsafeContinuation.
80+
//
81+
// CHECK: [[CONT:%.*]] = get_async_continuation_addr [throws] Array<NSObject>, [[RESULT]]
82+
// CHECK: [[UNSAFE_CONT:%.*]] = struct $UnsafeContinuation<Array<NSObject>, any Error> ([[CONT]])
83+
84+
// Then prepare the block storage.
85+
// CHECK: [[BLOCK_STORAGE:%.*]] = alloc_stack $@block_storage Any
86+
// CHECK: [[PROJECT_BLOCK_STORAGE:%.*]] = project_block_storage [[BLOCK_STORAGE]]
87+
// CHECK: [[EXISTENTIAL_BLOCK_STORAGE:%.*]] = init_existential_addr [[PROJECT_BLOCK_STORAGE]]
88+
89+
// Then create a checked continuation from the unsafe continuation.
90+
//
91+
// CHECK: [[CREATE_CHECKED_CONT:%.*]] = function_ref @$ss34_createCheckedThrowingContinuationyScCyxs5Error_pGSccyxsAB_pGnlF : $@convention(thin) <τ_0_0> (UnsafeContinuation<τ_0_0, any Error>) -> @out CheckedContinuation<τ_0_0, any Error>
92+
// CHECK: [[CHECKED_CONT:%.*]] = alloc_stack $CheckedContinuation<Array<NSObject>, any Error>
93+
// CHECK: apply [[CREATE_CHECKED_CONT]]<Array<NSObject>>([[CHECKED_CONT]], [[UNSAFE_CONT]])
94+
95+
// Then place the checked continuation into the block storage and perform a
96+
// merge_isolation_region in between the block storage and the result to
97+
// propagate that the result and the block storage are apart of the same
98+
// region despite the UnsafeContinuation blocking the relation in between
99+
// them.
100+
//
101+
// CHECK: copy_addr [take] [[CHECKED_CONT]] to [init] [[EXISTENTIAL_BLOCK_STORAGE]]
102+
// CHECK: merge_isolation_region [[BLOCK_STORAGE]], [[RESULT]]
103+
104+
// Then create the actual block. NOTE: Since the block is @Sendable, the block
105+
// does not propagate regions.
106+
//
107+
// CHECK: [[COMPLETION_HANDLER_BLOCK:%.*]] = function_ref @$sSo7NSArrayCSgSo7NSErrorCSgIeyBhyy_SaySo8NSObjectCGTz_ : $@convention(c) @Sendable (@inout_aliasable @block_storage Any, Optional<NSArray>, Optional<NSError>) -> ()
108+
// CHECK: [[COMPLETION_BLOCK:%.*]] = init_block_storage_header [[BLOCK_STORAGE]], invoke [[COMPLETION_HANDLER_BLOCK]]
109+
//
110+
// Since the block is @Sendable, it does not propagate the connection in
111+
// between self and the block storage when we just call the method. Thus we
112+
// need to perform a merge_isolation_region to communicate that the block
113+
// storage and self are part of the same region.
114+
//
115+
// CHECK: merge_isolation_region [[SELF]], [[BLOCK_STORAGE]]
116+
//
117+
// Then call the method.
118+
// CHECK: apply [[METHOD]]([[COMPLETION_BLOCK]], [[SELF]])
119+
// CHECK: } // end sil function '$sSo10ObjCObjectC20regionbasedisolationE12sendObjects2SaySo8NSObjectCGyYaKF'
120+
func sendObjects2() async throws -> sending [NSObject] {
121+
// We emit an error since loadObjects just returns an [NSObject], not a
122+
// sending [NSObject].
123+
try await loadObjects2()
124+
} // expected-error {{task or actor isolated value cannot be sent}}
125+
}

0 commit comments

Comments
 (0)