diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2352,6 +2352,16 @@ let Documentation = [SwiftIndirectResultDocs]; } +def SwiftAsync : InheritableAttr { + let Spellings = [Clang<"swift_async">]; + let Subjects = SubjectList<[Function, ObjCMethod]>; + let Args = [EnumArgument<"Kind", "Kind", + ["none", "swift_private", "not_swift_private"], + ["None", "SwiftPrivate", "NotSwiftPrivate"]>, + ParamIdxArgument<"CompletionHandlerIndex", /*opt=*/1>]; + let Documentation = [SwiftAsyncDocs]; +} + def Suppress : StmtAttr { let Spellings = [CXX11<"gsl", "suppress">]; let Args = [VariadicStringArgument<"DiagnosticIdentifiers">]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4400,6 +4400,37 @@ }]; } +def SwiftAsyncDocs : Documentation { + let Category = SwiftDocs; + let Heading = "swift_async"; + let Content = [{ +The ``swift_async`` attribute specifies if and how a particular function or +Objective-C method is imported into a swift async method. For instance: + +.. code-block:: objc + + @interface MyClass : NSObject + -(void)notActuallyAsync:(int)p1 withCompletionHandler:(void (^)())handler + __attribute__((swift_async(none))); + + -(void)actuallyAsync:(int)p1 callThisAsync:(void (^)())fun + __attribute__((swift_async(swift_private, 1))); + @end + +Here, ``notActuallyAsync:withCompletionHandler`` would have been imported as +``async`` (because it's last parameter's selector piece is +``withCompletionHandler``) if not for the ``swift_async(none)`` attribute. +Conversely, ``actuallyAsync:callThisAsync`` wouldn't have been imported as +``async`` if not for the ``swift_async`` attribute because it doesn't match the +naming convention. + +When using ``swift_async`` to enable importing, the first argument to the +attribute is either ``swift_private`` or ``not_swift_private`` to indicate +whether the function/method is private to the current framework, and the second +argument is the index of the completion handler parameter. + }]; +} + def SuppressDocs : Documentation { let Category = DocCatStmt; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4050,6 +4050,13 @@ "%0 attribute with '%1' convention can only be applied to a " "%select{function|method}2 returning %select{an integral type|a pointer}3">; +def err_swift_async_no_access : Error< + "first argument to 'swift_async' must be either 'none', 'swift_private', or " + "'not_swift_private'">; +def err_swift_async_bad_block_type : Error< + "'swift_async' completion handler parameter must have block type returning" + " 'void', type here is %0">; + def warn_ignored_objc_externally_retained : Warning< "'objc_externally_retained' can only be applied to local variables " "%select{of retainable type|with strong ownership}0">, diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6103,6 +6103,56 @@ D->addAttr(::new (S.Context) SwiftNewTypeAttr(S.Context, AL, Kind)); } +static void handleSwiftAsyncAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + if (!AL.isArgIdent(0)) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_n_type) + << AL << 1 << AANT_ArgumentIdentifier; + return; + } + + SwiftAsyncAttr::Kind Kind; + IdentifierInfo *II = AL.getArgAsIdent(0)->Ident; + if (!SwiftAsyncAttr::ConvertStrToKind(II->getName(), Kind)) { + S.Diag(AL.getLoc(), diag::err_swift_async_no_access) << AL << II; + return; + } + + ParamIdx Idx; + if (Kind == SwiftAsyncAttr::None) { + // If this is 'none', then there shouldn't be any additional arguments. + if (!checkAttributeNumArgs(S, AL, 1)) + return; + } else { + // Non-none swift_async requires a completion handler index argument. + if (!checkAttributeNumArgs(S, AL, 2)) + return; + + Expr *HandlerIdx = AL.getArgAsExpr(1); + if (!checkFunctionOrMethodParameterIndex(S, D, AL, 2, HandlerIdx, Idx)) + return; + + const ParmVarDecl *CompletionBlock = + getFunctionOrMethodParam(D, Idx.getASTIndex()); + QualType CompletionBlockType = CompletionBlock->getType(); + if (!CompletionBlockType->isBlockPointerType()) { + S.Diag(CompletionBlock->getLocation(), + diag::err_swift_async_bad_block_type) + << CompletionBlock->getType(); + return; + } + QualType BlockTy = + CompletionBlockType->getAs()->getPointeeType(); + if (!BlockTy->getAs()->getReturnType()->isVoidType()) { + S.Diag(CompletionBlock->getLocation(), + diag::err_swift_async_bad_block_type) + << CompletionBlock->getType(); + return; + } + } + + D->addAttr(::new (S.Context) SwiftAsyncAttr(S.Context, AL, Kind, Idx)); +} + //===----------------------------------------------------------------------===// // Microsoft specific attribute handlers. //===----------------------------------------------------------------------===// @@ -8041,6 +8091,9 @@ case ParsedAttr::AT_SwiftPrivate: handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_SwiftAsync: + handleSwiftAsyncAttr(S, D, AL); + break; // XRay attributes. case ParsedAttr::AT_XRayLogArgs: diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -147,6 +147,7 @@ // CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property) // CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member) // CHECK-NEXT: SpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method) +// CHECK-NEXT: SwiftAsync (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: SwiftAsyncName (SubjectMatchRule_objc_method, SubjectMatchRule_function) // CHECK-NEXT: SwiftBridgedTypedef (SubjectMatchRule_type_alias) // CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter) diff --git a/clang/test/SemaObjC/attr-swift-async.m b/clang/test/SemaObjC/attr-swift-async.m new file mode 100644 --- /dev/null +++ b/clang/test/SemaObjC/attr-swift-async.m @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -verify -fsyntax-only -fblocks %s +// RUN: %clang_cc1 -xobjective-c++ -verify -fsyntax-only -fblocks %s + +#define SA(...) __attribute__((swift_async(__VA_ARGS__))) + +SA(none) int a; // expected-warning{{'swift_async' attribute only applies to functions and Objective-C methods}} + +SA(none) void b(); + +SA(not_swift_private, 0) void c(); // expected-error{{'swift_async' attribute parameter 2 is out of bounds}} +SA(swift_private, 1) void d(); // expected-error{{'swift_async' attribute parameter 2 is out of bounds}} +SA(swift_private, 1) void e(int); // expected-error{{'swift_async' completion handler parameter must have block type returning 'void', type here is 'int'}} +SA(not_swift_private, 1) void f(int (^)()); // expected-error{{'swift_async' completion handler parameter must have block type returning 'void', type here is 'int (^)()'}} +SA(swift_private, 1) void g(void (^)()); + +SA(none, 1) void h(); // expected-error{{'swift_async' attribute takes one argument}} +SA() void i(); // expected-error{{'swift_async' attribute takes at least 1 argument}} +SA(not_swift_private) void j(); // expected-error{{'swift_async' attribute requires exactly 2 arguments}} +SA(43) void k(); // expected-error{{'swift_async' attribute requires parameter 1 to be an identifier}} +SA(not_a_thing, 0) void l(); // expected-error{{first argument to 'swift_async' must be either 'none', 'swift_private', or 'not_swift_private'}} + +@interface TestOnMethods +-(void)m1:(int (^)())callback SA(swift_private, 1); // expected-error{{'swift_async' completion handler parameter must have block type returning 'void', type here is 'int (^)()'}} +-(void)m2:(void (^)())callback SA(swift_private, 0); // expected-error{{'swift_async' attribute parameter 2 is out of bounds}} +-(void)m3:(void (^)())callback SA(swift_private, 2); // expected-error{{'swift_async' attribute parameter 2 is out of bounds}} +-(void)m4 SA(none); +-(void)m5:(int)p handler:(void (^)(int))callback SA(not_swift_private, 2); +@end + +#ifdef __cplusplus +struct S { + SA(none) void mf1(); + SA(swift_private, 2) void mf2(void (^)()); + SA(swift_private, 1) void mf3(void (^)()); // expected-error{{'swift_async' attribute is invalid for the implicit this argument}} + SA(swift_private, 0) void mf4(void (^)()); // expected-error{{'swift_async' attribute parameter 2 is out of bounds}} + SA(not_swift_private, 2) void mf5(int (^)()); // expected-error{{'swift_async' completion handler parameter must have block type returning 'void', type here is 'int (^)()'}} +}; +#endif