From ab47660d75a225b984c93d552591633aff6b40c9 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Mon, 29 Mar 2021 15:03:23 -0700 Subject: [PATCH 1/4] allow ObjC methods with completion handlers be imported as effectful properties either as an `async` or `async throws` property, by marking it with swift_async_name("getter:PROPERTY_NAME()") where `PROPERTY_NAME` will be the name of the property it will be imported as. This is in lieu of being imported as an async method. It's still imported as an `@objc` method as well. --- lib/ClangImporter/ImportDecl.cpp | 113 ++++++++++++++++-- lib/ClangImporter/ImportName.cpp | 8 +- lib/ClangImporter/ImportType.cpp | 43 +++++++ lib/ClangImporter/ImporterImpl.h | 7 ++ .../usr/include/EffectfulProperties.h | 78 ++++++++++++ .../clang-importer-sdk/usr/include/module.map | 5 + 6 files changed, 244 insertions(+), 10 deletions(-) create mode 100644 test/Inputs/clang-importer-sdk/usr/include/EffectfulProperties.h diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 767dd4b8c3217..ecb317b974b03 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -4590,7 +4590,27 @@ namespace { if (auto Known = Impl.importDeclCached(decl, getVersion())) return Known; - return importObjCMethodDecl(decl, dc, None); + ImportedName importedName; + Optional correctSwiftName; // TODO: not sure if we need this. + importedName = importFullName(decl, correctSwiftName); + if (!importedName) + return nullptr; + + // some ObjC method decls are imported as computed properties. + switch(importedName.getAccessorKind()) { + case ImportedAccessorKind::PropertyGetter: + if (importedName.getAsyncInfo()) + return importObjCMethodAsEffectfulProp(decl, dc, importedName); + + // if there is no valid async info, then fall-back to method import. + LLVM_FALLTHROUGH; + + case ImportedAccessorKind::PropertySetter: + case ImportedAccessorKind::SubscriptGetter: + case ImportedAccessorKind::SubscriptSetter: + case ImportedAccessorKind::None: + return importObjCMethodDecl(decl, dc, None); + } } /// Check whether we have already imported a method with the given @@ -4666,6 +4686,78 @@ namespace { return (accessor && accessor->getAccessorKind() == accessorInfo->Kind); } + /// Creates a fresh VarDecl with a single 'get' accessor to represent + /// an ObjC method that takes no arguments other than a completion-handler + /// (where the handler may have an NSError argument). + Decl *importObjCMethodAsEffectfulProp(const clang::ObjCMethodDecl *decl, + DeclContext *dc, + ImportedName name) { + assert(name.getAsyncInfo() && "expected to be for an effectful prop!"); + + if (name.getAccessorKind() != ImportedAccessorKind::PropertyGetter) { + assert(false && "unexpected accessor kind as a computed prop"); + // NOTE: to handle setters, we would need to search for an existing + // VarDecl corresponding to the one we might have already created + // for the 'get' accessor, and tack this accessor onto it. + return nullptr; + } + + auto importedType = Impl.importEffectfulPropertyType(decl, dc, name, + isInSystemModule(dc)); + if (!importedType) + return nullptr; + + auto type = importedType.getType(); + const auto access = getOverridableAccessLevel(dc); + auto ident = name.getDeclName().getBaseIdentifier(); + auto propDecl = Impl.createDeclWithClangNode(decl, access, + /*IsStatic*/decl->isClassMethod(), VarDecl::Introducer::Var, + Impl.importSourceLoc(decl->getLocation()), ident, dc); + propDecl->setInterfaceType(type); + Impl.recordImplicitUnwrapForDecl(propDecl, + importedType.isImplicitlyUnwrapped()); + + //// + // Build the getter + AccessorInfo info{propDecl, AccessorKind::Get}; + auto *getter = cast_or_null( + importObjCMethodDecl(decl, dc, info)); + if (!getter) + return nullptr; + + Impl.importAttributes(decl, getter); + + //// + // Combine the getter and the VarDecl into a computed property. + + // NOTE: since it's an ObjC method we're turning into a Swift computed + // property, we infer that it has no ObjC 'atomic' guarantees. + auto inferredObjCPropertyAttrs = + static_cast + ( clang::ObjCPropertyAttribute::Kind::kind_readonly + | clang::ObjCPropertyAttribute::Kind::kind_nonatomic + | (decl->isInstanceMethod() + ? clang::ObjCPropertyAttribute::Kind::kind_class + : clang::ObjCPropertyAttribute::Kind::kind_noattr) + ); + + // FIXME: Fake locations for '{' and '}'? + propDecl->setIsSetterMutating(false); + makeComputed(propDecl, getter, /*setter=*/nullptr); + addObjCAttribute(propDecl, Impl.importIdentifier(decl->getIdentifier())); + applyPropertyOwnership(propDecl, inferredObjCPropertyAttrs); + + //// + // Check correctness + + if (getter->getParameters()->size() != 0) { + assert(false && "this should not happen!"); + return nullptr; + } + + return propDecl; + } + Decl *importObjCMethodDecl(const clang::ObjCMethodDecl *decl, DeclContext *dc, bool forceClassMethod, @@ -4797,11 +4889,16 @@ namespace { prop = nullptr; } - // If we have an accessor-import request but didn't find a property, - // reject the import request. - if (accessorInfo && !prop) { + const bool nameImportIsGetter = + importedName.getAccessorKind() == ImportedAccessorKind::PropertyGetter; + + const bool needAccessorDecl = prop || nameImportIsGetter; + + // If we have an accessor-import request, but didn't find a property + // or it's ImportedName doesn't indicate a getter, + // then reject the import request. + if (accessorInfo && !needAccessorDecl) return nullptr; - } // Import the parameter list and result type. ParameterList *bodyParams = nullptr; @@ -4854,7 +4951,7 @@ namespace { // If the method has a related result type that is representable // in Swift as DynamicSelf, do so. - if (!prop && decl->hasRelatedResultType()) { + if (!needAccessorDecl && decl->hasRelatedResultType()) { resultTy = dc->getSelfInterfaceType(); if (dc->getSelfClassDecl()) resultTy = DynamicSelfType::get(resultTy, Impl.SwiftContext); @@ -5020,7 +5117,7 @@ namespace { FuncDecl *setter); /// Import the accessor and its attributes. - AccessorDecl *importAccessor(clang::ObjCMethodDecl *clangAccessor, + AccessorDecl *importAccessor(const clang::ObjCMethodDecl *clangAccessor, AbstractStorageDecl *storage, AccessorKind accessorKind, DeclContext *dc); @@ -7389,7 +7486,7 @@ SwiftDeclConverter::importSubscript(Decl *decl, } AccessorDecl * -SwiftDeclConverter::importAccessor(clang::ObjCMethodDecl *clangAccessor, +SwiftDeclConverter::importAccessor(const clang::ObjCMethodDecl *clangAccessor, AbstractStorageDecl *storage, AccessorKind accessorKind, DeclContext *dc) { diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 66a2644f59205..3920760527dbc 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1618,8 +1618,12 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, else if (parsedName.IsSetter) result.info.accessorKind = ImportedAccessorKind::PropertySetter; - if (method && parsedName.IsFunctionName && - result.info.accessorKind == ImportedAccessorKind::None) { + // only allow effectful property imports if through `swift_async_name` + const bool effectfulProperty = parsedName.IsGetter && nameAttr->isAsync + && swiftCtx.LangOpts.EnableExperimentalConcurrency; + + // Consider throws and async imports. + if (method && (parsedName.IsFunctionName || effectfulProperty)) { // Get the parameters. ArrayRef params{method->param_begin(), method->param_end()}; diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 28e3575713410..da6baa36ade56 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -2203,6 +2203,49 @@ static Type decomposeCompletionHandlerType( } } +ImportedType ClangImporter::Implementation::importEffectfulPropertyType( + const clang::ObjCMethodDecl *decl, + DeclContext *dc, + importer::ImportedName name, + bool isFromSystemModule) { + // here we expect a method that is being imported as an effectful property. + // thus, we currently require async info. + if (!name.getAsyncInfo()) + return ImportedType(); + + // a variadic method doesn't make sense here + if (decl->isVariadic()) + return ImportedType(); + + // Our strategy here is to determine what the return type of the method would + // be, had we imported it as a method. + + Optional asyncConvention; + Optional errorConvention; + + const auto kind = SpecialMethodKind::Regular; + + // Import the parameter list and result type. + ParameterList *bodyParams = nullptr; + ImportedType importedType; + + auto methodReturnType = importMethodParamsAndReturnType( + dc, decl, decl->parameters(), false, + isFromSystemModule, &bodyParams, name, + asyncConvention, errorConvention, kind); + + // getter mustn't have any parameters! + if (bodyParams->size() != 0) { + return ImportedType(); + } + + // We expect that the method, after import, will have only an async convention + if (!asyncConvention || errorConvention) + return ImportedType(); + + return methodReturnType; +} + ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType( const DeclContext *dc, const clang::ObjCMethodDecl *clangDecl, ArrayRef params, bool isVariadic, diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index 0debea3da17bf..ac1a979bb2a86 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -1199,6 +1199,13 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation ImportedType importPropertyType(const clang::ObjCPropertyDecl *clangDecl, bool isFromSystemModule); + /// Determines what the type of an effectful, computed read-only property + /// would be, if the given method were imported as such a property. + ImportedType importEffectfulPropertyType(const clang::ObjCMethodDecl *decl, + DeclContext *dc, + importer::ImportedName name, + bool isFromSystemModule); + /// Attempt to infer a default argument for a parameter with the /// given Clang \c type, \c baseName, and optionality. static DefaultArgumentKind diff --git a/test/Inputs/clang-importer-sdk/usr/include/EffectfulProperties.h b/test/Inputs/clang-importer-sdk/usr/include/EffectfulProperties.h new file mode 100644 index 0000000000000..e41637ca1ea3c --- /dev/null +++ b/test/Inputs/clang-importer-sdk/usr/include/EffectfulProperties.h @@ -0,0 +1,78 @@ +@import Foundation; + +#define MAIN_ACTOR __attribute__((__swift_attr__("@MainActor"))) +#define ASYNC_NAME(X) __attribute__((swift_async_name(X))); + +#pragma clang assume_nonnull begin + +@interface EffProps : NSObject + +///// +// decls imported as an effectful property + +-(void)getDogWithCompletion:(void (^)(NSObject*))completionHandler + ASYNC_NAME("getter:doggo()"); + +-(void)obtainCat:(void (^)(NSObject* _Nullable_result, NSError* _Nullable))completionHandler + ASYNC_NAME("getter:catto()"); + +-(void)checkAvailabilityWithCompletionHandler:(void (^)(BOOL isAvailable))completionHandler + ASYNC_NAME("getter:available()"); + +// FIXME: this does not raise an error, and generates two identical properties! +// -(void)anotherExampleWithCompletionBlock:(void (^)(NSString *))block +// ASYNC_NAME("getter:manifestedString()"); +// -(void)finalExampleWithReplyTo:(void (^)(NSString *))block +// ASYNC_NAME("getter:manifestedString()"); + +-(void)returnNothingWithCompletion:(void (^)(void))completionHandler + ASYNC_NAME("getter:touch()"); + +-(void)nullableHandler:(void (^ _Nullable)(NSString *))completion +ASYNC_NAME("getter:fromNullableHandler()"); + +-(void)getMainDog:(MAIN_ACTOR void (^)(NSString *))completion +ASYNC_NAME("getter:mainDogProp()"); +-(void)regularMainDog:(MAIN_ACTOR void (^)(NSString *))completion; + +@end + +@interface NotEffProps : NSObject + +///// +// decls that are _not_ imported as an effectful property + +// FIXME: even prior to objc imported effectful properties, this triggers an assert. +// -(NSObject*)getGreenDog __attribute__((swift_name("getter:greenDoggie()"))); + +-(void)doSomethingSlow:(NSString *)operation completionHandler:(void (^)(NSInteger))handler + ASYNC_NAME("getter:slow(self:)"); + +-(void)doSomethingDangerous:(NSString *)operation completionHandler:(void (^ _Nullable)(NSString *_Nullable, NSError * _Nullable))handler + ASYNC_NAME("getter:slowAndDanger(self:)"); + +// This means it can throw in two ways: produce an error synchronously, returning +// false and put error in out parameter. Or return true and error/success goes to completion handler. +// NOTE: no need to support this, but make sure we have a test case. +-(BOOL)getChicken:(void (^)(NSObject* _Nullable_result, NSError* _Nullable))completionHandler + error: (NSError**)error + ASYNC_NAME("getter:chicken()"); + + +// plain throws, with `swift_name`. should be ignored and imported like before. +-(NSObject* _Nullable)getCow1:(NSError**)error + __attribute__((swift_name("getter:noCow1()"))); + +// plain throws, with `swift_async_name`. Sensible to support in the future, but should be ignored. +-(NSObject* _Nullable)getCow2:(NSError**)error ASYNC_NAME("getter:noCow2()"); + +@end + +// make sure import-as-member does not also get turned into an effectful prop + +struct __attribute__((swift_name("Struct1"))) IAMStruct1 {}; + +extern double EffPropGetDogWithCompletion(const struct Struct1 *s, void (^myBlock)(NSObject*)) + ASYNC_NAME("getter:Struct1.dog(self:)"); + +#pragma clang assume_nonnull end diff --git a/test/Inputs/clang-importer-sdk/usr/include/module.map b/test/Inputs/clang-importer-sdk/usr/include/module.map index 366101cbbb723..01abea7128482 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/module.map +++ b/test/Inputs/clang-importer-sdk/usr/include/module.map @@ -140,4 +140,9 @@ module WinBOOL { module ObjCConcurrency { header "ObjCConcurrency.h" export * +} + +module EffectfulProperties { + header "EffectfulProperties.h" + export * } \ No newline at end of file From f5551f32752c25eabff887615afcd384c1d2075f Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Thu, 1 Apr 2021 19:55:33 -0700 Subject: [PATCH 2/4] Add ObjC interface test for effectful property imports --- ...rint_clang_objc_effectful_properties.swift | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 test/IDE/print_clang_objc_effectful_properties.swift diff --git a/test/IDE/print_clang_objc_effectful_properties.swift b/test/IDE/print_clang_objc_effectful_properties.swift new file mode 100644 index 0000000000000..071f8a68ed335 --- /dev/null +++ b/test/IDE/print_clang_objc_effectful_properties.swift @@ -0,0 +1,39 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -print-module -source-filename %s -module-to-print=EffectfulProperties -function-definitions=false -enable-experimental-concurrency > %t/EffectfulProperties.printed.txt +// RUN: %FileCheck -input-file %t/EffectfulProperties.printed.txt %s + +// REQUIRES: objc_interop +// REQUIRES: concurrency + +// CHECK-LABEL: class EffProps : NSObject { +// CHECK: func getDogWithCompletion(_ completionHandler: @escaping (NSObject) -> Void) +// CHECK: var doggo: NSObject { get async } + +// CHECK: func obtainCat(_ completionHandler: @escaping (NSObject?, Error?) -> Void) +// CHECK-NEXT: var catto: NSObject? { get async throws } + +// CHECK: func checkAvailability(completionHandler: @escaping (Bool) -> Void) +// CHECK-NEXT: var available: Bool { get async } +// CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "checkAvailability(completionHandler:)") +// CHECK-NEXT: func checkAvailabilityWithCompletionHandler(_ completionHandler: @escaping (Bool) -> Void) + +// CHECK: func returnNothing(completion completionHandler: @escaping () -> Void) +// CHECK-NEXT: var touch: Void { get async } +// CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "returnNothing(completion:)") +// CHECK-NEXT: func returnNothingWithCompletion(_ completionHandler: @escaping () -> Void) + +// CHECK: func nullableHandler(_ completion: ((String) -> Void)? = nil) +// CHECK-NEXT: var fromNullableHandler: String { get async } + +// CHECK: func getMainDog(_ completion: @escaping @MainActor (String) -> Void) +// CHECK-NEXT: var mainDogProp: String { get async } + +// CHECK: @completionHandlerAsync("regularMainDog()", completionHandleIndex: 0) +// CHECK-NEXT: func regularMainDog(_ completion: @escaping @MainActor (String) -> Void) +// CHECK-NEXT: func regularMainDog() async -> String +// CHECK: } + +// CHECK-LABEL: class NotEffProps : NSObject { +// CHECK-NOT: var +// CHECK: func EffPropGetDogWithCompletion(_ s: OpaquePointer, _ myBlock: @escaping (NSObject) -> Void) -> Double From 304169496e6dc9a48dbd36886b8142ca1286c129 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Fri, 2 Apr 2021 07:19:26 -0700 Subject: [PATCH 3/4] add SILGen test of ObjC effectful property imports --- test/SILGen/objc_effectful_properties.swift | 63 +++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 test/SILGen/objc_effectful_properties.swift diff --git a/test/SILGen/objc_effectful_properties.swift b/test/SILGen/objc_effectful_properties.swift new file mode 100644 index 0000000000000..3c6f150c19fa2 --- /dev/null +++ b/test/SILGen/objc_effectful_properties.swift @@ -0,0 +1,63 @@ +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-silgen -I %S/Inputs/custom-modules -enable-experimental-concurrency %s -verify | %FileCheck --enable-var-scope --check-prefix=CHECK --check-prefix=CHECK-%target-cpu %s +// REQUIRES: concurrency +// REQUIRES: objc_interop + +import Foundation +import EffectfulProperties + +// CHECK-LABEL: sil {{.*}}@${{.*}}13testJustAsync +func testJustAsync(eff : EffProps) async { + // CHECK: [[RESUME_BUF:%.*]] = alloc_stack $NSObject + // CHECK: [[METHOD:%.*]] = objc_method {{.*}} $@convention(objc_method) (@convention(block) (NSObject) -> (), EffProps) -> () + // CHECK: [[CONT:%.*]] = get_async_continuation_addr NSObject, [[RESUME_BUF]] + // CHECK: [[WRAPPED:%.*]] = struct $UnsafeContinuation ([[CONT]] : $Builtin.RawUnsafeContinuation) + // CHECK: [[BLOCK_STORAGE:%.*]] = alloc_stack $@block_storage UnsafeContinuation + // CHECK: [[CONT_SLOT:%.*]] = project_block_storage [[BLOCK_STORAGE]] + // CHECK: store [[WRAPPED]] to [trivial] [[CONT_SLOT]] + // CHECK: [[BLOCK_IMPL:%.*]] = function_ref @[[NSO_COMPLETION_BLOCK:.*]] : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation, NSObject) -> () + // CHECK: [[BLOCK:%.*]] = init_block_storage_header [[BLOCK_STORAGE]] {{.*}}, invoke [[BLOCK_IMPL]] + // CHECK: apply [[METHOD]]([[BLOCK]], %0) + // CHECK: await_async_continuation [[CONT]] {{.*}}, resume [[RESUME:bb[0-9]+]] + + // CHECK: [[RESUME]]: + // CHECK: [[RESULT:%.*]] = load [take] [[RESUME_BUF]] + // CHECK: dealloc_stack [[RESUME_BUF]] + let _ = await eff.doggo +} + +// CHECK-LABEL: sil {{.*}}@${{.*}}15testAsyncThrows +func testAsyncThrows(eff : EffProps) async { + // CHECK: [[RESUME_BUF:%.*]] = alloc_stack $Optional + // CHECK: [[METHOD:%.*]] = objc_method {{.*}} $@convention(objc_method) (@convention(block) (Optional, Optional) -> (), EffProps) -> () + // CHECK: [[CONT:%.*]] = get_async_continuation_addr [throws] Optional, [[RESUME_BUF]] + // CHECK: [[WRAPPED:%.*]] = struct $UnsafeContinuation, Error> ([[CONT]] : $Builtin.RawUnsafeContinuation) + // CHECK: [[BLOCK_STORAGE:%.*]] = alloc_stack $@block_storage UnsafeContinuation, Error> + // CHECK: [[CONT_SLOT:%.*]] = project_block_storage [[BLOCK_STORAGE]] + // CHECK: store [[WRAPPED]] to [trivial] [[CONT_SLOT]] + // CHECK: [[BLOCK_IMPL:%.*]] = function_ref @[[NSO_COMPLETION_BLOCK:.*]] : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation, Error>, Optional, Optional) -> () + // CHECK: [[BLOCK:%.*]] = init_block_storage_header [[BLOCK_STORAGE]] {{.*}}, invoke [[BLOCK_IMPL]] + // CHECK: apply [[METHOD]]([[BLOCK]], %0) + // CHECK: await_async_continuation [[CONT]] {{.*}}, resume [[RESUME:bb[0-9]+]], error [[RESUME_ERROR:bb[0-9]+]] + + // CHECK: [[RESUME]]: + // CHECK: [[RESULT:%.*]] = load [take] [[RESUME_BUF]] + // CHECK: dealloc_stack [[RESUME_BUF]] + + // CHECK: [[RESUME_ERROR]]([[ERROR_VAL:%[0-9]+]] : @owned $Error): + // CHECK: dealloc_stack [[RESUME_BUF]] + let _ = try? await eff.catto +} + +// CHECK-LABEL: sil {{.*}}@${{.*}}17testMainActorProp +func testMainActorProp(eff : EffProps) async { + // CHECK-NOT: hop_to_executor + // CHECK: } // end sil function '${{.*}}17testMainActorProp + let _ = await eff.mainDogProp +} + +// CHECK-LABEL: sil {{.*}}@${{.*}}19testMainActorMethod +func testMainActorMethod(eff : EffProps) async { + // CHECK-NOT: hop_to_executor + // CHECK: } // end sil function '${{.*}}19testMainActorMethod + let _ = await eff.regularMainDog() +} \ No newline at end of file From 1586ca83d9df7fe07cedc7573f82ed1a19e33a17 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Fri, 2 Apr 2021 07:57:50 -0700 Subject: [PATCH 4/4] add execution test for ObjC-imported effectful properties --- test/Concurrency/Runtime/Inputs/objc_async.h | 10 +++++++ test/Concurrency/Runtime/Inputs/objc_async.m | 14 +++++++++ test/Concurrency/Runtime/objc_async.swift | 30 +++++++++++++++++--- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/test/Concurrency/Runtime/Inputs/objc_async.h b/test/Concurrency/Runtime/Inputs/objc_async.h index e79da06927515..33c9dc869450b 100644 --- a/test/Concurrency/Runtime/Inputs/objc_async.h +++ b/test/Concurrency/Runtime/Inputs/objc_async.h @@ -7,3 +7,13 @@ - (void)butt:(NSInteger)x completionHandler:(void (^ _Nonnull)(NSInteger))handler; @end + +@interface Farm: NSObject + +-(void)getDogWithCompletion:(void (^ _Nonnull)(NSInteger))completionHandler + __attribute__((swift_async_name("getter:doggo()"))); + +-(void)obtainCat:(void (^ _Nonnull)(NSInteger, NSError* _Nullable))completionHandler +__attribute__((swift_async_name("getter:catto()"))); + +@end diff --git a/test/Concurrency/Runtime/Inputs/objc_async.m b/test/Concurrency/Runtime/Inputs/objc_async.m index ae67b5f693537..e6acafff4ad2a 100644 --- a/test/Concurrency/Runtime/Inputs/objc_async.m +++ b/test/Concurrency/Runtime/Inputs/objc_async.m @@ -13,3 +13,17 @@ - (void)butt:(NSInteger)x completionHandler:(void (^)(NSInteger))handler { } @end + +@implementation Farm + +-(void)getDogWithCompletion:(void (^ _Nonnull)(NSInteger))completionHandler { + printf("getting dog\n"); + completionHandler(123); +} + +-(void)obtainCat:(void (^ _Nonnull)(NSInteger, NSError* _Nullable))completionHandler { + printf("obtaining cat has failed!\n"); + completionHandler(nil, [NSError errorWithDomain:@"obtainCat" code:456 userInfo:nil]); +} + +@end \ No newline at end of file diff --git a/test/Concurrency/Runtime/objc_async.swift b/test/Concurrency/Runtime/objc_async.swift index da5ce1ee39cac..cddf89eb6ecdd 100644 --- a/test/Concurrency/Runtime/objc_async.swift +++ b/test/Concurrency/Runtime/objc_async.swift @@ -10,13 +10,35 @@ // rdar://76038845 // UNSUPPORTED: use_os_stdlib -@main struct Main { - static func main() async { +func buttTest() async { let butt = Butt() let result = await butt.butt(1738) print("finishing \(result)") +} + +func farmTest() async { + let farm = Farm() + let dogNumber = await farm.doggo + print("dog number = \(dogNumber)") + do { + let _ = try await farm.catto + } catch { + print("caught exception") } } -// CHECK: starting 1738 -// CHECK-NEXT: finishing 679 +@main struct Main { + static func main() async { + // CHECK: starting 1738 + // CHECK-NEXT: finishing 679 + await buttTest() + + // CHECK-NEXT: getting dog + // CHECK-NEXT: dog number = 123 + // CHECK-NEXT: obtaining cat has failed! + // CHECK-NEXT: caught exception + await farmTest() + } +} + +