[clang] add a `swift_async_name` attribute

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.

Differential Revision: https://reviews.llvm.org/D92355
This commit is contained in:
Alex Lorenz 2020-12-04 14:45:27 -08:00
parent 03dcd57ecf
commit eddd1d192b
8 changed files with 123 additions and 19 deletions

View File

@ -2149,6 +2149,13 @@ def Regparm : TypeAttr {
let ASTNode = 0;
}
def SwiftAsyncName : InheritableAttr {
let Spellings = [GNU<"swift_async_name">];
let Args = [StringArgument<"Name">];
let Subjects = SubjectList<[ObjCMethod, Function], ErrorDiag>;
let Documentation = [SwiftAsyncNameDocs];
}
def SwiftAttr : InheritableAttr {
let Spellings = [GNU<"swift_attr">];
let Args = [StringArgument<"Attribute">];

View File

@ -3628,6 +3628,27 @@ Swift.
}];
}
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 or 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";

View File

@ -4028,7 +4028,12 @@ def warn_attr_swift_name_subscript_getter_newValue
: Warning<"%0 attribute for 'subscript' getter cannot have a 'newValue:' parameter">,
InGroup<SwiftNameAttribute>;
def warn_attr_swift_name_num_params
: Warning<"too %select{few|many}0 parameters in %1 attribute (expected %2; got %3)">,
: Warning<"too %select{few|many}0 parameters in the signature specified by "
"the %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<

View File

@ -1977,7 +1977,7 @@ public:
///
/// \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.

View File

@ -5922,7 +5922,7 @@ validateSwiftFunctionName(Sema &S, const ParsedAttr &AL, SourceLocation Loc,
}
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 @@ bool Sema::DiagnoseSwiftName(Decl *D, StringRef Name, SourceLocation Loc,
}
}
// 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);
return false;
}
ParamCount -= 1;
}
unsigned SwiftParamCount;
bool IsSingleParamInit;
if (!validateSwiftFunctionName(*this, AL, Loc, Name,
@ -5976,10 +5986,11 @@ bool Sema::DiagnoseSwiftName(Decl *D, StringRef Name, SourceLocation Loc,
<< 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('.');
@ -6010,12 +6021,24 @@ static void handleSwiftName(Sema &S, Decl *D, const ParsedAttr &AL) {
if (!S.checkStringLiteralArgumentAttr(AL, 0, Name, &Loc))
return;
if (!S.DiagnoseSwiftName(D, Name, Loc, AL))
if (!S.DiagnoseSwiftName(D, Name, Loc, AL, /*IsAsync=*/false))
return;
D->addAttr(::new (S.Context) SwiftNameAttr(S.Context, AL, Name));
}
static void handleSwiftAsyncName(Sema &S, Decl *D, const ParsedAttr &AL) {
StringRef Name;
SourceLocation Loc;
if (!S.checkStringLiteralArgumentAttr(AL, 0, Name, &Loc))
return;
if (!S.DiagnoseSwiftName(D, Name, Loc, AL, /*IsAsync=*/true))
return;
D->addAttr(::new (S.Context) SwiftAsyncNameAttr(S.Context, AL, Name));
}
static void handleSwiftNewType(Sema &S, Decl *D, const ParsedAttr &AL) {
// Make sure that there is an identifier as the annotation's single argument.
if (!checkAttributeNumArgs(S, AL, 1))
@ -7951,6 +7974,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
break;
// Swift attributes.
case ParsedAttr::AT_SwiftAsyncName:
handleSwiftAsyncName(S, D, AL);
break;
case ParsedAttr::AT_SwiftAttr:
handleSwiftAttrAttr(S, D, AL);
break;

View File

@ -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: SwiftAsyncName (SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: SwiftBridgedTypedef (SubjectMatchRule_type_alias)
// CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: SwiftError (SubjectMatchRule_function, SubjectMatchRule_objc_method)

View File

@ -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;
@ -27,31 +28,31 @@ __attribute__((__swift_name__("IClass")))
// expected-warning@-1 {{'__swift_name__' attribute argument must be a string literal specifying a Swift function name}}
+ (I *)iWithAnotherValue:(int)value SWIFT_NAME("i()");
// expected-warning@-1 {{too few parameters in '__swift_name__' attribute (expected 1; got 0)}}
// expected-warning@-1 {{too few parameters in the signature specified by the '__swift_name__' attribute (expected 1; got 0)}}
+ (I *)iWithYetAnotherValue:(int)value SWIFT_NAME("i(value:extra:)");
// expected-warning@-1 {{too many parameters in '__swift_name__' attribute (expected 1; got 2}}
// expected-warning@-1 {{too many parameters in the signature specified by the '__swift_name__' attribute (expected 1; got 2}}
+ (I *)iAndReturnErrorCode:(int *)errorCode SWIFT_NAME("i()"); // no-warning
+ (I *)iWithValue:(int)value andReturnErrorCode:(int *)errorCode SWIFT_NAME("i(value:)"); // no-warning
+ (I *)iFromErrorCode:(const int *)errorCode SWIFT_NAME("i()");
// expected-warning@-1 {{too few parameters in '__swift_name__' attribute (expected 1; got 0)}}
// expected-warning@-1 {{too few parameters in the signature specified by the '__swift_name__' attribute (expected 1; got 0)}}
+ (I *)iWithPointerA:(int *)value andReturnErrorCode:(int *)errorCode SWIFT_NAME("i()"); // no-warning
+ (I *)iWithPointerB:(int *)value andReturnErrorCode:(int *)errorCode SWIFT_NAME("i(pointer:)"); // no-warning
+ (I *)iWithPointerC:(int *)value andReturnErrorCode:(int *)errorCode SWIFT_NAME("i(pointer:errorCode:)"); // no-warning
+ (I *)iWithOtherI:(I *)other SWIFT_NAME("i()");
// expected-warning@-1 {{too few parameters in '__swift_name__' attribute (expected 1; got 0)}}
// expected-warning@-1 {{too few parameters in the signature specified by the '__swift_name__' attribute (expected 1; got 0)}}
+ (instancetype)specialI SWIFT_NAME("init(options:)");
+ (instancetype)specialJ SWIFT_NAME("init(options:extra:)");
// expected-warning@-1 {{too many parameters in '__swift_name__' attribute (expected 0; got 2)}}
// expected-warning@-1 {{too many parameters in the signature specified by the '__swift_name__' attribute (expected 0; got 2)}}
+ (instancetype)specialK SWIFT_NAME("init(_:)");
// expected-warning@-1 {{too many parameters in '__swift_name__' attribute (expected 0; got 1)}}
// expected-warning@-1 {{too many parameters in the signature specified by the '__swift_name__' attribute (expected 0; got 1)}}
+ (instancetype)specialL SWIFT_NAME("i(options:)");
// expected-warning@-1 {{too many parameters in '__swift_name__' attribute (expected 0; got 1)}}
// expected-warning@-1 {{too many parameters in the signature specified by the '__swift_name__' attribute (expected 0; got 1)}}
+ (instancetype)trailingParen SWIFT_NAME("foo(");
// expected-warning@-1 {{'__swift_name__' attribute argument must be a string literal specifying a Swift function name}}
@ -82,10 +83,10 @@ void f0(int i) SWIFT_NAME("f_0");
// expected-warning@-1 {{'__swift_name__' attribute argument must be a string literal specifying a Swift function name}}
void f1(int i) SWIFT_NAME("f_1()");
// expected-warning@-1 {{too few parameters in '__swift_name__' attribute (expected 1; got 0)}}
// expected-warning@-1 {{too few parameters in the signature specified by the '__swift_name__' attribute (expected 1; got 0)}}
void f2(int i) SWIFT_NAME("f_2(a:b:)");
// expected-warning@-1 {{too many parameters in '__swift_name__' attribute (expected 1; got 2)}}
// expected-warning@-1 {{too many parameters in the signature specified by the '__swift_name__' attribute (expected 1; got 2)}}
void f3(int x, int y) SWIFT_NAME("fWithX(_:y:)");
void f4(int x, int *error) SWIFT_NAME("fWithX(_:)");
@ -95,7 +96,7 @@ typedef int int_t SWIFT_NAME("IntType");
struct Point3D createPoint3D(float x, float y, float z) SWIFT_NAME("Point3D.init(x:y:z:)");
struct Point3D rotatePoint3D(Point3D point, float radians) SWIFT_NAME("Point3D.rotate(self:radians:)");
struct Point3D badRotatePoint3D(Point3D point, float radians) SWIFT_NAME("Point3D.rotate(radians:)");
// expected-warning@-1 {{too few parameters in '__swift_name__' attribute (expected 2; got 1)}}
// expected-warning@-1 {{too few parameters in the signature specified by the '__swift_name__' attribute (expected 2; got 1)}}
extern struct Point3D identityPoint SWIFT_NAME("Point3D.identity");
@ -172,3 +173,33 @@ void g(int i) SWIFT_NAME("function(int:)");
// 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 the signature specified by the '__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 the signature specified by the '__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()");
// expected-error@+1 {{'__swift_async_name__' attribute takes one argument}}
- (void)brokenAttr __attribute__((__swift_async_name__("brokenAttr", 2)));
@end
void asyncFunc(CallbackTy callback) SWIFT_ASYNC_NAME("asyncFunc()");
// expected-warning@+1 {{'__swift_async_name__' attribute cannot be applied to a function with no parameters}}
void asyncNoParams(void) SWIFT_ASYNC_NAME("asyncNoParams()");
// expected-error@+1 {{'__swift_async_name__' attribute only applies to Objective-C methods and functions}}
SWIFT_ASYNC_NAME("NoAsync")
@protocol NoAsync @end

View File

@ -0,0 +1,13 @@
// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s
#define SWIFT_ASYNC_NAME(name) __attribute__((__swift_async_name__(name)))
typedef int (^CallbackTy)(void);
class CXXClass {
public:
virtual void doSomethingWithCallback(CallbackTy callback) SWIFT_ASYNC_NAME("doSomething()");
// expected-warning@+1 {{too few parameters in the signature specified by the '__swift_async_name__' attribute (expected 1; got 0)}}
virtual void doSomethingWithCallback(int x, CallbackTy callback) SWIFT_ASYNC_NAME("doSomething()");
};