Skip to content

Commit 8c839b6

Browse files
committed
Merge pull request #618 from kballard/enum-objc-name
Allow @objc(Name) on enums. rdar://problem/21930334
2 parents 4d09d11 + 1dc44b2 commit 8c839b6

File tree

12 files changed

+123
-20
lines changed

12 files changed

+123
-20
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2333,9 +2333,7 @@ ERROR(objc_setter_for_nonobjc_subscript,sema_tcd,none,
23332333
ERROR(objc_enum_generic,sema_tcd,none,
23342334
"'@objc' enum cannot be generic", ())
23352335
ERROR(objc_name_req_nullary,sema_objc,none,
2336-
"'@objc' %select{class|protocol|property}0 must have a simple name", (int))
2337-
ERROR(objc_name_enum,sema_objc,none,
2338-
"'@objc' enum cannot have a name", ())
2336+
"'@objc' %select{class|protocol|enum|property}0 must have a simple name", (int))
23392337
ERROR(objc_name_subscript,sema_objc,none,
23402338
"'@objc' subscript cannot have a name; did you mean to put "
23412339
"the name on the getter or setter?", ())

lib/ClangImporter/ImportDecl.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5083,8 +5083,9 @@ classifyEnum(clang::Preprocessor &pp, const clang::EnumDecl *decl) {
50835083
auto loc = decl->getLocStart();
50845084
if (loc.isMacroID()) {
50855085
StringRef MacroName = pp.getImmediateMacroName(loc);
5086-
if (MacroName == "CF_ENUM" || MacroName == "OBJC_ENUM" ||
5087-
MacroName == "SWIFT_ENUM" || MacroName == "__CF_NAMED_ENUM")
5086+
if (MacroName == "CF_ENUM" || MacroName == "__CF_NAMED_ENUM" ||
5087+
MacroName == "OBJC_ENUM" ||
5088+
MacroName == "SWIFT_ENUM" || MacroName == "SWIFT_ENUM_NAMED")
50885089
return EnumKind::Enum;
50895090
if (MacroName == "CF_OPTIONS" || MacroName == "OBJC_OPTIONS"
50905091
|| MacroName == "SWIFT_OPTIONS")

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ namespace {
7373

7474
static Identifier getNameForObjC(const NominalTypeDecl *NTD,
7575
CustomNamesOnly_t customNamesOnly = Normal) {
76-
// FIXME: Should we support renaming for enums too?
77-
assert(isa<ClassDecl>(NTD) || isa<ProtocolDecl>(NTD));
76+
assert(isa<ClassDecl>(NTD) || isa<ProtocolDecl>(NTD) || isa<EnumDecl>(NTD));
7877
if (auto objc = NTD->getAttrs().getAttribute<ObjCAttr>()) {
7978
if (auto name = objc->getName()) {
8079
assert(name->getNumSelectorPieces() == 1);
@@ -235,16 +234,33 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
235234

236235
void visitEnumDecl(EnumDecl *ED) {
237236
printDocumentationComment(ED);
238-
llvm::SmallString<32> scratch;
239-
os << "typedef SWIFT_ENUM(";
237+
os << "typedef ";
238+
Identifier customName = getNameForObjC(ED, CustomNamesOnly);
239+
if (customName.empty()) {
240+
os << "SWIFT_ENUM(";
241+
} else {
242+
os << "SWIFT_ENUM_NAMED(";
243+
}
240244
print(ED->getRawType(), OTK_None);
241-
os << ", " << ED->getName() << ") {\n";
245+
if (customName.empty()) {
246+
os << ", " << ED->getName();
247+
} else {
248+
os << ", " << customName
249+
<< ", \"" << ED->getName() << "\"";
250+
}
251+
os << ") {\n";
242252
for (auto Elt : ED->getAllElements()) {
243253
printDocumentationComment(Elt);
244254

245255
// Print the cases as the concatenation of the enum name with the case
246256
// name.
247-
os << " " << ED->getName() << Elt->getName();
257+
os << " ";
258+
if (customName.empty()) {
259+
os << ED->getName();
260+
} else {
261+
os << customName;
262+
}
263+
os << Elt->getName();
248264

249265
if (auto ILE = cast_or_null<IntegerLiteralExpr>(Elt->getRawValueExpr())) {
250266
os << " = ";
@@ -1614,7 +1630,7 @@ class ModuleWriter {
16141630
"#endif\n"
16151631
"#if !defined(SWIFT_CLASS)\n"
16161632
"# if defined(__has_attribute) && "
1617-
"__has_attribute(objc_subclassing_restricted) \n"
1633+
"__has_attribute(objc_subclassing_restricted)\n"
16181634
"# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) "
16191635
"__attribute__((objc_subclassing_restricted)) "
16201636
"SWIFT_CLASS_EXTRA\n"
@@ -1656,6 +1672,15 @@ class ModuleWriter {
16561672
"# define SWIFT_ENUM(_type, _name) "
16571673
"enum _name : _type _name; "
16581674
"enum SWIFT_ENUM_EXTRA _name : _type\n"
1675+
"# if defined(__has_feature) && "
1676+
"__has_feature(generalized_swift_name)\n"
1677+
"# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) "
1678+
"enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); "
1679+
"enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_EXTRA _name : _type\n"
1680+
"# else\n"
1681+
"# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) "
1682+
"SWIFT_ENUM(_type, _name)\n"
1683+
"# endif\n"
16591684
"#endif\n"
16601685
;
16611686
static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4,

lib/Sema/TypeCheckDecl.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7033,14 +7033,16 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
70337033
// If there is a name, check whether the kind of name is
70347034
// appropriate.
70357035
if (auto objcName = objcAttr->getName()) {
7036-
if (isa<ClassDecl>(D) || isa<ProtocolDecl>(D) || isa<VarDecl>(D)) {
7036+
if (isa<ClassDecl>(D) || isa<ProtocolDecl>(D) || isa<EnumDecl>(D) ||
7037+
isa<VarDecl>(D)) {
70377038
// Types and properties can only have nullary
70387039
// names. Complain and recover by chopping off everything
70397040
// after the first name.
70407041
if (objcName->getNumArgs() > 0) {
7041-
int which = isa<ClassDecl>(D)? 0
7042+
int which = isa<ClassDecl>(D)? 0
70427043
: isa<ProtocolDecl>(D)? 1
7043-
: 2;
7044+
: isa<EnumDecl>(D)? 2
7045+
: 3;
70447046
SourceLoc firstNameLoc = objcAttr->getNameLocs().front();
70457047
SourceLoc afterFirstNameLoc =
70467048
Lexer::getLocForEndOfToken(TC.Context.SourceMgr, firstNameLoc);
@@ -7050,10 +7052,6 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
70507052
ObjCSelector(TC.Context, 0, objcName->getSelectorPieces()[0]),
70517053
/*implicit=*/false);
70527054
}
7053-
} else if (isa<EnumDecl>(D)) {
7054-
// Enums don't have runtime names.
7055-
TC.diagnose(objcAttr->getLParenLoc(), diag::objc_name_enum);
7056-
const_cast<ObjCAttr *>(objcAttr)->clearName();
70577055
} else if (isa<SubscriptDecl>(D)) {
70587056
// Subscripts can never have names.
70597057
TC.diagnose(objcAttr->getLParenLoc(), diag::objc_name_subscript);

test/ClangModules/Inputs/enum-objc.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@import Foundation;
2+
3+
#define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
4+
#define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_EXTRA _name : _type
5+
6+
typedef SWIFT_ENUM_NAMED(NSInteger, ObjCEnum, "SwiftEnum") {
7+
ObjCEnumOne = 1,
8+
ObjCEnumTwo,
9+
ObjCEnumThree
10+
};

test/ClangModules/MixedSource/Inputs/resolve-cross-language/Base.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@ extension BaseClass {
2121
case Zung
2222
}
2323

24+
@objc(RenamedEnum) public enum SwiftEnum: CShort {
25+
case Quux
26+
case Corge
27+
case Grault
28+
}
29+
2430
@objc public class AnotherClass {
2531
@objc public func getEnum() -> BaseEnum { return .Zung }
32+
@objc public func getSwiftEnum() -> SwiftEnum { return .Quux }
2633
public init() {}
2734
}

test/ClangModules/MixedSource/Inputs/resolve-cross-language/BaseUser.framework/Headers/BaseUser.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ void useBaseProtoObjC(id <BaseProto>);
1313
@interface BaseClass (ObjCExtensions)
1414
- (void)categoryMethod;
1515
- (BaseEnum)baseEnumMethod:(BaseEnum)be;
16+
- (RenamedEnum)renamedEnumMethod:(RenamedEnum)se;
1617
@end
1718

1819
typedef OBJC_ENUM(unsigned char, BaseEnumObjC) {
@@ -27,8 +28,29 @@ void useBaseEnum(BaseEnum);
2728
BaseEnumObjC getBaseEnumObjC();
2829
void useBaseEnumObjC(BaseEnumObjC);
2930

31+
// temporarily redefine OBJC_ENUM because ClangImporter cares about the macro name
32+
#undef OBJC_ENUM
33+
#define OBJC_ENUM(_type, _name, SWIFT_NAME) enum _name : _type _name __attribute__((swift_name(SWIFT_NAME))); enum __attribute__((swift_name(SWIFT_NAME))) _name : _type
34+
35+
typedef OBJC_ENUM(unsigned char, RenamedEnumObjC, "SwiftEnumObjC") {
36+
RenamedEnumObjCQuux = RenamedEnumQuux,
37+
RenamedEnumObjCCorge = RenamedEnumCorge,
38+
RenamedEnumObjCGrault = RenamedEnumGrault,
39+
};
40+
41+
// put OBJC_ENUM back just in case
42+
#undef OBJC_ENUM
43+
#define OBJC_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
44+
45+
RenamedEnum getRenamedEnum();
46+
void useRenamedEnum(RenamedEnum);
47+
48+
RenamedEnumObjC getRenamedEnumObjC();
49+
void useRenamedEnumObjC(RenamedEnumObjC);
50+
3051
@protocol EnumProto
3152
- (BaseEnum)getEnum;
53+
- (RenamedEnum)getSwiftEnum;
3254
@end
3355

3456
@interface AnotherClass (EnumProtoConformance) <EnumProto>

test/ClangModules/MixedSource/resolve-cross-language.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ be = getBaseClass().baseEnumMethod(be)
2020
be = AnotherClass().getEnum()
2121
var beo: BaseEnumObjC = getBaseEnumObjC()
2222
useBaseEnumObjC(beo)
23+
var se: SwiftEnum = getRenamedEnum()
24+
useRenamedEnum(se)
25+
se = getBaseClass().renamedEnumMethod(se)
26+
se = AnotherClass().getSwiftEnum()
27+
var seo: SwiftEnumObjC = getRenamedEnumObjC()
28+
useRenamedEnumObjC(seo)
2329

2430
// Check type resolution.
2531
useBaseClass(getBaseClassObjC())
@@ -44,5 +50,17 @@ beo = BaseEnumObjC.Dah
4450

4551
var beoRaw: CUnsignedChar = beo.rawValue
4652

53+
se = SwiftEnum.Quux
54+
se = SwiftEnum.Corge
55+
se = SwiftEnum.Grault
56+
57+
var seRaw: CShort = se.rawValue
58+
59+
seo = SwiftEnumObjC.Quux
60+
seo = SwiftEnumObjC.Corge
61+
seo = SwiftEnumObjC.Grault
62+
63+
var seoRaw: CUnsignedChar = seo.rawValue
64+
4765
// Make sure we're actually parsing stuff.
4866
useBaseClass() // expected-error{{missing argument for parameter #1}}

test/ClangModules/enum-objc.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %target-swift-frontend -emit-sil %s -import-objc-header %S/Inputs/enum-objc.h -verify
2+
3+
// REQUIRES: objc_interop
4+
5+
func test(value: SwiftEnum) {
6+
switch value {
7+
case .One: break
8+
case .Two: break
9+
case .Three: break
10+
} // no error
11+
}

test/Parse/objc_enum.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
case Zim, Zang, Zung
99
}
1010

11-
@objc(EnumRuntimeName) enum RuntimeNamed: Int { // expected-error{{'@objc' enum cannot have a name}}
11+
@objc(EnumRuntimeName) enum RuntimeNamed: Int {
1212
case Zim, Zang, Zung
1313
}
1414

test/PrintAsObjC/enums.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ import Foundation
2626
@objc func acceptPlainEnum(_: NSMalformedEnumMissingTypedef) {}
2727
}
2828

29+
// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(NSInteger, ObjcEnumNamed, "EnumNamed") {
30+
// CHECK-NEXT: ObjcEnumNamedA = 0,
31+
// CHECK-NEXT: ObjcEnumNamedB = 1,
32+
// CHECK-NEXT: ObjcEnumNamedC = 2,
33+
// CHECK-NEXT: };
34+
35+
@objc(ObjcEnumNamed) enum EnumNamed: Int {
36+
case A, B, C
37+
}
38+
2939
// CHECK-LABEL: typedef SWIFT_ENUM(unsigned int, ExplicitValues) {
3040
// CHECK-NEXT: ExplicitValuesZim = 0,
3141
// CHECK-NEXT: ExplicitValuesZang = 219,

test/attr/attr_objc.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1749,6 +1749,9 @@ class BadClass1 { }
17491749
@objc(Protocol:) // expected-error{{'@objc' protocol must have a simple name}}{{15-16=}}
17501750
protocol BadProto1 { }
17511751

1752+
@objc(Enum:) // expected-error{{'@objc' enum must have a simple name}}{{11-12=}}
1753+
enum BadEnum1: Int { case X }
1754+
17521755
class BadClass2 {
17531756
@objc(badprop:foo:wibble:) // expected-error{{'@objc' property must have a simple name}}{{16-28=}}
17541757
var badprop: Int = 5

0 commit comments

Comments
 (0)