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 @@ -2130,6 +2130,17 @@ let ASTNode = 0; } +def SwiftError : InheritableAttr { + let Spellings = [GNU<"swift_error">]; + let Args = [ + EnumArgument<"Convention", "ConventionKind", + ["none", "nonnull_error", "null_result", "zero_result", "nonzero_result"], + ["None", "NonNullError", "NullResult", "ZeroResult", "NonZeroResult"]> + ]; + let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>; + let Documentation = [SwiftErrorDocs]; +} + def NoDeref : TypeAttr { let Spellings = [Clang<"noderef">]; let Documentation = [NoDerefDocs]; 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 @@ -3469,6 +3469,53 @@ }]; } +def SwiftDocs : DocumentationCategory<"Customizing Swift Import"> { + let Content = [{ +Clang supports additional attributes for customizing how APIs are imported into +Swift. + }]; +} + +def SwiftErrorDocs : Documentation { + let Category = SwiftDocs; + let Heading = "swift_error"; + let Content = [{ +The ``swift_error`` attribute controls whether a particular function (or +Objective-C method) is imported into Swift as a throwing function, and if so, +which dynamic convention it uses. + +All of these conventions except ``none`` require the function to have an error +parameter. Currently, the error parameter is always the last parameter of type +``NSError**`` or ``CFErrorRef*``. Swift will remove the error parameter from +the imported API. When calling the API, Swift will always pass a valid address +initialized to a null pointer. + +* ``swift_error(none)`` means that the function should not be imported as +throwing. The error parameter and result type will be imported normally. + +* ``swift_error(null_result)`` means that calls to the function should be +considered to have thrown if they return a null value. The return type must be +a pointer type, and it will be imported into Swift with a non-optional type. +This is the default error convention for Objective-C methods that return +pointers. + +* ``swift_error(zero_result)`` means that calls to the function should be +considered to have thrown if they return a zero result. The return type must be +an integral type. If the return type would have been imported as ``Bool``, it +is instead imported as ``Void``. This is the default error convention for +Objective-C methods that return a type that would be imported as ``Bool``. + +* ``swift_error(nonzero_result)`` means that calls to the function should be +considered to have thrown if they return a non-zero result. The return type must +be an integral type. If the return type would have been imported as ``Bool``, +it is instead imported as ``Void``. + +* ``swift_error(nonnull_error)`` means that calls to the function should be +considered to have thrown if they leave a non-null error in the error parameter. +The return type is left unmodified. + }]; +} + def OMPDeclareSimdDocs : Documentation { let Category = DocCatFunction; let Heading = "#pragma omp declare simd"; 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 @@ -3974,6 +3974,13 @@ def err_objc_attr_protocol_requires_definition : Error< "attribute %0 can only be applied to @protocol definitions, not forward declarations">; +def err_attr_swift_error_no_error_parameter : Error< + "%0 attribute can only be applied to a %select{function|method}1 with an " + "error parameter">; +def err_attr_swift_error_return_type : Error< + "%0 attribute with '%1' convention can only be applied to a " + "%select{function|method}2 returning %select{an integral type|a pointer}3">; + 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 @@ -5524,6 +5524,102 @@ D->addAttr(::new (S.Context) ObjCPreciseLifetimeAttr(S.Context, AL)); } +static bool isErrorParameter(Sema &S, QualType QT) { + const auto *PT = QT->getAs(); + if (!PT) + return false; + + QualType Pointee = PT->getPointeeType(); + + // Check for NSError**. + if (const auto *OPT = Pointee->getAs()) + if (const auto *ID = OPT->getInterfaceDecl()) + if (ID->getIdentifier() == S.getNSErrorIdent()) + return true; + + // Check for CFError**. + if (const auto *PT = Pointee->getAs()) + if (const auto *RT = PT->getPointeeType()->getAs()) + if (S.isCFError(RT->getDecl())) + return true; + + return false; +} + +static void handleSwiftError(Sema &S, Decl *D, const ParsedAttr &AL) { + auto hasErrorParameter = [](Sema &S, Decl *D, const ParsedAttr &AL) -> bool { + for (unsigned I = 0, E = getFunctionOrMethodNumParams(D); I != E; ++I) { + if (isErrorParameter(S, getFunctionOrMethodParamType(D, I))) + return true; + } + + S.Diag(AL.getLoc(), diag::err_attr_swift_error_no_error_parameter) + << AL << isa(D); + return false; + }; + + auto hasPointerResult = [](Sema &S, Decl *D, const ParsedAttr &AL) -> bool { + // - C, ObjC, and block pointers are definitely okay. + // - References are definitely not okay. + // - nullptr_t is weird, but acceptable. + QualType RT = getFunctionOrMethodResultType(D); + if (RT->hasPointerRepresentation() && !RT->isReferenceType()) + return true; + + S.Diag(AL.getLoc(), diag::err_attr_swift_error_return_type) + << AL << AL.getArgAsIdent(0)->Ident->getName() << isa(D) + << /*pointer*/ 1; + return false; + }; + + auto hasIntegerResult = [](Sema &S, Decl *D, const ParsedAttr &AL) -> bool { + QualType RT = getFunctionOrMethodResultType(D); + if (RT->isIntegralType(S.Context)) + return true; + + S.Diag(AL.getLoc(), diag::err_attr_swift_error_return_type) + << AL << AL.getArgAsIdent(0)->Ident->getName() << isa(D) + << /*integral*/ 0; + return false; + }; + + if (D->isInvalidDecl()) + return; + + IdentifierLoc *Loc = AL.getArgAsIdent(0); + SwiftErrorAttr::ConventionKind Convention; + if (!SwiftErrorAttr::ConvertStrToConventionKind(Loc->Ident->getName(), + Convention)) { + S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) + << AL << Loc->Ident; + return; + } + + switch (Convention) { + case SwiftErrorAttr::None: + // No additional validation required. + break; + + case SwiftErrorAttr::NonNullError: + if (!hasErrorParameter(S, D, AL)) + return; + break; + + case SwiftErrorAttr::NullResult: + if (!hasErrorParameter(S, D, AL) || !hasPointerResult(S, D, AL)) + return; + break; + + case SwiftErrorAttr::NonZeroResult: + case SwiftErrorAttr::ZeroResult: + if (!hasErrorParameter(S, D, AL) || !hasIntegerResult(S, D, AL)) + return; + break; + } + + D->addAttr(::new (S.Context) SwiftErrorAttr(S.Context, AL, Convention)); +} + //===----------------------------------------------------------------------===// // Microsoft specific attribute handlers. //===----------------------------------------------------------------------===// @@ -7436,6 +7532,11 @@ handleTypeTagForDatatypeAttr(S, D, AL); break; + // Swift attributes. + case ParsedAttr::AT_SwiftError: + handleSwiftError(S, D, AL); + break; + // XRay attributes. case ParsedAttr::AT_XRayLogArgs: handleXRayLogArgsAttr(S, D, AL); 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: SetTypestate (SubjectMatchRule_function_is_member) // CHECK-NEXT: SpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: SwiftError (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: SwiftErrorResult (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: SwiftIndirectResult (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: TLSModel (SubjectMatchRule_variable_is_thread_local) diff --git a/clang/test/SemaObjC/attr-swift-error.m b/clang/test/SemaObjC/attr-swift-error.m new file mode 100644 --- /dev/null +++ b/clang/test/SemaObjC/attr-swift-error.m @@ -0,0 +1,93 @@ +// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s + +@class NSError; + +#if __SIZEOF_POINTER__ == 4 +typedef unsigned char BOOL; +#else +typedef _Bool BOOL; +#endif + +typedef struct __attribute__((__objc_bridge__(NSError))) __CFError *CFErrorRef; + +extern int f0(void) __attribute__((__swift_error__)); +// expected-error@-1 {{'__swift_error__' attribute takes one argument}} +extern int f1(void) __attribute__((__swift_error__(invalid))); +// expected-warning@-1 {{'__swift_error__' attribute argument not supported: 'invalid'}} +extern int f2(void) __attribute__((__swift_error__(none,zero_result))); +// expected-error@-1 {{use of undeclared identifier 'zero_result'}} + +@interface Erroneous +- (BOOL)m0:(NSError **)error __attribute__((__swift_error__(none))); +- (BOOL)m1:(NSError **)error __attribute__((__swift_error__(nonnull_error))); +- (BOOL)m2:(NSError **)error __attribute__((__swift_error__(null_result))); +// expected-error@-1 {{'__swift_error__' attribute with 'null_result' convention can only be applied to a method returning a pointer}} +- (BOOL)m3:(NSError **)error __attribute__((__swift_error__(nonzero_result))); +- (BOOL)m4:(NSError **)error __attribute__((__swift_error__(zero_result))); + +- (Undeclared)n0:(NSError **)error __attribute__((__swift_error__(none))); +// expected-error@-1 {{expected a type}} +- (Undeclared)n1:(NSError **)error __attribute__((__swift_error__(nonnull_error))); +// expected-error@-1 {{expected a type}} +- (Undeclared)n2:(NSError **)error __attribute__((__swift_error__(null_result))); +// expected-error@-1 {{expected a type}} +- (Undeclared)n3:(NSError **)error __attribute__((__swift_error__(nonzero_result))); +// expected-error@-1 {{expected a type}} +// FIXME: the follow-on warning should really be suppressed, but apparently +// having an ill-formed return type doesn't mark anything as invalid. +// expected-error@-4 {{can only be applied}} +- (Undeclared)n4:(NSError **)error __attribute__((__swift_error__(zero_result))); +// expected-error@-1 {{expected a type}} +// FIXME: the follow-on warning should really be suppressed, but apparently +// having an ill-formed return type doesn't mark anything as invalid. +// expected-error@-4 {{can only be applied}} + +- (instancetype)o0 __attribute__((__swift_error__(none))); +- (instancetype)o1 __attribute__((__swift_error__(nonnull_error))); +// expected-error@-1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}} +- (instancetype)o2 __attribute__((__swift_error__(null_result))); +// expected-error@-1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}} +- (instancetype)o3 __attribute__((__swift_error__(nonzero_result))); +// expected-error@-1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}} +- (instancetype)o4 __attribute__((__swift_error__(zero_result))); +// expected-error@-1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}} +@end + +extern BOOL m0(CFErrorRef *) __attribute__((__swift_error__(none))); +extern BOOL m1(CFErrorRef *) __attribute__((__swift_error__(nonnull_error))); +extern BOOL m2(CFErrorRef *) __attribute__((__swift_error__(null_result))); +// expected-error@-1 {{'__swift_error__' attribute with 'null_result' convention can only be applied to a function returning a pointer}} +extern BOOL m3(CFErrorRef *) __attribute__((__swift_error__(nonzero_result))); +extern BOOL m4(CFErrorRef *) __attribute__((__swift_error__(zero_result))); + +extern Undeclared n0(CFErrorRef *) __attribute__((__swift_error__(none))); +// expected-error@-1 {{unknown type name 'Undeclared'}} +extern Undeclared n1(CFErrorRef *) __attribute__((__swift_error__(nonnull_error))); +// expected-error@-1 {{unknown type name 'Undeclared'}} +extern Undeclared n2(CFErrorRef *) __attribute__((__swift_error__(null_result))); +// expected-error@-1 {{unknown type name 'Undeclared'}} +extern Undeclared n3(CFErrorRef *) __attribute__((__swift_error__(nonzero_result))); +// expected-error@-1 {{unknown type name 'Undeclared'}} +extern Undeclared n4(CFErrorRef *) __attribute__((__swift_error__(zero_result))); +// expected-error@-1 {{unknown type name 'Undeclared'}} + +extern void *o0(CFErrorRef *) __attribute__((__swift_error__(none))); +extern void *o1(CFErrorRef *) __attribute__((__swift_error__(nonnull_error))); +extern void *o2(CFErrorRef *) __attribute__((__swift_error__(null_result))); +extern void *o3(CFErrorRef *) __attribute__((__swift_error__(nonzero_result))); +// expected-error@-1 {{'__swift_error__' attribute with 'nonzero_result' convention can only be applied to a function returning an integral type}} +extern void *o4(CFErrorRef *) __attribute__((__swift_error__(zero_result))); +// expected-error@-1 {{'__swift_error__' attribute with 'zero_result' convention can only be applied to a function returning an integral type}} + +extern void *p0(void) __attribute__((__swift_error__(none))); +extern void *p1(void) __attribute__((__swift_error__(nonnull_error))); +// expected-error@-1 {{'__swift_error__' attribute can only be applied to a function with an error parameter}} +extern void *p2(void) __attribute__((__swift_error__(null_result))); +// expected-error@-1 {{'__swift_error__' attribute can only be applied to a function with an error parameter}} +extern void *p3(void) __attribute__((__swift_error__(nonzero_result))); +// expected-error@-1 {{'__swift_error__' attribute can only be applied to a function with an error parameter}} +extern void *p4(void) __attribute__((__swift_error__(zero_result))); +// expected-error@-1 {{'__swift_error__' attribute can only be applied to a function with an error parameter}} + +extern BOOL b __attribute__((__swift_error__(none))); +// expected-error@-1 {{attribute only applies to functions and Objective-C methods}}