Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -2118,6 +2118,17 @@ let ASTNode = 0; } +def SwiftError : InheritableAttr { + let Spellings = [GCC<"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]; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -3374,6 +3374,53 @@ }]; } +def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { + let Content = [{ +Clang supports additional attributes for controlling 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, +the 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, and dynamically 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 left alone. + +* ``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 dedfault 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 integeral 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 integeral 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"; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3971,6 +3971,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 integeral 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">, Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -5524,6 +5524,108 @@ D->addAttr(::new (S.Context) ObjCPreciseLifetimeAttr(S.Context, AL)); } +static bool isErrorParameter(Sema &S, QualType QT) { + auto PT = QT->getAs(); + if (!PT) + return false; + + QualType Pointee = PT->getPointeeType(); + + // Check for NSError**. + if (auto OPT = Pointee->getAs()) + if (auto *ID = OPT->getInterfaceDecl()) + if (ID->getIdentifier() == S.getNSErrorIdent()) + return true; + + // Check for CFError**. + if (auto PT = Pointee->getAs()) + if (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 { + if (D->isInvalidDecl()) + return true; + + 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.getNormalizedFullName() << isa(D); + return false; + }; + + auto hasPointerResult = [](Sema &S, Decl *D, const ParsedAttr &AL) -> bool { + if (D->isInvalidDecl()) + return true; + + // - 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.getNormalizedFullName() << AL.getArgAsIdent(0)->Ident->getName() + << isa(D) << /*pointer*/ 1; + return false; + }; + + auto hasIntegerResult = [](Sema &S, Decl *D, const ParsedAttr &AL) -> bool { + if (D->isInvalidDecl()) + return true; + + QualType RT = getFunctionOrMethodResultType(D); + if (RT->isIntegralType(S.Context)) + return true; + + S.Diag(AL.getLoc(), diag::err_attr_swift_error_return_type) + << AL.getNormalizedFullName() << AL.getArgAsIdent(0)->Ident->getName() + << isa(D) << /*integeral*/ 0; + return false; + }; + + 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.getNormalizedFullName() << 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 +7538,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); Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ 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) Index: clang/test/SemaObjC/attr-swift-error.m =================================================================== --- /dev/null +++ clang/test/SemaObjC/attr-swift-error.m @@ -0,0 +1,86 @@ +// 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; + +@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 integeral 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 integeral 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}}