compnerd created this revision.
compnerd added a reviewer: aaron.ballman.
Herald added a reviewer: jdoerfert.
Herald added a project: clang.
compnerd requested review of this revision.

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


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D87331

Files:
  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
  clang/test/SemaObjC/attr-swift-error.m

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}}
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/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<PointerType>();
+  if (!PT)
+    return false;
+
+  QualType Pointee = PT->getPointeeType();
+
+  // Check for NSError**.
+  if (auto OPT = Pointee->getAs<ObjCObjectPointerType>())
+    if (auto *ID = OPT->getInterfaceDecl())
+      if (ID->getIdentifier() == S.getNSErrorIdent())
+        return true;
+
+  // Check for CFError**.
+  if (auto PT = Pointee->getAs<PointerType>())
+    if (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 {
+    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<ObjCMethodDecl>(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<ObjCMethodDecl>(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<ObjCMethodDecl>(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/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/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/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];
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to