Skip to content

Commit 5a28e1d

Browse files
committed
[clang] Add support for attribute 'swift_async'
This attributes specifies how (or if) a given function or method will be imported into a swift async method. rdar://70111252 Differential revision: https://reviews.llvm.org/D92742
1 parent 9cd2413 commit 5a28e1d

File tree

6 files changed

+140
-0
lines changed

6 files changed

+140
-0
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2352,6 +2352,16 @@ def SwiftIndirectResult : ParameterABIAttr {
23522352
let Documentation = [SwiftIndirectResultDocs];
23532353
}
23542354

2355+
def SwiftAsync : InheritableAttr {
2356+
let Spellings = [Clang<"swift_async">];
2357+
let Subjects = SubjectList<[Function, ObjCMethod]>;
2358+
let Args = [EnumArgument<"Kind", "Kind",
2359+
["none", "swift_private", "not_swift_private"],
2360+
["None", "SwiftPrivate", "NotSwiftPrivate"]>,
2361+
ParamIdxArgument<"CompletionHandlerIndex", /*opt=*/1>];
2362+
let Documentation = [SwiftAsyncDocs];
2363+
}
2364+
23552365
def Suppress : StmtAttr {
23562366
let Spellings = [CXX11<"gsl", "suppress">];
23572367
let Args = [VariadicStringArgument<"DiagnosticIdentifiers">];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4400,6 +4400,37 @@ optimizations like C++'s named return value optimization (NRVO).
44004400
}];
44014401
}
44024402

4403+
def SwiftAsyncDocs : Documentation {
4404+
let Category = SwiftDocs;
4405+
let Heading = "swift_async";
4406+
let Content = [{
4407+
The ``swift_async`` attribute specifies if and how a particular function or
4408+
Objective-C method is imported into a swift async method. For instance:
4409+
4410+
.. code-block:: objc
4411+
4412+
@interface MyClass : NSObject
4413+
-(void)notActuallyAsync:(int)p1 withCompletionHandler:(void (^)())handler
4414+
__attribute__((swift_async(none)));
4415+
4416+
-(void)actuallyAsync:(int)p1 callThisAsync:(void (^)())fun
4417+
__attribute__((swift_async(swift_private, 1)));
4418+
@end
4419+
4420+
Here, ``notActuallyAsync:withCompletionHandler`` would have been imported as
4421+
``async`` (because it's last parameter's selector piece is
4422+
``withCompletionHandler``) if not for the ``swift_async(none)`` attribute.
4423+
Conversely, ``actuallyAsync:callThisAsync`` wouldn't have been imported as
4424+
``async`` if not for the ``swift_async`` attribute because it doesn't match the
4425+
naming convention.
4426+
4427+
When using ``swift_async`` to enable importing, the first argument to the
4428+
attribute is either ``swift_private`` or ``not_swift_private`` to indicate
4429+
whether the function/method is private to the current framework, and the second
4430+
argument is the index of the completion handler parameter.
4431+
}];
4432+
}
4433+
44034434
def SuppressDocs : Documentation {
44044435
let Category = DocCatStmt;
44054436
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4050,6 +4050,13 @@ def err_attr_swift_error_return_type : Error<
40504050
"%0 attribute with '%1' convention can only be applied to a "
40514051
"%select{function|method}2 returning %select{an integral type|a pointer}3">;
40524052

4053+
def err_swift_async_no_access : Error<
4054+
"first argument to 'swift_async' must be either 'none', 'swift_private', or "
4055+
"'not_swift_private'">;
4056+
def err_swift_async_bad_block_type : Error<
4057+
"'swift_async' completion handler parameter must have block type returning"
4058+
" 'void', type here is %0">;
4059+
40534060
def warn_ignored_objc_externally_retained : Warning<
40544061
"'objc_externally_retained' can only be applied to local variables "
40554062
"%select{of retainable type|with strong ownership}0">,

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6103,6 +6103,56 @@ static void handleSwiftNewType(Sema &S, Decl *D, const ParsedAttr &AL) {
61036103
D->addAttr(::new (S.Context) SwiftNewTypeAttr(S.Context, AL, Kind));
61046104
}
61056105

6106+
static void handleSwiftAsyncAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
6107+
if (!AL.isArgIdent(0)) {
6108+
S.Diag(AL.getLoc(), diag::err_attribute_argument_n_type)
6109+
<< AL << 1 << AANT_ArgumentIdentifier;
6110+
return;
6111+
}
6112+
6113+
SwiftAsyncAttr::Kind Kind;
6114+
IdentifierInfo *II = AL.getArgAsIdent(0)->Ident;
6115+
if (!SwiftAsyncAttr::ConvertStrToKind(II->getName(), Kind)) {
6116+
S.Diag(AL.getLoc(), diag::err_swift_async_no_access) << AL << II;
6117+
return;
6118+
}
6119+
6120+
ParamIdx Idx;
6121+
if (Kind == SwiftAsyncAttr::None) {
6122+
// If this is 'none', then there shouldn't be any additional arguments.
6123+
if (!checkAttributeNumArgs(S, AL, 1))
6124+
return;
6125+
} else {
6126+
// Non-none swift_async requires a completion handler index argument.
6127+
if (!checkAttributeNumArgs(S, AL, 2))
6128+
return;
6129+
6130+
Expr *HandlerIdx = AL.getArgAsExpr(1);
6131+
if (!checkFunctionOrMethodParameterIndex(S, D, AL, 2, HandlerIdx, Idx))
6132+
return;
6133+
6134+
const ParmVarDecl *CompletionBlock =
6135+
getFunctionOrMethodParam(D, Idx.getASTIndex());
6136+
QualType CompletionBlockType = CompletionBlock->getType();
6137+
if (!CompletionBlockType->isBlockPointerType()) {
6138+
S.Diag(CompletionBlock->getLocation(),
6139+
diag::err_swift_async_bad_block_type)
6140+
<< CompletionBlock->getType();
6141+
return;
6142+
}
6143+
QualType BlockTy =
6144+
CompletionBlockType->getAs<BlockPointerType>()->getPointeeType();
6145+
if (!BlockTy->getAs<FunctionType>()->getReturnType()->isVoidType()) {
6146+
S.Diag(CompletionBlock->getLocation(),
6147+
diag::err_swift_async_bad_block_type)
6148+
<< CompletionBlock->getType();
6149+
return;
6150+
}
6151+
}
6152+
6153+
D->addAttr(::new (S.Context) SwiftAsyncAttr(S.Context, AL, Kind, Idx));
6154+
}
6155+
61066156
//===----------------------------------------------------------------------===//
61076157
// Microsoft specific attribute handlers.
61086158
//===----------------------------------------------------------------------===//
@@ -8041,6 +8091,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
80418091
case ParsedAttr::AT_SwiftPrivate:
80428092
handleSimpleAttribute<SwiftPrivateAttr>(S, D, AL);
80438093
break;
8094+
case ParsedAttr::AT_SwiftAsync:
8095+
handleSwiftAsyncAttr(S, D, AL);
8096+
break;
80448097

80458098
// XRay attributes.
80468099
case ParsedAttr::AT_XRayLogArgs:

clang/test/Misc/pragma-attribute-supported-attributes-list.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@
147147
// CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property)
148148
// CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member)
149149
// CHECK-NEXT: SpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method)
150+
// CHECK-NEXT: SwiftAsync (SubjectMatchRule_function, SubjectMatchRule_objc_method)
150151
// CHECK-NEXT: SwiftAsyncName (SubjectMatchRule_objc_method, SubjectMatchRule_function)
151152
// CHECK-NEXT: SwiftBridgedTypedef (SubjectMatchRule_type_alias)
152153
// CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %clang_cc1 -verify -fsyntax-only -fblocks %s
2+
// RUN: %clang_cc1 -xobjective-c++ -verify -fsyntax-only -fblocks %s
3+
4+
#define SA(...) __attribute__((swift_async(__VA_ARGS__)))
5+
6+
SA(none) int a; // expected-warning{{'swift_async' attribute only applies to functions and Objective-C methods}}
7+
8+
SA(none) void b();
9+
10+
SA(not_swift_private, 0) void c(); // expected-error{{'swift_async' attribute parameter 2 is out of bounds}}
11+
SA(swift_private, 1) void d(); // expected-error{{'swift_async' attribute parameter 2 is out of bounds}}
12+
SA(swift_private, 1) void e(int); // expected-error{{'swift_async' completion handler parameter must have block type returning 'void', type here is 'int'}}
13+
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 (^)()'}}
14+
SA(swift_private, 1) void g(void (^)());
15+
16+
SA(none, 1) void h(); // expected-error{{'swift_async' attribute takes one argument}}
17+
SA() void i(); // expected-error{{'swift_async' attribute takes at least 1 argument}}
18+
SA(not_swift_private) void j(); // expected-error{{'swift_async' attribute requires exactly 2 arguments}}
19+
SA(43) void k(); // expected-error{{'swift_async' attribute requires parameter 1 to be an identifier}}
20+
SA(not_a_thing, 0) void l(); // expected-error{{first argument to 'swift_async' must be either 'none', 'swift_private', or 'not_swift_private'}}
21+
22+
@interface TestOnMethods
23+
-(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 (^)()'}}
24+
-(void)m2:(void (^)())callback SA(swift_private, 0); // expected-error{{'swift_async' attribute parameter 2 is out of bounds}}
25+
-(void)m3:(void (^)())callback SA(swift_private, 2); // expected-error{{'swift_async' attribute parameter 2 is out of bounds}}
26+
-(void)m4 SA(none);
27+
-(void)m5:(int)p handler:(void (^)(int))callback SA(not_swift_private, 2);
28+
@end
29+
30+
#ifdef __cplusplus
31+
struct S {
32+
SA(none) void mf1();
33+
SA(swift_private, 2) void mf2(void (^)());
34+
SA(swift_private, 1) void mf3(void (^)()); // expected-error{{'swift_async' attribute is invalid for the implicit this argument}}
35+
SA(swift_private, 0) void mf4(void (^)()); // expected-error{{'swift_async' attribute parameter 2 is out of bounds}}
36+
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 (^)()'}}
37+
};
38+
#endif

0 commit comments

Comments
 (0)