Skip to content

Commit f5ab5b2

Browse files
committed
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 llvm/llvm-project-staging@8afaf3a Differential Revision: https://reviews.llvm.org/D87331 Reviewed By: John McCall, Aaron Ballman, Dmitri Gribenko
1 parent ccb4124 commit f5ab5b2

File tree

6 files changed

+260
-0
lines changed

6 files changed

+260
-0
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,6 +2130,17 @@ def Regparm : TypeAttr {
21302130
let ASTNode = 0;
21312131
}
21322132

2133+
def SwiftError : InheritableAttr {
2134+
let Spellings = [GNU<"swift_error">];
2135+
let Args = [
2136+
EnumArgument<"Convention", "ConventionKind",
2137+
["none", "nonnull_error", "null_result", "zero_result", "nonzero_result"],
2138+
["None", "NonNullError", "NullResult", "ZeroResult", "NonZeroResult"]>
2139+
];
2140+
let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>;
2141+
let Documentation = [SwiftErrorDocs];
2142+
}
2143+
21332144
def NoDeref : TypeAttr {
21342145
let Spellings = [Clang<"noderef">];
21352146
let Documentation = [NoDerefDocs];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3469,6 +3469,53 @@ For example:
34693469
}];
34703470
}
34713471

3472+
def SwiftDocs : DocumentationCategory<"Customizing Swift Import"> {
3473+
let Content = [{
3474+
Clang supports additional attributes for customizing how APIs are imported into
3475+
Swift.
3476+
}];
3477+
}
3478+
3479+
def SwiftErrorDocs : Documentation {
3480+
let Category = SwiftDocs;
3481+
let Heading = "swift_error";
3482+
let Content = [{
3483+
The ``swift_error`` attribute controls whether a particular function (or
3484+
Objective-C method) is imported into Swift as a throwing function, and if so,
3485+
which dynamic convention it uses.
3486+
3487+
All of these conventions except ``none`` require the function to have an error
3488+
parameter. Currently, the error parameter is always the last parameter of type
3489+
``NSError**`` or ``CFErrorRef*``. Swift will remove the error parameter from
3490+
the imported API. When calling the API, Swift will always pass a valid address
3491+
initialized to a null pointer.
3492+
3493+
* ``swift_error(none)`` means that the function should not be imported as
3494+
throwing. The error parameter and result type will be imported normally.
3495+
3496+
* ``swift_error(null_result)`` means that calls to the function should be
3497+
considered to have thrown if they return a null value. The return type must be
3498+
a pointer type, and it will be imported into Swift with a non-optional type.
3499+
This is the default error convention for Objective-C methods that return
3500+
pointers.
3501+
3502+
* ``swift_error(zero_result)`` means that calls to the function should be
3503+
considered to have thrown if they return a zero result. The return type must be
3504+
an integral type. If the return type would have been imported as ``Bool``, it
3505+
is instead imported as ``Void``. This is the default error convention for
3506+
Objective-C methods that return a type that would be imported as ``Bool``.
3507+
3508+
* ``swift_error(nonzero_result)`` means that calls to the function should be
3509+
considered to have thrown if they return a non-zero result. The return type must
3510+
be an integral type. If the return type would have been imported as ``Bool``,
3511+
it is instead imported as ``Void``.
3512+
3513+
* ``swift_error(nonnull_error)`` means that calls to the function should be
3514+
considered to have thrown if they leave a non-null error in the error parameter.
3515+
The return type is left unmodified.
3516+
}];
3517+
}
3518+
34723519
def OMPDeclareSimdDocs : Documentation {
34733520
let Category = DocCatFunction;
34743521
let Heading = "#pragma omp declare simd";

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3974,6 +3974,13 @@ def err_objc_bridged_related_known_method : Error<
39743974
def err_objc_attr_protocol_requires_definition : Error<
39753975
"attribute %0 can only be applied to @protocol definitions, not forward declarations">;
39763976

3977+
def err_attr_swift_error_no_error_parameter : Error<
3978+
"%0 attribute can only be applied to a %select{function|method}1 with an "
3979+
"error parameter">;
3980+
def err_attr_swift_error_return_type : Error<
3981+
"%0 attribute with '%1' convention can only be applied to a "
3982+
"%select{function|method}2 returning %select{an integral type|a pointer}3">;
3983+
39773984
def warn_ignored_objc_externally_retained : Warning<
39783985
"'objc_externally_retained' can only be applied to local variables "
39793986
"%select{of retainable type|with strong ownership}0">,

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5524,6 +5524,102 @@ static void handleObjCPreciseLifetimeAttr(Sema &S, Decl *D,
55245524
D->addAttr(::new (S.Context) ObjCPreciseLifetimeAttr(S.Context, AL));
55255525
}
55265526

5527+
static bool isErrorParameter(Sema &S, QualType QT) {
5528+
const auto *PT = QT->getAs<PointerType>();
5529+
if (!PT)
5530+
return false;
5531+
5532+
QualType Pointee = PT->getPointeeType();
5533+
5534+
// Check for NSError**.
5535+
if (const auto *OPT = Pointee->getAs<ObjCObjectPointerType>())
5536+
if (const auto *ID = OPT->getInterfaceDecl())
5537+
if (ID->getIdentifier() == S.getNSErrorIdent())
5538+
return true;
5539+
5540+
// Check for CFError**.
5541+
if (const auto *PT = Pointee->getAs<PointerType>())
5542+
if (const auto *RT = PT->getPointeeType()->getAs<RecordType>())
5543+
if (S.isCFError(RT->getDecl()))
5544+
return true;
5545+
5546+
return false;
5547+
}
5548+
5549+
static void handleSwiftError(Sema &S, Decl *D, const ParsedAttr &AL) {
5550+
auto hasErrorParameter = [](Sema &S, Decl *D, const ParsedAttr &AL) -> bool {
5551+
for (unsigned I = 0, E = getFunctionOrMethodNumParams(D); I != E; ++I) {
5552+
if (isErrorParameter(S, getFunctionOrMethodParamType(D, I)))
5553+
return true;
5554+
}
5555+
5556+
S.Diag(AL.getLoc(), diag::err_attr_swift_error_no_error_parameter)
5557+
<< AL << isa<ObjCMethodDecl>(D);
5558+
return false;
5559+
};
5560+
5561+
auto hasPointerResult = [](Sema &S, Decl *D, const ParsedAttr &AL) -> bool {
5562+
// - C, ObjC, and block pointers are definitely okay.
5563+
// - References are definitely not okay.
5564+
// - nullptr_t is weird, but acceptable.
5565+
QualType RT = getFunctionOrMethodResultType(D);
5566+
if (RT->hasPointerRepresentation() && !RT->isReferenceType())
5567+
return true;
5568+
5569+
S.Diag(AL.getLoc(), diag::err_attr_swift_error_return_type)
5570+
<< AL << AL.getArgAsIdent(0)->Ident->getName() << isa<ObjCMethodDecl>(D)
5571+
<< /*pointer*/ 1;
5572+
return false;
5573+
};
5574+
5575+
auto hasIntegerResult = [](Sema &S, Decl *D, const ParsedAttr &AL) -> bool {
5576+
QualType RT = getFunctionOrMethodResultType(D);
5577+
if (RT->isIntegralType(S.Context))
5578+
return true;
5579+
5580+
S.Diag(AL.getLoc(), diag::err_attr_swift_error_return_type)
5581+
<< AL << AL.getArgAsIdent(0)->Ident->getName() << isa<ObjCMethodDecl>(D)
5582+
<< /*integral*/ 0;
5583+
return false;
5584+
};
5585+
5586+
if (D->isInvalidDecl())
5587+
return;
5588+
5589+
IdentifierLoc *Loc = AL.getArgAsIdent(0);
5590+
SwiftErrorAttr::ConventionKind Convention;
5591+
if (!SwiftErrorAttr::ConvertStrToConventionKind(Loc->Ident->getName(),
5592+
Convention)) {
5593+
S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported)
5594+
<< AL << Loc->Ident;
5595+
return;
5596+
}
5597+
5598+
switch (Convention) {
5599+
case SwiftErrorAttr::None:
5600+
// No additional validation required.
5601+
break;
5602+
5603+
case SwiftErrorAttr::NonNullError:
5604+
if (!hasErrorParameter(S, D, AL))
5605+
return;
5606+
break;
5607+
5608+
case SwiftErrorAttr::NullResult:
5609+
if (!hasErrorParameter(S, D, AL) || !hasPointerResult(S, D, AL))
5610+
return;
5611+
break;
5612+
5613+
case SwiftErrorAttr::NonZeroResult:
5614+
case SwiftErrorAttr::ZeroResult:
5615+
if (!hasErrorParameter(S, D, AL) || !hasIntegerResult(S, D, AL))
5616+
return;
5617+
break;
5618+
}
5619+
5620+
D->addAttr(::new (S.Context) SwiftErrorAttr(S.Context, AL, Convention));
5621+
}
5622+
55275623
//===----------------------------------------------------------------------===//
55285624
// Microsoft specific attribute handlers.
55295625
//===----------------------------------------------------------------------===//
@@ -7436,6 +7532,11 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
74367532
handleTypeTagForDatatypeAttr(S, D, AL);
74377533
break;
74387534

7535+
// Swift attributes.
7536+
case ParsedAttr::AT_SwiftError:
7537+
handleSwiftError(S, D, AL);
7538+
break;
7539+
74397540
// XRay attributes.
74407541
case ParsedAttr::AT_XRayLogArgs:
74417542
handleXRayLogArgsAttr(S, D, AL);

clang/test/Misc/pragma-attribute-supported-attributes-list.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@
147147
// CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member)
148148
// CHECK-NEXT: SpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method)
149149
// CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter)
150+
// CHECK-NEXT: SwiftError (SubjectMatchRule_function, SubjectMatchRule_objc_method)
150151
// CHECK-NEXT: SwiftErrorResult (SubjectMatchRule_variable_is_parameter)
151152
// CHECK-NEXT: SwiftIndirectResult (SubjectMatchRule_variable_is_parameter)
152153
// CHECK-NEXT: TLSModel (SubjectMatchRule_variable_is_thread_local)
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s
2+
3+
@class NSError;
4+
5+
#if __SIZEOF_POINTER__ == 4
6+
typedef unsigned char BOOL;
7+
#else
8+
typedef _Bool BOOL;
9+
#endif
10+
11+
typedef struct __attribute__((__objc_bridge__(NSError))) __CFError *CFErrorRef;
12+
13+
extern int f0(void) __attribute__((__swift_error__));
14+
// expected-error@-1 {{'__swift_error__' attribute takes one argument}}
15+
extern int f1(void) __attribute__((__swift_error__(invalid)));
16+
// expected-warning@-1 {{'__swift_error__' attribute argument not supported: 'invalid'}}
17+
extern int f2(void) __attribute__((__swift_error__(none,zero_result)));
18+
// expected-error@-1 {{use of undeclared identifier 'zero_result'}}
19+
20+
@interface Erroneous
21+
- (BOOL)m0:(NSError **)error __attribute__((__swift_error__(none)));
22+
- (BOOL)m1:(NSError **)error __attribute__((__swift_error__(nonnull_error)));
23+
- (BOOL)m2:(NSError **)error __attribute__((__swift_error__(null_result)));
24+
// expected-error@-1 {{'__swift_error__' attribute with 'null_result' convention can only be applied to a method returning a pointer}}
25+
- (BOOL)m3:(NSError **)error __attribute__((__swift_error__(nonzero_result)));
26+
- (BOOL)m4:(NSError **)error __attribute__((__swift_error__(zero_result)));
27+
28+
- (Undeclared)n0:(NSError **)error __attribute__((__swift_error__(none)));
29+
// expected-error@-1 {{expected a type}}
30+
- (Undeclared)n1:(NSError **)error __attribute__((__swift_error__(nonnull_error)));
31+
// expected-error@-1 {{expected a type}}
32+
- (Undeclared)n2:(NSError **)error __attribute__((__swift_error__(null_result)));
33+
// expected-error@-1 {{expected a type}}
34+
- (Undeclared)n3:(NSError **)error __attribute__((__swift_error__(nonzero_result)));
35+
// expected-error@-1 {{expected a type}}
36+
// FIXME: the follow-on warning should really be suppressed, but apparently
37+
// having an ill-formed return type doesn't mark anything as invalid.
38+
// expected-error@-4 {{can only be applied}}
39+
- (Undeclared)n4:(NSError **)error __attribute__((__swift_error__(zero_result)));
40+
// expected-error@-1 {{expected a type}}
41+
// FIXME: the follow-on warning should really be suppressed, but apparently
42+
// having an ill-formed return type doesn't mark anything as invalid.
43+
// expected-error@-4 {{can only be applied}}
44+
45+
- (instancetype)o0 __attribute__((__swift_error__(none)));
46+
- (instancetype)o1 __attribute__((__swift_error__(nonnull_error)));
47+
// expected-error@-1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}}
48+
- (instancetype)o2 __attribute__((__swift_error__(null_result)));
49+
// expected-error@-1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}}
50+
- (instancetype)o3 __attribute__((__swift_error__(nonzero_result)));
51+
// expected-error@-1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}}
52+
- (instancetype)o4 __attribute__((__swift_error__(zero_result)));
53+
// expected-error@-1 {{'__swift_error__' attribute can only be applied to a method with an error parameter}}
54+
@end
55+
56+
extern BOOL m0(CFErrorRef *) __attribute__((__swift_error__(none)));
57+
extern BOOL m1(CFErrorRef *) __attribute__((__swift_error__(nonnull_error)));
58+
extern BOOL m2(CFErrorRef *) __attribute__((__swift_error__(null_result)));
59+
// expected-error@-1 {{'__swift_error__' attribute with 'null_result' convention can only be applied to a function returning a pointer}}
60+
extern BOOL m3(CFErrorRef *) __attribute__((__swift_error__(nonzero_result)));
61+
extern BOOL m4(CFErrorRef *) __attribute__((__swift_error__(zero_result)));
62+
63+
extern Undeclared n0(CFErrorRef *) __attribute__((__swift_error__(none)));
64+
// expected-error@-1 {{unknown type name 'Undeclared'}}
65+
extern Undeclared n1(CFErrorRef *) __attribute__((__swift_error__(nonnull_error)));
66+
// expected-error@-1 {{unknown type name 'Undeclared'}}
67+
extern Undeclared n2(CFErrorRef *) __attribute__((__swift_error__(null_result)));
68+
// expected-error@-1 {{unknown type name 'Undeclared'}}
69+
extern Undeclared n3(CFErrorRef *) __attribute__((__swift_error__(nonzero_result)));
70+
// expected-error@-1 {{unknown type name 'Undeclared'}}
71+
extern Undeclared n4(CFErrorRef *) __attribute__((__swift_error__(zero_result)));
72+
// expected-error@-1 {{unknown type name 'Undeclared'}}
73+
74+
extern void *o0(CFErrorRef *) __attribute__((__swift_error__(none)));
75+
extern void *o1(CFErrorRef *) __attribute__((__swift_error__(nonnull_error)));
76+
extern void *o2(CFErrorRef *) __attribute__((__swift_error__(null_result)));
77+
extern void *o3(CFErrorRef *) __attribute__((__swift_error__(nonzero_result)));
78+
// expected-error@-1 {{'__swift_error__' attribute with 'nonzero_result' convention can only be applied to a function returning an integral type}}
79+
extern void *o4(CFErrorRef *) __attribute__((__swift_error__(zero_result)));
80+
// expected-error@-1 {{'__swift_error__' attribute with 'zero_result' convention can only be applied to a function returning an integral type}}
81+
82+
extern void *p0(void) __attribute__((__swift_error__(none)));
83+
extern void *p1(void) __attribute__((__swift_error__(nonnull_error)));
84+
// expected-error@-1 {{'__swift_error__' attribute can only be applied to a function with an error parameter}}
85+
extern void *p2(void) __attribute__((__swift_error__(null_result)));
86+
// expected-error@-1 {{'__swift_error__' attribute can only be applied to a function with an error parameter}}
87+
extern void *p3(void) __attribute__((__swift_error__(nonzero_result)));
88+
// expected-error@-1 {{'__swift_error__' attribute can only be applied to a function with an error parameter}}
89+
extern void *p4(void) __attribute__((__swift_error__(zero_result)));
90+
// expected-error@-1 {{'__swift_error__' attribute can only be applied to a function with an error parameter}}
91+
92+
extern BOOL b __attribute__((__swift_error__(none)));
93+
// expected-error@-1 {{attribute only applies to functions and Objective-C methods}}

0 commit comments

Comments
 (0)