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 @@ -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">]; 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 @@ -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"; 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 @@ -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; +def warn_attr_swift_name_decl_missing_params + : Warning<"%0 attribute cannot be applied to a %select{function|method}1 " + "with no parameters">, + InGroup; def err_attr_swift_error_no_error_parameter : Error< "%0 attribute can only be applied to a %select{function|method}1 with an " diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/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. 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 @@ -5922,7 +5922,7 @@ } bool Sema::DiagnoseSwiftName(Decl *D, StringRef Name, SourceLocation Loc, - const ParsedAttr &AL) { + const ParsedAttr &AL, bool IsAsync) { if (isa(D) || isa(D)) { ArrayRef 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(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(D) || isa(D) || - isa(D) || isa(D) || - isa(D) || isa(D) || isa(D) || - isa(D) || isa(D)) { + } else if ((isa(D) || isa(D) || + isa(D) || isa(D) || + isa(D) || isa(D) || isa(D) || + isa(D) || isa(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; diff --git a/clang/test/SemaObjC/attr-swift_name.m b/clang/test/SemaObjC/attr-swift_name.m --- a/clang/test/SemaObjC/attr-swift_name.m +++ b/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

+ +- (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