Skip to content

Allow @objc(Name) on enums #618

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 24, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -2333,9 +2333,7 @@ ERROR(objc_setter_for_nonobjc_subscript,sema_tcd,none,
ERROR(objc_enum_generic,sema_tcd,none,
"'@objc' enum cannot be generic", ())
ERROR(objc_name_req_nullary,sema_objc,none,
"'@objc' %select{class|protocol|property}0 must have a simple name", (int))
ERROR(objc_name_enum,sema_objc,none,
"'@objc' enum cannot have a name", ())
"'@objc' %select{class|protocol|enum|property}0 must have a simple name", (int))
ERROR(objc_name_subscript,sema_objc,none,
"'@objc' subscript cannot have a name; did you mean to put "
"the name on the getter or setter?", ())
Expand Down
5 changes: 3 additions & 2 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5083,8 +5083,9 @@ classifyEnum(clang::Preprocessor &pp, const clang::EnumDecl *decl) {
auto loc = decl->getLocStart();
if (loc.isMacroID()) {
StringRef MacroName = pp.getImmediateMacroName(loc);
if (MacroName == "CF_ENUM" || MacroName == "OBJC_ENUM" ||
MacroName == "SWIFT_ENUM" || MacroName == "__CF_NAMED_ENUM")
if (MacroName == "CF_ENUM" || MacroName == "__CF_NAMED_ENUM" ||
MacroName == "OBJC_ENUM" ||
MacroName == "SWIFT_ENUM" || MacroName == "SWIFT_ENUM_NAMED")
return EnumKind::Enum;
if (MacroName == "CF_OPTIONS" || MacroName == "OBJC_OPTIONS"
|| MacroName == "SWIFT_OPTIONS")
Expand Down
39 changes: 32 additions & 7 deletions lib/PrintAsObjC/PrintAsObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ namespace {

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

void visitEnumDecl(EnumDecl *ED) {
printDocumentationComment(ED);
llvm::SmallString<32> scratch;
os << "typedef SWIFT_ENUM(";
os << "typedef ";
Identifier customName = getNameForObjC(ED, CustomNamesOnly);
if (customName.empty()) {
os << "SWIFT_ENUM(";
} else {
os << "SWIFT_ENUM_NAMED(";
}
print(ED->getRawType(), OTK_None);
os << ", " << ED->getName() << ") {\n";
if (customName.empty()) {
os << ", " << ED->getName();
} else {
os << ", " << customName
<< ", \"" << ED->getName() << "\"";
}
os << ") {\n";
for (auto Elt : ED->getAllElements()) {
printDocumentationComment(Elt);

// Print the cases as the concatenation of the enum name with the case
// name.
os << " " << ED->getName() << Elt->getName();
os << " ";
if (customName.empty()) {
os << ED->getName();
} else {
os << customName;
}
os << Elt->getName();

if (auto ILE = cast_or_null<IntegerLiteralExpr>(Elt->getRawValueExpr())) {
os << " = ";
Expand Down Expand Up @@ -1614,7 +1630,7 @@ class ModuleWriter {
"#endif\n"
"#if !defined(SWIFT_CLASS)\n"
"# if defined(__has_attribute) && "
"__has_attribute(objc_subclassing_restricted) \n"
"__has_attribute(objc_subclassing_restricted)\n"
"# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) "
"__attribute__((objc_subclassing_restricted)) "
"SWIFT_CLASS_EXTRA\n"
Expand Down Expand Up @@ -1656,6 +1672,15 @@ class ModuleWriter {
"# define SWIFT_ENUM(_type, _name) "
"enum _name : _type _name; "
"enum SWIFT_ENUM_EXTRA _name : _type\n"
"# if defined(__has_feature) && "
"__has_feature(generalized_swift_name)\n"
"# 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\n"
"# else\n"
"# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME) "
"SWIFT_ENUM(_type, _name)\n"
"# endif\n"
"#endif\n"
;
static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4,
Expand Down
12 changes: 5 additions & 7 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7033,14 +7033,16 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
// If there is a name, check whether the kind of name is
// appropriate.
if (auto objcName = objcAttr->getName()) {
if (isa<ClassDecl>(D) || isa<ProtocolDecl>(D) || isa<VarDecl>(D)) {
if (isa<ClassDecl>(D) || isa<ProtocolDecl>(D) || isa<EnumDecl>(D) ||
isa<VarDecl>(D)) {
// Types and properties can only have nullary
// names. Complain and recover by chopping off everything
// after the first name.
if (objcName->getNumArgs() > 0) {
int which = isa<ClassDecl>(D)? 0
int which = isa<ClassDecl>(D)? 0
: isa<ProtocolDecl>(D)? 1
: 2;
: isa<EnumDecl>(D)? 2
: 3;
SourceLoc firstNameLoc = objcAttr->getNameLocs().front();
SourceLoc afterFirstNameLoc =
Lexer::getLocForEndOfToken(TC.Context.SourceMgr, firstNameLoc);
Expand All @@ -7050,10 +7052,6 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
ObjCSelector(TC.Context, 0, objcName->getSelectorPieces()[0]),
/*implicit=*/false);
}
} else if (isa<EnumDecl>(D)) {
// Enums don't have runtime names.
TC.diagnose(objcAttr->getLParenLoc(), diag::objc_name_enum);
const_cast<ObjCAttr *>(objcAttr)->clearName();
} else if (isa<SubscriptDecl>(D)) {
// Subscripts can never have names.
TC.diagnose(objcAttr->getLParenLoc(), diag::objc_name_subscript);
Expand Down
10 changes: 10 additions & 0 deletions test/ClangModules/Inputs/enum-objc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@import Foundation;

#define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X)))
#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

typedef SWIFT_ENUM_NAMED(NSInteger, ObjCEnum, "SwiftEnum") {
ObjCEnumOne = 1,
ObjCEnumTwo,
ObjCEnumThree
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@ extension BaseClass {
case Zung
}

@objc(RenamedEnum) public enum SwiftEnum: CShort {
case Quux
case Corge
case Grault
}

@objc public class AnotherClass {
@objc public func getEnum() -> BaseEnum { return .Zung }
@objc public func getSwiftEnum() -> SwiftEnum { return .Quux }
public init() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ void useBaseProtoObjC(id <BaseProto>);
@interface BaseClass (ObjCExtensions)
- (void)categoryMethod;
- (BaseEnum)baseEnumMethod:(BaseEnum)be;
- (RenamedEnum)renamedEnumMethod:(RenamedEnum)se;
@end

typedef OBJC_ENUM(unsigned char, BaseEnumObjC) {
Expand All @@ -27,8 +28,29 @@ void useBaseEnum(BaseEnum);
BaseEnumObjC getBaseEnumObjC();
void useBaseEnumObjC(BaseEnumObjC);

// temporarily redefine OBJC_ENUM because ClangImporter cares about the macro name
#undef OBJC_ENUM
#define OBJC_ENUM(_type, _name, SWIFT_NAME) enum _name : _type _name __attribute__((swift_name(SWIFT_NAME))); enum __attribute__((swift_name(SWIFT_NAME))) _name : _type

typedef OBJC_ENUM(unsigned char, RenamedEnumObjC, "SwiftEnumObjC") {
RenamedEnumObjCQuux = RenamedEnumQuux,
RenamedEnumObjCCorge = RenamedEnumCorge,
RenamedEnumObjCGrault = RenamedEnumGrault,
};

// put OBJC_ENUM back just in case
#undef OBJC_ENUM
#define OBJC_ENUM(_type, _name) enum _name : _type _name; enum _name : _type

RenamedEnum getRenamedEnum();
void useRenamedEnum(RenamedEnum);

RenamedEnumObjC getRenamedEnumObjC();
void useRenamedEnumObjC(RenamedEnumObjC);

@protocol EnumProto
- (BaseEnum)getEnum;
- (RenamedEnum)getSwiftEnum;
@end

@interface AnotherClass (EnumProtoConformance) <EnumProto>
Expand Down
18 changes: 18 additions & 0 deletions test/ClangModules/MixedSource/resolve-cross-language.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ be = getBaseClass().baseEnumMethod(be)
be = AnotherClass().getEnum()
var beo: BaseEnumObjC = getBaseEnumObjC()
useBaseEnumObjC(beo)
var se: SwiftEnum = getRenamedEnum()
useRenamedEnum(se)
se = getBaseClass().renamedEnumMethod(se)
se = AnotherClass().getSwiftEnum()
var seo: SwiftEnumObjC = getRenamedEnumObjC()
useRenamedEnumObjC(seo)

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

var beoRaw: CUnsignedChar = beo.rawValue

se = SwiftEnum.Quux
se = SwiftEnum.Corge
se = SwiftEnum.Grault

var seRaw: CShort = se.rawValue

seo = SwiftEnumObjC.Quux
seo = SwiftEnumObjC.Corge
seo = SwiftEnumObjC.Grault

var seoRaw: CUnsignedChar = seo.rawValue

// Make sure we're actually parsing stuff.
useBaseClass() // expected-error{{missing argument for parameter #1}}
11 changes: 11 additions & 0 deletions test/ClangModules/enum-objc.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// RUN: %target-swift-frontend -emit-sil %s -import-objc-header %S/Inputs/enum-objc.h -verify

// REQUIRES: objc_interop

func test(value: SwiftEnum) {
switch value {
case .One: break
case .Two: break
case .Three: break
} // no error
}
2 changes: 1 addition & 1 deletion test/Parse/objc_enum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
case Zim, Zang, Zung
}

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

Expand Down
10 changes: 10 additions & 0 deletions test/PrintAsObjC/enums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ import Foundation
@objc func acceptPlainEnum(_: NSMalformedEnumMissingTypedef) {}
}

// CHECK-LABEL: typedef SWIFT_ENUM_NAMED(NSInteger, ObjcEnumNamed, "EnumNamed") {
// CHECK-NEXT: ObjcEnumNamedA = 0,
// CHECK-NEXT: ObjcEnumNamedB = 1,
// CHECK-NEXT: ObjcEnumNamedC = 2,
// CHECK-NEXT: };

@objc(ObjcEnumNamed) enum EnumNamed: Int {
case A, B, C
}

// CHECK-LABEL: typedef SWIFT_ENUM(unsigned int, ExplicitValues) {
// CHECK-NEXT: ExplicitValuesZim = 0,
// CHECK-NEXT: ExplicitValuesZang = 219,
Expand Down
3 changes: 3 additions & 0 deletions test/attr/attr_objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1749,6 +1749,9 @@ class BadClass1 { }
@objc(Protocol:) // expected-error{{'@objc' protocol must have a simple name}}{{15-16=}}
protocol BadProto1 { }

@objc(Enum:) // expected-error{{'@objc' enum must have a simple name}}{{11-12=}}
enum BadEnum1: Int { case X }

class BadClass2 {
@objc(badprop:foo:wibble:) // expected-error{{'@objc' property must have a simple name}}{{16-28=}}
var badprop: Int = 5
Expand Down