arphaman created this revision. arphaman added reviewers: erik.pilkington, aaron.ballman. Herald added subscribers: ributzka, jkorous. Herald added a project: clang. arphaman requested review of this revision.
The `swift_async_name` attribute provides a name for a function/method that can be used to call the `async` overload of this method from Swift. This name specified in this attribute assumes that the last parameter in the function/method its applied to is removed when Swift invokes it, as the the Swift's await/async transformation implicitly constructs the callback. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D92355 Files: clang/include/clang/Basic/Attr.td clang/include/clang/Basic/AttrDocs.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaDeclAttr.cpp clang/test/SemaObjC/attr-swift_name.m
Index: clang/test/SemaObjC/attr-swift_name.m =================================================================== --- clang/test/SemaObjC/attr-swift_name.m +++ clang/test/SemaObjC/attr-swift_name.m @@ -1,6 +1,7 @@ -// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc %s +// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s #define SWIFT_NAME(name) __attribute__((__swift_name__(name))) +#define SWIFT_ASYNC_NAME(name) __attribute__((__swift_async_name__(name))) typedef struct { float x, y, z; @@ -172,3 +173,28 @@ // expected-error@+1 {{'swift_name' and 'swift_name' attributes are not compatible}} void g(int i) SWIFT_NAME("function(_:)") { } + +typedef int (^CallbackTy)(void); + +@interface AsyncI<P> + +- (void)doSomethingWithCallback:(CallbackTy)callback SWIFT_ASYNC_NAME("doSomething()"); +- (void)doSomethingX:(int)x withCallback:(CallbackTy)callback SWIFT_ASYNC_NAME("doSomething(x:)"); + +// expected-warning@+1 {{too many parameters in '__swift_async_name__' attribute (expected 1; got 2)}} +- (void)doSomethingY:(int)x withCallback:(CallbackTy)callback SWIFT_ASYNC_NAME("doSomething(x:y:)"); + +// expected-warning@+1 {{too few parameters in '__swift_async_name__' attribute (expected 1; got 0)}} +- (void)doSomethingZ:(int)x withCallback:(CallbackTy)callback SWIFT_ASYNC_NAME("doSomething()"); + +// expected-warning@+1 {{'__swift_async_name__' attribute cannot be applied to a method with no parameters}} +- (void)doSomethingNone SWIFT_ASYNC_NAME("doSomething()"); + +@end + +// expected-warning@+1 {{'__swift_async_name__' attribute cannot be applied to a function with no parameters}} +void asyncNoParams(void) SWIFT_ASYNC_NAME("asyncNoParams()"); + +// expected-warning@+1 {{'__swift_async_name__' attribute cannot be applied to this declaration}} +SWIFT_ASYNC_NAME("NoAsync") +@protocol NoAsync @end Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -5922,7 +5922,7 @@ } bool Sema::DiagnoseSwiftName(Decl *D, StringRef Name, SourceLocation Loc, - const ParsedAttr &AL) { + const ParsedAttr &AL, bool IsAsync) { if (isa<ObjCMethodDecl>(D) || isa<FunctionDecl>(D)) { ArrayRef<ParmVarDecl*> Params; unsigned ParamCount; @@ -5943,6 +5943,16 @@ } } + // The async name drops the last callback parameter. + if (IsAsync) { + if (ParamCount == 0) { + Diag(Loc, diag::warn_attr_swift_name_decl_missing_params) + << AL << (isa<ObjCMethodDecl>(D) ? 1 : 0); + return false; + } + ParamCount -= 1; + } + unsigned SwiftParamCount; bool IsSingleParamInit; if (!validateSwiftFunctionName(*this, AL, Loc, Name, @@ -5976,10 +5986,11 @@ << SwiftParamCount; return false; } - } else if (isa<EnumConstantDecl>(D) || isa<ObjCProtocolDecl>(D) || - isa<ObjCInterfaceDecl>(D) || isa<ObjCPropertyDecl>(D) || - isa<VarDecl>(D) || isa<TypedefNameDecl>(D) || isa<TagDecl>(D) || - isa<IndirectFieldDecl>(D) || isa<FieldDecl>(D)) { + } else if ((isa<EnumConstantDecl>(D) || isa<ObjCProtocolDecl>(D) || + isa<ObjCInterfaceDecl>(D) || isa<ObjCPropertyDecl>(D) || + isa<VarDecl>(D) || isa<TypedefNameDecl>(D) || isa<TagDecl>(D) || + isa<IndirectFieldDecl>(D) || isa<FieldDecl>(D)) && + !IsAsync) { StringRef ContextName, BaseName; std::tie(ContextName, BaseName) = Name.split('.'); @@ -6004,16 +6015,20 @@ return true; } -static void handleSwiftName(Sema &S, Decl *D, const ParsedAttr &AL) { +static void handleSwiftName(Sema &S, Decl *D, const ParsedAttr &AL, + bool IsAsync = false) { StringRef Name; SourceLocation Loc; if (!S.checkStringLiteralArgumentAttr(AL, 0, Name, &Loc)) return; - if (!S.DiagnoseSwiftName(D, Name, Loc, AL)) + if (!S.DiagnoseSwiftName(D, Name, Loc, AL, IsAsync)) return; - D->addAttr(::new (S.Context) SwiftNameAttr(S.Context, AL, Name)); + D->addAttr(IsAsync ? (Attr *)::new (S.Context) + SwiftAsyncNameAttr(S.Context, AL, Name) + : (Attr *)::new (S.Context) + SwiftNameAttr(S.Context, AL, Name)); } static void handleSwiftNewType(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -7951,6 +7966,9 @@ break; // Swift attributes. + case ParsedAttr::AT_SwiftAsyncName: + handleSwiftName(S, D, AL, /*IsAsync=*/true); + break; case ParsedAttr::AT_SwiftAttr: handleSwiftAttrAttr(S, D, AL); break; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -1977,7 +1977,7 @@ /// /// \returns true if the name is a valid swift name for \p D, false otherwise. bool DiagnoseSwiftName(Decl *D, StringRef Name, SourceLocation Loc, - const ParsedAttr &AL); + const ParsedAttr &AL, bool IsAsync); /// A derivative of BoundTypeDiagnoser for which the diagnostic's type /// parameter is preceded by a 0/1 enum that is 1 if the type is sizeless. Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4030,6 +4030,10 @@ def warn_attr_swift_name_num_params : Warning<"too %select{few|many}0 parameters in %1 attribute (expected %2; got %3)">, InGroup<SwiftNameAttribute>; +def warn_attr_swift_name_decl_missing_params + : Warning<"%0 attribute cannot be applied to a %select{function|method}1 " + "with no parameters">, + InGroup<SwiftNameAttribute>; def err_attr_swift_error_no_error_parameter : Error< "%0 attribute can only be applied to a %select{function|method}1 with an " Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -3628,6 +3628,27 @@ }]; } +def SwiftAsyncNameDocs : Documentation { + let Category = SwiftDocs; + let Heading = "swift_async_name"; + let Content = [{ +The ``swift_async_name`` attribute provides the name of the ``async`` overload for +the given declaration in Swift. If this attribute is absent, the name is +transformed according to the algorithm built into the Swift compiler. + +The argument is a string literal that contains the Swift name of the function or +method. The name may be a compound Swift name. The function of method with such +an attribute must have more than zero parameters, as its last parameter is +assumed to be a callback that's eliminated in the Swift ``async`` name. + + .. code-block:: objc + + @interface URL + + (void) loadContentsFrom:(URL *)url callback:(void (^)(NSData *))data __attribute__((__swift_async_name__("URL.loadContentsFrom(_:)"))) + @end + }]; +} + def SwiftAttrDocs : Documentation { let Category = SwiftDocs; let Heading = "swift_attr"; Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -2149,6 +2149,12 @@ let ASTNode = 0; } +def SwiftAsyncName : InheritableAttr { + let Spellings = [GNU<"swift_async_name">]; + let Args = [StringArgument<"Name">]; + let Documentation = [SwiftAsyncNameDocs]; +} + def SwiftAttr : InheritableAttr { let Spellings = [Clang<"swift_attr">]; let Args = [StringArgument<"Attribute">];
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits