[clang] f5ab5b2 - Sema: add support for `__attribute__((__swift_error__))`

Saleem Abdulrasool via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 11 14:21:13 PDT 2020


Author: Saleem Abdulrasool
Date: 2020-09-11T21:20:38Z
New Revision: f5ab5b20fb2aae5567e6c50cc642ff63eb2146d4

URL: https://github.com/llvm/llvm-project/commit/f5ab5b20fb2aae5567e6c50cc642ff63eb2146d4
DIFF: https://github.com/llvm/llvm-project/commit/f5ab5b20fb2aae5567e6c50cc642ff63eb2146d4.diff

LOG: Sema: add support for `__attribute__((__swift_error__))`

Introduce a new attribute that is used to indicate the error handling
convention used by a function.  This is used to translate the error
semantics from the decorated interface to a compatible Swift interface.

The supported error convention is one of:
- none: no error handling
- nonnull_error: a non-null error parameter indicates an error signifier
- null_result: a return value of NULL is an error signifier
- zero_result: a return value of 0 is an error signifier
- nonzero_result: a non-zero return value is an error signifier

Since this is the first of the attributes needed to support the semantic
annotation for Swift, this change also includes the necessary supporting
infrastructure for a new category of attributes (Swift).

This is based on the work of the original changes in
https://github.com/llvm/llvm-project-staging/commit/8afaf3aad2af43cfedca7a24cd817848c4e95c0c

Differential Revision: https://reviews.llvm.org/D87331
Reviewed By: John McCall, Aaron Ballman, Dmitri Gribenko

Added: 
    clang/test/SemaObjC/attr-swift-error.m

Modified: 
    clang/include/clang/Basic/Attr.td
    clang/include/clang/Basic/AttrDocs.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaDeclAttr.cpp
    clang/test/Misc/pragma-attribute-supported-attributes-list.test

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 5676e9aa1678..1790ae01497f 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2130,6 +2130,17 @@ def Regparm : TypeAttr {
   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
index 6daf9ca67896..842ffe050adc 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3469,6 +3469,53 @@ For example:
   }];
 }
 
+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
index 98dc6dfba4ef..e0d700c66724 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3974,6 +3974,13 @@ def err_objc_bridged_related_known_method : Error<
 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
index 49fd22fb2198..e317211d8bee 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5524,6 +5524,102 @@ static void handleObjCPreciseLifetimeAttr(Sema &S, Decl *D,
   D->addAttr(::new (S.Context) ObjCPreciseLifetimeAttr(S.Context, AL));
 }
 
+static bool isErrorParameter(Sema &S, QualType QT) {
+  const auto *PT = QT->getAs<PointerType>();
+  if (!PT)
+    return false;
+
+  QualType Pointee = PT->getPointeeType();
+
+  // Check for NSError**.
+  if (const auto *OPT = Pointee->getAs<ObjCObjectPointerType>())
+    if (const auto *ID = OPT->getInterfaceDecl())
+      if (ID->getIdentifier() == S.getNSErrorIdent())
+        return true;
+
+  // Check for CFError**.
+  if (const auto *PT = Pointee->getAs<PointerType>())
+    if (const auto *RT = PT->getPointeeType()->getAs<RecordType>())
+      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<ObjCMethodDecl>(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<ObjCMethodDecl>(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<ObjCMethodDecl>(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 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
     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
index 194c92e40eec..12800b9d54ea 100644
--- 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
index 000000000000..0132a8b200f5
--- /dev/null
+++ b/clang/test/SemaObjC/attr-swift-error.m
@@ -0,0 +1,93 @@
+// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s
+
+ at 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 at -1 {{'__swift_error__' attribute takes one argument}}
+extern int f1(void) __attribute__((__swift_error__(invalid)));
+// expected-warning at -1 {{'__swift_error__' attribute argument not supported: 'invalid'}}
+extern int f2(void) __attribute__((__swift_error__(none,zero_result)));
+// expected-error at -1 {{use of undeclared identifier 'zero_result'}}
+
+ at 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 at -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 at -1 {{expected a type}}
+- (Undeclared)n1:(NSError **)error __attribute__((__swift_error__(nonnull_error)));
+// expected-error at -1 {{expected a type}}
+- (Undeclared)n2:(NSError **)error __attribute__((__swift_error__(null_result)));
+// expected-error at -1 {{expected a type}}
+- (Undeclared)n3:(NSError **)error __attribute__((__swift_error__(nonzero_result)));
+// expected-error at -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 at -4 {{can only be applied}}
+- (Undeclared)n4:(NSError **)error __attribute__((__swift_error__(zero_result)));
+// expected-error at -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 at -4 {{can only be applied}}
+
+- (instancetype)o0 __attribute__((__swift_error__(none)));
+- (instancetype)o1 __attribute__((__swift_error__(nonnull_error)));
+// expected-error at -1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}}
+- (instancetype)o2 __attribute__((__swift_error__(null_result)));
+// expected-error at -1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}}
+- (instancetype)o3 __attribute__((__swift_error__(nonzero_result)));
+// expected-error at -1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}}
+- (instancetype)o4 __attribute__((__swift_error__(zero_result)));
+// expected-error at -1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}}
+ at 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 at -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 at -1 {{unknown type name 'Undeclared'}}
+extern Undeclared n1(CFErrorRef *) __attribute__((__swift_error__(nonnull_error)));
+// expected-error at -1 {{unknown type name 'Undeclared'}}
+extern Undeclared n2(CFErrorRef *) __attribute__((__swift_error__(null_result)));
+// expected-error at -1 {{unknown type name 'Undeclared'}}
+extern Undeclared n3(CFErrorRef *) __attribute__((__swift_error__(nonzero_result)));
+// expected-error at -1 {{unknown type name 'Undeclared'}}
+extern Undeclared n4(CFErrorRef *) __attribute__((__swift_error__(zero_result)));
+// expected-error at -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 at -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 at -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 at -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 at -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 at -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 at -1 {{'__swift_error__' attribute can only be applied to a function with an error parameter}}
+
+extern BOOL b __attribute__((__swift_error__(none)));
+// expected-error at -1 {{attribute only applies to functions and Objective-C methods}}


        


More information about the cfe-commits mailing list