Skip to content

Commit 6db8520

Browse files
committed
SIL: Abstraction pattern support for multiple foreign async returns.
An ObjC API maybe imported as async that had multiple non-error arguments to its completion handler, which we treat in Swift as returning a tuple. Use a new form of abstraction pattern to represent this return type, to maintain the correct relation between individual tuple elements and the Clang block parameter types they map to.
1 parent 368dc0f commit 6db8520

File tree

4 files changed

+134
-22
lines changed

4 files changed

+134
-22
lines changed

include/swift/SIL/AbstractionPattern.h

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ class AbstractionPattern {
179179
/// type. ObjCMethod is valid. OtherData is an encoded foreign
180180
/// error index.
181181
ObjCMethodType,
182+
/// The type of an ObjC block used as a completion handler for
183+
/// an API that has been imported into Swift as async,
184+
/// representing the tuple of results of the async projection of the
185+
/// API.
186+
ObjCCompletionHandlerArgumentsType,
182187
/// The uncurried imported type of a C++ non-operator non-static member
183188
/// function. OrigType is valid and is a function type. CXXMethod is valid.
184189
CXXMethodType,
@@ -410,6 +415,7 @@ class AbstractionPattern {
410415
case Kind::CFunctionAsMethodType:
411416
case Kind::CurriedCFunctionAsMethodType:
412417
case Kind::PartialCurriedCFunctionAsMethodType:
418+
case Kind::ObjCCompletionHandlerArgumentsType:
413419
return true;
414420

415421
default:
@@ -445,7 +451,16 @@ class AbstractionPattern {
445451
}
446452

447453
bool hasStoredForeignInfo() const {
448-
return hasStoredObjCMethod();
454+
switch (getKind()) {
455+
case Kind::CurriedObjCMethodType:
456+
case Kind::PartialCurriedObjCMethodType:
457+
case Kind::ObjCMethodType:
458+
case Kind::ObjCCompletionHandlerArgumentsType:
459+
return true;
460+
461+
default:
462+
return false;
463+
}
449464
}
450465

451466
bool hasImportAsMemberStatus() const {
@@ -552,6 +567,7 @@ class AbstractionPattern {
552567
case Kind::CXXOperatorMethodType:
553568
case Kind::CurriedCXXOperatorMethodType:
554569
case Kind::PartialCurriedCXXOperatorMethodType:
570+
case Kind::ObjCCompletionHandlerArgumentsType:
555571
return true;
556572
case Kind::Invalid:
557573
case Kind::Opaque:
@@ -584,6 +600,22 @@ class AbstractionPattern {
584600
return pattern;
585601
}
586602

603+
/// Return an abstraction pattern for a result tuple
604+
/// corresponding to the parameters of a completion handler
605+
/// block of an API that was imported as async.
606+
static AbstractionPattern
607+
getObjCCompletionHandlerArgumentsType(CanGenericSignature sig,
608+
CanType origTupleType,
609+
const clang::Type *clangBlockType,
610+
EncodedForeignInfo foreignInfo) {
611+
AbstractionPattern pattern(Kind::ObjCCompletionHandlerArgumentsType);
612+
pattern.initClangType(sig, origTupleType, clangBlockType,
613+
Kind::ObjCCompletionHandlerArgumentsType);
614+
pattern.OtherData = foreignInfo.getOpaqueValue();
615+
616+
return pattern;
617+
}
618+
587619
public:
588620
/// Return an abstraction pattern for the curried type of an
589621
/// Objective-C method.
@@ -592,6 +624,7 @@ class AbstractionPattern {
592624
const Optional<ForeignErrorConvention> &foreignError,
593625
const Optional<ForeignAsyncConvention> &foreignAsync);
594626

627+
595628
/// Return an abstraction pattern for the uncurried type of a C function
596629
/// imported as a method.
597630
///
@@ -927,6 +960,7 @@ class AbstractionPattern {
927960
case Kind::OpaqueDerivativeFunction:
928961
llvm_unreachable("opaque derivative function pattern has no type");
929962
case Kind::ClangType:
963+
case Kind::ObjCCompletionHandlerArgumentsType:
930964
case Kind::CurriedObjCMethodType:
931965
case Kind::PartialCurriedObjCMethodType:
932966
case Kind::ObjCMethodType:
@@ -980,6 +1014,7 @@ class AbstractionPattern {
9801014
case Kind::PartialCurriedCXXOperatorMethodType:
9811015
case Kind::Type:
9821016
case Kind::Discard:
1017+
case Kind::ObjCCompletionHandlerArgumentsType:
9831018
assert(signature || !type->hasTypeParameter());
9841019
assert(hasSameBasicTypeStructure(OrigType, type));
9851020
GenericSig = (type->hasTypeParameter() ? signature : nullptr);
@@ -1018,6 +1053,7 @@ class AbstractionPattern {
10181053
case Kind::CXXOperatorMethodType:
10191054
case Kind::CurriedCXXOperatorMethodType:
10201055
case Kind::PartialCurriedCXXOperatorMethodType:
1056+
case Kind::ObjCCompletionHandlerArgumentsType:
10211057
return true;
10221058
}
10231059
llvm_unreachable("bad kind");
@@ -1097,6 +1133,7 @@ class AbstractionPattern {
10971133
case Kind::PartialCurriedCXXOperatorMethodType:
10981134
case Kind::OpaqueFunction:
10991135
case Kind::OpaqueDerivativeFunction:
1136+
case Kind::ObjCCompletionHandlerArgumentsType:
11001137
return false;
11011138
case Kind::PartialCurriedObjCMethodType:
11021139
case Kind::CurriedObjCMethodType:
@@ -1136,6 +1173,7 @@ class AbstractionPattern {
11361173
case Kind::PartialCurriedCXXOperatorMethodType:
11371174
case Kind::Type:
11381175
case Kind::Discard:
1176+
case Kind::ObjCCompletionHandlerArgumentsType:
11391177
return dyn_cast<TYPE>(getType());
11401178
}
11411179
llvm_unreachable("bad kind");
@@ -1167,6 +1205,7 @@ class AbstractionPattern {
11671205
case Kind::PartialCurriedCXXOperatorMethodType:
11681206
case Kind::OpaqueFunction:
11691207
case Kind::OpaqueDerivativeFunction:
1208+
case Kind::ObjCCompletionHandlerArgumentsType:
11701209
// We assume that the Clang type might provide additional structure.
11711210
return false;
11721211
case Kind::Type:
@@ -1200,6 +1239,7 @@ class AbstractionPattern {
12001239
case Kind::OpaqueFunction:
12011240
case Kind::OpaqueDerivativeFunction:
12021241
return false;
1242+
case Kind::ObjCCompletionHandlerArgumentsType:
12031243
case Kind::Tuple:
12041244
return true;
12051245
case Kind::Type:
@@ -1232,6 +1272,7 @@ class AbstractionPattern {
12321272
llvm_unreachable("pattern is not a tuple");
12331273
case Kind::Tuple:
12341274
return getNumTupleElements_Stored();
1275+
case Kind::ObjCCompletionHandlerArgumentsType:
12351276
case Kind::Type:
12361277
case Kind::Discard:
12371278
case Kind::ClangType:

lib/SIL/IR/AbstractionPattern.cpp

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ AbstractionPattern::getOptional(AbstractionPattern object) {
203203
case Kind::PartialCurriedCXXOperatorMethodType:
204204
case Kind::OpaqueFunction:
205205
case Kind::OpaqueDerivativeFunction:
206+
case Kind::ObjCCompletionHandlerArgumentsType:
206207
llvm_unreachable("cannot add optionality to non-type abstraction");
207208
case Kind::Opaque:
208209
return AbstractionPattern::getOpaque();
@@ -310,6 +311,7 @@ bool AbstractionPattern::matchesTuple(CanTupleType substType) {
310311
return true;
311312
case Kind::Tuple:
312313
return getNumTupleElements_Stored() == substType->getNumElements();
314+
case Kind::ObjCCompletionHandlerArgumentsType:
313315
case Kind::ClangType:
314316
case Kind::Type:
315317
case Kind::Discard: {
@@ -399,6 +401,19 @@ AbstractionPattern::getTupleElementType(unsigned index) const {
399401
return AbstractionPattern::getOpaque();
400402
return AbstractionPattern(getGenericSignature(),
401403
getCanTupleElementType(getType(), index));
404+
405+
case Kind::ObjCCompletionHandlerArgumentsType: {
406+
// Match up the tuple element with the parameter from the Clang block type,
407+
// skipping the error parameter index if any.
408+
auto callback = cast<clang::FunctionProtoType>(getClangType());
409+
auto errorIndex = getEncodedForeignInfo()
410+
.getAsyncCompletionHandlerErrorParamIndex();
411+
unsigned paramIndex = index + (errorIndex && index >= *errorIndex);
412+
return AbstractionPattern(getGenericSignature(),
413+
getCanTupleElementType(getType(), index),
414+
callback->getParamType(paramIndex).getTypePtr());
415+
}
416+
402417
}
403418
llvm_unreachable("bad kind");
404419
}
@@ -465,6 +480,7 @@ AbstractionPattern AbstractionPattern::getFunctionResultType() const {
465480
switch (getKind()) {
466481
case Kind::Invalid:
467482
llvm_unreachable("querying invalid abstraction pattern!");
483+
case Kind::ObjCCompletionHandlerArgumentsType:
468484
case Kind::Tuple:
469485
llvm_unreachable("abstraction pattern for tuple cannot be function");
470486
case Kind::Opaque:
@@ -524,25 +540,45 @@ AbstractionPattern AbstractionPattern::getFunctionResultType() const {
524540
->getPointeeType()
525541
->getAs<clang::FunctionProtoType>();
526542

527-
// The result is the first non-error argument to the callback.
528-
unsigned callbackResultIndex = 0;
529-
if (auto callbackErrorIndex = getEncodedForeignInfo()
530-
.getAsyncCompletionHandlerErrorParamIndex()) {
531-
if (*callbackErrorIndex == 0) {
532-
callbackResultIndex = 1;
533-
}
543+
// The result comprises the non-error argument(s) to the callback, if
544+
// any.
545+
546+
auto callbackErrorIndex = getEncodedForeignInfo()
547+
.getAsyncCompletionHandlerErrorParamIndex();
548+
assert((!callbackErrorIndex.hasValue()
549+
|| callbackParamTy->getNumParams() > *callbackErrorIndex)
550+
&& "completion handler has invalid error param index?!");
551+
unsigned numNonErrorParams
552+
= callbackParamTy->getNumParams() - callbackErrorIndex.hasValue();
553+
554+
switch (numNonErrorParams) {
555+
case 0:
556+
// If there are no result arguments, then the imported result type is
557+
// Void, with no interesting abstraction properties.
558+
return AbstractionPattern(TupleType::getEmpty(getType()->getASTContext()));
559+
560+
case 1: {
561+
// If there's a single argument, abstract it according to its formal type
562+
// in the ObjC signature.
563+
unsigned callbackResultIndex
564+
= callbackErrorIndex && *callbackErrorIndex == 0;
565+
auto clangResultType = callbackParamTy
566+
->getParamType(callbackResultIndex)
567+
.getTypePtr();
568+
569+
return AbstractionPattern(getGenericSignatureForFunctionComponent(),
570+
getResultType(getType()), clangResultType);
534571
}
535-
536-
const clang::Type *clangResultType = nullptr;
537-
if (callbackResultIndex < callbackParamTy->getNumParams()) {
538-
clangResultType = callbackParamTy->getParamType(callbackResultIndex)
539-
.getTypePtr();
540-
} else {
541-
clangResultType = getObjCMethod()->getASTContext().VoidTy.getTypePtr();
572+
573+
default:
574+
// If there are multiple results, we have a special abstraction pattern
575+
// form to represent the mapping from block parameters to tuple elements
576+
// in the return type.
577+
return AbstractionPattern::getObjCCompletionHandlerArgumentsType(
578+
getGenericSignatureForFunctionComponent(),
579+
getResultType(getType()), callbackParamTy,
580+
getEncodedForeignInfo());
542581
}
543-
544-
return AbstractionPattern(getGenericSignatureForFunctionComponent(),
545-
getResultType(getType()), clangResultType);
546582
}
547583

548584
return AbstractionPattern(getGenericSignatureForFunctionComponent(),
@@ -594,6 +630,7 @@ AbstractionPattern::getObjCMethodAsyncCompletionHandlerType(
594630
case Kind::CurriedCFunctionAsMethodType:
595631
case Kind::CurriedCXXMethodType:
596632
case Kind::CurriedCXXOperatorMethodType:
633+
case Kind::ObjCCompletionHandlerArgumentsType:
597634
swift_unreachable("not appropriate for this kind");
598635
}
599636
}
@@ -791,6 +828,7 @@ AbstractionPattern AbstractionPattern::getOptionalObjectType() const {
791828
case Kind::Tuple:
792829
case Kind::OpaqueFunction:
793830
case Kind::OpaqueDerivativeFunction:
831+
case Kind::ObjCCompletionHandlerArgumentsType:
794832
llvm_unreachable("pattern for function or tuple cannot be for optional");
795833

796834
case Kind::Opaque:
@@ -837,6 +875,7 @@ AbstractionPattern AbstractionPattern::getReferenceStorageReferentType() const {
837875
case Kind::Tuple:
838876
case Kind::OpaqueFunction:
839877
case Kind::OpaqueDerivativeFunction:
878+
case Kind::ObjCCompletionHandlerArgumentsType:
840879
return *this;
841880
case Kind::Type:
842881
return AbstractionPattern(getGenericSignature(),
@@ -897,12 +936,15 @@ void AbstractionPattern::print(raw_ostream &out) const {
897936
case Kind::CurriedCFunctionAsMethodType:
898937
case Kind::PartialCurriedCFunctionAsMethodType:
899938
case Kind::CFunctionAsMethodType:
939+
case Kind::ObjCCompletionHandlerArgumentsType:
900940
out << (getKind() == Kind::ClangType
901941
? "AP::ClangType(" :
902942
getKind() == Kind::CurriedCFunctionAsMethodType
903943
? "AP::CurriedCFunctionAsMethodType(" :
904944
getKind() == Kind::PartialCurriedCFunctionAsMethodType
905-
? "AP::PartialCurriedCFunctionAsMethodType("
945+
? "AP::PartialCurriedCFunctionAsMethodType(" :
946+
getKind() == Kind::ObjCCompletionHandlerArgumentsType
947+
? "AP::ObjCCompletionHandlerArgumentsType("
906948
: "AP::CFunctionAsMethodType(");
907949
if (auto sig = getGenericSignature()) {
908950
sig->print(out);
@@ -922,6 +964,12 @@ void AbstractionPattern::print(raw_ostream &out) const {
922964
out << "static";
923965
}
924966
}
967+
if (hasStoredForeignInfo()) {
968+
if (auto errorIndex
969+
= getEncodedForeignInfo().getAsyncCompletionHandlerErrorParamIndex()){
970+
out << ", errorParamIndex=" << *errorIndex;
971+
}
972+
}
925973
out << ")";
926974
return;
927975
case Kind::CXXMethodType:
@@ -1069,6 +1117,9 @@ const {
10691117
case Kind::OpaqueDerivativeFunction:
10701118
llvm_unreachable("should not have an opaque derivative function pattern "
10711119
"matching a struct/enum type");
1120+
case Kind::ObjCCompletionHandlerArgumentsType:
1121+
llvm_unreachable("should not have a completion handler argument pattern "
1122+
"matching a struct/enum type");
10721123
case Kind::PartialCurriedObjCMethodType:
10731124
case Kind::CurriedObjCMethodType:
10741125
case Kind::PartialCurriedCFunctionAsMethodType:

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
-(void)getMagicNumberAsynchronouslyWithSeed:(NSInteger)seed completionHandler:(void (^)(NSInteger, NSError * _Nullable))handler;
1515
@property(readwrite) void (^completionHandler)(NSInteger);
1616

17+
-(void)findMultipleAnswersWithCompletionHandler:(void (^)(NSString *_Nullable, NSInteger, NSError * _Nullable))handler __attribute__((swift_name("findMultipleAnswers(completionHandler:)")));
18+
19+
-(void)findDifferentlyFlavoredBooleansWithCompletionHandler:(void (^)(BOOL wholeMilk, _Bool onePercent, NSError *_Nullable))handler __attribute__((swift_name("findDifferentlyFlavoredBooleans(completionHandler:)")));
20+
1721
-(void)doSomethingConflicted:(NSString *)operation completionHandler:(void (^)(NSInteger))handler;
1822
-(NSInteger)doSomethingConflicted:(NSString *)operation;
1923
-(void)server:(NSString *)name restartWithCompletionHandler:(void (^)(void))block;

test/SILGen/objc_async.swift

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify | %FileCheck %s
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s
22
// REQUIRES: objc_interop
33

44
import Foundation
@@ -41,9 +41,16 @@ func testSlowServer(slowServer: SlowServer) async throws {
4141
// CHECK: [[BLOCK_IMPL:%.*]] = function_ref @[[VOID_COMPLETION_BLOCK:.*]] : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation<()>) -> ()
4242
await slowServer.serverRestart("somewhere")
4343

44+
// CHECK: [[BLOCK_IMPL:%.*]] = function_ref @[[NSSTRING_INT_THROW_COMPLETION_BLOCK:.*]] : $@convention(c) (@inout_aliasable @block_storage UnsafeThrowingContinuation<(String, Int)>, Optional<NSString>, Int, Optional<NSError>) -> ()
45+
let (_, _): (String, Int) = try await slowServer.findMultipleAnswers()
46+
47+
let (_, _): (Bool, Bool) = try await slowServer.findDifferentlyFlavoredBooleans()
48+
4449
// CHECK: [[ERROR]]([[ERROR_VALUE:%.*]] : @owned $Error):
45-
// CHECK: dealloc_stack [[RESUME_BUF]]
46-
// CHECK: throw [[ERROR_VALUE]]
50+
// CHECK: dealloc_stack [[RESUME_BUF]]
51+
// CHECK: br [[THROWBB:bb[0-9]+]]([[ERROR_VALUE]]
52+
// CHECK: [[THROWBB]]([[ERROR_VALUE:%.*]] : @owned $Error):
53+
// CHECK: throw [[ERROR_VALUE]]
4754

4855
}
4956

@@ -85,3 +92,12 @@ func testSlowServer(slowServer: SlowServer) async throws {
8592
// CHECK: [[RESULT_BUF:%.*]] = alloc_stack $()
8693
// CHECK: [[RESUME:%.*]] = function_ref @{{.*}}resumeUnsafeContinuation
8794
// CHECK: apply [[RESUME]]<()>([[CONT]], [[RESULT_BUF]])
95+
96+
// CHECK: sil{{.*}}@[[NSSTRING_INT_THROW_COMPLETION_BLOCK]]
97+
// CHECK: [[RESULT_BUF:%.*]] = alloc_stack $(String, Int)
98+
// CHECK: [[RESULT_0_BUF:%.*]] = tuple_element_addr [[RESULT_BUF]] {{.*}}, 0
99+
// CHECK: [[BRIDGE:%.*]] = function_ref @{{.*}}unconditionallyBridgeFromObjectiveC
100+
// CHECK: [[BRIDGED:%.*]] = apply [[BRIDGE]]
101+
// CHECK: store [[BRIDGED]] to [init] [[RESULT_0_BUF]]
102+
// CHECK: [[RESULT_1_BUF:%.*]] = tuple_element_addr [[RESULT_BUF]] {{.*}}, 1
103+
// CHECK: store %2 to [trivial] [[RESULT_1_BUF]]

0 commit comments

Comments
 (0)