Skip to content

Commit c39901d

Browse files
[SILGen] Handled foreign funcs with async, error, and more. (#39960)
In 1ae317d, all the machinery was added to enable calling ObjC functions that have both async and error conventions. The handling that was added for matching up arguments and parameters, however, failed to handle functions that had both foreign async and foreign error and other arguments. Here, that handling is fixed. The fix is in four parts: - Reverting to a single call to CallSite::emit to emit the formal params. At this point, the foreign async and/or error params will be claimed as well. - Separately counting the async and error parameters in ParamLowering::claimParams to ensure we get the right parameter slices. - Letting ArgEmitter::maybeEmitForeignArgument look for an error parameter even if it already found an async parameter. - Letting ArgEmitter::maybeEmitForeignArgument keep looking for foreign arguments after it finds one. rdar://80704984
1 parent 61ad514 commit c39901d

File tree

4 files changed

+163
-33
lines changed

4 files changed

+163
-33
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3270,26 +3270,33 @@ class ArgEmitter {
32703270
}
32713271

32723272
void maybeEmitForeignArgument() {
3273-
if (Foreign.async
3274-
&& Foreign.async->completionHandlerParamIndex() == Args.size()) {
3275-
SILParameterInfo param = claimNextParameters(1).front();
3276-
(void)param;
3277-
3278-
// Leave a placeholder in the position. We'll fill this in with a block
3279-
// capturing the current continuation right before we invoke the
3280-
// function.
3281-
// (We can't do this immediately, because evaluating other arguments
3282-
// may require suspending the async task, which is not allowed while its
3283-
// continuation is active.)
3284-
Args.push_back(ManagedValue::forInContext());
3285-
} else if (Foreign.error
3286-
&& Foreign.error->getErrorParameterIndex() == Args.size()) {
3287-
SILParameterInfo param = claimNextParameters(1).front();
3288-
assert(param.getConvention() == ParameterConvention::Direct_Unowned);
3289-
(void) param;
3290-
3291-
// Leave a placeholder in the position.
3292-
Args.push_back(ManagedValue::forInContext());
3273+
bool keepGoing = true;
3274+
while (keepGoing) {
3275+
keepGoing = false;
3276+
if (Foreign.async &&
3277+
Foreign.async->completionHandlerParamIndex() == Args.size()) {
3278+
SILParameterInfo param = claimNextParameters(1).front();
3279+
(void)param;
3280+
3281+
// Leave a placeholder in the position. We'll fill this in with a block
3282+
// capturing the current continuation right before we invoke the
3283+
// function.
3284+
// (We can't do this immediately, because evaluating other arguments
3285+
// may require suspending the async task, which is not allowed while its
3286+
// continuation is active.)
3287+
Args.push_back(ManagedValue::forInContext());
3288+
keepGoing = true;
3289+
}
3290+
if (Foreign.error &&
3291+
Foreign.error->getErrorParameterIndex() == Args.size()) {
3292+
SILParameterInfo param = claimNextParameters(1).front();
3293+
assert(param.getConvention() == ParameterConvention::Direct_Unowned);
3294+
(void)param;
3295+
3296+
// Leave a placeholder in the position.
3297+
Args.push_back(ManagedValue::forInContext());
3298+
keepGoing = true;
3299+
}
32933300
}
32943301
}
32953302

@@ -3515,7 +3522,9 @@ struct ParamLowering {
35153522
}
35163523
}
35173524

3518-
if (foreign.error || foreign.async)
3525+
if (foreign.error)
3526+
++count;
3527+
if (foreign.async)
35193528
++count;
35203529

35213530
if (foreign.self.isImportAsMember()) {
@@ -4172,18 +4181,12 @@ ApplyOptions CallEmission::emitArgumentsForNormalApply(
41724181

41734182
args.push_back({});
41744183

4175-
if (!foreign.async || (foreign.async && foreign.error)) {
4176-
// Claim the foreign error argument(s) with the method formal params.
4177-
auto siteForeignError = CalleeTypeInfo::ForeignInfo{foreign.error, {}, {}};
4178-
std::move(*callSite).emit(SGF, origFormalType, substFnType, paramLowering,
4179-
args.back(), delayedArgs, siteForeignError);
4180-
}
4181-
if (foreign.async) {
4182-
// Claim the foreign async argument(s) with the method formal params.
4183-
auto siteForeignAsync = CalleeTypeInfo::ForeignInfo{{}, foreign.async, {}};
4184-
std::move(*callSite).emit(SGF, origFormalType, substFnType, paramLowering,
4185-
args.back(), delayedArgs, siteForeignAsync);
4186-
}
4184+
// Claim the foreign error and/or async arguments when claiming the formal
4185+
// params.
4186+
auto siteForeignError = CalleeTypeInfo::ForeignInfo{foreign.error, foreign.async, {}};
4187+
// Claim the method formal params.
4188+
std::move(*callSite).emit(SGF, origFormalType, substFnType, paramLowering,
4189+
args.back(), delayedArgs, siteForeignError);
41874190
}
41884191

41894192
uncurriedLoc = callSite->Loc;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <Foundation/Foundation.h>
2+
3+
#pragma clang assume_nonnull begin
4+
5+
typedef void (^CompletionHandler)(int status, NSInteger bytesTransferred);
6+
7+
@interface PFXObject : NSObject
8+
- (BOOL)enqueueFailingRequestWithData:(nullable NSMutableData *)data
9+
error:(NSError *_Nullable *)error
10+
completionHandler:
11+
(nullable CompletionHandler)completionHandler;
12+
- (BOOL)enqueuePassingRequestWithData:(nullable NSMutableData *)data
13+
error:(NSError *_Nullable *)error
14+
completionHandler:
15+
(nullable CompletionHandler)completionHandler;
16+
- (BOOL)enqueueFailingRequestWithData:(nullable NSMutableData *)data
17+
completionTimeout:(NSTimeInterval)completionTimeout
18+
error:(NSError *_Nullable *)error
19+
completionHandler:
20+
(nullable CompletionHandler)completionHandler;
21+
- (BOOL)enqueuePassingRequestWithData:(nullable NSMutableData *)data
22+
completionTimeout:(NSTimeInterval)completionTimeout
23+
error:(NSError *_Nullable *)error
24+
completionHandler:
25+
(nullable CompletionHandler)completionHandler;
26+
@end
27+
28+
#pragma clang assume_nonnull end
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include "rdar80704984_3.h"
2+
3+
#pragma clang assume_nonnull begin
4+
5+
@implementation PFXObject
6+
7+
- (BOOL)enqueueFailingRequestWithData:(nullable NSMutableData *)data
8+
error:(NSError *_Nullable *)error
9+
completionHandler:
10+
(nullable CompletionHandler)completionHandler {
11+
*error = [[NSError alloc] initWithDomain:@"d" code:1 userInfo:nil];
12+
return NO;
13+
}
14+
- (BOOL)enqueuePassingRequestWithData:(nullable NSMutableData *)data
15+
error:(NSError *_Nullable *)error
16+
completionHandler:
17+
(nullable CompletionHandler)completionHandler {
18+
dispatch_async(dispatch_get_main_queue(), ^{
19+
completionHandler(0, 2);
20+
});
21+
return YES;
22+
}
23+
24+
- (BOOL)enqueueFailingRequestWithData:(nullable NSMutableData *)data
25+
completionTimeout:(NSTimeInterval)completionTimeout
26+
error:(NSError *_Nullable *)error
27+
completionHandler:
28+
(nullable CompletionHandler)completionHandler {
29+
*error = [[NSError alloc] initWithDomain:@"d" code:2 userInfo:nil];
30+
return NO;
31+
}
32+
- (BOOL)enqueuePassingRequestWithData:(nullable NSMutableData *)data
33+
completionTimeout:(NSTimeInterval)completionTimeout
34+
error:(NSError *_Nullable *)error
35+
completionHandler:
36+
(nullable CompletionHandler)completionHandler {
37+
dispatch_async(dispatch_get_main_queue(), ^{
38+
completionHandler(0, 3);
39+
});
40+
return YES;
41+
}
42+
43+
@end
44+
45+
#pragma clang assume_nonnull end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-clang %S/Inputs/rdar80704984_3.m -I %S/Inputs -c -o %t/rdar80704984_3.o
3+
// RUN: %target-build-swift -Xfrontend -disable-availability-checking -import-objc-header %S/Inputs/rdar80704984_3.h -Xlinker %t/rdar80704984_3.o -parse-as-library %s -o %t/a.out
4+
// RUN: %target-codesign %t/a.out
5+
// RUN: %target-run %t/a.out | %FileCheck %s
6+
7+
// REQUIRES: executable_test
8+
// REQUIRES: objc_interop
9+
10+
// rdar://82123254
11+
// UNSUPPORTED: use_os_stdlib
12+
// UNSUPPORTED: back_deployment_runtime
13+
14+
func run1(on object: PFXObject) async throws {
15+
do {
16+
_ = try await object.enqueueFailingRequest(with: nil)
17+
}
18+
catch let error {
19+
// CHECK: Domain=d Code=1
20+
print(error)
21+
}
22+
}
23+
24+
func run2(on object: PFXObject) async throws {
25+
// CHECK: (0, 2)
26+
print(try await object.enqueuePassingRequest(with: nil))
27+
28+
}
29+
30+
func run3(on object: PFXObject) async throws {
31+
do {
32+
_ = try await object.enqueueFailingRequest(with: nil, completionTimeout: 57.0)
33+
}
34+
catch let error {
35+
// CHECK: Domain=d Code=2
36+
print(error)
37+
}
38+
}
39+
40+
func run4(on object: PFXObject) async throws {
41+
// CHECK: (0, 3)
42+
print(try await object.enqueuePassingRequest(with: nil, completionTimeout: 57.0))
43+
44+
}
45+
46+
@main struct Main {
47+
static func main() async throws {
48+
let object = PFXObject()
49+
try await run1(on: object)
50+
try await run2(on: object)
51+
try await run3(on: object)
52+
try await run4(on: object)
53+
}
54+
}

0 commit comments

Comments
 (0)