Skip to content

Commit 0b0264c

Browse files
committed
SILGen: Implement native-to-foreign thunks for async interfaces with nullable completion handlers.
If the ObjC interface is passed a null completion handler, just discard the result of the native async call after it finishes.
1 parent bd2a6a4 commit 0b0264c

File tree

4 files changed

+114
-15
lines changed

4 files changed

+114
-15
lines changed

lib/SILGen/SILGenBridging.cpp

Lines changed: 89 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,7 +1362,12 @@ static SILFunctionType *emitObjCThunkArguments(SILGenFunction &SGF,
13621362
}
13631363

13641364
if (foreignAsync && i == foreignAsync->completionHandlerParamIndex()) {
1365-
foreignAsyncSlot = arg;
1365+
// Copy the block.
1366+
foreignAsyncSlot = SGF.B.createCopyBlock(loc, arg);
1367+
// If the argument is consumed, we're still responsible for releasing the
1368+
// original.
1369+
if (inputs[i].isConsumed())
1370+
SGF.emitManagedRValueWithCleanup(arg);
13661371
continue;
13671372
}
13681373

@@ -1597,7 +1602,8 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
15971602
// Bridge the arguments.
15981603
Optional<ForeignErrorConvention> foreignError;
15991604
Optional<ForeignAsyncConvention> foreignAsync;
1600-
SILValue foreignErrorSlot, foreignAsyncSlot;
1605+
SILValue foreignErrorSlot;
1606+
SILValue foreignAsyncSlot;
16011607
CanType nativeFormalResultType, bridgedFormalResultType;
16021608
auto objcFnTy = emitObjCThunkArguments(*this, loc, thunk, args,
16031609
foreignErrorSlot, foreignAsyncSlot,
@@ -1634,16 +1640,63 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
16341640

16351641
SILValue result;
16361642

1643+
CanSILFunctionType completionTy;
1644+
bool completionIsOptional = false;
1645+
if (foreignAsyncSlot) {
1646+
completionTy = foreignAsyncSlot->getType().getAs<SILFunctionType>();
1647+
if (!completionTy) {
1648+
completionTy = foreignAsyncSlot->getType().getOptionalObjectType()
1649+
.castTo<SILFunctionType>();
1650+
completionIsOptional = true;
1651+
}
1652+
}
1653+
1654+
// Helper function to take ownership of the completion handler from the
1655+
// foreign async slot, and unwrap it if it's in an optional.
1656+
auto consumeAndUnwrapCompletionBlock = [&](SILValue &completionBlock,
1657+
SILBasicBlock *&doneBBOrNull) {
1658+
auto completionBlockMV = emitManagedRValueWithCleanup(foreignAsyncSlot);
1659+
1660+
// If the completion handler argument is nullable, and the caller gave us
1661+
// no completion handler, discard the result.
1662+
completionBlock = completionBlockMV.borrow(*this, loc).getValue();
1663+
doneBBOrNull = nullptr;
1664+
if (completionIsOptional) {
1665+
doneBBOrNull = createBasicBlock();
1666+
auto hasCompletionBB = createBasicBlock();
1667+
auto noCompletionBB = createBasicBlock();
1668+
1669+
std::pair<EnumElementDecl *, SILBasicBlock *> dests[] = {
1670+
{getASTContext().getOptionalSomeDecl(), hasCompletionBB},
1671+
{getASTContext().getOptionalNoneDecl(), noCompletionBB},
1672+
};
1673+
1674+
B.createSwitchEnum(loc, completionBlock, nullptr, dests);
1675+
1676+
B.emitBlock(noCompletionBB);
1677+
B.createBranch(loc, doneBBOrNull);
1678+
1679+
B.emitBlock(hasCompletionBB);
1680+
completionBlock = hasCompletionBB->createPhiArgument(
1681+
SILType::getPrimitiveObjectType(completionTy),
1682+
OwnershipKind::Guaranteed);
1683+
1684+
}
1685+
};
1686+
16371687
// Helper function to pass a native async function's result as arguments to
16381688
// the ObjC completion handler block.
16391689
auto passResultToCompletionHandler = [&](SILValue result) -> SILValue {
16401690
Scope completionArgScope(*this, loc);
16411691

16421692
SmallVector<SILValue, 2> completionHandlerArgs;
1643-
auto completionTy = foreignAsyncSlot->getType().castTo<SILFunctionType>();
16441693

16451694
auto asyncResult = emitManagedRValueWithCleanup(result);
16461695

1696+
SILValue completionBlock;
1697+
SILBasicBlock *doneBB;
1698+
consumeAndUnwrapCompletionBlock(completionBlock, doneBB);
1699+
16471700
auto pushArg = [&](ManagedValue arg,
16481701
CanType nativeFormalTy,
16491702
SILParameterInfo param) {
@@ -1654,7 +1707,9 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
16541707
SILType::getPrimitiveObjectType(bridgedTy));
16551708
completionHandlerArgs.push_back(bridgedArg.borrow(*this, loc).getValue());
16561709
};
1657-
1710+
1711+
Scope completionArgDestructureScope(*this, loc);
1712+
16581713
auto errorParamIndex = foreignAsync->completionHandlerErrorParamIndex();
16591714
auto pushErrorPlaceholder = [&]{
16601715
auto errorArgTy = completionTy->getParameters()[*errorParamIndex]
@@ -1696,7 +1751,13 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
16961751
}
16971752
}
16981753
// Pass the bridged results on to the completion handler.
1699-
B.createApply(loc, foreignAsyncSlot, {}, completionHandlerArgs);
1754+
B.createApply(loc, completionBlock, {}, completionHandlerArgs);
1755+
completionArgDestructureScope.pop();
1756+
1757+
if (doneBB) {
1758+
B.createBranch(loc, doneBB);
1759+
B.emitBlock(doneBB);
1760+
}
17001761

17011762
// The immediate function result is an empty tuple.
17021763
return SILUndef::get(SGM.Types.getEmptyTupleType(), F);
@@ -1763,6 +1824,7 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
17631824
// Emit the error destination.
17641825
{
17651826
B.emitBlock(errorBB);
1827+
17661828
SILValue nativeError = errorBB->createPhiArgument(
17671829
substConv.getSILErrorType(getTypeExpansionContext()),
17681830
OwnershipKind::Owned);
@@ -1772,15 +1834,23 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
17721834
// completion handler, with dummy values for the other argument(s).
17731835
Scope completionArgScope(*this, loc);
17741836

1837+
auto nativeErrorMV = emitManagedRValueWithCleanup(nativeError);
1838+
1839+
SILValue completionBlock;
1840+
SILBasicBlock *doneBB;
1841+
consumeAndUnwrapCompletionBlock(completionBlock, doneBB);
1842+
1843+
Scope completionErrorScope(*this, loc);
1844+
17751845
SmallVector<SILValue, 2> completionHandlerArgs;
1776-
auto completionTy = foreignAsyncSlot->getType().castTo<SILFunctionType>();
1846+
auto completionTy = completionBlock->getType().castTo<SILFunctionType>();
17771847
auto errorParamIndex = *foreignAsync->completionHandlerErrorParamIndex();
17781848
auto completionErrorTy = completionTy->getParameters()[errorParamIndex]
17791849
.getInterfaceType();
17801850
auto bridgedError = emitNativeToBridgedError(loc,
1781-
emitManagedRValueWithCleanup(nativeError),
1782-
nativeError->getType().getASTType(),
1783-
completionErrorTy);
1851+
nativeErrorMV,
1852+
nativeError->getType().getASTType(),
1853+
completionErrorTy);
17841854

17851855
// Fill in placeholder arguments, and put the bridged error in its
17861856
// rightful place.
@@ -1805,10 +1875,17 @@ void SILGenFunction::emitNativeToForeignThunk(SILDeclRef thunk) {
18051875
completionHandlerArgs.push_back(none);
18061876
}
18071877
}
1808-
// Pass the bridged results on to the completion handler.
1809-
B.createApply(loc, foreignAsyncSlot, {}, completionHandlerArgs);
1810-
completionArgScope.pop();
1878+
// Pass the bridged error on to the completion handler.
1879+
B.createApply(loc, completionBlock, {}, completionHandlerArgs);
1880+
1881+
completionErrorScope.pop();
18111882

1883+
if (doneBB) {
1884+
B.createBranch(loc, doneBB);
1885+
B.emitBlock(doneBB);
1886+
}
1887+
completionArgScope.pop();
1888+
18121889
B.createBranch(loc, contBB);
18131890
} else {
18141891
// In this branch, the eventual return value is mostly invented.

test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res
4545
-(void)repeatTrick:(NSString *)trick completionHandler:(void (^)(NSInteger))handler __attribute__((swift_async(none)));
4646

4747
-(void)doSomethingSlowNullably:(NSString *)operation completionHandler:(void (^ _Nullable)(NSInteger))handler;
48+
-(void)findAnswerNullably:(NSString *)operation completionHandler:(void (^ _Nullable)(NSString *))handler;
49+
-(void)doSomethingDangerousNullably:(NSString *)operation completionHandler:(void (^ _Nullable)(NSString *_Nullable, NSError *_Nullable))handler;
4850
@end
4951

5052
@protocol RefrigeratorDelegate<NSObject>

test/SILGen/objc_async.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ func testSlowServer(slowServer: SlowServer) async throws {
5656
// CHECK: [[THROWBB]]([[ERROR_VALUE:%.*]] : @owned $Error):
5757
// CHECK: throw [[ERROR_VALUE]]
5858

59+
let _: String = await slowServer.findAnswerNullably("foo")
60+
let _: String = await try slowServer.doSomethingDangerousNullably("foo")
5961
}
6062

6163
// CHECK: sil{{.*}}@[[INT_COMPLETION_BLOCK]]

test/SILGen/objc_async_from_swift.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,23 @@ class SlowSwiftServer: NSObject, SlowServing {
3535
// CHECK: [[RUN_TASK:%.*]] = function_ref @${{.*}}29_runTaskForBridgedAsyncMethod
3636
// CHECK: apply [[RUN_TASK]]([[CLOSURE]])
3737
// CHECK: sil {{.*}} [[CLOSURE_IMP]]
38+
// CHECK: [[BLOCK_COPY:%.*]] = copy_block %0
3839
// CHECK: [[NATIVE_RESULT:%.*]] = apply{{.*}}@async
39-
// CHECK: apply %0([[NATIVE_RESULT]])
40+
// CHECK: [[BLOCK_BORROW:%.*]] = begin_borrow [[BLOCK_COPY]]
41+
// CHECK: apply [[BLOCK_BORROW]]([[NATIVE_RESULT]])
4042
func requestInt() async -> Int { return 0 }
4143
func requestString() async -> String { return "" }
4244
// CHECK-LABEL: sil {{.*}} @${{.*}}16tryRequestString{{.*}}U_To :
45+
// CHECK: [[BLOCK_COPY:%.*]] = copy_block %0
4346
// CHECK: try_apply{{.*}}@async{{.*}}, normal [[NORMAL:bb[0-9]+]], error [[ERROR:bb[0-9]+]]
4447
// CHECK: [[NORMAL]]([[NATIVE_RESULT:%.*]] : @owned $String):
48+
// CHECK: [[BLOCK_BORROW:%.*]] = begin_borrow [[BLOCK_COPY]]
4549
// CHECK: [[NIL_ERROR:%.*]] = enum $Optional<NSError>, #Optional.none
46-
// CHECK: apply %0({{%.*}}, [[NIL_ERROR]])
50+
// CHECK: apply [[BLOCK_BORROW]]({{%.*}}, [[NIL_ERROR]])
4751
// CHECK: [[ERROR]]([[NATIVE_RESULT:%.*]] : @owned $Error):
52+
// CHECK: [[BLOCK_BORROW:%.*]] = begin_borrow [[BLOCK_COPY]]
4853
// CHECK: [[NIL_NSSTRING:%.*]] = enum $Optional<NSString>, #Optional.none
49-
// CHECK: apply %0([[NIL_NSSTRING]], {{%.*}})
54+
// CHECK: apply [[BLOCK_BORROW]]([[NIL_NSSTRING]], {{%.*}})
5055
func tryRequestString() async throws -> String { return "" }
5156
func requestIntAndString() async -> (Int, String) { return (0, "") }
5257
func tryRequestIntAndString() async throws -> (Int, String) { return (0, "") }
@@ -61,3 +66,16 @@ protocol NativelySlowServing {
6166
}
6267

6368
extension SlowServer: NativelySlowServing {}
69+
70+
class SlowServerlet: SlowServer {
71+
override func doSomethingSlowNullably(_: String) async -> Int {
72+
return 0
73+
}
74+
override func findAnswerNullably(_ x: String) async -> String {
75+
return x
76+
}
77+
override func doSomethingDangerousNullably(_ x: String) async throws -> String {
78+
return x
79+
}
80+
}
81+

0 commit comments

Comments
 (0)