Make NS_ENUM &c. Sendable even when audited

An explicit swift_attr("@_nonSendable") will override it (except for ns_error_domain where the type is embedded in another type that's forced to be Sendable), but swift_attr("@_nonSendable(_assumed)") will not.
This commit is contained in:
Becca Royal-Gordon
2021-11-12 14:34:02 -08:00
parent 8007d70659
commit ad0e7096ba
11 changed files with 171 additions and 29 deletions

View File

@@ -2231,6 +2231,11 @@ public:
/// attribute list, or \c nullptr if there are none.
const DeclAttribute *getEffectiveSendableAttr() const;
DeclAttribute *getEffectiveSendableAttr() {
return const_cast<DeclAttribute *>(
const_cast<const DeclAttributes *>(this)->getEffectiveSendableAttr());
}
private:
/// Predicate used to filter MatchingAttributeRange.
template <typename ATTR, bool AllowInvalid> struct ToAttributeKind {

View File

@@ -85,6 +85,10 @@ NOTE(unresolvable_clang_decl_is_a_framework_bug,none,
WARNING(clang_swift_attr_unhandled,none,
"Ignoring unknown Swift attribute or modifier '%0'", (StringRef))
WARNING(clang_error_code_must_be_sendable,none,
"cannot make error code type '%0' non-sendable because Swift errors "
"are always sendable", (StringRef))
WARNING(implicit_bridging_header_imported_from_module,none,
"implicit import of bridging header '%0' via module %1 "
"is deprecated and will be removed in a later version of Swift",

View File

@@ -8498,6 +8498,36 @@ ClangImporter::Implementation::importSwiftAttrAttributes(Decl *MappedDecl) {
}
}
// The rest of this concerns '@Sendable' and '@_nonSendable`. These don't
// affect typealiases, even when there's an underlying nominal type in clang.
if (isa<TypeAliasDecl>(MappedDecl))
return;
// Some types have an implicit '@Sendable' attribute.
if (ClangDecl->hasAttr<clang::SwiftNewTypeAttr>() ||
ClangDecl->hasAttr<clang::EnumExtensibilityAttr>() ||
ClangDecl->hasAttr<clang::FlagEnumAttr>() ||
ClangDecl->hasAttr<clang::NSErrorDomainAttr>())
MappedDecl->getAttrs().add(
new (SwiftContext) SendableAttr(/*isImplicit=*/true));
// 'Error' conforms to 'Sendable', so error wrappers have to be 'Sendable'
// and it doesn't make sense for the 'Code' enum to be non-'Sendable'.
if (ClangDecl->hasAttr<clang::NSErrorDomainAttr>()) {
// If any @_nonSendable attributes are running the show, invalidate and
// diagnose them.
while (NonSendableAttr *attr = dyn_cast_or_null<NonSendableAttr>(
MappedDecl->getAttrs().getEffectiveSendableAttr())) {
assert(attr->Specificity == NonSendableKind::Specific &&
"didn't we just add an '@Sendable' that should beat this "
"'@_nonSendable(_assumed)'?");
attr->setInvalid();
diagnose(HeaderLoc(ClangDecl->getLocation()),
diag::clang_error_code_must_be_sendable,
ClangDecl->getNameAsString());
}
}
// Now that we've collected all @Sendable and @_nonSendable attributes, we
// can see if we should synthesize a Sendable conformance.
if (auto nominal = dyn_cast<NominalTypeDecl>(MappedDecl)) {

View File

@@ -124,7 +124,7 @@ class ObjCTest {
#endif
// HEADER: struct TestError : _BridgedStoredNSError {
// HEADER: enum Code : Int32, _ErrorCodeProtocol {
// HEADER: enum Code : Int32, _ErrorCodeProtocol, @unchecked Sendable {
// HEADER: init?(rawValue: Int32)
// HEADER: var rawValue: Int32 { get }
// HEADER: typealias _ErrorType = TestError
@@ -136,7 +136,7 @@ class ObjCTest {
// HEADER: func getErr() -> TestError.Code
// HEADER-NO-PRIVATE: struct TestError : CustomNSError, Hashable, Error {
// HEADER-NO-PRIVATE: enum Code : Int32, Equatable {
// HEADER-NO-PRIVATE: enum Code : Int32, @unchecked Sendable, Equatable {
// HEADER-NO-PRIVATE: init?(rawValue: Int32)
// HEADER-NO-PRIVATE: var rawValue: Int32 { get }
// HEADER-NO-PRIVATE: typealias _ErrorType = TestError

View File

@@ -1,4 +1,4 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -disable-availability-checking %s -verify -warn-concurrency
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -disable-availability-checking %s -verify -verify-additional-file %swift_src_root/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h -warn-concurrency
// REQUIRES: objc_interop
// REQUIRES: concurrency
@@ -111,15 +111,37 @@ func testSendableInAsync() async {
print(x)
}
func testSendableClasses(sendable: SendableClass, nonSendable: NonSendableClass) async {
func testSendableAttrs(
sendableClass: SendableClass, nonSendableClass: NonSendableClass,
sendableEnum: SendableEnum, nonSendableEnum: NonSendableEnum,
sendableOptions: SendableOptions, nonSendableOptions: NonSendableOptions,
sendableError: SendableError, nonSendableError: NonSendableError,
sendableStringEnum: SendableStringEnum, nonSendableStringEnum: NonSendableStringEnum,
sendableStringStruct: SendableStringStruct, nonSendableStringStruct: NonSendableStringStruct
) async {
func takesSendable<T: Sendable>(_: T) {}
takesSendable(sendable) // no-error
takesSendable(nonSendable) // expected-FIXME-warning{{something about missing conformance}}
takesSendable(sendableClass) // no-error
takesSendable(nonSendableClass) // expected-FIXME-warning{{something about missing conformance}}
doSomethingConcurrently {
print(sendable) // no-error
print(nonSendable) // expected-warning{{cannot use parameter 'nonSendable' with a non-sendable type 'NonSendableClass' from concurrently-executed code}}
print(sendableClass) // no-error
print(nonSendableClass) // expected-warning{{cannot use parameter 'nonSendableClass' with a non-sendable type 'NonSendableClass' from concurrently-executed code}}
print(sendableEnum) // no-error
print(nonSendableEnum) // expected-warning{{cannot use parameter 'nonSendableEnum' with a non-sendable type 'NonSendableEnum' from concurrently-executed code}}
print(sendableOptions) // no-error
print(nonSendableOptions) // expected-warning{{cannot use parameter 'nonSendableOptions' with a non-sendable type 'NonSendableOptions' from concurrently-executed code}}
print(sendableError) // no-error
print(nonSendableError) // no-error--we don't respect `@_nonSendable` on `ns_error_domain` types because all errors are Sendable
print(sendableStringEnum) // no-error
print(nonSendableStringEnum) // expected-warning{{cannot use parameter 'nonSendableStringEnum' with a non-sendable type 'NonSendableStringEnum' from concurrently-executed code}}
print(sendableStringStruct) // no-error
print(nonSendableStringStruct) // expected-warning{{cannot use parameter 'nonSendableStringStruct' with a non-sendable type 'NonSendableStringStruct' from concurrently-executed code}}
}
}

View File

@@ -5,7 +5,7 @@
// RUN: %target-typecheck-verify-swift -sdk %clang-importer-sdk -I %S/Inputs/custom-modules -I %t
// REQUIRES: objc_interop
// PRINT-LABEL: struct ErrorDomain : _ObjectiveCBridgeable, Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-LABEL: struct ErrorDomain : _ObjectiveCBridgeable, Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: String)
// PRINT-NEXT: init(rawValue: String)
// PRINT-NEXT: var rawValue: String { get }
@@ -26,7 +26,7 @@
// PRINT-NEXT: extension Food {
// PRINT-NEXT: static let err: ErrorDomain
// PRINT-NEXT: }
// PRINT-NEXT: struct ClosedEnum : _ObjectiveCBridgeable, Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-NEXT: struct ClosedEnum : _ObjectiveCBridgeable, Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(rawValue: String)
// PRINT-NEXT: var rawValue: String { get }
// PRINT-NEXT: typealias RawValue = String
@@ -37,14 +37,14 @@
// PRINT-NEXT: static let secondEntry: ClosedEnum
// PRINT-NEXT: static let thirdEntry: ClosedEnum
// PRINT-NEXT: }
// PRINT-NEXT: struct IUONewtype : _ObjectiveCBridgeable, Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-NEXT: struct IUONewtype : _ObjectiveCBridgeable, Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: String)
// PRINT-NEXT: init(rawValue: String)
// PRINT-NEXT: var rawValue: String { get }
// PRINT-NEXT: typealias RawValue = String
// PRINT-NEXT: typealias _ObjectiveCType = NSString
// PRINT-NEXT: }
// PRINT-NEXT: struct MyFloat : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-NEXT: struct MyFloat : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: Float)
// PRINT-NEXT: init(rawValue: Float)
// PRINT-NEXT: let rawValue: Float
@@ -56,7 +56,7 @@
// PRINT-NEXT: static let version: MyFloat{{$}}
// PRINT-NEXT: }
//
// PRINT-LABEL: struct MyInt : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-LABEL: struct MyInt : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: Int32)
// PRINT-NEXT: init(rawValue: Int32)
// PRINT-NEXT: let rawValue: Int32
@@ -83,7 +83,7 @@
// PRINT-NEXT: let Notification: String
// PRINT-NEXT: let swiftNamedNotification: String
//
// PRINT-LABEL: struct CFNewType : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-LABEL: struct CFNewType : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: CFString)
// PRINT-NEXT: init(rawValue: CFString)
// PRINT-NEXT: let rawValue: CFString
@@ -97,7 +97,7 @@
// PRINT-NEXT: func FooAudited() -> CFNewType
// PRINT-NEXT: func FooUnaudited() -> Unmanaged<CFString>
//
// PRINT-LABEL: struct MyABINewType : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-LABEL: struct MyABINewType : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: CFString)
// PRINT-NEXT: init(rawValue: CFString)
// PRINT-NEXT: let rawValue: CFString
@@ -114,7 +114,7 @@
// PRINT-NEXT: func takeMyABIOldType(_: MyABIOldType!)
// PRINT-NEXT: func takeMyABINewTypeNonNull(_: MyABINewType)
// PRINT-NEXT: func takeMyABIOldTypeNonNull(_: MyABIOldType)
// PRINT-LABEL: struct MyABINewTypeNS : _ObjectiveCBridgeable, Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-LABEL: struct MyABINewTypeNS : _ObjectiveCBridgeable, Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: String)
// PRINT-NEXT: init(rawValue: String)
// PRINT-NEXT: var rawValue: String { get }
@@ -133,7 +133,7 @@
// PRINT-NEXT: var i: Int32
// PRINT-NEXT: }
// PRINT-NEXT: extension NSSomeContext {
// PRINT-NEXT: struct Name : _ObjectiveCBridgeable, Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-NEXT: struct Name : _ObjectiveCBridgeable, Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: String)
// PRINT-NEXT: init(rawValue: String)
// PRINT-NEXT: var rawValue: String { get }
@@ -145,13 +145,13 @@
// PRINT-NEXT: static let myContextName: NSSomeContext.Name
// PRINT-NEXT: }
//
// PRINT-NEXT: struct TRef : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-NEXT: struct TRef : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: OpaquePointer)
// PRINT-NEXT: init(rawValue: OpaquePointer)
// PRINT-NEXT: let rawValue: OpaquePointer
// PRINT-NEXT: typealias RawValue = OpaquePointer
// PRINT-NEXT: }
// PRINT-NEXT: struct ConstTRef : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-NEXT: struct ConstTRef : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: OpaquePointer)
// PRINT-NEXT: init(rawValue: OpaquePointer)
// PRINT-NEXT: let rawValue: OpaquePointer
@@ -169,13 +169,13 @@
// PRINT-NEXT: func use()
// PRINT-NEXT: }
//
// PRINT-NEXT: struct TRefRef : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-NEXT: struct TRefRef : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: UnsafeMutablePointer<OpaquePointer>)
// PRINT-NEXT: init(rawValue: UnsafeMutablePointer<OpaquePointer>)
// PRINT-NEXT: let rawValue: UnsafeMutablePointer<OpaquePointer>
// PRINT-NEXT: typealias RawValue = UnsafeMutablePointer<OpaquePointer>
// PRINT-NEXT: }
// PRINT-NEXT: struct ConstTRefRef : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable {
// PRINT-NEXT: struct ConstTRefRef : Hashable, Equatable, _SwiftNewtypeWrapper, RawRepresentable, @unchecked Sendable {
// PRINT-NEXT: init(_ rawValue: UnsafePointer<OpaquePointer>)
// PRINT-NEXT: init(rawValue: UnsafePointer<OpaquePointer>)
// PRINT-NEXT: let rawValue: UnsafePointer<OpaquePointer>

View File

@@ -5,21 +5,21 @@
// REQUIRES: objc_interop
// CHECK: enum Normal : Int {
// CHECK-LABEL: enum Normal : Int, @unchecked Sendable {
// CHECK-NOT: {{^}}}
// CHECK: case one
// CHECK-NEXT: case two
// CHECK-NEXT: case three
// CHECK-NEXT: }
// CHECK: enum SwiftEnum : Int {
// CHECK-LABEL: enum SwiftEnum : Int, @unchecked Sendable {
// CHECK-NOT: {{^}}}
// CHECK: case one
// CHECK-NEXT: case two
// CHECK-NEXT: case three
// CHECK-NEXT: }
// CHECK: enum SwiftEnumTwo : Int {
// CHECK-LABEL: enum SwiftEnumTwo : Int, @unchecked Sendable {
// CHECK-NOT: {{^}}}
// CHECK: case SwiftEnumTwoA
// CHECK-NEXT: case SwiftEnumTwoB

View File

@@ -23,6 +23,7 @@ import _Concurrency
// CHECK-SAME: @unchecked Sendable
// CHECK-LABEL: class NonSendableClass
// CHECK-NOT: @unchecked Sendable
// CHECK: @available(*, unavailable)
// CHECK-NEXT: extension NonSendableClass : @unchecked Sendable {
@@ -30,10 +31,54 @@ import _Concurrency
// CHECK-SAME: @unchecked Sendable
// CHECK-LABEL: class AuditedNonSendable
// CHECK-NOT: @unchecked Sendable
// CHECK: @available(*, unavailable)
// CHECK-NEXT: extension AuditedNonSendable : @unchecked Sendable {
// CHECK-LABEL: class AuditedBoth
// CHECK-NOT: @unchecked Sendable
// CHECK: @available(*, unavailable)
// CHECK-NEXT: extension AuditedBoth : @unchecked Sendable {
// CHECK-LABEL: enum SendableEnum :
// CHECK-SAME: @unchecked Sendable
// CHECK-LABEL: enum NonSendableEnum :
// CHECK-NOT: @unchecked Sendable
// CHECK: @available(*, unavailable)
// CHECK-NEXT: extension NonSendableEnum : @unchecked Sendable {
// CHECK-LABEL: struct SendableOptions :
// CHECK-SAME: @unchecked Sendable
// CHECK-LABEL: struct NonSendableOptions :
// CHECK-NOT: @unchecked Sendable
// CHECK: @available(*, unavailable)
// CHECK-NEXT: extension NonSendableOptions : @unchecked Sendable {
// CHECK-LABEL: public struct SendableError :
// CHECK-NOT: @unchecked Sendable
// CHECK: public enum Code :
// CHECK-SAME: @unchecked Sendable
// CHECK-LABEL: public struct NonSendableError :
// CHECK-NOT: @unchecked Sendable
// CHECK: public enum Code :
// CHECK-SAME: @unchecked Sendable
// CHECK-LABEL: struct SendableStringEnum :
// CHECK-SAME: @unchecked Sendable
// CHECK-LABEL: struct NonSendableStringEnum :
// CHECK-NOT: @unchecked Sendable
// CHECK: @available(*, unavailable)
// CHECK-NEXT: extension NonSendableStringEnum : @unchecked Sendable {
// CHECK-LABEL: struct SendableStringStruct :
// CHECK-SAME: @unchecked Sendable
// CHECK-LABEL: struct NonSendableStringStruct :
// CHECK-NOT: @unchecked Sendable
// CHECK: @available(*, unavailable)
// CHECK-NEXT: extension NonSendableStringStruct : @unchecked Sendable {

View File

@@ -19,8 +19,15 @@
#define ASSUME_NONSENDABLE_END
#endif
#define NS_ENUM(_type, _name) enum _name : _type _name; \
enum __attribute__((enum_extensibility(open))) _name : _type
#define NS_OPTIONS(_type, _name) enum _name : _type _name; \
enum __attribute__((enum_extensibility(open), flag_enum)) _name : _type
#define NS_ERROR_ENUM(_type, _name, _domain) \
enum _name : _type _name; enum __attribute__((ns_error_domain(_domain))) _name : _type
#define NS_STRING_ENUM __attribute((swift_newtype(enum)))
#define NS_EXTENSIBLE_STRING_ENUM __attribute__((swift_wrapper(struct)))
#define NS_EXTENSIBLE_STRING_ENUM __attribute__((swift_wrapper(struct)));
typedef NSString *Flavor NS_EXTENSIBLE_STRING_ENUM;
@protocol ServiceProvider
@@ -210,6 +217,35 @@ SENDABLE @interface AuditedSendable : NSObject @end
@interface AuditedNonSendable : NSObject @end
NONSENDABLE SENDABLE @interface AuditedBoth : NSObject @end
typedef NS_ENUM(unsigned, SendableEnum) {
SendableEnumFoo, SendableEnumBar
};
typedef NS_ENUM(unsigned, NonSendableEnum) {
NonSendableEnumFoo, NonSendableEnumBar
} NONSENDABLE;
typedef NS_OPTIONS(unsigned, SendableOptions) {
SendableOptionsFoo = 1 << 0, SendableOptionsBar = 1 << 1
};
typedef NS_OPTIONS(unsigned, NonSendableOptions) {
NonSendableOptionsFoo = 1 << 0, NonSendableOptionsBar = 1 << 1
} NONSENDABLE;
NSString *SendableErrorDomain, *NonSendableErrorDomain;
typedef NS_ERROR_ENUM(unsigned, SendableErrorCode, SendableErrorDomain) {
SendableErrorCodeFoo, SendableErrorCodeBar
};
typedef NS_ERROR_ENUM(unsigned, NonSendableErrorCode, NonSendableErrorDomain) {
NonSendableErrorCodeFoo, NonSendableErrorCodeBar
} NONSENDABLE;
// expected-warning@-3 {{cannot make error code type 'NonSendableErrorCode' non-sendable because Swift errors are always sendable}}
typedef NSString *SendableStringEnum NS_STRING_ENUM;
typedef NSString *NonSendableStringEnum NS_STRING_ENUM NONSENDABLE;
typedef NSString *SendableStringStruct NS_EXTENSIBLE_STRING_ENUM;
typedef NSString *NonSendableStringStruct NS_EXTENSIBLE_STRING_ENUM NONSENDABLE;
ASSUME_NONSENDABLE_END
#pragma clang assume_nonnull end

View File

@@ -17,7 +17,7 @@
// CHECK-NEXT: var rawValue: [[ENUM_UNDERLYING_TYPE]]
// CHECK-NEXT: typealias RawValue = [[ENUM_UNDERLYING_TYPE]]
// CHECK-NEXT: }
// CHECK-NEXT: @frozen enum PublicClosedEnum : [[ENUM_UNDERLYING_TYPE]] {
// CHECK-NEXT: @frozen enum PublicClosedEnum : [[ENUM_UNDERLYING_TYPE]], @unchecked Sendable {
// CHECK-NEXT: init?(rawValue: [[ENUM_UNDERLYING_TYPE]])
// CHECK-NEXT: var rawValue: [[ENUM_UNDERLYING_TYPE]] { get }
// CHECK-NEXT: typealias RawValue = [[ENUM_UNDERLYING_TYPE]]
@@ -25,7 +25,7 @@
// CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "value1")
// CHECK-NEXT: static var Value1: PublicPrivate.PublicClosedEnum { get }
// CHECK-NEXT: }
// CHECK-NEXT: enum PublicOpenEnum : [[ENUM_UNDERLYING_TYPE]] {
// CHECK-NEXT: enum PublicOpenEnum : [[ENUM_UNDERLYING_TYPE]], @unchecked Sendable {
// CHECK-NEXT: init?(rawValue: [[ENUM_UNDERLYING_TYPE]])
// CHECK-NEXT: var rawValue: [[ENUM_UNDERLYING_TYPE]] { get }
// CHECK-NEXT: typealias RawValue = [[ENUM_UNDERLYING_TYPE]]
@@ -33,7 +33,7 @@
// CHECK-NEXT: @available(swift, obsoleted: 3, renamed: "value1")
// CHECK-NEXT: static var Value1: PublicPrivate.PublicOpenEnum { get }
// CHECK-NEXT: }
// CHECK-NEXT: struct PublicFlagEnum : OptionSet {
// CHECK-NEXT: struct PublicFlagEnum : OptionSet, @unchecked Sendable {
// CHECK-NEXT: init(rawValue: [[ENUM_UNDERLYING_TYPE]])
// CHECK-NEXT: let rawValue: [[ENUM_UNDERLYING_TYPE]]
// CHECK-NEXT: typealias RawValue = [[ENUM_UNDERLYING_TYPE]]

View File

@@ -4,7 +4,7 @@
// RUN: %FileCheck -input-file=%t.response %s
// CHECK: struct MyError : CustomNSError, Hashable, Error {
// CHECK: enum Code : Int32, Equatable {
// CHECK: enum Code : Int32, @unchecked Sendable, Equatable {
// CHECK: case errFirst
// CHECK: case errSecond
// CHECK: }